From b52ffb8db98b869b3a442ee14c2620d4d29da02f Mon Sep 17 00:00:00 2001 From: jpgiannetti Date: Sun, 8 Feb 2026 17:49:12 +0100 Subject: [PATCH] =?UTF-8?q?feat(rgpd):=20compl=C3=A9ter=20documentation=20?= =?UTF-8?q?RGPD=20avec=2012=20nouvelles=20sections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Règles RGPD (docs/domains/_shared/rules/rgpd.md): - Ajouter sections 13.11-13.22 (droits utilisateurs, mineurs, sécurité) - Droit de rectification, opposition, limitation du traitement - Gestion des mineurs: 13 ans minimum + consentement parental 13-15 ans - Protection renforcée: RoadWave Kids pour < 13 ans - Sécurité: chiffrement multi-niveaux, procédure breach 72h CNIL - Politique de confidentialité avec versioning - Sous-traitants, DPIA, délais de réponse Entités (6 nouvelles): - PARENTAL_CONSENTS + PARENTAL_CONTROLS (workflow 13-15 ans) - PRIVACY_POLICY_VERSIONS + USER_POLICY_ACCEPTANCES - ACCOUNT_DELETIONS (grace period 30j) - BREACH_INCIDENTS + BREACH_AFFECTED_USERS - USER_PROFILE_HISTORY (audit trail rectification) - DATA_RETENTION_LOGS (purge 5 ans) Diagrammes séquences (5 nouveaux): - Consentement parental avec validation email - Anonymisation GPS automatique après 24h - Notification breach CNIL (procédure 72h) - Export données asynchrone - Suppression compte avec grace period Cycles de vie (3 nouveaux + 1 enrichi): - parental-consent-lifecycle.md - breach-incident-lifecycle.md - account-deletion-lifecycle.md - user-account-lifecycle.md (ajout états mineurs, frozen) Features BDD (4 nouvelles, 195 scénarios RGPD): - minors-protection.feature (9 scénarios) - data-security.feature (12 scénarios) - privacy-policy.feature (8 scénarios) - user-rights.feature (8 scénarios) Infrastructure: - Réorganiser docs générées: docs/bdd + output → generated/bdd + generated/pdf - Mettre à jour mkdocs.yml, Makefile, scripts Python - Ajouter /generated/ au .gitignore --- .gitignore | 2 +- Makefile | 2 +- .../_shared/entities/account-deletions.md | 40 + .../_shared/entities/breach-incidents.md | 51 + .../_shared/entities/data-retention-logs.md | 44 + .../_shared/entities/entities-overview.md | 104 + .../_shared/entities/parental-consents.md | 49 + .../entities/privacy-policy-versions.md | 41 + .../_shared/entities/user-profile-history.md | 38 + .../rgpd-compliance/data-security.feature | 110 + .../rgpd-compliance/minors-protection.feature | 112 + .../rgpd-compliance/privacy-policy.feature | 99 + .../rgpd-compliance/user-rights.feature | 77 + docs/domains/_shared/rules/rgpd.md | 318 +- .../_shared/sequences/anonymisation-gps.md | 55 + .../sequences/consentement-parental.md | 43 + .../_shared/sequences/export-donnees.md | 65 + .../_shared/sequences/notification-breach.md | 63 + .../_shared/sequences/suppression-compte.md | 51 + .../states/account-deletion-lifecycle.md | 42 + .../states/breach-incident-lifecycle.md | 44 + .../states/parental-consent-lifecycle.md | 32 + .../_shared/states/user-account-lifecycle.md | 30 +- mkdocs.yml | 30 + output/RoadWave_Documentation_protected.pdf | 12236 ----- output/documentation_complete.html | 30529 ------------- output/documentation_complete.md | 37888 ---------------- scripts/generate-bdd-docs.py | 2 +- scripts/generate-pdf-docs.py | 4 +- 29 files changed, 1498 insertions(+), 80703 deletions(-) create mode 100644 docs/domains/_shared/entities/account-deletions.md create mode 100644 docs/domains/_shared/entities/breach-incidents.md create mode 100644 docs/domains/_shared/entities/data-retention-logs.md create mode 100644 docs/domains/_shared/entities/parental-consents.md create mode 100644 docs/domains/_shared/entities/privacy-policy-versions.md create mode 100644 docs/domains/_shared/entities/user-profile-history.md create mode 100644 docs/domains/_shared/features/rgpd-compliance/data-security.feature create mode 100644 docs/domains/_shared/features/rgpd-compliance/minors-protection.feature create mode 100644 docs/domains/_shared/features/rgpd-compliance/privacy-policy.feature create mode 100644 docs/domains/_shared/features/rgpd-compliance/user-rights.feature create mode 100644 docs/domains/_shared/sequences/anonymisation-gps.md create mode 100644 docs/domains/_shared/sequences/consentement-parental.md create mode 100644 docs/domains/_shared/sequences/export-donnees.md create mode 100644 docs/domains/_shared/sequences/notification-breach.md create mode 100644 docs/domains/_shared/sequences/suppression-compte.md create mode 100644 docs/domains/_shared/states/account-deletion-lifecycle.md create mode 100644 docs/domains/_shared/states/breach-incident-lifecycle.md create mode 100644 docs/domains/_shared/states/parental-consent-lifecycle.md delete mode 100644 output/RoadWave_Documentation_protected.pdf delete mode 100644 output/documentation_complete.html delete mode 100644 output/documentation_complete.md diff --git a/.gitignore b/.gitignore index 472563a..e44de68 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,4 @@ config/*.local.yaml # MkDocs site/ .cache/ -docs/bdd/ +docs/generated/ diff --git a/Makefile b/Makefile index 13200f7..e45ae39 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ clean: ## docs-clean: Remove generated documentation (BDD docs and PDF) docs-clean: @echo "$(YELLOW)Cleaning generated documentation...$(NC)" - @rm -rf docs/bdd/ output/RoadWave_Documentation.pdf + @rm -rf docs/generated/ @docker rmi roadwave-pdf-generator 2>/dev/null || true @echo "$(GREEN)✓ Documentation cleaned$(NC)" diff --git a/docs/domains/_shared/entities/account-deletions.md b/docs/domains/_shared/entities/account-deletions.md new file mode 100644 index 0000000..904e6fc --- /dev/null +++ b/docs/domains/_shared/entities/account-deletions.md @@ -0,0 +1,40 @@ +# Account Deletions + +📖 Suppressions de compte avec grace period 30 jours (Article 17 RGPD) + +## Diagramme + +```mermaid +erDiagram + USERS ||--o| ACCOUNT_DELETIONS : "demande" + + ACCOUNT_DELETIONS { + uuid id PK + uuid user_id FK + string status + string cancellation_token + timestamp requested_at + timestamp effective_at "requested_at + 30j" + timestamp cancelled_at + timestamp deleted_at + string deletion_reason + json deleted_data_summary + } +``` + +## Légende + +**Statuts** : +- `pending`: Grace period actif (30j), compte désactivé, annulation possible +- `cancelled`: Utilisateur a annulé via lien email +- `completed`: Suppression effective réalisée après 30j + +**Processus** : +1. Demande → compte désactivé, contenus cachés +2. Email avec `cancellation_token` (valide 30j) +3. Si annulation → `status = cancelled`, compte réactivé +4. Si 30j écoulés → job cron supprime données, anonymise contenus + +**Données supprimées** : +- Profil utilisateur, historique GPS/écoute, sessions +- Contenus créés : anonymisés (`créateur = "Utilisateur supprimé"`) diff --git a/docs/domains/_shared/entities/breach-incidents.md b/docs/domains/_shared/entities/breach-incidents.md new file mode 100644 index 0000000..d9738d8 --- /dev/null +++ b/docs/domains/_shared/entities/breach-incidents.md @@ -0,0 +1,51 @@ +# Breach Incidents + +📖 Registre violations de données (Article 33 RGPD) + +## Diagramme + +```mermaid +erDiagram + BREACH_INCIDENTS ||--o{ BREACH_AFFECTED_USERS : "impacte" + USERS ||--o{ BREACH_AFFECTED_USERS : "est impacté" + + BREACH_INCIDENTS { + uuid id PK + string severity "low/medium/high/critical" + text description + json data_categories_affected + int estimated_users_count + timestamp detected_at + timestamp contained_at + timestamp cnil_notified_at + timestamp users_notified_at + text mitigation_actions + boolean cnil_notification_required + boolean user_notification_required + } + + BREACH_AFFECTED_USERS { + uuid id PK + uuid breach_id FK + uuid user_id FK + timestamp notified_at + string notification_channel "email/push/sms" + } +``` + +## Légende + +**Sévérité** : +- `low`: Pas de notification requise (mesures techniques suffisantes) +- `medium`: Notification CNIL uniquement +- `high`: Notification CNIL + utilisateurs +- `critical`: Notification immédiate tous canaux + SMS fondateur + +**Timeline 72h** : +- H+0 : Détection, confinement +- H+24 : Évaluation gravité +- H+48 : Notification CNIL si requis +- H+72 : Notification utilisateurs si risque élevé + +**Catégories de données** : +- `data_categories_affected`: JSON `["gps", "email", "listening_history"]` diff --git a/docs/domains/_shared/entities/data-retention-logs.md b/docs/domains/_shared/entities/data-retention-logs.md new file mode 100644 index 0000000..326478e --- /dev/null +++ b/docs/domains/_shared/entities/data-retention-logs.md @@ -0,0 +1,44 @@ +# Data Retention Logs + +📖 Logs purges automatiques inactivité (Article 5 RGPD - Minimisation) + +## Diagramme + +```mermaid +erDiagram + DATA_RETENTION_LOGS { + uuid id PK + string action_type + int users_processed + int users_warned + int users_deleted + json details + timestamp executed_at + bigint execution_duration_ms + } +``` + +## Légende + +**Action types** : +- `check_inactive`: Vérification quotidienne comptes inactifs > 5 ans +- `send_warnings`: Envoi notifications (90j/30j/7j avant suppression) +- `delete_accounts`: Suppression effective comptes inactifs + +**Règles de conservation** : +- Auditeur : 5 ans inactivité → suppression +- Créateur actif : jamais (tant que contenus écoutés) +- Créateur inactif : 5 ans + 2 ans sans écoute → suppression + +**Details JSON** : +```json +{ + "threshold_date": "2021-02-08", + "user_ids_deleted": ["uuid1", "uuid2"], + "notifications_sent": { + "90_days": 15, + "30_days": 8, + "7_days": 3 + } +} +``` diff --git a/docs/domains/_shared/entities/entities-overview.md b/docs/domains/_shared/entities/entities-overview.md index ea03df7..d9b3f28 100644 --- a/docs/domains/_shared/entities/entities-overview.md +++ b/docs/domains/_shared/entities/entities-overview.md @@ -16,6 +16,15 @@ erDiagram USERS ||--o{ INTEREST_GAUGES : "possède" USERS ||--o{ REPORTS : "signale" USERS ||--o{ DATA_EXPORTS : "demande" + USERS ||--o{ PARENTAL_CONSENTS : "a" + USERS ||--o{ ACCOUNT_DELETIONS : "demande" + USERS ||--o{ USER_PROFILE_HISTORY : "modifie" + + PARENTAL_CONSENTS ||--|| PARENTAL_CONTROLS : "configure" + PRIVACY_POLICY_VERSIONS ||--o{ USER_POLICY_ACCEPTANCES : "acceptée par" + USERS ||--o{ USER_POLICY_ACCEPTANCES : "accepte" + BREACH_INCIDENTS ||--o{ BREACH_AFFECTED_USERS : "impacte" + USERS ||--o{ BREACH_AFFECTED_USERS : "est impacté" CONTENTS ||--o{ LISTENING_HISTORY : "écouté" CONTENTS }o--|| USERS : "créé par" @@ -161,6 +170,80 @@ erDiagram timestamp generated_at timestamp expires_at } + + PARENTAL_CONSENTS { + uuid id PK + uuid user_id FK + string parent_email + boolean validated + timestamp validated_at + timestamp revoked_at + } + + PARENTAL_CONTROLS { + uuid id PK + uuid parental_consent_id FK + boolean gps_enabled + boolean messaging_enabled + boolean content_16plus_enabled + } + + PRIVACY_POLICY_VERSIONS { + uuid id PK + string version + boolean major_change + timestamp effective_date + } + + USER_POLICY_ACCEPTANCES { + uuid id PK + uuid user_id FK + uuid policy_version_id FK + boolean accepted + timestamp accepted_at + } + + ACCOUNT_DELETIONS { + uuid id PK + uuid user_id FK + string status + timestamp requested_at + timestamp effective_at + timestamp deleted_at + } + + BREACH_INCIDENTS { + uuid id PK + string severity + int estimated_users_count + timestamp detected_at + timestamp cnil_notified_at + boolean user_notification_required + } + + BREACH_AFFECTED_USERS { + uuid id PK + uuid breach_id FK + uuid user_id FK + timestamp notified_at + } + + USER_PROFILE_HISTORY { + uuid id PK + uuid user_id FK + string field_name + text old_value + text new_value + timestamp changed_at + } + + DATA_RETENTION_LOGS { + uuid id PK + string action_type + int users_processed + int users_deleted + timestamp executed_at + } ``` ## Légende @@ -205,6 +288,27 @@ erDiagram - **DATA_EXPORTS** : Exports de données utilisateur ([détails](exports.md)) - Portabilité RGPD Article 20, délai 48h max +- **PARENTAL_CONSENTS** : Consentements parentaux 13-15 ans ([détails](parental-consents.md)) + - Workflow validation email parent, token expire 7j + +- **PARENTAL_CONTROLS** : Paramètres contrôle parental ([détails](parental-consents.md)) + - GPS, messagerie, contenus +16 configurables par parent + +- **PRIVACY_POLICY_VERSIONS** : Versioning politique confidentialité ([détails](privacy-policy-versions.md)) + - Popup si changement majeur, historique acceptations + +- **ACCOUNT_DELETIONS** : Suppressions avec grace period 30j ([détails](account-deletions.md)) + - Annulation possible, suppression effective automatique + +- **BREACH_INCIDENTS** : Registre violations de données ([détails](breach-incidents.md)) + - Procédure 72h CNIL, notification utilisateurs si risque élevé + +- **USER_PROFILE_HISTORY** : Audit trail modifications profil ([détails](user-profile-history.md)) + - Droit rectification Article 16, preuve légale + +- **DATA_RETENTION_LOGS** : Logs purges automatiques ([détails](data-retention-logs.md)) + - Inactivité 5 ans, notifications 90j/30j/7j + **Entités recommandation & modération** : - **INTEREST_GAUGES** : Jauges de centres d'intérêt ([détails](interest-gauges.md)) diff --git a/docs/domains/_shared/entities/parental-consents.md b/docs/domains/_shared/entities/parental-consents.md new file mode 100644 index 0000000..e19e68e --- /dev/null +++ b/docs/domains/_shared/entities/parental-consents.md @@ -0,0 +1,49 @@ +# Parental Consents + +📖 Consentements parentaux pour utilisateurs 13-15 ans (Article 8 RGPD) + +## Diagramme + +```mermaid +erDiagram + USERS ||--o{ PARENTAL_CONSENTS : "a" + PARENTAL_CONSENTS ||--|| PARENTAL_CONTROLS : "configure" + + PARENTAL_CONSENTS { + uuid id PK + uuid user_id FK "Ado 13-15 ans" + string parent_email + string validation_token + boolean validated + timestamp token_expires_at + timestamp validated_at + inet parent_ip + string parent_user_agent + timestamp revoked_at + string revocation_reason + } + + PARENTAL_CONTROLS { + uuid id PK + uuid parental_consent_id FK + boolean gps_enabled + boolean messaging_enabled + boolean content_16plus_enabled + json weekly_digest_config + timestamp updated_at + } +``` + +## Légende + +**Workflow** : +1. Ado saisit email parent → `validation_token` généré (expire 7j) +2. Parent clique lien → `validated = true` +3. Parent configure `PARENTAL_CONTROLS` +4. Révocation possible → `revoked_at` renseigné + +**Restrictions par défaut (13-15 ans)** : +- `gps_enabled`: `false` (GeoIP uniquement) +- `messaging_enabled`: `false` +- `content_16plus_enabled`: `false` +- Dashboard parent : notifications hebdomadaires activité diff --git a/docs/domains/_shared/entities/privacy-policy-versions.md b/docs/domains/_shared/entities/privacy-policy-versions.md new file mode 100644 index 0000000..70f7a15 --- /dev/null +++ b/docs/domains/_shared/entities/privacy-policy-versions.md @@ -0,0 +1,41 @@ +# Privacy Policy Versions + +📖 Versioning politique de confidentialité (Articles 13-14 RGPD) + +## Diagramme + +```mermaid +erDiagram + PRIVACY_POLICY_VERSIONS ||--o{ USER_POLICY_ACCEPTANCES : "acceptée par" + USERS ||--o{ USER_POLICY_ACCEPTANCES : "accepte" + + PRIVACY_POLICY_VERSIONS { + uuid id PK + string version "v1.0, v2.0, etc." + text content_markdown + boolean major_change + text changelog + timestamp effective_date + timestamp created_at + } + + USER_POLICY_ACCEPTANCES { + uuid id PK + uuid user_id FK + uuid policy_version_id FK + boolean accepted + timestamp accepted_at + inet ip_address + } +``` + +## Légende + +**Versioning** : +- `major_change`: `true` → popup obligatoire pour tous les utilisateurs +- `major_change`: `false` → notification simple +- Fichier source : `docs/legal/politique-confidentialite.md` (versionné Git) + +**Popup si changement majeur** : +- Utilisateur doit accepter nouvelle version pour continuer +- Refus → compte gelé (lecture seule) diff --git a/docs/domains/_shared/entities/user-profile-history.md b/docs/domains/_shared/entities/user-profile-history.md new file mode 100644 index 0000000..d2a4ff7 --- /dev/null +++ b/docs/domains/_shared/entities/user-profile-history.md @@ -0,0 +1,38 @@ +# User Profile History + +📖 Audit trail modifications profil (Article 16 RGPD - Droit de rectification) + +## Diagramme + +```mermaid +erDiagram + USERS ||--o{ USER_PROFILE_HISTORY : "modifie" + + USER_PROFILE_HISTORY { + uuid id PK + uuid user_id FK + string field_name "email/username/bio/etc." + text old_value + text new_value + string change_reason "user_edit/admin_correction/gdpr_request" + inet ip_address + timestamp changed_at + } +``` + +## Légende + +**Champs trackés** : +- `email`: Re-vérification requise +- `username`: Limite 1 changement/30j +- `bio`, `avatar_url`, `date_of_birth` + +**Change reasons** : +- `user_edit`: Modification self-service utilisateur +- `admin_correction`: Correction par admin +- `gdpr_request`: Suite demande RGPD formelle + +**Audit** : +- Historique complet conservé (preuve légale) +- Accessible utilisateur : "Historique de mes modifications" +- Accessible DPO : investigations diff --git a/docs/domains/_shared/features/rgpd-compliance/data-security.feature b/docs/domains/_shared/features/rgpd-compliance/data-security.feature new file mode 100644 index 0000000..9b0b3d9 --- /dev/null +++ b/docs/domains/_shared/features/rgpd-compliance/data-security.feature @@ -0,0 +1,110 @@ +# language: fr + +@privacy @rgpd @security +Fonctionnalité: Sécurité des données (Article 32 RGPD) + En tant que responsable de traitement + Je veux garantir la sécurité des données personnelles + Afin de prévenir les violations et fuites + + # Chiffrement transport + @encryption @tls + Scénario: TLS 1.3 obligatoire pour toutes les communications + Quand un client tente de se connecter à l'API + Alors la connexion utilise TLS 1.3 uniquement + Et les protocoles TLS 1.0, 1.1, 1.2 sont refusés + Et le certificat est valide et à jour + + # Chiffrement stockage + @encryption @database + Scénario: Encryption at rest pour la base de données + Étant donné que PostgreSQL stocke des données utilisateurs + Alors le chiffrement AES-256 at rest est activé + Et les backups sont également chiffrés AES-256 + Et les backups sont stockés offsite (règle 3-2-1) + + # Tokens JWT sécurisés + @encryption @jwt + Scénario: Rotation des clés JWT tous les 90 jours + Étant donné que l'API utilise des tokens JWT RS256 + Quand 90 jours se sont écoulés depuis la dernière rotation + Alors une nouvelle paire de clés RSA est générée + Et l'ancienne clé reste valide 7 jours (overlap) + Et tous les tokens sont progressivement re-signés + + # CDN avec signed URLs + @cdn @signed-urls + Scénario: URLs signées expirables pour les fichiers audio + Quand un utilisateur demande à écouter un contenu + Alors l'API génère une signed URL valide 1 heure + Et l'URL contient un token HMAC + Et après expiration, l'URL retourne 403 Forbidden + + # Détection breach + @breach @monitoring + Scénario: Alerte immédiate si erreurs critiques backend + Quand une erreur critique survient dans l'API (500, crash) + Alors Sentry déclenche une alerte Discord/Slack immédiate + Et l'équipe est notifiée en temps réel + Et les logs sont consultables dans Grafana + + @breach @monitoring + Scénario: Alerte si pic de requêtes anormal (potentiel DDoS) + Étant donné que le trafic habituel est ~1000 req/min + Quand le trafic atteint 10000 req/min + Alors Grafana déclenche une alerte email + Et l'équipe vérifie s'il s'agit d'une attaque + + @breach @monitoring + Scénario: Alerte si accès DB non autorisé + Quand une connexion PostgreSQL provient d'une IP non whitelistée + Alors PostgreSQL bloque la connexion + Et un SMS est envoyé au fondateur + Et l'IP est loggée pour investigation + + # Procédure breach 72h CNIL + @breach @procedure + Scénario: Notification CNIL si violation de données sous 72h + Étant donné qu'une violation de données est détectée + Et que des données GPS utilisateurs ont fuité + Quand l'équipe évalue la gravité + Alors le runbook "docs/rgpd/procedure-breach.md" est suivi: + | Étape | Délai | Action | + | 1 | H+0 | Détection et confinement | + | 2 | H+24 | Évaluation gravité et impact | + | 3 | H+48 | Notification CNIL si risque | + | 4 | H+72 | Notification utilisateurs si élevé| + Et un email pré-rédigé est envoyé à la CNIL + Et le formulaire en ligne CNIL est rempli + + # Mesures organisationnelles + @security @access-control + Scénario: Accès DB uniquement via VPN et whitelist IP + Étant donné que je suis un développeur + Quand je tente d'accéder à la DB de production + Alors je dois être connecté au VPN OVH + Et mon IP doit être dans la whitelist + Sinon la connexion est refusée + + @security @2fa + Scénario: 2FA obligatoire pour les comptes administrateurs + Étant donné que je suis un administrateur + Quand je me connecte à Zitadel admin + Alors le 2FA (TOTP) est obligatoire + Et je ne peux pas désactiver le 2FA + Et chaque connexion est loggée avec IP et timestamp + + @security @logs + Scénario: Anonymisation des IP dans les logs (rotation 90j) + Quand un utilisateur fait une requête API + Alors son IP est loggée pour debug + Mais les 2 derniers octets sont masqués (ex: 192.168.x.x) + Et les logs sont automatiquement supprimés après 90 jours + + # Tests de sécurité + @security @pentest + Scénario: Pentest annuel obligatoire + Étant donné que l'application est en production + Quand une année s'est écoulée depuis le dernier pentest + Alors un pentest externe est planifié + Et les vulnérabilités découvertes sont corrigées sous 30 jours + Et un rapport est produit pour audit diff --git a/docs/domains/_shared/features/rgpd-compliance/minors-protection.feature b/docs/domains/_shared/features/rgpd-compliance/minors-protection.feature new file mode 100644 index 0000000..40cbd28 --- /dev/null +++ b/docs/domains/_shared/features/rgpd-compliance/minors-protection.feature @@ -0,0 +1,112 @@ +# language: fr + +@privacy @rgpd @minors @parental-consent +Fonctionnalité: Protection des mineurs et consentement parental (Article 8 RGPD) + En tant que plateforme responsable + Je veux protéger les mineurs avec consentement parental + Afin de respecter l'Article 8 RGPD (13-15 ans) + + Contexte: + Étant donné que je suis sur la page d'inscription + + Scénario: Blocage inscription si moins de 13 ans + Quand je saisis ma date de naissance "15/03/2014" + Et que je valide le formulaire + Alors je vois le message "RoadWave est réservé aux personnes de 13 ans et plus" + Et je vois un lien vers "RoadWave Kids" + Et mon inscription est bloquée + + Scénario: Inscription directe si 16 ans ou plus + Quand je saisis ma date de naissance "10/01/2008" + Et que je valide le formulaire + Alors mon compte est créé immédiatement + Et aucun consentement parental n'est requis + Et j'ai accès à toutes les fonctionnalités + + Plan du Scénario: Workflow consentement parental pour 13-15 ans + Quand je saisis ma date de naissance "" + Et que je saisis l'email de mon parent "parent@example.com" + Et que je valide le formulaire + Alors un email est envoyé à "parent@example.com" + Et le lien de validation expire dans 7 jours + Et mon compte est créé mais inactif + Et je vois "En attente validation parentale" + + Exemples: + | date_naissance | âge | + | 15/03/2011 | 13 | + | 20/06/2010 | 14 | + | 01/12/2009 | 15 | + + Scénario: Validation du consentement parental + Étant donné que je suis un mineur de 14 ans avec compte inactif + Et que mon parent a reçu l'email de validation + Quand mon parent clique sur le lien de validation + Alors il voit une page avec: + | Section | + | Résumé données collectées | + | Paramètres contrôle parental| + | Checkbox consentement | + Et quand il coche "J'autorise mon enfant" et valide + Alors mon compte est activé + Et je reçois un email "Compte activé par ton parent" + Et les restrictions 13-15 ans sont appliquées + + Scénario: Restrictions pour comptes 13-15 ans + Étant donné que je suis un utilisateur de 14 ans avec compte validé + Alors je peux écouter des contenus autorisés + Mais je NE peux PAS: + | Restriction | + | Activer le GPS précis sans accord parent | + | Utiliser la messagerie privée | + | Voir les contenus marqués +16 | + | Afficher ma ville précise sur le profil | + + Scénario: Dashboard parent - Visualisation activité enfant + Étant donné que je suis un parent avec enfant de 14 ans + Quand je me connecte à "roadwave.fr/parent/[child_id]" + Alors je vois: + | Information | + | Historique d'écoute | + | Temps d'écoute hebdomadaire | + | Dernière connexion | + Et je peux: + | Action | + | Activer/désactiver GPS précis | + | Activer/désactiver messagerie | + | Révoquer le consentement | + + Scénario: Révocation du consentement parental + Étant donné que je suis un parent + Et que mon enfant de 14 ans a un compte actif + Quand je clique sur "Révoquer le consentement" + Alors le compte de mon enfant est immédiatement désactivé + Et il ne peut plus se connecter + Et je reçois un email de confirmation + + @roadwave-kids + Scénario: RoadWave Kids - Inscription via compte parent + Étant donné que je suis un parent avec compte RoadWave actif + Quand je vais dans "Mon compte > Ajouter un profil enfant" + Et que je saisis: + | Champ | Valeur | + | Pseudo enfant | "Emma" | + | Date naissance | "12/08/2016" | + Alors un profil Kids est créé + Et je reçois un QR code pour l'app RoadWave Kids + Et l'enfant ne peut écouter que les contenus whitelist + + @roadwave-kids + Scénario: RoadWave Kids - Restrictions strictes + Étant donné que je suis connecté sur l'app RoadWave Kids + Alors je peux uniquement: + | Fonctionnalité | + | Écouter contenus présélectionnés | + | Voir ma position ville (GeoIP) | + Mais je NE peux PAS: + | Restriction | + | Activer le GPS précis | + | Créer du contenu | + | Avoir un profil public | + | Utiliser la messagerie | + | Voir du contenu UGC | diff --git a/docs/domains/_shared/features/rgpd-compliance/privacy-policy.feature b/docs/domains/_shared/features/rgpd-compliance/privacy-policy.feature new file mode 100644 index 0000000..833fa94 --- /dev/null +++ b/docs/domains/_shared/features/rgpd-compliance/privacy-policy.feature @@ -0,0 +1,99 @@ +# language: fr + +@privacy @rgpd @transparency +Fonctionnalité: Politique de confidentialité et transparence (Articles 13-14 RGPD) + En tant qu'utilisateur + Je veux comprendre comment mes données sont utilisées + Afin de donner un consentement éclairé + + Contexte: + Étant donné que l'application RoadWave est disponible + + # Popup première connexion + @first-launch + Scénario: Affichage de la politique de confidentialité à l'inscription + Quand je m'inscris pour la première fois + Alors une popup s'affiche avec la politique de confidentialité + Et je dois scroller jusqu'en bas pour activer le bouton "J'accepte" + Et je dois cocher "J'ai lu et j'accepte la politique de confidentialité" + Et je ne peux pas créer de compte sans accepter + + # Contenu obligatoire + @content + Scénario: Vérification du contenu de la politique de confidentialité + Quand je consulte la page "roadwave.fr/confidentialite" + Alors je vois les informations suivantes: + | Section | + | Identité responsable traitement + DPO | + | Finalités détaillées par traitement | + | Base légale (consentement/contrat/intérêt)| + | Destinataires données (CDN, Matomo, etc.) | + | Durées de conservation | + | Droits utilisateurs (accès, rectif, etc.) | + | Droit réclamation CNIL | + | Transferts hors UE (aucun) | + + # Versioning + @versioning + Scénario: Versioning de la politique avec Git et DB + Étant donné que la politique de confidentialité est modifiée + Quand l'équipe commit les changements + Alors le fichier "docs/legal/politique-confidentialite.md" est versionné Git + Et une entrée est créée dans "privacy_policy_versions" + Avec les champs: + | Champ | Valeur | + | version | "2.0" | + | effective_date| "2026-03-01" | + | major_change | true | + | changelog | "Ajout tracking..." | + + @versioning + Scénario: Notification utilisateurs si changement majeur + Étant donné qu'une nouvelle version majeure de la politique est publiée + Quand un utilisateur se connecte + Alors une popup s'affiche "Politique de confidentialité mise à jour" + Et il doit l'accepter à nouveau pour continuer + Et s'il refuse, son compte est gelé (accès lecture seule) + + # Transparence algorithme + @algorithm-transparency + Scénario: Explication simplifiée de l'algorithme de recommandation + Quand je vais sur "roadwave.fr/comment-ca-marche" + Alors je vois une page "Comment fonctionne la recommandation ?" + Avec les explications suivantes: + | Critère | Explication | + | Distance géographique | Contenus près de vous en priorité | + | Centres d'intérêt | Jauges automatiques selon écoutes | + | Popularité | Contenus les plus écoutés | + Et je peux désactiver la personnalisation (mode anonyme) + + # Contact DPO + @dpo-contact + Scénario: Accès facile au contact DPO + Quand je vais dans "Paramètres > Confidentialité et données" + Alors je vois un bouton "Contacter le DPO" + Et le lien email "dpo@roadwave.fr" est cliquable + Et je vois "Délai de réponse : 1 mois maximum" + + # Profilage et décisions automatisées + @profiling + Scénario: Information sur le profilage et opposition possible + Quand je consulte la politique de confidentialité + Alors je vois une section "Profilage et décisions automatisées" + Qui explique: + | Type décision | Impact | Opposition possible | + | Recommandations | Faible | Oui (mode anonyme) | + | Modération automatique| Élevé | Oui (contestation) | + Et je peux activer le mode anonyme à tout moment + + # Sous-traitants + @subprocessors + Scénario: Liste transparente des sous-traitants + Quand je consulte "roadwave.fr/confidentialite#sous-traitants" + Alors je vois la liste complète: + | Service | Finalité | Localisation | DPA | + | OVH | Hébergement | France | ✅ | + | Bunny.net | CDN audio | UE | ✅ | + | Brevo | Emails | France | ✅ | + | Mangopay | Paiements | Luxembourg | ✅ | + Et je vois "Aucun transfert hors UE" diff --git a/docs/domains/_shared/features/rgpd-compliance/user-rights.feature b/docs/domains/_shared/features/rgpd-compliance/user-rights.feature new file mode 100644 index 0000000..4862112 --- /dev/null +++ b/docs/domains/_shared/features/rgpd-compliance/user-rights.feature @@ -0,0 +1,77 @@ +# language: fr + +@privacy @rgpd @user-rights +Fonctionnalité: Exercice des droits utilisateurs RGPD + En tant qu'utilisateur + Je veux exercer mes droits RGPD facilement + Afin de contrôler mes données personnelles + + Contexte: + Étant donné que je suis connecté à mon compte + + # Article 16 - Droit de rectification + @rectification + Scénario: Modification du pseudo (limite 1/30j) + Quand je vais dans "Paramètres > Mon profil" + Et que je modifie mon pseudo de "Alice123" à "AliceM" + Alors le changement est immédiat + Et je ne peux plus modifier mon pseudo pendant 30 jours + Et l'historique est enregistré dans "user_profile_history" + + @rectification + Scénario: Modification de l'email avec re-vérification + Quand je modifie mon email de "old@example.com" à "new@example.com" + Alors je reçois un email de vérification sur "new@example.com" + Et je dois cliquer sur le lien dans les 24h + Et mon ancien email reste actif jusqu'à validation + + # Article 21 - Droit d'opposition + @opposition + Scénario: Opposition au marketing par email + Quand je vais dans "Paramètres > Notifications" + Et que je décoche "Recevoir les emails marketing" + Alors je ne reçois plus d'emails promotionnels + Mais je reçois toujours les emails transactionnels + + @opposition @anonymous-mode + Scénario: Activation du mode anonyme (recommandations génériques) + Quand je vais dans "Paramètres > Confidentialité" + Et que j'active "Mode anonyme" + Alors mes jauges d'intérêt sont ignorées + Et je reçois uniquement les top contenus de ma zone géographique + Et mon historique d'écoute n'est pas utilisé pour les recommandations + Et je vois un badge "Mode anonyme actif" + + # Article 18 - Droit à la limitation du traitement + @limitation + Scénario: Gel temporaire du compte + Quand je vais dans "Paramètres > Gestion du compte" + Et que je clique sur "Mettre en pause mon compte" + Alors mon compte est gelé immédiatement + Et mes contenus sont cachés (non diffusés) + Et mon profil est invisible + Mais je peux toujours me connecter en lecture seule + Et je peux réactiver à tout moment + + @limitation + Scénario: Réactivation compte gelé + Étant donné que mon compte est gelé depuis 15 jours + Quand je me connecte + Et que je clique sur "Réactiver mon compte" + Alors mon compte redevient actif immédiatement + Et mes contenus sont à nouveau visibles + + # Article 12 - Délai de réponse 1 mois max + @response-time + Scénario: Demande d'accès aux données (délai 48h) + Quand je demande un export de mes données + Alors je reçois un email sous 48h maximum + Avec un lien de téléchargement valide 7 jours + + @response-time + Scénario: Contestation d'une décision automatisée (délai 24h) + Étant donné que mon contenu a été bloqué par la modération automatique + Quand je clique sur "Contester cette décision" + Alors un humain examine mon cas sous 24h + Et je reçois une réponse motivée + Et mon contenu est rétabli si la décision était erronée diff --git a/docs/domains/_shared/rules/rgpd.md b/docs/domains/_shared/rules/rgpd.md index 8cf3e31..f1d8e9b 100644 --- a/docs/domains/_shared/rules/rgpd.md +++ b/docs/domains/_shared/rules/rgpd.md @@ -34,15 +34,7 @@ 1. Données précises conservées **24h** (recommandation personnalisée) 2. Après 24h : conversion en geohash précision 5 (~5km²) 3. Coordonnées originales supprimées définitivement - -**Implémentation PostGIS** : -```sql --- Job quotidien -UPDATE location_history -SET location = ST_SetSRID(ST_GeomFromGeoHash(ST_GeoHash(location::geography, 5)), 4326)::geography, - anonymized = true -WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false; -``` +4. Job quotidien automatique via cron **Exceptions** : - ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif @@ -59,17 +51,7 @@ WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false; **Décision** : JSON + HTML + ZIP, génération asynchrone -**Contenu de l'export** : -``` -export-roadwave-[user_id]-[date].zip -├── export.json # Machine-readable -├── index.html # Human-readable (stylé) -├── audio/ -│ ├── content-123.opus -│ ├── content-456.opus -│ └── ... -└── README.txt # Instructions -``` +**Format export** : Archive ZIP contenant JSON (machine-readable), HTML (human-readable), fichiers audio, README **Données exportées** : - Profil utilisateur (email, pseudo, date inscription, bio) @@ -306,31 +288,283 @@ export-roadwave-[user_id]-[date].zip --- -## Récapitulatif Section 13 +### 13.11 Droit de rectification -| Mesure | Implémentation | Coût | -|--------|----------------|------| -| **Consentement** | Tarteaucitron.js + PostgreSQL | 0€ | -| **Anonymisation GPS** | Geohash PostGIS (24h) | 0€ | -| **Export données** | JSON+HTML+ZIP asynchrone | 0€ | -| **Suppression compte** | Grace period 30j + anonymisation | 0€ | -| **Mode dégradé** | GeoIP IP2Location + GPS optionnel | 0€ | -| **Conservation** | Purge auto 5 ans inactivité | 0€ | -| **Analytics** | Matomo self-hosted | ~5€/mois | -| **Registre traitements** | Markdown Git | 0€ | -| **Breach detection** | Sentry + Grafana + runbook | 0€ | -| **DPO** | Fondateur formé CNIL | 0€ | +**Décision** : Interface self-service + validation immédiate -**Coût total RGPD : ~5€/mois** +**Données rectifiables** : +- Email (avec re-vérification) +- Pseudo (unique, disponibilité vérifiée) +- Bio / description +- Centres d'intérêt (jauges) +- Photo de profil + +**Processus** : +- Changements immédiats (sauf email) +- Email : lien vérification → validation sous 24h +- Historique modifications conservé (audit trail) + +**Limitations** : +- Pseudo : max 1 changement/30j (anti-squat) + +**Justification** : Conformité Article 16 RGPD, self-service 0€ --- -## Points d'attention pour Gherkin +### 13.12 Droit d'opposition -- Tester consentement géolocalisation (accept/refuse → contenus différents) -- Tester anonymisation GPS après 24h (job cron) -- Tester export données (génération complète + vérification contenu) -- Tester grace period suppression (annulation possible) -- Tester mode GeoIP (ville détectée correctement) -- Tester purge automatique (5 ans inactivité) -- Tester notifications avant purge (90j/30j/7j) +**Décision** : Opt-out granulaire, effet immédiat + +| Traitement | Toggle | Effet | +|------------|--------|-------| +| **Marketing email** | Paramètres | Stop emails promo | +| **Notifications push** | Paramètres | Stop push marketing | +| **Analytics** | Banner RGPD | Exclusion Matomo | +| **Recommandations personnalisées** | "Mode anonyme" | Reco génériques uniquement | + +**Mode anonyme** : +- Désactive algorithme (jauges ignorées) +- Recommandations = top contenus zone géo uniquement +- Historique non utilisé + +**Justification** : Conformité Article 21 RGPD + +--- + +### 13.13 Droit à la limitation du traitement + +**Décision** : "Geler mon compte" temporaire + +**Effets** : +- Compte gelé, contenus cachés, profil invisible +- Connexion lecture seule OK +- Réactivation à tout moment + +**Justification** : Conformité Article 18 RGPD + +--- + +### 13.14 Politique de confidentialité + +**Décision** : Page web + popup in-app + versioning Git + +**Emplacement** : +- Web : `roadwave.fr/confidentialite` +- App : page dédiée paramètres +- Popup première connexion (scroll requis) + +**Contenu** : Identité responsable, finalités, base légale, destinataires, durées, droits, transferts UE + +**Versioning** : Git + DB `privacy_policy_versions`, popup si changement majeur + +**Justification** : Conformité Articles 13-14 RGPD + +--- + +### 13.15 Minimisation des données + +**Décision** : Collecte strictement nécessaire + +| Donnée | Finalité | Optionnel | +|--------|----------|-----------| +| Email | Authentification | ❌ | +| Pseudo | Identité publique | ❌ | +| GPS précis | Reco hyperlocales | ✅ (GeoIP fallback) | +| Jauges intérêt | Reco thématiques | ✅ | +| Date naissance | Vérifier âge minimum | ❌ (année seule) | + +**Non collecté** : nom/prénom réels, adresse postale (sauf créateurs payés), téléphone (sauf 2FA optionnel) + +**Justification** : Conformité Article 5.1.c RGPD + +--- + +### 13.16 Sécurité des données + +**Décision** : Chiffrement multi-niveaux + +| Couche | Implémentation | +|--------|----------------| +| Transport | TLS 1.3 ([ADR-006](../../../adr/006-chiffrement.md)) | +| DB | PostgreSQL encryption at rest AES-256 | +| Tokens | JWT RS256 + rotation 90j | +| CDN | Signed URLs expirables | +| Backups | AES-256 + offsite | + +**Mesures orga** : Whitelist IP, Vault secrets, logs anonymisés, 2FA admins, pentest annuel + +**Justification** : Conformité Article 32 RGPD + +--- + +### 13.17 Transferts hors UE + +**Décision** : Hébergement 100% France/UE + +| Service | Localisation | Transfert UE | +|---------|--------------|--------------| +| Hébergement | OVH France | ❌ | +| Database | OVH France | ❌ | +| CDN | Bunny.net EU | ❌ | +| Matomo | Self-hosted France | ❌ | + +**Si CDN global futur** : Clauses Contractuelles Types (CCE) 2021 + +**Justification** : Conformité Articles 44-50 RGPD, souveraineté données + +--- + +### 13.18 Profilage et décisions automatisées + +**Décision** : Transparence + droit opposition + +| Traitement | Impact | Intervention humaine | Opposition | +|------------|--------|---------------------|------------| +| Recommandations | Faible | ❌ | ✅ (mode anonyme) | +| Modération auto | Élevé | ✅ (review 24h) | ✅ (appeal) | + +**Transparence** : Page "Comment fonctionne l'algo ?", explications simplifiées + +**Justification** : Conformité Article 22 RGPD + +--- + +### 13.19 Gestion des mineurs + +**Décision** : 13 ans minimum + consentement parental 13-15 ans + RoadWave Kids + +#### App principale (RoadWave) + +**Âge minimum** : **13 ans** (alignement YouTube/TikTok) + +**Processus inscription** : +1. Saisie date naissance (JJ/MM/AAAA) +2. **Si < 13 ans** : blocage + message redirection RoadWave Kids +3. **Si 13-15 ans** : workflow consentement parental +4. **Si ≥ 16 ans** : inscription directe + +**Workflow consentement parental (13-15 ans)** : +1. Ado saisit email parent +2. Email automatique parent avec lien validation (expire 7j) +3. Parent clique lien → page dédiée avec résumé données collectées, paramètres contrôle parental, checkbox consentement +4. Validation parent → compte ado activé avec restrictions + +**Restrictions 13-15 ans** : +- ✅ Écoute contenus autorisés +- ✅ Création contenus (modération renforcée) +- ⚠️ GPS précis : consentement parental explicite requis +- ⚠️ Messagerie privée : désactivée par défaut +- ⚠️ Contenus sensibles : filtrés (pas de contenu +16) +- ⚠️ Profil public limité (pas d'affichage ville précise) + +**Contrôles parentaux** : +- Dashboard parent : `roadwave.fr/parent/[child_id]` +- Visualisation historique écoute +- Activation/désactivation GPS précis +- Activation/désactivation messagerie +- Révocation consentement à tout moment +- Notification hebdomadaire activité + +**Vérification légère identité parent** : +- Email parent ≠ email ado (vérification domaine) +- Lien expiration 7 jours +- Pas de vérification identité forte (MVP) + +#### RoadWave Kids (< 13 ans) + +**App dédiée** : Version séparée avec contrôles renforcés + +**Caractéristiques** : +- ❌ Pas de GPS précis (GeoIP ville uniquement) +- ❌ Pas de création contenu +- ❌ Pas de profil public +- ❌ Pas de messagerie +- ✅ Contenus présélectionnés (whitelist éditoriale) +- ✅ Mode lecture seule +- ✅ Contrôle parental obligatoire + +**Contenus autorisés** : +- Contes audio enfants +- Guides touristiques famille +- Podcasts éducatifs labellisés +- Histoires locales patrimoine + +**Workflow inscription** : +1. Création compte parent (RoadWave standard) +2. Ajout profil enfant dans dashboard parent +3. App Kids : login via QR code parent +4. Pas de compte autonome enfant + +**Modération** : +- 100% contenus présélectionnés par équipe éditoriale +- Aucun UGC accessible +- Whitelist créateurs vérifiés uniquement + +**Justification** : +- Conformité Article 8 RGPD (13-16 ans selon pays) +- 13 ans France = seuil légal avec consentement parental +- App Kids = protection renforcée < 13 ans +- Alignement marché (YouTube 13+, YouTube Kids) + +**Roadmap** : +- **MVP** : App principale 16+ uniquement (simplicité) +- **Phase 2** : Workflow 13-15 ans + consentement parental +- **Phase 3** : RoadWave Kids (app séparée) + +--- + +### 13.20 Sous-traitants et DPA + +**Décision** : DPA systématique, audit annuel + +| Service | Traitement | Localisation | DPA | Certifications | +|---------|------------|--------------|-----|----------------| +| OVH | Hébergement | France | ✅ | ISO 27001, HDS | +| Bunny.net | CDN | UE | ✅ | ISO 27001 | +| Brevo | Emailing | France | ✅ | RGPD certified | +| Mangopay | Paiements | Luxembourg | ✅ | PCI-DSS, ACPR | + +**Obligations DPA** : Traitement selon instructions, confidentialité, sécurité, assistance droits, suppression fin contrat + +**Gestion** : `docs/rgpd/sous-traitants.md`, review annuelle + +**Justification** : Conformité Article 28 RGPD + +--- + +### 13.21 Analyse d'impact (DPIA) + +**Décision** : DPIA obligatoire (GPS + profilage grande échelle) + +**Raisons** : +- Traitement grande échelle données GPS sensibles +- Profilage automatisé recommandations +- Surveillance zones publiques + +**Contenu** : Description traitement, finalités, nécessité, risques (tracking, profilage, fuite), mesures atténuation (anonymisation 24h, consentement, chiffrement, mode dégradé) + +**Fichier** : `docs/rgpd/dpia-geolocalisation.md`, review annuelle + +**Justification** : Conformité Article 35 RGPD (critères CNIL remplis) + +--- + +### 13.22 Délai de réponse aux demandes + +**Décision** : 1 mois max, automatisation maximale + +**Canaux** : Email dpo@roadwave.fr, formulaire in-app, courrier postal + +| Droit | Délai cible | Automatisation | +|-------|-------------|----------------| +| Accès (export) | 48h | ✅ Worker | +| Rectification | Immédiat | ✅ Self-service | +| Suppression | Immédiat | ✅ Self-service | +| Opposition | Immédiat | ✅ Toggles | +| Limitation | Immédiat | ✅ Gel compte | +| Portabilité | 48h | ✅ Export | +| Contestation décision | 24h | ⚠️ Manuel | + +**Vérification identité** : Si email vérifié = aucune vérif supplémentaire + +**Justification** : Conformité Article 12 RGPD diff --git a/docs/domains/_shared/sequences/anonymisation-gps.md b/docs/domains/_shared/sequences/anonymisation-gps.md new file mode 100644 index 0000000..f81fd42 --- /dev/null +++ b/docs/domains/_shared/sequences/anonymisation-gps.md @@ -0,0 +1,55 @@ +# Anonymisation automatique GPS après 24h + +```mermaid +sequenceDiagram + participant User as Utilisateur + participant App as Application + participant DB as Base de données (PostGIS) + participant Cron as Job Cron Quotidien + + Note over User,App: Écoute de contenu avec GPS + + User->>App: Écoute contenu (GPS activé) + App->>App: Capturer position GPS précise + App->>DB: INSERT position (lat, lon, anonymized: false) + Note over DB: Position précise stockée
Utilisée pour recommandations + + Note over DB,Cron: Moins de 24h : position précise conservée + + App->>DB: SELECT positions pour recommandations + DB->>App: Positions GPS précises (< 24h) + App->>User: Recommandations hyperlocales + + Note over Cron: 24h+ plus tard + + Cron->>DB: SELECT positions WHERE created_at < NOW() - 24h AND anonymized = false + DB->>Cron: Liste positions à anonymiser + + loop Pour chaque position + Cron->>DB: Convertir (lat, lon) → geohash précision 5 (~5km²) + Cron->>DB: UPDATE position avec geohash + Cron->>DB: Supprimer coordonnées précises + Cron->>DB: SET anonymized = true + end + + Cron->>DB: Log anonymisation (nombre positions traitées) + + Note over DB: Positions anonymisées utilisées pour analytics + + App->>DB: SELECT positions anonymisées (analytics globales) + DB->>App: Positions geohash uniquement + App->>App: Générer heatmap trafic (~5km² précision) + + Note over User: Exception : historique personnel + + User->>App: Consulter "Mon historique d'écoute" + App->>DB: SELECT historique personnel utilisateur + DB->>App: Positions précises conservées (tant que compte actif) + App->>User: Trajets détaillés +``` + +**Légende** : +- **< 24h** : GPS précis conservé (recommandations hyperlocales) +- **> 24h** : Conversion automatique en geohash précision 5 (~5km²) +- **Exception** : Historique personnel conservé intact tant que compte actif +- **Analytics** : Uniquement positions anonymisées (geohash) diff --git a/docs/domains/_shared/sequences/consentement-parental.md b/docs/domains/_shared/sequences/consentement-parental.md new file mode 100644 index 0000000..73abf72 --- /dev/null +++ b/docs/domains/_shared/sequences/consentement-parental.md @@ -0,0 +1,43 @@ +# Consentement parental (13-15 ans) + +```mermaid +sequenceDiagram + participant Ado as Adolescent (13-15 ans) + participant App as Application + participant DB as Base de données + participant Email as Service Email + participant Parent as Parent + + Ado->>App: Inscription (date naissance 13-15 ans) + App->>Ado: Demande email parent + Ado->>App: Saisit email parent + + App->>DB: Créer compte (statut: pending_parental_consent) + App->>Email: Envoyer email validation parent + Email->>Parent: Email avec lien (expire 7j) + App->>Ado: "En attente validation parentale" + + Note over Parent: Parent clique lien validation + + Parent->>App: Accès page consentement + App->>Parent: Afficher résumé données + contrôles + Parent->>App: Valider consentement + paramètres + + App->>DB: Enregistrer consentement parental + App->>DB: Activer compte (statut: active_minor) + App->>DB: Appliquer restrictions 13-15 ans + + App->>Email: Notification ado (compte activé) + Email->>Ado: Email confirmation + + App->>Email: Notification parent (récapitulatif) + Email->>Parent: Email + lien dashboard parental + + Ado->>App: Connexion + App->>Ado: Accès restreint (GPS/messagerie selon config parent) +``` + +**Légende** : +- Délai expiration lien : 7 jours +- Restrictions 13-15 ans : GPS précis, messagerie, contenus +16 (configurables par parent) +- Dashboard parent : `roadwave.fr/parent/[child_id]` diff --git a/docs/domains/_shared/sequences/export-donnees.md b/docs/domains/_shared/sequences/export-donnees.md new file mode 100644 index 0000000..b985382 --- /dev/null +++ b/docs/domains/_shared/sequences/export-donnees.md @@ -0,0 +1,65 @@ +# Export de données (portabilité) + +```mermaid +sequenceDiagram + participant User as Utilisateur + participant App as Application + participant DB as Base de données + participant Queue as File d'attente + participant Worker as Worker Background + participant CDN as CDN (fichiers audio) + participant Storage as Stockage temporaire + participant Email as Service Email + + User->>App: Demande export données + + App->>DB: Vérifier dernière demande + alt Dernière demande < 30 jours + DB->>App: Demande refusée + App->>User: "Prochain export disponible dans X jours" + else Demande autorisée + App->>DB: Créer demande export (statut: pending) + App->>Queue: Ajouter job export + App->>User: "Export en cours, email sous 48h" + + Queue->>Worker: Job export disponible + + Note over Worker: Génération asynchrone + + Worker->>DB: Récupérer profil utilisateur + Worker->>DB: Récupérer historique d'écoute + Worker->>DB: Récupérer contenus créés (métadonnées) + Worker->>DB: Récupérer centres d'intérêt + Worker->>DB: Récupérer historique consentements + + Worker->>CDN: Télécharger fichiers audio utilisateur + CDN->>Worker: Fichiers audio (.opus) + + Worker->>Worker: Générer export.json (machine-readable) + Worker->>Worker: Générer index.html (human-readable) + Worker->>Worker: Générer README.txt + Worker->>Worker: Créer archive ZIP + + Worker->>Storage: Stocker ZIP (expire 7j) + Storage->>Worker: URL signée (expire 7j) + + Worker->>DB: Mettre à jour demande (statut: completed) + Worker->>DB: Enregistrer URL + date expiration + + Worker->>Email: Envoyer email avec lien + Email->>User: Email + lien téléchargement (valide 7j) + + User->>Storage: Clic lien téléchargement + Storage->>User: Téléchargement ZIP + + Note over Storage: Après 7 jours + + Storage->>Storage: Suppression automatique ZIP + end +``` + +**Légende** : +- Limite : 1 export / 30 jours (anti-abus) +- Délai génération : 48h maximum (conformité RGPD Article 20) +- Expiration lien : 7 jours +- Format : ZIP contenant JSON, HTML, audio, README diff --git a/docs/domains/_shared/sequences/notification-breach.md b/docs/domains/_shared/sequences/notification-breach.md new file mode 100644 index 0000000..f314542 --- /dev/null +++ b/docs/domains/_shared/sequences/notification-breach.md @@ -0,0 +1,63 @@ +# Notification violation de données (breach) - Procédure 72h CNIL + +```mermaid +sequenceDiagram + participant Monitoring as Monitoring (Sentry/Grafana) + participant Equipe as Équipe Technique + participant DPO as DPO + participant DB as Base de données + participant CNIL as CNIL + participant Users as Utilisateurs impactés + + Note over Monitoring: H+0 - Détection + + Monitoring->>Equipe: Alerte breach détecté + Equipe->>Equipe: Confinement immédiat + Equipe->>DB: Bloquer accès compromis + Equipe->>DPO: Notification breach + + Note over DPO,Equipe: H+0 à H+24 - Évaluation + + DPO->>DB: Investigation périmètre + DB->>DPO: Données compromises + DPO->>DB: Liste utilisateurs impactés + DB->>DPO: X utilisateurs + + DPO->>DPO: Évaluation gravité + Note over DPO: - Type données (GPS, email, etc.)
- Nombre utilisateurs
- Risque pour droits/libertés + + alt Risque pour utilisateurs + Note over DPO: H+24 à H+48 - Préparation notification CNIL + + DPO->>DPO: Rédaction rapport breach + Note over DPO: - Nature violation
- Catégories/nb données
- Conséquences probables
- Mesures prises/envisagées + + DPO->>CNIL: Notification sous 72h (email + formulaire en ligne) + CNIL->>DPO: Accusé réception + + alt Risque élevé pour utilisateurs + Note over DPO: H+48 à H+72 - Notification utilisateurs + + DPO->>Users: Email notification breach + Note over Users: - Nature violation
- Coordonnées DPO
- Mesures prises
- Recommandations (changer mdp, etc.) + + DPO->>Users: Notification in-app + end + else Risque faible (mesures techniques suffisantes) + DPO->>DPO: Documentation interne uniquement + Note over DPO: Pas de notification CNIL requise + end + + Note over DPO: Post-incident + + DPO->>Equipe: Audit sécurité complet + DPO->>DB: Enregistrement incident (registre violations) + DPO->>DPO: Plan correctif +``` + +**Légende** : +- **H+0 à H+24** : Détection, confinement, évaluation périmètre +- **H+24 à H+48** : Évaluation gravité, préparation rapport +- **H+48 à H+72** : Notification CNIL (si risque) + utilisateurs (si risque élevé) +- Délai CNIL : **72h maximum** (Article 33 RGPD) +- Notification utilisateurs obligatoire si **risque élevé** pour droits/libertés (Article 34 RGPD) diff --git a/docs/domains/_shared/sequences/suppression-compte.md b/docs/domains/_shared/sequences/suppression-compte.md new file mode 100644 index 0000000..f6f564c --- /dev/null +++ b/docs/domains/_shared/sequences/suppression-compte.md @@ -0,0 +1,51 @@ +# Suppression compte avec grace period + +```mermaid +sequenceDiagram + participant User as Utilisateur + participant App as Application + participant DB as Base de données + participant Email as Service Email + participant Cron as Job Cron + + User->>App: Demande suppression compte + App->>User: Confirmation (êtes-vous sûr ?) + User->>App: Confirmer + + App->>DB: Désactiver compte (statut: pending_deletion) + App->>DB: Cacher contenus (visible: false) + App->>DB: Révoquer sessions/tokens + App->>DB: Enregistrer date suppression effective (J+30) + + App->>Email: Email confirmation + lien annulation + Email->>User: Email (lien valide 30j) + App->>User: "Compte désactivé. Suppression dans 30 jours." + + Note over User,App: Grace period 30 jours + + alt Utilisateur annule + User->>App: Clic lien annulation + App->>DB: Réactiver compte (statut: active) + App->>DB: Restaurer visibilité contenus + App->>Email: Email confirmation réactivation + Email->>User: "Compte réactivé" + else 30 jours sans annulation + Cron->>DB: Job quotidien (vérif comptes pending_deletion) + DB->>Cron: Liste comptes J+30 dépassé + + loop Pour chaque compte + Cron->>DB: Supprimer données personnelles + Cron->>DB: Anonymiser contenus (créateur: "Utilisateur supprimé") + Cron->>DB: Supprimer historique GPS/écoute + Cron->>DB: Marquer statut: deleted + end + + Cron->>Email: Email confirmation suppression effective + Email->>User: "Compte définitivement supprimé" + end +``` + +**Légende** : +- Grace period : 30 jours +- Pendant grace period : compte inaccessible, contenus cachés +- Après 30j : suppression définitive, contenus anonymisés conservés diff --git a/docs/domains/_shared/states/account-deletion-lifecycle.md b/docs/domains/_shared/states/account-deletion-lifecycle.md new file mode 100644 index 0000000..1e67431 --- /dev/null +++ b/docs/domains/_shared/states/account-deletion-lifecycle.md @@ -0,0 +1,42 @@ +# Cycle de vie - Suppression de compte + +## Diagramme + +```mermaid +stateDiagram-v2 + [*] --> Requested: Utilisateur demande suppression + + Requested --> GracePeriod: Compte désactivé, email envoyé + + GracePeriod --> Cancelled: Clic lien annulation (< 30j) + GracePeriod --> PendingDeletion: Délai 30j écoulé + + Cancelled --> [*] + + PendingDeletion --> Deleted: Job cron suppression effective + + Deleted --> [*] +``` + +## Règles + +| État | Valeur | Description | +|------|--------|-------------| +| Requested | `requested` | Demande initiée, validation requise | +| Grace Period | `grace_period` | 30j annulation possible, compte inaccessible | +| Cancelled | `cancelled` | Utilisateur a annulé, compte réactivé | +| Pending Deletion | `pending_deletion` | File job cron (< 24h) | +| Deleted | `deleted` | Données supprimées, contenus anonymisés | + +**Grace period** : 30 jours +**Pendant grace period** : +- Compte désactivé (login impossible) +- Contenus cachés (non diffusés) +- Sessions/tokens révoqués +- Email avec token annulation (valide 30j) + +**Après 30j** : +- Données personnelles supprimées +- Contenus créés anonymisés (créateur = "Utilisateur supprimé") +- Historique GPS/écoute supprimé +- Irréversible diff --git a/docs/domains/_shared/states/breach-incident-lifecycle.md b/docs/domains/_shared/states/breach-incident-lifecycle.md new file mode 100644 index 0000000..9164d05 --- /dev/null +++ b/docs/domains/_shared/states/breach-incident-lifecycle.md @@ -0,0 +1,44 @@ +# Cycle de vie - Incident de violation de données + +## Diagramme + +```mermaid +stateDiagram-v2 + [*] --> Detected: Alerte monitoring + + Detected --> Contained: Confinement immédiat (H+0) + + Contained --> UnderInvestigation: Évaluation gravité (H+24) + + UnderInvestigation --> Resolved: Risque faible (mesures suffisantes) + UnderInvestigation --> CNILNotificationRequired: Risque utilisateurs + + CNILNotificationRequired --> CNILNotified: Notification CNIL (< H+72) + + CNILNotified --> Resolved: Pas de risque élevé utilisateurs + CNILNotified --> UsersNotificationRequired: Risque élevé + + UsersNotificationRequired --> UsersNotified: Email + push utilisateurs (< H+72) + + UsersNotified --> Resolved: Post-mortem + correctifs + + Resolved --> [*] +``` + +## Règles + +| État | Valeur | Délai max | +|------|--------|-----------| +| Detected | `detected` | H+0 | +| Contained | `contained` | H+0 (immédiat) | +| Under Investigation | `under_investigation` | H+24 | +| CNIL Notification Required | `cnil_notification_required` | H+48 | +| CNIL Notified | `cnil_notified` | H+72 (Article 33 RGPD) | +| Users Notification Required | `users_notification_required` | H+48 | +| Users Notified | `users_notified` | H+72 (Article 34 RGPD) | +| Resolved | `resolved` | Post-incident | + +**Sévérité** : `low` / `medium` / `high` / `critical` +**Notification CNIL** : Obligatoire si risque pour droits/libertés utilisateurs +**Notification utilisateurs** : Obligatoire si risque **élevé** +**Runbook** : `docs/rgpd/procedure-breach.md` diff --git a/docs/domains/_shared/states/parental-consent-lifecycle.md b/docs/domains/_shared/states/parental-consent-lifecycle.md new file mode 100644 index 0000000..bb68e2f --- /dev/null +++ b/docs/domains/_shared/states/parental-consent-lifecycle.md @@ -0,0 +1,32 @@ +# Cycle de vie - Consentement parental + +## Diagramme + +```mermaid +stateDiagram-v2 + [*] --> PendingValidation: Ado saisit email parent + + PendingValidation --> Validated: Parent clique lien (< 7j) + PendingValidation --> Expired: Délai 7j écoulé + + Validated --> Revoked: Parent révoque consentement + Validated --> AutoRevoked: Ado atteint 16 ans + + Expired --> [*] + Revoked --> [*] + AutoRevoked --> [*] +``` + +## Règles + +| État | Valeur | Description | +|------|--------|-------------| +| Pending Validation | `pending_validation` | Email envoyé parent, token valide 7j | +| Validated | `validated` | Parent a validé, restrictions 13-15 ans actives | +| Expired | `expired` | Token expiré sans validation, compte inactif | +| Revoked | `revoked` | Parent révoque, compte désactivé immédiatement | +| Auto-Revoked | `auto_revoked` | Ado atteint 16 ans, restrictions levées automatiquement | + +**Délai expiration** : 7 jours +**Révocation** : Possible à tout moment via dashboard parent +**Transition automatique** : À 16 ans → compte passe en `active` standard diff --git a/docs/domains/_shared/states/user-account-lifecycle.md b/docs/domains/_shared/states/user-account-lifecycle.md index a280382..5e9833f 100644 --- a/docs/domains/_shared/states/user-account-lifecycle.md +++ b/docs/domains/_shared/states/user-account-lifecycle.md @@ -4,18 +4,31 @@ ```mermaid stateDiagram-v2 - [*] --> Active: Inscription validée + [*] --> PendingEmailVerification: Inscription + [*] --> PendingParentalConsent: Inscription 13-15 ans + + PendingEmailVerification --> Active: Email vérifié (16+ ans) + PendingParentalConsent --> ActiveMinor: Parent valide + PendingParentalConsent --> Expired: Token expiré (7j) Active --> Suspended: Strikes 3/4/5 Active --> GracePeriod: Demande suppression + Active --> Frozen: Gel temporaire (limitation traitement) Active --> Deleted: Inactivité 5 ans + ActiveMinor --> Active: 16 ans atteints + ActiveMinor --> Suspended: Modération + ActiveMinor --> Deleted: Parent révoque + + Frozen --> Active: Réactivation utilisateur + Suspended --> Active: Fin suspension / Appel Suspended --> Deleted: Suspension définitive GracePeriod --> Active: Annulation < 30j GracePeriod --> Deleted: Après 30j + Expired --> [*] Deleted --> [*] ``` @@ -23,9 +36,20 @@ stateDiagram-v2 | État | Valeur | Durée/Condition | |------|--------|-----------------| -| Active | `active` | Compte fonctionnel | +| Pending Email Verification | `pending_email_verification` | Email non vérifié (expire 24h) | +| Pending Parental Consent | `pending_parental_consent` | Ado 13-15 ans, attente validation parent (expire 7j) | +| Active | `active` | Compte fonctionnel standard (16+ ans) | +| Active Minor | `active_minor` | Compte 13-15 ans avec restrictions parentales | +| Frozen | `frozen` | Gel temporaire (lecture seule), réactivable à tout moment | | Suspended | `suspended` | Strike 3: 7j, Strike 4: 30j, Strike 5: définitif | | Grace Period | `grace_period` | 30j avant suppression, annulable | -| Deleted | `deleted` | Données anonymisées, irréversible | +| Expired | `expired` | Token expiré sans validation | +| Deleted | `deleted` | Données supprimées, contenus anonymisés, irréversible | + +**Restrictions Active Minor** : +- GPS précis : configurable par parent +- Messagerie privée : désactivée par défaut +- Contenus +16 : filtrés +- Transition auto vers `active` à 16 ans **Purge inactivité** : 5 ans sans connexion (notifications 90j/30j/7j avant) diff --git a/mkdocs.yml b/mkdocs.yml index d7c0fee..d593547 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -103,6 +103,20 @@ nav: - RGPD: domains/_shared/rules/rgpd.md - Gestion Erreurs: domains/_shared/rules/gestion-erreurs.md - 'Annexe Post-MVP': domains/_shared/rules/ANNEXE-POST-MVP.md + - Features BDD: + - 'RGPD & Conformité': + - Consentement: generated/bdd/_shared/features/rgpd-compliance/consentement.md + - Anonymisation GPS: generated/bdd/_shared/features/rgpd-compliance/anonymisation-gps.md + - Portabilité Données: generated/bdd/_shared/features/rgpd-compliance/portabilite-donnees.md + - Suppression Compte: generated/bdd/_shared/features/rgpd-compliance/suppression-compte.md + - Conservation Données: generated/bdd/_shared/features/rgpd-compliance/conservation-donnees.md + - Cookies & Analytics: generated/bdd/_shared/features/rgpd-compliance/cookies-analytics.md + - Mode Dégradé GeoIP: generated/bdd/_shared/features/rgpd-compliance/mode-degrade-geoip.md + - Compliance Administrative: generated/bdd/_shared/features/rgpd-compliance/compliance-administrative.md + - Protection Mineurs: generated/bdd/_shared/features/rgpd-compliance/minors-protection.md + - Sécurité Données: generated/bdd/_shared/features/rgpd-compliance/data-security.md + - Politique Confidentialité: generated/bdd/_shared/features/rgpd-compliance/privacy-policy.md + - Droits Utilisateurs: generated/bdd/_shared/features/rgpd-compliance/user-rights.md - Entités: - "Vue d'ensemble": domains/_shared/entities/entities-overview.md - 'Auth & Sécurité': @@ -112,6 +126,12 @@ nav: - Consentements: domains/_shared/entities/consents.md - Historique GPS: domains/_shared/entities/location-history.md - Exports Données: domains/_shared/entities/exports.md + - Consentements Parentaux: domains/_shared/entities/parental-consents.md + - Politique Confidentialité: domains/_shared/entities/privacy-policy-versions.md + - Suppressions Compte: domains/_shared/entities/account-deletions.md + - Incidents Breach: domains/_shared/entities/breach-incidents.md + - Historique Profil: domains/_shared/entities/user-profile-history.md + - Logs Rétention Données: domains/_shared/entities/data-retention-logs.md - 'Recommandation & Modération': - Jauges Intérêt: domains/_shared/entities/interest-gauges.md - Signalements: domains/_shared/entities/reports.md @@ -121,13 +141,21 @@ nav: - Session: domains/_shared/states/session-lifecycle.md - Signalement: domains/_shared/states/report-lifecycle.md - Export Données: domains/_shared/states/export-lifecycle.md + - Consentement Parental: domains/_shared/states/parental-consent-lifecycle.md + - Suppression Compte: domains/_shared/states/account-deletion-lifecycle.md + - Incident Breach: domains/_shared/states/breach-incident-lifecycle.md - Séquences: - Authentification: domains/_shared/sequences/authentication-flow.md - Refresh Token: domains/_shared/sequences/token-refresh.md - Modération Contenu: domains/_shared/sequences/content-moderation.md - Signalement: domains/_shared/sequences/content-report.md - Export Données: domains/_shared/sequences/data-export.md + - Export Données (RGPD): domains/_shared/sequences/export-donnees.md - Suppression Compte: domains/_shared/sequences/account-deletion.md + - Suppression Compte (RGPD): domains/_shared/sequences/suppression-compte.md + - Consentement Parental: domains/_shared/sequences/consentement-parental.md + - Anonymisation GPS: domains/_shared/sequences/anonymisation-gps.md + - Notification Breach: domains/_shared/sequences/notification-breach.md - 'Supporting Subdomains': - '🎯 Recommendation': - Vue d'ensemble: domains/recommendation/README.md @@ -198,6 +226,8 @@ nav: - 'Validation TestFlight': mobile/testflight-validation-plan.md - Conformité & Compliance: - 'Soumission aux Stores': compliance/stores-submission.md + - Tests BDD: + - "Vue d'ensemble": generated/bdd/index.md - Architecture & Techniques: - Séquences: - 'Cache Géospatial': architecture/sequences/cache-geospatial.md diff --git a/output/RoadWave_Documentation_protected.pdf b/output/RoadWave_Documentation_protected.pdf deleted file mode 100644 index 3b51a44..0000000 --- a/output/RoadWave_Documentation_protected.pdf +++ /dev/null @@ -1,12236 +0,0 @@ -%PDF-1.7 -% -1 0 obj -<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 8 >> >> /Lang <5283971751aa95a7a910b5815e0b4ae20d3d106e287ad3383ca76075ea246e7f> /Names << /Dests << /Names [ [ 95 0 R /XYZ 62.692913 525.95685 0 ] [ 95 0 R /XYZ 62.692913 329.80685 0 ] <0b62b7328f5ba267cca17a4df67bf30722a1c93770c2c0aa344c3d1c1b7f2269323c8c11b0a971bdf5a0e82ceaeea6d6> [ 97 0 R /XYZ 62.692913 785.19685 0 ] [ 97 0 R /XYZ 62.692913 571.44685 0 ] [ 97 0 R /XYZ 62.692913 392.89685 0 ] [ 97 0 R /XYZ 62.692913 161.54685 0 ] <0eb808f260359fb7bac7b609e2c79d807f5543cb6cd9ad6c1b875ff29def537d> [ 99 0 R /XYZ 62.692913 686.64685 0 ] <83bad42f316ce3c1e099e5a7d2ebaf243471e8131c52cff0ce78f30b30424ad2f5fcf25d45278e4262ae4da4fba3e956> [ 99 0 R /XYZ 62.692913 490.49685 0 ] <4ee9a831b06b9bc6f217a972e37676971c7fb2125e1f188ec1fd978f7a11a7dc392f1497b1eac6843dafa2fcaa461947> [ 99 0 R /XYZ 62.692913 259.14685 0 ] <6b289c7369029e4c485bbe78dcf61180ed36c63dedaf57ee9677c1978b1b28d5b6348ba8cda532919db83b1ed006fa694189b635c78eac022d33d300f8cdb211> [ 716 0 R /XYZ 62.692913 396.38685 0 ] [ 903 0 R /XYZ 62.692913 396.38685 0 ] <36291328532451a57b500f243ff1f6a23d2ca030bbf1a3d35eff7371f15150c3d880eda635e17dbcc9670391f0944976afafdbec6af75fbabb3ec843a184fab3> [ 910 0 R /XYZ 62.692913 396.38685 0 ] [ 373 0 R /XYZ 62.692913 434.78685 0 ] [ 898 0 R /XYZ 62.692913 413.98685 0 ] [ 378 0 R /XYZ 62.692913 378.78685 0 ] [ 942 0 R /XYZ 62.692913 518.23685 0 ] <2ae241b4c18bff8a28ea998ebaad9faa75592c1ed64427630e88d74b88414c7f094192422162ef4f44692fb0a3b44b51a3d4eeb9ba073f58264064a619cf2b62> [ 536 0 R /XYZ 62.692913 479.83685 0 ] <7fb8ea4524143bec9882dd399aee531a60520258964242c6195a0984835dda0f7fe85da91a5e518de7b28c9aac109e3532b5c67a77dab2a9e57374f34043afb5> [ 821 0 R /XYZ 62.692913 417.18685 0 ] <1e102dc833836f96df044baeda0baaab178d89bc0fd156af321cccf8a21366924dceca8b325dd99aa19a5e978e6a5020168a696e62a70baf95e0886bc0587c62b4f88842706a4dd9d5f36a2ac2307d43> [ 425 0 R /XYZ 62.692913 441.43685 0 ] [ 457 0 R /XYZ 62.692913 378.78685 0 ] <4234eecba412a90037c5a324c418ba02d267efc344454ebc167fe663a336d19acc8f148df73338d01fb5d10dcb65ca5ef5129f60e61ea8c373a95b62af7017b3e880043efd6525243a357b731b3c63c2> [ 384 0 R /XYZ 62.692913 452.38685 0 ] <48628d61ee8b3752bd7f4109c4faa208de837130880d96a4cebddfd9d2b84f8e60c8afc21733c41c439b0ce0343e2a5e8ae0acb66d132180d93a3f5d814aa8c1d1d98576bbaabadeee990ddd9cbab7ca> [ 949 0 R /XYZ 62.692913 497.43685 0 ] [ 111 0 R /XYZ 62.692913 762.69685 0 ] [ 245 0 R /XYZ 62.692913 378.78685 0 ] <35da7d9bae824347b902f33fe73a0109782d7e37709fa467977db7cf5baad4ccaad99009f19c323311edcc64fbc7207dde8e0f7087b0c66e5fb0c9187d69ed40f057ecabfb9733b8db0a05fbfaa6ec04> [ 685 0 R /XYZ 62.692913 434.78685 0 ] [ 918 0 R /XYZ 62.692913 396.38685 0 ] <6b56b03c49158fb857ea6e32978b89cdccafa5c45ce343ea4bbe493b22e0b0ab32bef328c5f79c645219023f7c0f8d65025c8f9b9c829b2a9d8b8f187cd6c6445624d3d10582c04a04a8d7884dfe95f7> [ 871 0 R /XYZ 62.692913 361.18685 0 ] <4459d76e6d3b4f1095681056e32e2a4037ea5fd1063d6081575b43acb75134d20d418b221d2d4c68341557d9532435b89cebf59485eccdd65593d2475ad1cd913916e3571b985874a1ade898a519d805> [ 231 0 R /XYZ 62.692913 478.80685 0 ] [ 359 0 R /XYZ 62.692913 434.78685 0 ] <69e55ebc0e24b0beddcf0a2e453c8bac103aa89355973827fb8e9952febc716d4e90f9a8d5eae1ebc35a301abdf322423f1d94316ac953aff40bf52315ad6a1dff5584c587c35bb582629aced4660776> [ 926 0 R /XYZ 62.692913 479.83685 0 ] <9f699b45c0dba0287693f9c4e2539e2d5295ffbc6cba518965836f5cf909523d14d7601efb9b7a45a876a22d5501aa9d38f2550b7aa7ddd989c07471778b59ff382ac4514cb05f03a8b5c182cf572cfc> [ 884 0 R /XYZ 62.692913 413.98685 0 ] <0ddf5a4076c2fd7259bd03a72f56f112107be61a967f3b049efaf0fb4a98533a8b874cf1ea358fcbde22d82855b4d46227e7389a078f05331a12d6c8875c8a0280da16b19e54fb5eeb2ccc7e1a2f31d3921e5f7ef728d9fc85996f8fade7baff> [ 429 0 R /XYZ 62.692913 479.83685 0 ] [ 957 0 R /XYZ 62.692913 497.43685 0 ] [ 619 0 R /XYZ 62.692913 434.78685 0 ] [ 861 0 R /XYZ 62.692913 396.38685 0 ] <4eb7ac6e1a5da65f1f68cc4d988064a077e8342c54e6e52eb07386e7681be115d7d099a058a8862f513eee2c4ef6807426d3afab3a1b24d6c2fedf65b40aa098f5a70d8f8f70227a383d0c871590c0fc> [ 353 0 R /XYZ 62.692913 413.98685 0 ] <554cb9b7fc16ca2b6d5fb2e549440eff9e3127c567384bdec34f78e3f22ba1dd85e8fc1cc39d9a3b73166a26b5ce4dec> [ 518 0 R /XYZ 62.692913 535.83685 0 ] [ 866 0 R /XYZ 62.692913 413.98685 0 ] <89f919c6a56cbcb78ba23eddee090f2bfe50d5310e8f9ce6e95ea772a418b92bc47f41b4b192d7eda4b95c5b57aa1a2f0938c38d3ac9efbab006793c36ebc7ee> [ 331 0 R /XYZ 62.692913 396.38685 0 ] <09593ddb40443617b69184927d879bf243454a2a55deb9df6ed969985c1260f768c28bef02f4500dfd3f8b6a896bb9bd3250884bd2041dd33e93a5024ef14a6a> [ 891 0 R /XYZ 62.692913 413.98685 0 ] <2bc2146d70c62e311657ef97ea9318a65643ee2bee363fad51e64b189c1d3dcc25b9f3b551eba4fa6aca364c3853933f5c3871705dcf8684b7fdf956660a6a79> [ 748 0 R /XYZ 62.692913 396.38685 0 ] <0bfa73bfbca647480c47220df59bbbb8daeb810b875d7e4931a6c8b4b51b7a7b272c63cc450f18238c5c4968f518b841d6acbfd97c64e79da56b0fb317d08370> [ 554 0 R /XYZ 62.692913 396.38685 0 ] <405fdd61067ebef629854753735caf110c5431d3bf06d960b4c3846b19213574c9ec802ffc17a0e3713364e049d9105c> [ 770 0 R /XYZ 62.692913 396.38685 0 ] <624f62c68ddc98655b792bba4adf3ad4e1af0ffb0bdea1ef73f13dd2430189096c39bf2c717ce7f9cfc8cece706988af90b2fcc489e9f6660f6c9e4b98a0263e4dbe22bc06533c869f35ca756b5259ed> [ 792 0 R /XYZ 62.692913 396.38685 0 ] <01d69566c44c639ccc43961a56845e6ef0f35257d62e5eff994595335e007d9f4e26053e197959211b587c8375b0c69b08f7890adfd8dc69c624eb0d46ca65b5> [ 981 0 R /XYZ 62.692913 441.43685 0 ] [ 972 0 R /XYZ 62.692913 479.83685 0 ] <6af4e691bd5f32f4e5b76eb8e9d8df5275c84d58591a61261a2d405d3e943e292db9b1de454e66396f4b856c193ef6f28b851bebbf48fab2067d167ef48018fd> [ 368 0 R /XYZ 62.692913 417.18685 0 ] <40ab6a37b5df1152fca077e15f50aa85e82c731428e60e8b32602ff9b54d9d072414cd0c8f93d0964dce43588d4cc7750ffc7f1b004b721af07ed9cb8a35fdcf> [ 575 0 R /XYZ 62.692913 396.38685 0 ] [ 260 0 R /XYZ 62.692913 396.38685 0 ] <16857829c7a7e0c6217668d1f33b8d6b5d338bef8727ab743342653f3afc453d79ccf6b7ac487e2306f3fb2c95d9115c7953d356a65639c5858f73c7ae2ea57a> [ 236 0 R /XYZ 62.692913 396.38685 0 ] [ 647 0 R /XYZ 62.692913 378.78685 0 ] <7d8b02ec56729bf37ae93b8fa061a6a203f4a872fe4b8e531e2278a574c16d75984417275ae6a5713906bc9858ae8f223bfd220a78209015db60899438e68f0790e595160c96950ef294a75295255625> [ 305 0 R /XYZ 62.692913 361.18685 0 ] <1097ee6d8e4ee5d3676274d318202a3f22e416992776f8b531397ab767bdc397aed481f19f8b69e80386a07ba7c3bace> [ 759 0 R /XYZ 62.692913 378.78685 0 ] <3d1f1c2b44744aee0fc88b1a23eb3f2d3ce35085ddb0636c18bbff8622729dbe059d2e4d039b863a185616ecd7bc09b83f51f4ce9e88b3a874df65d652515acf564242f4552eafeb166bd733a03b9360> [ 295 0 R /XYZ 62.692913 378.78685 0 ] <4c82f0f8a8438a08c7c856d0ffd715c95c76de1f3bb4cf9aa61916440af648a775cce2ba3d377b7a64ee2df7bb20c5f8> [ 726 0 R /XYZ 62.692913 434.78685 0 ] <43e965ee815306aa21e508fa5b0e216018328c50bc5d56d382f87b87b3c4b9e2d605929ae84dd545d51cc74e5e33cc68a538db52be6ac8472c585a154d695276> [ 781 0 R /XYZ 62.692913 378.78685 0 ] <24ba129faa85e850120f4d53724b2cb898ac7723cd8623c2e4fed46d177cf1972601ae03cd8da2e9d5e607b133ff52d37c43f06ee3310e3646cfa5e91f53c82f> [ 597 0 R /XYZ 62.692913 417.18685 0 ] [ 813 0 R /XYZ 62.692913 434.78685 0 ] <6c6265c110a0bc48c4ec436e70ba916b3bbd3b1322a21c1ceaaa3b4fe7d9890440a4a739317b099d7c122c0ce3a7e35857a9a47f485833b7ffa3ec2452a2ed28> [ 363 0 R /XYZ 62.692913 452.38685 0 ] <4ae43d931b62053526c5bb91a5a1c8b15f2244fce695af0dc6b938c44caea956379e9c1590312bd5db06ab25a26a19f1d77c7cc51be273696c9488da985b130e> [ 471 0 R /XYZ 62.692913 452.38685 0 ] <51ba2c492c0679c45649bb7be70405cab81d5935a2f72c3692d5ac4538c414583f8ddab40018385e7fc1447f64ca888fa836758f1219b3a35cd8f120fbaecc54> [ 463 0 R /XYZ 62.692913 417.18685 0 ] [ 477 0 R /XYZ 62.692913 434.78685 0 ] <84ed054bb115cf170b3d2c44a1cf96267fcdbfb997479ed884bbc79feb0c3b8f2d2e8158efdccf594088b88e66b23672d5c0886e931ded9d48263dc81ff6d29a> [ 630 0 R /XYZ 62.692913 417.18685 0 ] <4a1db4d079d0db02416a9c3e92a33ddaa4c68fa8cd375a47c23281b183e356d5ce8b2e08a88694bcbc3d4da985e8925b1c6a667a7622493b6a93774d8185f563> [ 251 0 R /XYZ 62.692913 396.38685 0 ] [ 398 0 R /XYZ 62.692913 396.38685 0 ] <6fb180f46db064cbdd69c5ab44bafcacff689bad201c1c3258445b85ffcc2aaab9e47b635e11eb99f23fc8f5e5b4c0af2a2307de63f7490c4638c1573caf3bae> [ 526 0 R /XYZ 62.692913 497.43685 0 ] [ 490 0 R /XYZ 62.692913 413.98685 0 ] <1d5b41c62b30798375fa9ac303b993e624d3dd1149002336bc4b0c88e6bdc5990e3bfd92517b972f1ee4314c824b662a47ff413bf40a063eaee4392283c51698a2383e869cedb88a194e0054e8daacf8> [ 674 0 R /XYZ 62.692913 378.78685 0 ] <44f1303e0e54de74dc95f09f892c33d0610f72c65a0a8229a7e2aeee02f46c078852bf6d2a519303c9281cfa787ed218d8a531cd93eae7d8d78a4d75648f4f42ba2ef367ca2306db75fd2570522f350a> [ 269 0 R /XYZ 62.692913 378.78685 0 ] [ 442 0 R /XYZ 62.692913 479.83685 0 ] <622a55fb9262fa0b99e306dd3f10f521249f0e5de8e12015287d7e7ba08854af93718c52eec8f688a0acf622e506807eab57318843a698fabd625de2ccc62ff8> [ 321 0 R /XYZ 62.692913 378.78685 0 ] <169e96439f0c94b120085602c325a905a60f7bac7bea7792ba42bd3e12091af50d9444cd2fc22b9bea0415a5ed5366fce75038f48bb555ce35ec9e0bdcc6c6495d2f505724a007062f49834c9449c9a5> [ 965 0 R /XYZ 62.692913 479.83685 0 ] <6b094e8dd0f152452dfd5c0e6f8866ae81711b89125e206483a3e17890d10df456c5552c5b926afc9a775c5ed7504d0d988cbdb2fbdd36801ce353f9e7f86aab> [ 666 0 R /XYZ 62.692913 378.78685 0 ] <94bb46f5b6e2256d6ecf1db0b0506d62888932d1f466bbe5c35073255db8d88e4bdd7a4916848fab000e9cabe77ea958edcd62bdad65d71d0b64634257be6f0bc4e0cb857e3a9790b12be15ee805f72a> [ 640 0 R /XYZ 62.692913 417.18685 0 ] <824f1f7012a2aff00df1eed23038075a71364c4408eaffe46408fd3f5db0b7aea2a2fcb225221b114a6d4ae3a7e1ab82247e1ecca2246ae9a7fbda66cbdc5a24> [ 416 0 R /XYZ 62.692913 396.38685 0 ] [ 388 0 R /XYZ 62.692913 396.38685 0 ] <3ec2615ca38efc608d5e6922991dc3c81d4540bf3dca9cccfa83ede2352a8cc91218f8645cbeae99197a66da9dc96fb8> [ 842 0 R /XYZ 62.692913 417.18685 0 ] <396d6787706806404048cf0749ce757db092737a835ebcb8d2fcaa794318fac6bac241f32f0747459f3c7125dda2dadb481db8b09153e4ad92dc5505007452f0> [ 586 0 R /XYZ 62.692913 396.38685 0 ] <934fbccc5b23957e58e79dabc403a4d4b4edd125fa71da39bace4a36a8857bf4f380b0370d161478a2a7c9b8c8a1763a2b8fe9e0fd60ca58058e63a8b5ea4566853272800cd16f7f10d25367d15bf9fe> [ 933 0 R /XYZ 62.692913 603.13685 0 ] <01799eafbd911df7174a32e28a75cb866afe0ba0d2b34ade8c2f38caeb93876361bcc961e459dab6958b937437d10454cea685286cd1b2713ac70d1cdb223d7531b193eeacb292bf3721f56e997883e0> [ 827 0 R /XYZ 62.692913 396.38685 0 ] [ 342 0 R /XYZ 62.692913 378.78685 0 ] <82a04acdb64bf782a1a1c702d6f57f7c962b7f0c2dfe5f154d0d6279dde7942b1a87bc69bee0cd95344a980fab1325fcf6898ff4ec133510f7a250980c68ed06> [ 607 0 R /XYZ 62.692913 434.78685 0 ] <524991e09117549d2b6948f3521679bfc93c3e10115426c22f84224313007e91f110e473ceae617ecc2e11ad343a73ff60b92bee0eacd55da764155015e1236478315598f127950f0669830b39cf7499> [ 544 0 R /XYZ 62.692913 497.43685 0 ] <6c70fadab919a9d4516652e5544d5da7209b04324368b66f64fd0604c95075daf3d8edc8fecddae3fb7e9d6e0704a0b8cdd96b8bf982db516a17a16a1125785202f4d96bdbbb837ee34be3cdba7bae5a> [ 704 0 R /XYZ 62.692913 452.38685 0 ] [ 282 0 R /XYZ 62.692913 396.38685 0 ] [ 564 0 R /XYZ 62.692913 452.38685 0 ] <8fbca6c030f828ffd70fbc2dbee6bc28064e74c83a5460448ffa903ed44ffbe596d6023a6073a2c4be3b5396bedd50f03e69ee740faeaa8faed4d3be0529ea5a754fc820d9f0f6d4a5ce5f16f178e7c7> [ 657 0 R /XYZ 62.692913 378.78685 0 ] [ 434 0 R /XYZ 62.692913 479.83685 0 ] [ 408 0 R /XYZ 62.692913 396.38685 0 ] <65067933c79766d08574fe0be36c91e419d57f390b4c0414e6fd02e0e7d2b831042782ffe2e5a9f89dec80a72c296b55> [ 736 0 R /XYZ 62.692913 452.38685 0 ] <99bce896dff22f4d2ab79ef4ba19149231e75d59f4fa12861627e0894fa63c92e51b0ae93f40e3bf89636669f654952573108944451c2ade7f39c1ab4f5519f31f0d53b4a125362a194435b57fb52029> [ 693 0 R /XYZ 62.692913 452.38685 0 ] [ 803 0 R /XYZ 62.692913 378.78685 0 ] [ 506 0 R /XYZ 62.692913 396.38685 0 ] <7a40df458c1046aff0570604d5c39a667c48119870646803a3da757b3938a55afaf54da41e71bc4cd66fea3983dec726789f17ceca1e3b981b30aada9522becc> [ 836 0 R /XYZ 62.692913 434.78685 0 ] [ 539 0 R /XYZ 62.692913 568.46685 0 ] <6511fb6b71a68592c1bfb3dd7905f63abaf3ba4e4e5a54a9cebd0ae121888e434b0a8da05bc6ec50270c1aff6be350d3ec0ab663fe440c1193b6552135b3e7933d92248a92c247c6dcfe8790b0fd5775> [ 529 0 R /XYZ 62.692913 476.51685 0 ] <305500ebc1267273bbac2a824a211b72cbeb1ac6f5f396b99f7dc6dd7e661c731aa10a06112e708dd10da3c393ef41b636fc3f693b9a05cbf29316cdee73fdb3> [ 284 0 R /XYZ 62.692913 131.05685 0 ] [ 376 0 R /XYZ 62.692913 636.94685 0 ] [ 984 0 R /XYZ 62.692913 467.74685 0 ] <4923318c5f8a7c76a0420bf1a6c96b3cdd094e92883269ef51f410bab47ee0b5b9654e3e4db8f36c9b5bd5fc8439005e97d5d7e46b06985ebf0aa6682635e8d3> [ 381 0 R /XYZ 62.692913 503.26685 0 ] [ 547 0 R /XYZ 62.692913 247.73685 0 ] <44c0d1a8f4ce1363c0d00a2d2bf8a6df76c27e54ae8c6b79510a1d9230b1db789a540c97a3e5d7b6809c7c442dd0492569bca5fadee0a5f77b9bf321d6426d37> [ 445 0 R /XYZ 62.692913 246.23685 0 ] [ 960 0 R /XYZ 62.692913 248.03685 0 ] <8de28fc7e32931041646e45833751db3a3a21f68a5cff66c7ded0f1425c7c9b63b2f6534a0055728fa6cccd12ea891ac821d040a3221c2a5cd89a664ae93a9090de7ba649c40cc578b65ae4f6aebdf9f> [ 387 0 R /XYZ 62.692913 761.94685 0 ] [ 308 0 R /XYZ 62.692913 404.53685 0 ] [ 968 0 R /XYZ 62.692913 332.61685 0 ] <041c2ddd14bb47bf50ed9bc0ec389cc74c4028cf85b53bd7302050c102f495a7d1245272b486040c93a6ff9b0be81d2f8fb05df863e3440542fb9d10627c44c8af39f57b89205b80fc02faa888579e67> [ 669 0 R /XYZ 62.692913 542.86685 0 ] [ 633 0 R /XYZ 62.692913 219.73685 0 ] [ 975 0 R /XYZ 62.692913 174.31685 0 ] <1c7886304bd25064be7ef19c94dd75b65fb6ce07e1275393aa88d1ca2698ee2b409bd5530ead640b7d12b3a050adad247a30ba6ab98ea6c3606937dceb5abbb0> [ 784 0 R /XYZ 62.692913 704.24685 0 ] <19cfa8395c11a71fe8e35aa7752ec513fc01e331a20ee28e464df5444337e56b95c3f19bf80f68d27cacca2e3d61fd60d29851e22eea644e5abd2c7ee0188c84> [ 751 0 R /XYZ 62.692913 603.66685 0 ] [ 650 0 R /XYZ 62.692913 418.16685 0 ] <39badf0f93f3e3c140ab3f76d11a350addf4934bfb7b5d9ad6afc3167f7eb451d3f740868336adaeef4523701b662a75> [ 239 0 R /XYZ 62.692913 704.24685 0 ] [ 622 0 R /XYZ 62.692913 785.19685 0 ] [ 864 0 R /XYZ 62.692913 179.93685 0 ] <1c4acd61c8eae6f9fc17da37fd1218fec1c8c3909356145ca83fd69af05f1f58ef83c2ef12e630cb016f6ae7c28eb8f4142f99ef6f34f1f2060e94b902b116d0712594033f5624cf4ba9597d91ed5361> [ 946 0 R /XYZ 62.692913 785.19685 0 ] <2e9399940b4d4c468175a8f3a1ef13e21db0e12a9d14bf5b2fc4890338f4dc6702cbb08c8420141ceb67be39abda9bc3a8dfbfb45be680f0d28c37a2766ef0c841bda514f3eab684ed9b69adf28303fd> [ 432 0 R /XYZ 62.692913 515.94685 0 ] <6fda54d2e1b3ad471ce632a46b19d4171f5d5ed23bd67435fccda4fefe6e84eef0e78eccd27791e902330a8ca7f42dd43e7681877bdd7300036acece711c2735ecfae0e1ee6d03e5e36aae7ffa5b79f9> [ 921 0 R /XYZ 62.692913 603.66685 0 ] [ 437 0 R /XYZ 62.692913 227.23685 0 ] <0c87627152e3166bb4fc2e531236501baf5dec2b6f2b2ae52c47666cb73ce4663a2f0c3cb5d4badd234719ac97713b96454ccd8cd96307cf950f8b49760a2ac446c61475789d77b847d2de8efeb3bd75> [ 952 0 R /XYZ 62.692913 297.56685 0 ] <37e76c489fba531b3365d9dcc1e0a6f2c3537ace39aab38b704a30a5ac6e3b98320526a460dfa2dd7c472c53ef54c637fbb5cfeae249563b97dc22d9ff2946c3e46f6f3fa061341fc1fd87548b2a93a5> [ 806 0 R /XYZ 62.692913 603.66685 0 ] <4daed7b6c4ba4d8924d2008086df4a0dca2eeebdd7c17e232ed05111ce8ec50114a8f4952c36d1a34e7412927dbb5ac7b06ff357caa0d6d15985bbfee209f62d789934b44e0084663ce0ebe4a094be87> [ 719 0 R /XYZ 62.692913 704.24685 0 ] <9db62c049138ba3452fe51678a5dfa22caa5adf30634e28e9f764dbe281309b3c885c77abc6da1d255f38bb41e5bd90c9542e63878a0011f76d516f6e981eadb> [ 521 0 R /XYZ 62.692913 423.91685 0 ] <2618e760961424e00e46462d13264142c073ea43ba0c13bb96fe9afd4bc2ae6a6a02686c1bde4b4878a771e914e9bbc0ecc38909ac1d695e577676db8db596cf> [ 599 0 R /XYZ 62.692913 140.65685 0 ] <98d9e99cd52ae123f0880e94ef3d950ae4e54f5fa222658dd8213ec51ee680351e38a1550f395f1e4e114540b0cbfb2c> [ 913 0 R /XYZ 62.692913 785.19685 0 ] [ 557 0 R /XYZ 62.692913 785.19685 0 ] [ 324 0 R /XYZ 62.692913 182.68685 0 ] [ 610 0 R /XYZ 62.692913 704.24685 0 ] [ 254 0 R /XYZ 62.692913 196.03685 0 ] <69f877ad57fb892b1ff2fc8f94e8075395ad8953904aaeb6db0df9185032f2b6128139d9ffa1af6e79234a89c7ad5036304c067e531b7881de4d05c02e78590e> [ 361 0 R /XYZ 62.692913 151.65685 0 ] <7e0ec5004d8ff1d1d6003c758e0648a90cf3358392d808c58e55bd3cebcebb9a5b44b0150ee9be7dc0d5ea1df8e41c872ee862e2b32d6008630d4ca5beea39b4> [ 707 0 R /XYZ 62.692913 785.19685 0 ] [ 480 0 R /XYZ 62.692913 785.19685 0 ] <819b498f6040577b6b3f24b4963ed7209aabb50dcd8e866ff30b8c0b419e1172a205f63c2992e140cb30769ce4e042199e7d64de4f34662de9adb24a3278b08d> [ 273 0 R /XYZ 62.692913 785.19685 0 ] [ 936 0 R /XYZ 62.692913 264.03685 0 ] <731afc79a5b57805e55e8f556d25b22492614044f1623bbb610da4c01279b170618cb4c19420f24e88c0a376b6d4d983850ae251f40200b1bc71258de1e7244c> [ 761 0 R /XYZ 62.692913 134.55685 0 ] [ 740 0 R /XYZ 62.692913 465.66685 0 ] [ 263 0 R /XYZ 62.692913 785.19685 0 ] <3fc3fd5db1c8682c5241f4938b400e611f1bd31be4007dc6d6733f0de984d7d9d95de5d0c018d967caa278df9283b13a3e2c6b7716361d1501696dc4787fbc422a331e794d5a2f17451917f4ab47072f> [ 366 0 R /XYZ 62.692913 761.94685 0 ] [ 695 0 R /XYZ 62.692913 159.65685 0 ] <89def36b8ea07474f5709297c56d4b6cf8d6bce0eaaab6f08524a9e35df95663616fb87ad1e8412d8634b7a38f8d13db364893d51e27fd518018f87dc91611a873e910121a2277f0f549ac26bacf4649> [ 728 0 R /XYZ 62.692913 159.65685 0 ] [ 894 0 R /XYZ 62.692913 761.94685 0 ] [ 688 0 R /XYZ 62.692913 290.24685 0 ] <1c10c70ed0785dd4235ef9762fe0989385f2cddd44cd9699c20843d2910e060842941fbee75b4b83fbd343b54a72df581418eadefad74c8ff90b26ec8dba2fb1> [ 677 0 R /XYZ 62.692913 307.73685 0 ] <531b1591a1fcfee77cb61913b0dfee6a9aebdb48b2e7202ac83127708056fef2ca31498de3ed45acf2c78905215add977284c0fee08d7e101115160de9cef834f85a1849bd4ea03b6216f40485ada3fb> [ 509 0 R /XYZ 62.692913 761.94685 0 ] <44c3e78b3232292beeb473e30c070bcc0389b6e34131a27c9a31a3820dc524d75a4ade486e0df5dda12722ac7a250f57791418f06eea4710098e7b37a2036d17c7267121248994ec6a903ea917370a5c> [ 371 0 R /XYZ 62.692913 761.94685 0 ] <23602ea89aea927ec7e7ff8d78ae72c7fba33a3474b0d5742d8728f1f78fcbc8d5e2105e92caa781bd141712610a9a1404018b6e708edf5d535218730d2845f0558953217d9a3abb08aead367395d645> [ 824 0 R /XYZ 62.692913 785.19685 0 ] <9af2f80fa5f5e02937ba1a2479eb05a6103bcfb6f9531b2e4d40feba9b0e0367548af6276659b15267822b22ba93f0d9aa6ada78e5529f9420a60c7dc781a7144d10f270903b29f8ebb6e53c8b1ad9c7> [ 869 0 R /XYZ 62.692913 603.66685 0 ] <36fc79e681bae64019df94dfb99b1bbc1b26d89401df88aeda6453206a7d8db2269acd14e75afac606ecde63afa3a35cb250066c28708556489335c3873cca86aa354acb3ce6d6a23581439b82328d09> [ 460 0 R /XYZ 62.692913 386.93685 0 ] <2278c4d18e92067ccddd6f886912cd0a78d5e5df609d3984cb74514976215b270f0c11631cf241fea0fa95a1f78d11418f4f891e4ac8d4a1f40a980e00c6fd15> [ 929 0 R /XYZ 62.692913 422.31685 0 ] <9104cbc91ea2d24fce7b18f029ec39d078d74248a3c6f5348f10df4e28443df3ee9af70cd2be89c5a1a6fd69f2fbc28c3ce967fdb2825407b11a3c86a5a9029d> [ 588 0 R /XYZ 62.692913 148.65685 0 ] [ 102 0 R /XYZ 62.692913 761.94685 0 ] <5546681a44e6fe618397a6b61aedb8b968c8418104a7da149a3a18f6f78651c1> [ 170 0 R /XYZ 62.692913 762.69685 0 ] <883965a772e5cd1cc59b360d8b5ed82e7abce5574abacd264d41ac1fb955a62f19cddba15595dd4191810637195561cdfd5de3a052e75b7a295856a6eef2ff4e> [ 815 0 R /XYZ 62.692913 223.00685 0 ] [ 400 0 R /XYZ 62.692913 160.70685 0 ] <96fd446dcaed16941761d5a63b97dd5c7706118cc3fc0dc4a6e271c401f0f0f9028ea05d599eaba3bee774b9498e1ea67affd6413875666563f245cae2792939> [ 356 0 R /XYZ 62.692913 242.18685 0 ] <4066710665bf3625be30572086ba34df9ea35ba7b9f2da559d5b14928052d28a810411fe74716f6d54477107c5bd2b28dd18d94b0667508ab524444e67f4a449> [ 845 0 R /XYZ 62.692913 245.28685 0 ] [ 830 0 R /XYZ 62.692913 640.44685 0 ] <8530f1ee4705526f6687466e047a06c4011223653661baf1fac82c6d9cf99db36f0c6c958e53c90600e37f8dc17686bc989c6ded5f23c11e81d328686694072a> [ 418 0 R /XYZ 62.692913 146.30685 0 ] <822c7252e53263a4c9148aeac490b575bb55a4ae5319f9188cdb62d28c5455d5f14037f6c9e165b2b898bfc65d5b3054df6317a3f6eb537aff2c2c9f8663d930165e7977d22fd4612d25594ea1fe0e40> [ 334 0 R /XYZ 62.692913 704.24685 0 ] <3775a6edc608c77e1185b7e9659631f951c7fdf2599f924186d3599c5e2ea3a0e4697b620d3b9d41485c105c6190aa84bb9dc89d6a066ba34bbc880619bfd630> [ 795 0 R /XYZ 62.692913 257.43685 0 ] [ 345 0 R /XYZ 62.692913 658.04685 0 ] <024cb2f2f161860ae19ccd30d45d5e4ef9736453288249b0a47bd5e98030e101a0d4ad6772bbb320b4c06438c3fe4484> [ 773 0 R /XYZ 62.692913 603.66685 0 ] <0a2f0915eafd954ce569a632c449d5c3c138a4293d2d797b946cc4a731fc5bfffb3b3aefd9cfcd0016e27e81b1e85663384f757b614306c4fe6bd77ff15c8c1d> [ 298 0 R /XYZ 62.692913 369.21685 0 ] <525d7dcc3472f79961ce703522fe1d0d2b762276d5d381ebaad393b582d89d5643c5d5f352291b44548f085c7ebf7efb7d4acbe9b62c704b224c7833e4cc60d9> [ 660 0 R /XYZ 62.692913 505.11685 0 ] [ 466 0 R /XYZ 62.692913 497.06685 0 ] [ 474 0 R /XYZ 62.692913 203.13685 0 ] <0157ef59fa926171a2b3a224e5e737edb5dd43881ff5c4aed38f1316bbf08ce09d5dbfb92c9b2797730ae26462b63ff74b9e69013ee8164e1a9bf6b37c191a17> [ 886 0 R /XYZ 62.692913 131.05685 0 ] <0c1441ef754cbdf4aa9b932f607d23b19b5f810eadcb4e48a2252fdae03b1f0399c2faa9619ec4e37c648fbc3f617132c40232fde28f86e0acdec1ca73716c42271d0ca09955b3f661c0c99326452956> [ 578 0 R /XYZ 62.692913 785.19685 0 ] <299064ed8008c71f1a061bd85d11b8fbf333476b68c47541ab0328229eeb65ea507c806e5b9ed01a4d4db6edc763b611cec3b31e1b5c1ad28e68a736d7c57719ed56cf8a8c350f9a7e85df31d76b52bc> [ 901 0 R /XYZ 62.692913 704.24685 0 ] [ 643 0 R /XYZ 62.692913 465.51685 0 ] [ 248 0 R /XYZ 62.692913 297.51685 0 ] <527ce4f4bc862e5ce3890aff9a5f27d468d95dcfe149eb1df6e3b1bdfed07a0cf701304ed83bde90719319537fd706c23401239b6fcf3e372eb297542151e355910a07d90218c66dfd5a8e739640021a> [ 494 0 R /XYZ 62.692913 785.19685 0 ] <077094845e4b4bf153296bb22b2bb2d298c88d8d59863d51085724708ba14e587aab5a8ea3d36018506edb717fa95b9ef27420e703ed99f55c40ae9b82f4f4d1ff3d722c8ad5041e5f6d1a8ec13ef4da> [ 566 0 R /XYZ 62.692913 159.65685 0 ] [ 906 0 R /XYZ 62.692913 603.66685 0 ] [ 839 0 R /XYZ 62.692913 785.19685 0 ] [ 410 0 R /XYZ 62.692913 514.53685 0 ] <04357796b0b80def13a2d79955b050d7caff2fe971aededd8429277002dd7c1323764075e46e531331e6a9a0e3631f83d6a2350f317e946f72c52c8159a05d2f5d7643e54ba6ffe3fa241113b4081364> [ 875 0 R /XYZ 62.692913 560.46685 0 ] <1f5edb997656891f8d3e461ff277cddb3706e697561ed5dce07b6c9b2b90663988cc8bf5d6d25ccaa345b612907075dd5d82c811c68427a42c69b21f4d80e54c> [ 390 0 R /XYZ 62.692913 326.40685 0 ] <80755cb6e4f85ac07fd556788e823a9800e089ed1345d9a8b12b3715ddbc1c5ac68ffd46cf726ffdb13afae1e8dfb040> [ 170 0 R /XYZ 62.692913 719.31685 0 ] <1c6ea8b1ef7d4445137c77bf16ad400d1ff3c3e07b61d08790877c24290e76d05c25cf8a96e12618e6593bb1ac1cfa8c0fe80099990213b0fd04e2fe268f2c5e> [ 170 0 R /XYZ 62.692913 222.26685 0 ] <1e222f7403545d3a4a232060be12d923aef175e2a137748d3d5018bc135e054fbd7143dcf20441150fa8318aa09a4c09> [ 171 0 R /XYZ 62.692913 412.09685 0 ] <3fdf7029149d3c269190ac5989c37a330d4014552d13338b2b9898a38bb821e66d0b42672f2eec13c651cac75008d9e9> [ 171 0 R /XYZ 62.692913 187.34685 0 ] <9bcd286922f49c7b14057938e8c22f048055d034c68b8d098b010e1b2d6c9becb324224b72d1a37a096c19f31a064301> [ 172 0 R /XYZ 62.692913 467.09685 0 ] <4d11426716315ee1ef7878dd76b70ed5ba55f427198d87947da45ffc816125abf36ac0986fc65d676f734e5ddb396d071e56400ab39a629c44ad32f0569ba0e8> [ 255 0 R /XYZ 62.692913 761.94685 0 ] <59099bd92db60812504de1d72a330274cb21690831b831a00c076e6834ae224a3bce0ba1ff0a8fd2d7eac4b5b80c883235ac6659b9d9127626ae30a60064291b2f5ab4f7ac6fe69fa92906ade716dc1f> [ 529 0 R /XYZ 62.692913 269.38685 0 ] <9ce2f0425d6c5b84bc41e7eaea4cf44d94e8408ce983aa7b786223a14d8d99b7f2ee03475b2953af11a4a76f670e63ea> [ 913 0 R /XYZ 62.692913 536.36685 0 ] <5dae8ee57ccd16c7e471c0c90aafe3f1db834be4bfe966350ec8eabe5160753e56bc691428860be5afde3f556eaac0164c5f544dadf66d39d10d14d7c4ecbdab> [ 273 0 R /XYZ 62.692913 491.76685 0 ] <39df730aa22ef05f4ad55f0d6d227319a1b6c96500b497516df6ab942503aeb7805c032bd69a9e7e7d10a038bd3fbaf888536b51447246cb80b995dde3c2c16a> [ 438 0 R /XYZ 62.692913 704.24685 0 ] <0ea680f1309cfbb4b7ec9fa659d14978941953d9c62278375a172ad47f0864ce65a1ca12cbadd05ea087dc0c64cbf71f> [ 480 0 R /XYZ 62.692913 603.66685 0 ] <57bcb31bb7fd5fec04328955742d8c31ab81911d2f49ac5c550b0b1d53360094352a8b5c25289c7f6ae88f57ef009605a411e6fb762cca89a4d072e95faeee68> [ 921 0 R /XYZ 62.692913 396.53685 0 ] <29449bd481c2571012803cd38af569cdd0fc84fdec2ff2d05c01531881fee4a0c59a9be833a5e1a0bd786234932ca950a31547e1d58c457f4a92df684339bd8e> [ 249 0 R /XYZ 62.692913 785.19685 0 ] <0533adc01dd091ea8953284816a6610ab1754cb427e8ff2f08a7ad177f605a1a8e0671852baca62dd19425bed9f30305338d91e3d98bc237b9c48d0cc3f2ffa6> [ 634 0 R /XYZ 62.692913 785.19685 0 ] <65841fbe6b6b97430edaea94165374e1b59d28870177221b8c78f150d2f716e10ccdb0da32ea5f0238cf19530de06c36633c6de1e72a85a514629d6ef1c96801> [ 784 0 R /XYZ 62.692913 505.11685 0 ] [ 751 0 R /XYZ 62.692913 308.63685 0 ] [ 650 0 R /XYZ 62.692913 236.63685 0 ] [ 622 0 R /XYZ 62.692913 603.66685 0 ] <2d657d967f99414aa0cda8d7d2e6636457447fa88e42e5d3345c0c1ffbda38fa5db6974852a2271c985c831f329d17009980dac474662073e32325d272a4b977> [ 285 0 R /XYZ 62.692913 579.74685 0 ] <4c9ee76dfd08dc837e2fadd8be64d79a81fbc99e5b574e99476d910a80dcf687c3de7402b2dfc44cecfcfa904a3207d033daeebf00f6e00a1e9e0d43a51bc1e1> [ 475 0 R /XYZ 62.692913 761.94685 0 ] [ 362 0 R /XYZ 62.692913 652.94685 0 ] [ 263 0 R /XYZ 62.692913 586.06685 0 ] [ 806 0 R /XYZ 62.692913 378.93685 0 ] [ 953 0 R /XYZ 62.692913 785.19685 0 ] <43bf82664ed6530417b6049f7484101482d36189ad3ab380dffd611cb62ceed84aeefa613c1ac3e8dd47956bc308222d970f8d920dce49b3079663a55eabb0c2db9f3e5d438fbd02f62f769cc27d2f3b> [ 521 0 R /XYZ 62.692913 207.18685 0 ] <7068b6687a532a56dfd57f38a26ddfe86f98f5cca759ccdb1dadfb542cfff2873baaed4eeadec4a9a7969c7d4378af30334e8ad72a818301aeffa89c99ec06dabd216fc3d0168b8acabc2e50fd608b92> [ 557 0 R /XYZ 62.692913 450.16685 0 ] [ 334 0 R /XYZ 62.692913 479.51685 0 ] <27344b8bf59c8dadf8f231d5baad2c7cc549f40fa1ebe1426c116e799f53f27f84774171e9a7536489fe09d84e63dfa6e619cb1404835c43c69423f8892577ae6431b164396b71efc92b527ae2c5c161> [ 376 0 R /XYZ 62.692913 429.81685 0 ] <8493a54285416bf3386f73f3e7416404fa2f453f415fb3904e670ececb9ffcaafb433435f43a871d5fecbbaf50d709322ac2350b4c4b2179f1b4621f82460b18bb94d3c4636d971471825ca9c346e953> [ 371 0 R /XYZ 62.692913 485.91685 0 ] [ 410 0 R /XYZ 62.692913 361.60685 0 ] [ 762 0 R /XYZ 62.692913 693.24685 0 ] <05a694ab67ec46a6aa5d0d7e594aca75e577f3d504718325b5b79869cf78ac023c28cf94fde5d53f577ca875d82a44482295bbf9283f56295f59f905669f915d> [ 460 0 R /XYZ 62.692913 179.80685 0 ] <26b9ff920d5a4932cbd920f7e02716369f7a0e13fc79ce083449d31ca8a2a177b0b26cadfefabcc8263cee908eeeecbcc2d2a03a3fe3bdf0a691955d805b411d> [ 976 0 R /XYZ 62.692913 589.39685 0 ] <113d4021768e8662fccb910360a12f4736ad13d9d5a2ab2600508b85cbc86904ea3c74da833c1c1e869c700ca2085dee> [ 887 0 R /XYZ 62.692913 547.74685 0 ] [ 539 0 R /XYZ 62.692913 361.33685 0 ] <3b837750c0f6d1884f08eea90026a88e3122fd7b7a27e64b30a80aa29be10b3fe6c6d4ce625f0b1fd953fc39555c8b55594a0c510bc18c5edccdc38aed5bc385> [ 643 0 R /XYZ 62.692913 266.38685 0 ] <20e0ce8ae79bfd3ca9fad7916435ba7db55e9747f1040e50ac4df5e6dd5507b9b0c286c37f3d22d46615594b13464348b345108890966fa52cf0dd60ac8bb011> [ 929 0 R /XYZ 62.692913 223.18685 0 ] <8e8b418740a69e7bc2d4c8f9b7eadf4d30bb34849691738e6fb511d138d5684cb034b99e8d7975e5b5a7d5a6936d1eba31531a2cb9f8cfd9c9d64f7ff926b377> [ 401 0 R /XYZ 62.692913 624.34685 0 ] <0c2e032e035ce45ab5a768b8337eec5f0a8fecd29d4e5b9c51e261a396cb2edb8ef2b6ee82e2f6b9d4cb388ae41ac3476b4b3290ed9d91ce18144bf3b354b6c3> [ 366 0 R /XYZ 62.692913 580.41685 0 ] [ 830 0 R /XYZ 62.692913 398.11685 0 ] <13cc9e896457f5905531dfeb567190c3295f52aa2a2e5ee86ff5eccb51b9e8af37a2934dcac1ad8a31d0ea6b900e873ed7fa0a5142eabb68a7b2a94df746499ad721fc728c510c8dc8b60506dc684eec> [ 669 0 R /XYZ 62.692913 318.13685 0 ] <3350b386882b15ee61b323075210fff92acaf7407a41ac56ca1368648ad7a9154c47e881cae3ce3c87f6751d292cfb0c93af80fb07dc656c23b4728d7ea5b78c53bf8d351bf55206aea44eee63dcc93e> [ 696 0 R /XYZ 62.692913 704.24685 0 ] <02f79d9d88bff20972ee9b8f56212664c95fabff19f3069f2a60756206fa4349cb6b29d49066cde0d0b349084b48e1ac73c43fbf2bb4b1e1e997ce3ae2f1afaf5e657d11182b17a2f269a60d07967b46> [ 729 0 R /XYZ 62.692913 546.14685 0 ] <46f169eddb654446ceb3366aa75e6d8329061daf6aca8371f59693a4cbd7eacea6d8179361fb0c7219263bcbc950b6c99729856c98dcdb95896060fc4a29fec4dcdbce32c1597206123f53ce76adb134> [ 984 0 R /XYZ 62.692913 251.01685 0 ] <8130a3f3188b306e3035331b7f9c1255c2b183f54489f3d7fd004afcedc68c270aab207fe5bf7258a41c359e33f4ab87> [ 839 0 R /XYZ 62.692913 603.66685 0 ] <72e5ea3dfa463ac6ea9cb3ec33861fa24947a1c3def12685a4df295f8af1d23f9c11efc0dcbecee4eddf755c72d7b606beda5b8b29e19441fa1d31994ace779f> [ 961 0 R /XYZ 62.692913 785.19685 0 ] <5815219c9918ff244b9c45369ca6482a1590a521355e31e2be717135e478f31f6f774c68eefd7621dfcac9c979e66eea2ca7c3ffbb45bb62ef614d64d3746f8950e526466fa60b647a331dcdbdf22ee3> [ 678 0 R /XYZ 62.692913 696.19685 0 ] <99c328541af7aa94fd8a0bde6c35f9605e10198ad61eb0548b24de353a7862697092cef60185125e9ab37b1cf93ea7bf> [ 111 0 R /XYZ 62.692913 719.31685 0 ] <75ebd95b66b3d4c444e0b003e99a49def68992400b48f1b6460a80956ca5d7261533a8da73c708dc6a58294ead8dc79ea2cc50e1ea3a034bf3a1b817450df080> [ 906 0 R /XYZ 62.692913 422.13685 0 ] [ 968 0 R /XYZ 62.692913 133.48685 0 ] <3d15e827e03c790ff5f3012f570d822db0cc8f8c01b289d3d0db1bb8ddb1b379> [ 102 0 R /XYZ 62.692913 548.19685 0 ] [ 174 0 R /XYZ 62.692913 762.69685 0 ] <0ba7cbb4a3d66f068138a6ba051ef541ae452afe7ad04b1b727c03f2ec69b1093bef2cf9c8469e24e5d269ddd41d6df7b749afc3ed20f2dd836cf2c0abc7fbaa59eed556a9d2fc42542b9865bdc9dcdd> [ 894 0 R /XYZ 62.692913 442.91685 0 ] <6e0922a9e19fa5b8980de6744bee7efccde938a46759b9e53c99e1c76f196557182deb835d1382d2de0b48c49c87736e87519742bab25d1790a23b194203bff3> [ 937 0 R /XYZ 62.692913 667.59685 0 ] <8b7e9caca5a71f6d6b6ce80e527bc7bc235cffb3374519466b71df3c8e90e21ee880019611c4e787a9805ee27486824034f95fc72b2321534a0bf0293587e05fa34eebd1d395c7c0c2a3d55ae816c8f6> [ 357 0 R /XYZ 62.692913 667.59685 0 ] <7e2a38a00934ec65f3601565ebd439725bc4dc98a52b5c8a1e6d1e3352b6fca8f79aeefe131ea598e33b188a66abfb83fcd780dc181e20d00240c50bf852e02b> [ 869 0 R /XYZ 62.692913 404.53685 0 ] <06e25685745669b938ea9a71e8562451474c0cfe07f9ee8c21298400cc6b6e32ea537ee97312c8419ddc6d0353fa8524b3a00ef97edac9ed3cb61e67ca1eb3bb> [ 600 0 R /XYZ 62.692913 675.64685 0 ] <74ac87b1f53df91ea087b416bc0e3bbf0361062a29d3c54302581e80b7350d20f0c454618f0c10fe838eef268241fef7> [ 660 0 R /XYZ 62.692913 323.58685 0 ] <8a2819c858f79b8104becc64f524f84f2dc23d245633bef095f041b4c3c3e2a628c6f32853a26adcc0d11101ea5c1e12ab2cd44b449c60e5a897c622db017f0f> [ 509 0 R /XYZ 62.692913 398.31685 0 ] [ 433 0 R /XYZ 62.692913 785.19685 0 ] [ 589 0 R /XYZ 62.692913 704.24685 0 ] <49d7bc5f81be05f7fc68f01e953d58b824efaa2e62312234ab2d94fb5ad74e5067560692158ff7ea63e93f3fd3eaaa08352f13078465cd1968c7104620ee8d51> [ 239 0 R /XYZ 62.692913 540.31685 0 ] [ 298 0 R /XYZ 62.692913 187.68685 0 ] [ 308 0 R /XYZ 62.692913 223.00685 0 ] [ 689 0 R /XYZ 62.692913 649.99685 0 ] <8d366548405b45baeb0363812ba910cc60df864744dcfde9b9fd453144b2a97c874d5780f2ae657f778e3440d13a71cf56a6b8812674db5fa5fc1f7c0629d4c5> [ 773 0 R /XYZ 62.692913 422.13685 0 ] [ 325 0 R /XYZ 62.692913 693.24685 0 ] [ 578 0 R /XYZ 62.692913 595.66685 0 ] [ 901 0 R /XYZ 62.692913 497.11685 0 ] <24645bd4ccd084864c79ea6e85483c7b2c1c1e0c1a29a7f8c42e25380af4a2970d4c1b1bc36be69d3bee2fbcb73721b68dd62509a9faa944ba0313e8d098c2a0> [ 740 0 R /XYZ 62.692913 266.53685 0 ] <80d2ec8a541cfa37521fec53fdb934de6831f882cbd2cec539b92082f5842d249a3bfcf0a3fcc3d5445322ebea5d84012ff44f2c9986f2d60520224f8ad1d8d3> [ 707 0 R /XYZ 62.692913 621.26685 0 ] <4da9289499f642da00392dbf5f1beb4186e2a7d25b837d78b7fa0eff88a804082ed56f96fe89ab583df07678ccab9a91f36b67da280b16a54ba8444a86a0ed63> [ 446 0 R /XYZ 62.692913 761.94685 0 ] <00d0edaaaa617d796c642d061fbea73f30a7f1e25c75d837a445b211fd71fe6c8415c07d7c6ce3f42c37bc5553d39b9c99c3e2f9abce052b9eb75a635aee3878415f31241ac1b0571c6083a1d78f8e66> [ 345 0 R /XYZ 62.692913 458.91685 0 ] [ 946 0 R /XYZ 62.692913 542.86685 0 ] <88ee72c4aa91c1a0548ad918232defa7643f50aed3c1028b0058eb8d2bad0dd23775a0aaec96d9c199d34bab12378f7483728c5aea7f5674ffb185bcaf3ec248> [ 846 0 R /XYZ 62.692913 696.19685 0 ] <1ef13071c1b0e7e18cbfbbb2aaccc101aaa92f9d19a3dcc640504b8623148b1f50de10a684d943e77b2f1be8ab6955e22acc4adb75c7d4139abb4f225bbcc989a113e6b02ac2aad84900776148385d7c> [ 548 0 R /XYZ 62.692913 785.19685 0 ] [ 466 0 R /XYZ 62.692913 315.53685 0 ] [ 816 0 R /XYZ 62.692913 761.94685 0 ] [ 824 0 R /XYZ 62.692913 560.46685 0 ] [ 610 0 R /XYZ 62.692913 505.11685 0 ] <1662e79f7b9461676b253f368ad5fe2ee0dc4e0eb00fc512686cd7e7dc87028d6e8f11ace53bb9fbf14cbe18d1decf946bb06fa7f519fd071395a53c0c67223b> [ 796 0 R /XYZ 62.692913 621.39685 0 ] [ 567 0 R /XYZ 62.692913 675.64685 0 ] [ 875 0 R /XYZ 62.692913 318.13685 0 ] <22735aaba4f30e21aceb2176a24a1290d3d33a2b764f78be77b1967886f402fcc5383ff244088eaea66e0524800bbcf249abd713375ab2188d7040b6726dea82> [ 719 0 R /XYZ 62.692913 497.11685 0 ] [ 494 0 R /XYZ 62.692913 595.66685 0 ] <3aa6779a0453d83f98ecdf64d1e5427c970512648297c1c8306564e6cfede96d1f2797dd53c82726ecf16ec046bd146b> [ 419 0 R /XYZ 62.692913 721.84685 0 ] <4c034280f17dd3c9ad1e2274f4f35e57fd0e190a80e411f3fdf8558f5733cee8ece9b8d7d323bc557af1def96fa096ccbdf54748ddf251160a8ae1ec9fcd7388> [ 381 0 R /XYZ 62.692913 321.73685 0 ] <798c1ac58f989404f03c47ffe0ca20db5448c731cdef47d89a0bc57675c9a27077ab58ea845525bed025f8196db5452aba0028cc76bd904b6fbe3eb5ff35b9a9> [ 390 0 R /XYZ 62.692913 155.87685 0 ] <2dfd83e5a241fd1b1daa503fa6afc4940962dc67e587d28d7ceda326ddb75d04ec4f7f5c7cb11d90891221eb7b530324> [ 114 0 R /XYZ 62.692913 296.09685 0 ] <2c9e119e33bdc98d86ebe1732b7c71b0227334ceeecc19a9359b52fba59c35a6d217176e7df8d23001c938d72047a266> [ 174 0 R /XYZ 62.692913 719.31685 0 ] <072d195527d202a156285bae2adb7262893ab2ea4f3d223cf2764ca7468dc63bf5c66b8bea557947e61465ce6f299b1e> [ 175 0 R /XYZ 62.692913 617.14685 0 ] <74887169df69ca990e22da506c30be74ece65258c63e571bb7f9a13f05a087576a7f92a6d5ef21df30a26f988d899a18a1f05081f5e90f14fb8cb6ba44aedf99> [ 175 0 R /XYZ 62.692913 140.09685 0 ] <9ce3edc901ef3f891079b4be1b1cdaad50ab83f30ef39c5d0fc18a125b0c79a6e31ae018ba0dfdbff76706091c96089e60a530d67b05be9b0452122552657dc58af05a310b301020c43ab5c79b476afb> [ 946 0 R /XYZ 62.692913 225.73685 0 ] <5fb64ba2ec8319ce0ca2dae6fb5d0d5ffe9304bf223d6afa797b9ff53ca41203f55ad08bfd3bdbc97a58f6bab94a094e7df7da934d9535c399339ebf6f7608b00f2e751bb864589c4b0e07f13fcd2799> [ 309 0 R /XYZ 62.692913 566.19685 0 ] [ 887 0 R /XYZ 62.692913 366.21685 0 ] <95b19642ce032cdeffc9324d33b9763588c2c64268bc7d0a26b2dc35ae652949dc2850822b7fb0c47401d5905f9d47f3> [ 839 0 R /XYZ 62.692913 450.73685 0 ] [ 901 0 R /XYZ 62.692913 289.98685 0 ] <65cf882ba8dab433ca38d766bf6b4dc36ce572d31983e05b1c85292536168ea28820fb272815d64a2ea99c085de73f5e52d0c4a1a522b91b8b731cfc539e5fba> [ 285 0 R /XYZ 62.692913 341.01685 0 ] <2b43062c7e91c48d1ed48fd6905be63c5c126f074cfd9282a8428276500c6f8c3ffb146a38094429fc240da8a7f048e14adf97a21e1af4c08e228c7e1cb8e1daa2fa0f544360e3434b95ccce48595862> [ 661 0 R /XYZ 62.692913 785.19685 0 ] <85920f49b057144d27c87ee52fe868134200e4e5f93f4c772d07af377d49d432ba4121e7ce9166fe4e014d8795f56c9255cec61093f062934b38c321a71fa350> [ 600 0 R /XYZ 62.692913 494.11685 0 ] [ 741 0 R /XYZ 62.692913 785.19685 0 ] <97530669d256520ad2b5e135fe2c93c99c40baa08bf407ad732f3d4502467e89a1330f0d4a0d16e2b735caeaed10830716b972ef303c33a31f4e0e337acb98f8> [ 784 0 R /XYZ 62.692913 194.08685 0 ] <224b78eeed54b6a0cb587ebf67050f406c7177582a7ae01baf96c86dc79e7c55022cb903220829708fbfc81974d48fcc> [ 751 0 R /XYZ 62.692913 144.70685 0 ] [ 334 0 R /XYZ 62.692913 202.08685 0 ] [ 796 0 R /XYZ 62.692913 308.76685 0 ] <03c0f39a87ffe7d1123cda7ea501acab13d8c125e1f50a90be68770ea3ed203881c0c73e5478d9117cd5f8f576d361969d31b890db3b216516be1baf7c689649827bec85f8e6caf4580a569c9dd938b5> [ 273 0 R /XYZ 62.692913 214.33685 0 ] [ 480 0 R /XYZ 62.692913 294.23685 0 ] <84c344d69b4bb4984a459ffe60f0b440a0e032b8447d687c89b0add48df6457b51d3c3ef42fb105d5ff490ca166f35da8c17582897d056afd46f3995bf636e4de93ffdead00d229ae09e7fd83d422bac> [ 345 0 R /XYZ 62.692913 234.18685 0 ] <9243cb149b1669ea44d385aa5ca864f3ce4af573fffaeeb4c08c7d0d56dac2d59f552f243e2bcaa76ee862a03e13977cad62f839bc130babc33f349746744ae12ea91185a16dc6969abd93bb2cfbcbd6> [ 930 0 R /XYZ 62.692913 761.94685 0 ] <9b715dcc39d0ffc0d6981a2aee8fc024f66e83edc21bb8e3db810862e77d0d388be9559f0244f6066c774c7fc9d211f1f2f830e2f0b24fe8ae2ca5eee2d765a2> [ 824 0 R /XYZ 62.692913 296.13685 0 ] <8c6d06e10af4badce16369f277be428aaa2c83332f89803dbe465b6954b736780c5a0250e85ab339e75d175062b1797c6bf618a17d8a738cd512fd30b6b774dfae93bd0b748f60436a0b5cdb3a3bc00d> [ 876 0 R /XYZ 62.692913 785.19685 0 ] [ 985 0 R /XYZ 62.692913 704.24685 0 ] [ 467 0 R /XYZ 62.692913 785.19685 0 ] <36c242447cf520300c1c016645c054d7783efaa7603496d8db0a5843e522730f5d7c1ad4d48df452df29691bb48c29c9a418329ea2ed17bc994c6823437d6e99> [ 249 0 R /XYZ 62.692913 586.06685 0 ] <87acb5f246f169cf3bb6c1021cf42bf0972d325521770ce3050400879aaf5c4b1fbab1cfe62eeccef760cf9fa94f93bf> [ 806 0 R /XYZ 62.692913 197.40685 0 ] <4c3aed00ecdf9fe354295de0ae8bdc139870873a29edbeb0385c1d4b9c7894825af34d0ba70ed017eb2c8cf7d373e1dcf1dded4ea4c9af409bb896e2b52fddb3> [ 567 0 R /XYZ 62.692913 468.51685 0 ] <9de71c06b341461d114c9117d95ff20b7a6a3476d293dc6d4dff1e624264afc6044ba2532206547d1358c3813a8634a7c950d600919845b4b90065143f37e332cd6d35cca01590ca17972a5b9ea15631> [ 921 0 R /XYZ 62.692913 197.40685 0 ] [ 906 0 R /XYZ 62.692913 128.70685 0 ] <86e1ede7cf00f276c26071c6e0c70a8eea09eb88e7cf1ad947c137cef9997f6061800ece06c6331e0076f0fba2113d84d2081b0d530fe4cce4bd677f9da9d645> [ 255 0 R /XYZ 62.692913 562.81685 0 ] [ 719 0 R /XYZ 62.692913 297.98685 0 ] <5e1c7f2a2619022df32c9c3ecbbe17a18cb98439a6a87d1a23759d7b881511556cebcf90a8c53d2b9b3b5fa3e889e10e8cc9b229c32f037ee9e51a4f27896e1f> [ 366 0 R /XYZ 62.692913 398.88685 0 ] <11a5e1a48d2032b36c5ac92ec7dea6c804cc91c013f3645c207adbaf107b10af6e9a328836d5cb23337a565007b2b042b51a0f4ec615033d51499a0d629f9fc5e51f8474838ec7ca62399666118f7f60> [ 678 0 R /XYZ 62.692913 479.46685 0 ] [ 707 0 R /XYZ 62.692913 422.13685 0 ] <546b18530ca5cb2b577a2d815af1d1a8969d343de78f66398083c4ffb80766a31147071288522872343498aea1fe00ec9347c1ecd4b0c6292f65fd6eda50a8a0> [ 475 0 R /XYZ 62.692913 580.41685 0 ] [ 461 0 R /XYZ 62.692913 704.24685 0 ] <5106c5fc7473e3baebccfdf1f81569d065ac17514df174ecd7e901756e6c206b569cce7d86a2dc8b8c5e5299f13aa175689782a49190fa6f0317e81b26fc5b14> [ 762 0 R /XYZ 62.692913 511.71685 0 ] [ 689 0 R /XYZ 62.692913 341.61685 0 ] [ 976 0 R /XYZ 62.692913 267.36685 0 ] <55da5b9e69fd77d9d79b255f36a41c299b35f04ae4bd95cb4c4506e61c0f280627124d2a7d7cd9b9906b498ccbb76f77> [ 102 0 R /XYZ 62.692913 334.44685 0 ] <2ee2741a22a4f28a836b51dcbfd81e33c24de8d4697cdd10fe0e78ecf9fc3b4039d5bc76eef295bcbe1604e1be15e6f1> [ 178 0 R /XYZ 62.692913 762.69685 0 ] [ 239 0 R /XYZ 62.692913 376.38685 0 ] <87d91fcba16bed8b93b4d7f372c449a01a0771c347068a68ee0782cf7ca420b09bedc9a8d8becefdbe418512662725b5a0c370735f68428d79775ea750d1fc7182a126f8d015374f3fcab083ab226ff6> [ 644 0 R /XYZ 62.692913 785.19685 0 ] <52af884b04c1c62a87abdbc2a666b37cbd0cdb389422850c24a89cf155dde3fe5811d3138c2c4c2639edd50726b9bc3a3abc400a61739611b113202dfec7be4c> [ 651 0 R /XYZ 62.692913 572.94685 0 ] [ 830 0 R /XYZ 62.692913 216.58685 0 ] [ 299 0 R /XYZ 62.692913 693.24685 0 ] <746831eb802c8d56f2e716f3ed752892ebc8914db5f9669e36e278adef3d35338ea9779b15d62274895668e2afc995908db3a3081a29d2465a591321447414006c46db4a6d9f4a9495dc6c66d75587ec> [ 729 0 R /XYZ 62.692913 339.01685 0 ] [ 371 0 R /XYZ 62.692913 272.18685 0 ] <79334ba70e614a262b29f01805bcea0457ebc7ce3905a014b557cbe29f7aa01f937f0e1f45d41af99bab86b245d1b67a36639d4ea1707269273784fcb4c82f23> [ 263 0 R /XYZ 62.692913 404.53685 0 ] [ 494 0 R /XYZ 62.692913 396.53685 0 ] <8c1aeaf2dc5fa8a09f241b7148ab8cbd0c2fb6979fc05d0d523e7389f4ac911cb37c9cba7f05eecb41a5ce473050f1e1> [ 669 0 R /XYZ 62.692913 119.00685 0 ] <39f15ad3308095a10884d07c5590e36950f453f29f8a3d4ddbcddfa7e95907a9469d7b159cb692ef7b6f12d68c2f8c4f9d91bd952f4c88c42fe2b67b62e89349052ebc3337cc1da06f7dcb26b9423ba0> [ 937 0 R /XYZ 62.692913 295.96685 0 ] <487ba18ffe35bac5c3de2d20350e439868f8e1d21a7245639223d83007cdcaf290e4771928f1d5007d205a8de8cf132d219870a69280ed4a45ccb1194c99abbe7edd87f393cf5bb59e0e9bf433aad9c1> [ 438 0 R /XYZ 62.692913 505.11685 0 ] <4f184770d4f7545268b3640bec0c0f707b6227afb0f1f075b3d939b7b898a2f2f04d8989678d2057adb01b5d724594bd5b0e232516e65ef07ec258479d18e71a> [ 589 0 R /XYZ 62.692913 522.71685 0 ] <91868cc3ce8bf33a7657e13858fe8de78b450e634845abda886a90cc170ade56553f63d121ed3d8c36b3ffb2fe02ed863dd9e343056a56ab6be9a33ce0a2a934> [ 325 0 R /XYZ 62.692913 511.71685 0 ] [ 869 0 R /XYZ 62.692913 223.00685 0 ] <9bf2fa74bb74172a12351d9e261547188113c25b710d12d68ae9a5e3df50d37d56cd356963fb946805b0475010284cdf99adf984e3ed5fc899fa32d43e92d286> [ 410 0 R /XYZ 62.692913 180.07685 0 ] <3f88bb66cab789408de89f602135c20b1a06a5f38c1bcec9c8f704c0aa0818965b27f67350a3caf2a670e349fb0cd7a80c2e736a3efb9558e99fdcd4c8ca7b77> [ 540 0 R /XYZ 62.692913 721.84685 0 ] [ 953 0 R /XYZ 62.692913 542.86685 0 ] [ 401 0 R /XYZ 62.692913 442.81685 0 ] <005dc2fd86099076606cd9e07e490ab7dbc5b4f91973a5fa8cf8b79e686281837c95dc3cb0d80a11cf250dd9393d2d903cb6cbdcbac258dc22a6d49cd4803811> [ 509 0 R /XYZ 62.692913 216.78685 0 ] <554bd447acf3f576f46e68797c3020c43e0a2fd7b07e6f6cdface038b5750cb2bb50a172696d19b462b5824a6890f21a> [ 773 0 R /XYZ 62.692913 223.00685 0 ] [ 446 0 R /XYZ 62.692913 545.21685 0 ] <27dee9fd10d5b47d10c3031e25d7d3df9587dfbf9a0b24f69931edd9c1ea31c4e9332c12e7062e277bf2abfaf6ca130a8a6feaba3e3e34d6b03e2c63fd762526> [ 522 0 R /XYZ 62.692913 686.64685 0 ] <7715ee3a5035adab159bf202d762ec248e3655cf9eede3ffda5b29a4b53b8682e36baf5ef66b89e6725e160fbcb857a7> [ 419 0 R /XYZ 62.692913 557.91685 0 ] [ 961 0 R /XYZ 62.692913 463.16685 0 ] <9ecd5a3b2a0ee6c41c9ec13b2f66a6b2c80e11748febd62b8e8315c446b0dc819c2663eae07824122477c9b7872df67c48d77f211a06a8fb78d6a86a43d2929e> [ 382 0 R /XYZ 62.692913 761.94685 0 ] [ 816 0 R /XYZ 62.692913 554.81685 0 ] <88482323e756c318ccbe6df3449eb172a98a13df733c83d238c75e12c5913ed04b037bb40d0398b826111bc0943411682aacc0873d688cbc669943b692efab98> [ 846 0 R /XYZ 62.692913 434.76685 0 ] <0e36f3ffa39a451beb574a4266da25c0888ad611a0862c530eda8869af7c063c064ce6c47e3752ebeb4cd8d12b8f1b0fb2bc3f3f708fa4f361488b096e57be38> [ 376 0 R /XYZ 62.692913 248.28685 0 ] <6e7e64a1f7ecac037e4b878b891c56a097eae34a38c55ac0fd62caa928cd0e93e76a7639d39a18cf1ff1412bba23443c2834ca3e8470480f72f9bddc5c7cf5bb093b8f6d9dee151c50c92458f7b96ec0> [ 548 0 R /XYZ 62.692913 542.86685 0 ] <4ef100a66dbfc75c158446c71d56fdd3b6cd1755bd525afd474583bf67ac4dcc16ce5dd397e9b3451eebacef73bfe4b0e20823478bef4d1e94affa4e18969e97> [ 634 0 R /XYZ 62.692913 603.66685 0 ] <7656db6991d8942a37a12f1cc565a53b105dfa4f38c19e6b79e25720678dc5b2ca3bd38d6f7d2a6e50ee787931ac9cd5369cde786717a21419ce7931a236d007> [ 894 0 R /XYZ 62.692913 218.18685 0 ] <9d719a72a04a190877bff1631c0f024371c7ea302f08765a9210017f74c8017e226018f6b58d5d326102e3ca0d274a90264412dfa3fd6c2cadc917f9768c16b3> [ 530 0 R /XYZ 62.692913 785.19685 0 ] <37c3d7085e2eeb500d2c28b87f0e46617634d4c77fd74082d53defe4368901c16fee016ae39e72e467ebb21c07182502762db2b336e55259623dfd0d4107a7b9> [ 578 0 R /XYZ 62.692913 396.53685 0 ] <5db4576d7dae344fea89f9b2bf7bfc50fe458825bc8ae57a84d2537231612676225f63ab78c2faed2e31e972b85fd1ff23f084f818d9291776b9de9cd6c89fd9> [ 696 0 R /XYZ 62.692913 497.11685 0 ] <943aaca40ea8f8c99bd043ae1b86c7306efaaf294a6d3dfe51037ce75c242090ced4a47e9014a21a87adac31c7420cbee6e2e6eec05486ae2c65e6070f9e30d5> [ 913 0 R /XYZ 62.692913 354.83685 0 ] <0bd31800c9a3b6f3f6295dd239513483370abb2631b9f76e9b436d6ff09696a924b5a08ec67adb4b87e26b837c4ec22b072f199edfba5f43dd0efa30af954f00> [ 622 0 R /XYZ 62.692913 422.13685 0 ] [ 610 0 R /XYZ 62.692913 183.08685 0 ] [ 558 0 R /XYZ 62.692913 785.19685 0 ] <5af57c7425cc73affe27cd804e57a9c02840e79d7be02d3bfbc035f1f5978414215003c43b4bcead53f1df27b8f66f46> [ 969 0 R /XYZ 62.692913 658.04685 0 ] <1f8f8569e5a9cd443c0d76bcb3b2550027a6a3732f8ebd5dfc8ab583e239e8b41ee205840ca6235f5baf6cb094adda25f6773aff7a922a431be2ce18fb10ee630e6e85b5dba6270fb594d0cf81cc939f33a13b70e852c6f667ad796f5c8b92fe> [ 357 0 R /XYZ 62.692913 442.86685 0 ] <548779f3c1f598265ae059bc05267787d532832e5b74668ec10d9b90c05558f05ee07efb7b39dc2d1b5b7bfac0f79c8b> [ 111 0 R /XYZ 62.692913 494.56685 0 ] [ 391 0 R /XYZ 62.692913 704.24685 0 ] <45399348b173824dec1c56a84c954233095bf5dd1575d88431ea5973633982e4adbf8d4c28f29e1bbfbd5d3ff6e807de> [ 178 0 R /XYZ 62.692913 719.31685 0 ] [ 178 0 R /XYZ 62.692913 212.41685 0 ] <1994910abe03e1fef5bb69cf0bf401735573b1a0a53155516a3aac56f06d51a5a3a5e69549fbb22ae570d74fbea4dda0> [ 179 0 R /XYZ 62.692913 586.19685 0 ] <939a1f2ca7fdb5ade61db54521184d8a4c066335afc17fad26cd6a1929ece33e9ff019a8457c7b342d235afd50f9a8e9> [ 180 0 R /XYZ 62.692913 686.64685 0 ] [ 634 0 R /XYZ 62.692913 422.13685 0 ] <0807228e7834629b93674ceb2691991b4ca710980aa5a4b251d56374da636cb4177738f1799097cd29359f537d8751bcd182a30173f78794e45e5825e0f14c35> [ 938 0 R /XYZ 62.692913 785.19685 0 ] <6d43489c6dce2e0f4c9fce220a8ad40ab5a4c4acaa06ed33e7b2072708d6deabf3d29516d4c8c31ff27f9a301a1f9d6cfa8d1472336d1095fe255b29e240f38bf8b8f06c1536f63200ff9c447b27d57f> [ 239 0 R /XYZ 62.692913 194.85685 0 ] [ 689 0 R /XYZ 62.692913 177.68685 0 ] [ 548 0 R /XYZ 62.692913 318.13685 0 ] [ 494 0 R /XYZ 62.692913 197.40685 0 ] <9950e7590dcaa26c78bb115e082be5e6865139cee70e141bc216cd0ae3ac7dc183886cc2d863d371c45f60b9f2b265a1> [ 475 0 R /XYZ 62.692913 363.68685 0 ] <2c9c445763863d0c0719db96d4a78d9e65a97e8b9025ec0b8dc06431a94565860718ac591a69065e4880e018917d0be8> [ 785 0 R /XYZ 62.692913 621.39685 0 ] <7c365287005c963b0183e6e7f1430418cf02ae9e542c26cff3d25a39d03b7a661ce908d5a98d4cd62af24458bfc6529aff4191aa0d7b8e6c67faa8acc9511c57> [ 752 0 R /XYZ 62.692913 544.54685 0 ] <20806b3f78e3452189987763a2ac0b4b1a72b9567dbf1ac9cf81c696aec5d19749945d37858fe28235916ce0e16adc085d89f04b0cf99fde9854989a9d6ae1a8> [ 285 0 R /XYZ 62.692913 159.48685 0 ] <462bb5baf56b244570ee000350d0ab079afb3ed7c999b1b842178599e562f46d5c3bb9f856e7921c411675e48a61a8d52885bbd34aa57b8e75ca5a1f9df50ca0> [ 762 0 R /XYZ 62.692913 294.98685 0 ] [ 953 0 R /XYZ 62.692913 195.23685 0 ] [ 105 0 R /XYZ 62.692913 785.19685 0 ] [ 181 0 R /XYZ 62.692913 762.69685 0 ] <604eebfe3e1d75e64766be9c991c29ce0b53d4e8338ad3726e8db81acabae5d186cf74ac36de60efffb69fc91fea8e039d123bf9bf78b474e564f76039fb1009> [ 696 0 R /XYZ 62.692913 315.58685 0 ] [ 467 0 R /XYZ 62.692913 383.06685 0 ] <8117e8bbfc7d7541b8f6a786707b0b719034edddbbe4b9770b989f014b973149c55298c3b5f88628bd8ca295370aab9e71f5909ed3f236cf0ca5fda4618c0085> [ 438 0 R /XYZ 62.692913 288.38685 0 ] [ 670 0 R /XYZ 62.692913 535.14685 0 ] <450ef8f2e5405cdba08d9960a81dd9b541ae6afd8069ad83feedc8d7e802081a9ca4e11791b00ac1249a6efeb974e8bf35c22a7110a146955f0e72bc524190a2> [ 807 0 R /XYZ 62.692913 761.94685 0 ] <19075f29722d5e0ed3082d464885edb92d036e71b29a693ee521d90fed80a4e38b36bf26339ac8055563b9535a31e0ba76c9b4f0dbb57c84e4753deb0add231d> [ 255 0 R /XYZ 62.692913 363.68685 0 ] <0b03143972d37ce7c0c2a55fe99c944be4d443ca1f91466d9807d07c09be495c533a1ad184f5ff4a64e1471a80da462c7b5c2aff7bce9a844cc80b7b65bb733cc79a88a855f1a08ee7ce836ae950a308> [ 558 0 R /XYZ 62.692913 458.16685 0 ] [ 922 0 R /XYZ 62.692913 704.24685 0 ] <90895fe048f2c06359e76afc8eb490134f86c7a379cdfdaf7bd7a1c434727e3cbc190a24add7f61c58076f8e4313730fcc782e74b4e7c48e4504c627b4eda7bb> [ 977 0 R /XYZ 62.692913 785.19685 0 ] <0126d9dc65a0b9cb5f0859b5572a889affa81f7c02ab82ccb1dec7bef1dd130adbc94ff9331476a12f232ca5fe478666bab2bb918994cc4b8480a4d4272cd31d> [ 839 0 R /XYZ 62.692913 280.20685 0 ] <3cf958766f76e2dc3dac59244a606395b5f77f1f93fc634de7348a21dd0bf10a17a199917d90dc48d005b91a939f0be5044133ae6c7d2b861672bfeaccd6e303> [ 112 0 R /XYZ 62.692913 686.64685 0 ] [ 707 0 R /XYZ 62.692913 258.20685 0 ] <022720680c5d87795da607c83a30c193ba7bb364156043dfd964428472a13e2cf81e9124d2a5ab96c34fb44b512dd48d3c67ca08bf442fc92d0034730d574ba6> [ 774 0 R /XYZ 62.692913 686.64685 0 ] <514a8f7c33a7b2b19f2d4f33f016c9738001b5ec700d343a1c19e25a42af3db4582db63f48d34534cc3948e8582740f955594d381c28a2b81cf7297dd3ca5ff1ebe675d79b42a0f88dd0af5259c3f1eb> [ 947 0 R /XYZ 62.692913 669.04685 0 ] <1be8337b9b0e92b9c281a240db366cd3af926e31ab1473df4a32aee43f5fdc16b31356a523212a3745dfdb0dcafefefab367aafbbc55bdd95838f97627deb85e> [ 797 0 R /XYZ 62.692913 761.94685 0 ] <7d7e066067855d2e16f56efd576affadc407e07d4ba483da4bd41cdb297c927714bb2ce1a90fbdf89829368a2ca3ec1dbd896857cef309534f42e17c4345e343> [ 887 0 R /XYZ 62.692913 159.08685 0 ] <3b247990ee19d19f895d5250766b6a5c0f143394406056eced85d2e875c60e458fec03b76735e1105147a9a87961e9ca0c7534deffd4cc039e2e742f79fa9942> [ 930 0 R /XYZ 62.692913 519.61685 0 ] [ 611 0 R /XYZ 62.692913 608.34685 0 ] <04c1a27cb2e57ca96388ce11dcc42d9f348bf281e9d2f7eb4c4e6101569df6680406980ccf07c9e9326bf0ba9aeedb8deeea081d93febb1a9a490c0c87d9160d583e56e4608feb946520470fc0cc3d9a> [ 876 0 R /XYZ 62.692913 560.46685 0 ] <9fef604d6662b75252a4aca31ebffc3af1746a715d2f36f3dfd4579cc179c5e44dbb721c7bf4b7c5064b8ccb05d8d5d6d4621ed727f25a3a9b9ce27ce192ce910a0e89cea6a9c38077d411542cf9d6ae> [ 249 0 R /XYZ 62.692913 386.93685 0 ] [ 401 0 R /XYZ 62.692913 181.38685 0 ] <3f1a50e8de8ffda94694795a69e1836193bc72b1cd5fbb85c0a35b702dc31d3586d9b00adf862b2fa594dd5ca52072ad1de85d2f13dc16d12c567a6a954a3119> [ 969 0 R /XYZ 62.692913 458.91685 0 ] <16c8595519fb9dcb86b0e38a322f111d9577f5d5807629639736edcdeb787019092c0de49b13c3ec95f1e01514fe54d56ebd321b09bf5d43b50148a9274aaf68> [ 741 0 R /XYZ 62.692913 546.46685 0 ] <63b46a19598bfebad2c455cf2e52b19323c4cca9195a2f7dc94485c619c9f468150757f0c30b02f13912f9dcb977d6978c11a17eea483274f13ea979a936693d> [ 651 0 R /XYZ 62.692913 250.91685 0 ] <510c4090d644735483ae2b4f5b47e32ef3506c570add6896df2bed2f99c7a0d9f1f2270d338623fc888754e6e51799500d08b4ef61b716600c5b3363c556929662adb0353a1943eddb84144def8807e9> [ 831 0 R /XYZ 62.692913 704.24685 0 ] <75e51b26b4a356b7fa0998f2f80f126a76234403c8f3b710e34fccac7504b025176a190458e275702e33fec6f74b1ae4354e74a4db68b182483caa0c0285ee7b> [ 816 0 R /XYZ 62.692913 330.08685 0 ] [ 961 0 R /XYZ 62.692913 246.43685 0 ] <2fdfed65af9d9b053ea06c6b94c15cc582b54ada98f07dabba3e7c6f85a1d8de412fc368d804d23f1abdf394a3f3392fcfbb28f85bc62556dfe5e3c98ad75bc58fe37d8df13295abc97f2b2b190e852d> [ 895 0 R /XYZ 62.692913 761.94685 0 ] <47cd2157f58c1dd8fc27e9ab646fbbf7489dc3985a2c0343b0f6cede4026829f71a5811e2519325a5bd5a5ddaa558e4b90a65f7940043926cc458a48443eb087> [ 346 0 R /XYZ 62.692913 704.24685 0 ] <717c1f4d19c1bc60c6589c3e07ad42c8c46340fe0bb55dbf7ad213eb0df4004ed0836c6502c865069241b600312969105294a0b46b3d0a06c7d1ef880a8b95e1> [ 824 0 R /XYZ 62.692913 143.20685 0 ] [ 913 0 R /XYZ 62.692913 173.30685 0 ] [ 540 0 R /XYZ 62.692913 402.31685 0 ] [ 870 0 R /XYZ 62.692913 761.94685 0 ] <1dbbd6e6dd1bf2cb301fc8e744501ec6b165e452d23a2d410d0b462cb2f21bc152e51f409dd40171e3ad7f947c3272840be8578dc8f1c71c835a6936a892e84b> [ 372 0 R /XYZ 62.692913 785.19685 0 ] <2440b1fe97e19565fbeaf47163d23dfbfc1417263c41fc27330037813cab090bf6be6dba009f2b879c682f8b3b98cf1c7be6f77aaa61370bd81623bdfc563449> [ 263 0 R /XYZ 62.692913 205.40685 0 ] <1cd7cc6478585cd1d09a119da058a41a97bf6c10e5a8cb00cf78838111b5e4d602fdd4b0527f265c9b4560d3a612e2175497394f7a342b55ee3af331daab4076> [ 446 0 R /XYZ 62.692913 320.48685 0 ] <2e5be950d1df6be63c88b4549a9d0a42669743798a1db22b36a34f5ad02c396cadffc13176d322c98624082e34b98199348c6ca6868d37b41a2dee84ddaa21f0> [ 411 0 R /XYZ 62.692913 761.94685 0 ] [ 274 0 R /XYZ 62.692913 704.24685 0 ] <5c9e4d3de65d3c356825dda64c336168dac35ccbb0b663ea0a836b46fe8e543407099cdcad36beaeca0ac0b0b005ff3dad2b58a58d859e2026d6294027ab679d467ddca7cf6b043f5bdaa1eb14864ded> [ 846 0 R /XYZ 62.692913 253.23685 0 ] <68c66083a32ddc96a22ba4757a960250b5da4f7f8b9329b937c19435f2656432c01e8784bda48363d47cfda2ef4ca9f548a6b0ddae970b108066377e00720ad9b13327974c43c44f5d7199ca49d91c47> [ 720 0 R /XYZ 62.692913 785.19685 0 ] <9fb1d421077930a8f9e709b45d1efb1eb0590b899f1fb4e72d0b04e01d45768103b8490ac85d1da1ccaf8fb0cb844838bc99aa692fcef3e7f9d3a58bd0c0c5aa1e7207365d4273f3b51b9c0a9a7292a9> [ 730 0 R /XYZ 62.692913 785.19685 0 ] <07a5aec36b811388b159f8ece5f20c7fddff84be6f47c63a7107c8f5d1e903d4e3a2b5e10434b771478688c12d21b494453823180a7ffdc67202166352a5227c18cd88e70750e2d92b85e382015f5ee3> [ 578 0 R /XYZ 62.692913 215.00685 0 ] <076de3b0fce49d445403d3c1dadf02baebe9d4f94e131dd25b28a3847ab1aadaa8d1a6b0f4c2cbfd477af153ca7d828ced8e558a9a6b6e3eb457492dfca669b01b882a686e0327ff910e25ed58821019> [ 644 0 R /XYZ 62.692913 595.66685 0 ] <16765ea3f98a3f24364146c5478149c8d76729e9e90f4a8dbb42630eaa7322ca758c87739e9bf791f67dfd7afd370b381157b77f87354590e716fb534da886e0250176e4f3e9bf63d8f7d94ea3e07582> [ 309 0 R /XYZ 62.692913 165.56685 0 ] [ 678 0 R /XYZ 62.692913 254.73685 0 ] [ 510 0 R /XYZ 62.692913 785.19685 0 ] <2de46379a42cd408840f92318051ec116435b39cf8161af637c07250cd64c8a64a3311066396d8a1daa3b72217327d7c6bf1b4f307ec9500836e86138c99f86a> [ 366 0 R /XYZ 62.692913 228.35685 0 ] <0ea656c0788929a2c4ae1f5e115776c22a31546488f06be2410fff370991c0581106688856f125a0a1ae17a699a3cd7ec104ee28d4dbe75ee79999d3a5d65db0> [ 623 0 R /XYZ 62.692913 785.19685 0 ] [ 461 0 R /XYZ 62.692913 522.71685 0 ] <5eb35a4799ae1215e2a800914fe369226ddd85110a417dea8db5a53496dc62bf81f3f81ca703aa437926d10abb24ebb23dd78d22a0306f9c0b01bab95473bd16> [ 382 0 R /XYZ 62.692913 540.81685 0 ] <40d708cc46f69293148b6d16e88695155ece41c0a3eec75cec9ab643b6bc69e10e4939fcef2b25e277d1dddc239e8b60d8220f08ec40be3defc6d6efd06274d5> [ 299 0 R /XYZ 62.692913 399.81685 0 ] <288a108a47774ba9e23158393f50e026f6696746a4351ba5479d6100ae135ed41fbf843e55ce57b108475aa92fd5b944> [ 335 0 R /XYZ 62.692913 621.39685 0 ] <33e68d402ce7609ddf5ec941a8f3be2a5d1f5b82b2522e016c22e913ec2b359ee2a1ce7ca0441e93b0bfcb2962e498594a3d543caca7b94df078022c7b6209e0> [ 902 0 R /XYZ 62.692913 785.19685 0 ] <7c8fdfba4a3ea39d4cca2fd6cbd22c39e6e0e37032c75fea3ffb63faaa4266f7f4ec42c34e10f19562e01a0b53ae93233dff4dcfe63474ffe4028152097ecb2c88bcb36ab91c654a443689fb66757ee9> [ 357 0 R /XYZ 62.692913 218.13685 0 ] <9b1ff9eb1a8ceaecb9829b0f1cc33230375b7c27523f3a9af4cdd85c9131e03d490dca0d1fb5fa3e7583f486f694ec1da7134e6f92e6f13fd89540fb66936b2df908a94bb0e0449fbbea006d763a4255> [ 985 0 R /XYZ 62.692913 461.91685 0 ] [ 907 0 R /XYZ 62.692913 563.74685 0 ] <351ac5d5455490ae79185884e0d0e1c2022a645b25a322c2aee7afdbd476679aebec050b07795605afa8527e77d3fdc8dcf3837a419f85b28b2153203818796a> [ 481 0 R /XYZ 62.692913 785.19685 0 ] [ 522 0 R /XYZ 62.692913 487.51685 0 ] <4c162d4dd02706a4e7c1e7cf6fee50e11ad4330e3123b708e9590b063a920adb2a5596314f3f1874f7c8b5af3ebba7a77b4bb3aebc2101093fdf27d762d6fea9> [ 661 0 R /XYZ 62.692913 578.06685 0 ] [ 600 0 R /XYZ 62.692913 312.58685 0 ] [ 589 0 R /XYZ 62.692913 323.58685 0 ] <4e5502d526d07c795dedbdeb1a3de036afed62c05920abe83bc7c0e66242e99d3ff11b36f7f59997d6dac0d027d83b50782da3a6cdb9c6eede881382b85ce43b8d9838e4111328d1b5647d4313ccd178> [ 567 0 R /XYZ 62.692913 261.38685 0 ] <1801656b885f0fd47cd58823efc4da5d47933434e61d634f176b6a90acedd7130adfe9518faaedaeff7f47b9f62476cb6bf58cda76efa75170f35fd923d3a650> [ 377 0 R /XYZ 62.692913 675.64685 0 ] <5c8b15c562b7416fa1ae8075680f3b29fd0c7cbb8a3694c4b8a2a4063bf7adb4d0ca01bd13ca580210c48ab41c8212f422c145a7619ee806e9678034e6aa8e3f> [ 530 0 R /XYZ 62.692913 463.16685 0 ] <301c859e7d45c8e02637f4d9a8a1462ef63cda404c526880ecd25212b80fdd3fe6050cb662d02c31b6e8ba62dd0ccb46fa64717e96d0df6ee972c77adc2b7f01> [ 419 0 R /XYZ 62.692913 393.98685 0 ] [ 325 0 R /XYZ 62.692913 347.78685 0 ] <0c390a38c4c2053f7cd6f7967f1e960fc37a2352c3bb3aaca7a74ab3523362a84ada1cb762159ad35fdd16c8b306365f18685b12e3341bb6bae0a060c41f96c42108a346a3d71f5e9c48fb7fdd527efa> [ 391 0 R /XYZ 62.692913 551.31685 0 ] <0fcc6e05aa07fd8abe9e91beec8fcbf5423680eed6ee674e419f9f9a4ca749895239791551d4a4d4a8004a560980d614> [ 181 0 R /XYZ 62.692913 719.31685 0 ] <1cf483cc6e6b021273c3c63f28221777b8daa2138a9661b59c8661fe0a050c877725dd183ed3d150078a26ec59f506898fe44ce8112e1aced0b3567839ff9553> [ 186 0 R /XYZ 62.692913 611.84685 0 ] [ 181 0 R /XYZ 62.692913 402.16685 0 ] <8d3c6fd9445444ac642d079a16188b0ac4546c75fa4ca2f919e497ce2d579077918e3ea95f62e1ea24766646b0b7c965558e3750ed9948ccf91b511a7fbb0dc6> [ 182 0 R /XYZ 62.692913 704.24685 0 ] [ 182 0 R /XYZ 62.692913 214.59685 0 ] <739cadd60c4855e91c55505814bbd82f319ec1263b44b25597ae8c024dd9ed47f9497cb6b457cf55750339a67c3308cba783af0a4846b128c7d22b4b919063b5> [ 183 0 R /XYZ 62.692913 559.04685 0 ] <72d11c009a8e490d3fd3c5a0404a941720f1d497bf1f48311dd16db8c8bccb19849e5436f09f318d2d4f59a82747fdc8a42674c1c9fd40a8cb456763e5946ba7> [ 183 0 R /XYZ 62.692913 132.24685 0 ] <0617190546d54430989e1cc633c4801c438e72aab9df7dd5c9dc47acb729295b36ca8797930d17b7177b339306910417> [ 184 0 R /XYZ 62.692913 372.79685 0 ] [ 185 0 R /XYZ 62.692913 704.24685 0 ] <83aa7785bf36ed41dc52aca3ba941ea5aa3646c8e66b762d3627bd60d777f57ff7eed75298f135990f942cbd135a148e6dd17f24e60a53c1eeecd69bd6ed70df> [ 185 0 R /XYZ 62.692913 358.49685 0 ] <119f794e3b1f7827538a5b4bc57d7432950eeee6ea84df112fd01e9160510bd61d732b27fd16f74c3d9ccd840a1b0a11bd036340c6094f3f3d97d76f681f7fbb61e95be0623abbce0a8e78d6eb4e53f83fb94714d6a0a674e554c3e258cb3ef3> [ 382 0 R /XYZ 62.692913 359.28685 0 ] <6c8bdc7ab33bf8485cf0b1b80d59d79c2699a99bee8d89aba3431449c3305324269120e73925253dd9756293abed5559a9d706eebd83fea7a008f5eec3a576bb2c5c23f4a64ba858aa6dc68184244a77> [ 954 0 R /XYZ 62.692913 576.34685 0 ] <622717017ffc3819c8cf39a74d1d2a3fe18b90cab9098eb525bde88b8fd92ae9c29b51743f40f9e2f3230c5704281123fcbe8270086be848ff50a70f538602bc1441bd63c969257faaf00a2f6ecc75ef> [ 661 0 R /XYZ 62.692913 339.33685 0 ] <274fce8b7165d5004cbfa4224ff2c9fdbde4ccfda23e676555b5e709c80556aa29dd2bad7c85b17630b73505f973e146eda360bf3bb91c21eec953be189388fc3b6bba91eeb27554a3881139b2e16737> [ 439 0 R /XYZ 62.692913 785.19685 0 ] <62e5a68ba2cbe487b2dbaa1d89959c8065dc39126e99b663a496b4736f93d3b6e7c4ddeef8aec1f4f3538e10719091e9> [ 907 0 R /XYZ 62.692913 356.61685 0 ] <25a3640dd47ff3059b75107eb8ce88861b5fd03ebf652badb746f72ecc472ccd99b7b80c9bfba5a47b9a393a1cc9d668d2786d6126ab3f317cb651f109bf9dc9> [ 286 0 R /XYZ 62.692913 592.34685 0 ] [ 310 0 R /XYZ 62.692913 658.04685 0 ] <4a581ecc06fd6e80d1bd188c0094202c5c766a162632e59f67ee98f9ababa64509dac494862db360846e755b11db7871509604d9759b7bdb6420aa297ec07c2617ed7927f731ba2aa74ab7775f56111f> [ 495 0 R /XYZ 62.692913 704.24685 0 ] [ 785 0 R /XYZ 62.692913 439.86685 0 ] <4273fd841ad4f30317518947cfe63f20200c9ac3d1639a500611aaf0cbb3af4db6f74e35584c083dca040a04346f1b8ff47c2e2eaaa59bc83ece0badf19beec6ea9258adec7d65b7b5c33f1e899604d3> [ 831 0 R /XYZ 62.692913 522.71685 0 ] [ 730 0 R /XYZ 62.692913 578.06685 0 ] <4c442770754c034a5f6d93403eb49f11fd5d3f4d618c5b57a64f1e4bc01ccabb86177d2a16a46ed8e1d8562848544434ad6025a720cfe657aad6996106c35861> [ 763 0 R /XYZ 62.692913 785.19685 0 ] [ 644 0 R /XYZ 62.692913 370.93685 0 ] <85e7f16f4c4d550713fca657755126dde6e52166ebc8e7cf524e07a3ee0cefe1019c35c14666762807c73bfa780977220bae390b806e6c38fde7459ef89ffa4f> [ 530 0 R /XYZ 62.692913 264.03685 0 ] [ 652 0 R /XYZ 62.692913 651.59685 0 ] [ 947 0 R /XYZ 62.692913 255.81685 0 ] <9b1cc21b583b1e8b1f6609696a9ff9f6ad9905d653c7044cd124ee86bf565083ebee1ceeb9f05ae7877e835196b706a1e4f4adb0220ad7d20629d261b8f7723f> [ 807 0 R /XYZ 62.692913 580.41685 0 ] <4ab06510ca0ca1322dd587d4ac92dd865f7f22acc925c780fff7c5256df088d0768640d849485bb84e55b080306dff1135c0658a3d7711ed6dd3362f30264384> [ 249 0 R /XYZ 62.692913 187.80685 0 ] [ 568 0 R /XYZ 62.692913 785.19685 0 ] <13be589926a9e358cf0f50639bf95ce103cbd4dd68fb289e031ff6fadb5202e41233d13121ff6e7c18869a5560ec6607> [ 461 0 R /XYZ 62.692913 341.18685 0 ] [ 601 0 R /XYZ 62.692913 785.19685 0 ] <7b0de8386f8a50087aa389343b4fb902f3bdd9242b99d100b320bf82c1d59a5341e8b486b1e2f8c9488e5abda69e70119e033aa0d8afc5239cd9e8522ad36238d5969a27abe3fb3b4cfcd01a369a979b> [ 255 0 R /XYZ 62.692913 182.15685 0 ] <9c8e3b04f15debd15d9c6e3faae93f1140d19a7b53a9a7aff560a085283c3ad237852df4370d3ba28a3d3e11a57056d3ebe1cad4ad47ab2d1b5eb69d68a367473c0b52a213f3f92f33d95cd6e2abb3d7> [ 840 0 R /XYZ 62.692913 785.19685 0 ] [ 962 0 R /XYZ 62.692913 785.19685 0 ] <117ad1acfd4ce88a1a491974aae761580125885ad88d8a27808e551afc55bf547975b06e446cc474fb8057432dfaecc1b1aedd4883bdf91a08fe0b88272194e550b729066e7bff05e49a30e307f03f15> [ 977 0 R /XYZ 62.692913 586.06685 0 ] <7ddfc0c16fa1145bb90743a9250343b3bb4aaa2df27a4b6603b792b8da8f895a872859f3014a41e59a386d170a57ad629c281a0fcc4cbed8ef6bf660b95276f4> [ 611 0 R /XYZ 62.692913 314.91685 0 ] <6f3fb8290146bfa91fa6492fd6f02cefcbb8435af87ea73fa64c4c8bc8a8b9aacd20d93f9942d5b2628d644cbe9b0bd482b4508c9f29e1f8e1128e1c09dd7c14> [ 797 0 R /XYZ 62.692913 375.91685 0 ] <9a8df047e64bf9d1d00515feef45ae96ce544b21d3416af24c5ae1ef53017ddfcab3ca1e614db34f991eed136e5571391aec12e4a764229c41e538dd0ab84629> [ 741 0 R /XYZ 62.692913 233.63685 0 ] <8e82c5a2dc2d2b360da2aad613a61dc964f1e68839a90669cd1753d2eae52ebc12512575b118701a403b97b42088c9b20a9a3b16c8777a248fde14670d7daf200625adc00a015b5ca89ca56006dedc86> [ 670 0 R /XYZ 62.692913 292.81685 0 ] <01a64dd8b04666df1ae748efa86b1bcc5455e6b423fe3e901ed236d7056af8ef57d8b3912693e871bb315013700a12ccedfbc382b897fa27c50c5a3d0b920e26> [ 467 0 R /XYZ 62.692913 201.53685 0 ] [ 720 0 R /XYZ 62.692913 491.76685 0 ] <4fe1a9a0974b7f1d92c3934561e13e307302611c9151bb06ba6a109c0c3985f82fd4d6bc877ff5969f2c765fa79b2031> [ 590 0 R /XYZ 62.692913 785.19685 0 ] [ 367 0 R /XYZ 62.692913 785.19685 0 ] <62bbc78bc2a330bbf464d0e73ce8893491d77a85bb820889da50469527d68fac06c232f70d879375c3beedf738805013e92b2c875f8642a2889dd8075af4d027> [ 895 0 R /XYZ 62.692913 430.31685 0 ] <3146d5f407c1a97676e192e47ad92e66fa1eb4badf702cf6307c0df052a619daf27e26e33c8d0c384cb024220e73c5c263292486e498f635cfc50d821fd46846c80df5c11fa31e683931a84136e3481c> [ 635 0 R /XYZ 62.692913 785.19685 0 ] <0cda48506ba81c8bbae39ad536e7772468df028748890a936cdfbd259b7eb48a25573c93f46f0c725d3dc2819ece9945cfeef5e0384e1b7c1253f13825073943> [ 264 0 R /XYZ 62.692913 761.94685 0 ] [ 105 0 R /XYZ 62.692913 536.24685 0 ] [ 188 0 R /XYZ 62.692913 762.69685 0 ] [ 774 0 R /XYZ 62.692913 487.51685 0 ] [ 817 0 R /XYZ 62.692913 785.19685 0 ] [ 938 0 R /XYZ 62.692913 568.46685 0 ] <4622f082008939cbc43fc9306779125e0c1430775f59ba4730d7b5f651217b91aabdad95759b79d3640df354b33717a8792e32abed7fbac91044825ac79d069bdb6c7676bd46e3e1fec2d6cad4cbe281> [ 299 0 R /XYZ 62.692913 235.88685 0 ] [ 902 0 R /XYZ 62.692913 586.06685 0 ] <3a5e2c87fbe424647c6409932d94b68168f831bd1687d975d2ca926142162214d755f55c5759c4c7418abdb59593d98125d574f8529b25a6863dfc1cf03415ee> [ 540 0 R /XYZ 62.692913 185.58685 0 ] [ 335 0 R /XYZ 62.692913 439.86685 0 ] <2bd055a1274174c59d5ae225769c25bf876958e6480b371e77c5c5ed32e95c96ff395177d707cfee8ecb87ac4412d424634ddb90314e376914ff6cfd1096ae17> [ 914 0 R /XYZ 62.692913 704.24685 0 ] <4508488f58781faaa326ba9e7ac6003511531a495fe9c38d766e1829a9e70d0637446f0a372d2ad7c1b30d2ad0896272c03714c84ee627451a0d9623cab2271d> [ 411 0 R /XYZ 62.692913 598.01685 0 ] [ 549 0 R /XYZ 62.692913 761.94685 0 ] <9b8a09d65708365e8372904cc71cc9d4f8e6914d96634b1af262662e4a2616bd094a30f2a5674276b26065b0cc0776f4af2e04846637a8543363e7d04e5499b0b6313c14ff2a3bfd0df975a41b891e97> [ 690 0 R /XYZ 62.692913 761.94685 0 ] <7818b5cbffb42c08e00272a13f51e96f6c191d8fc21a56dfd7925cdfb9b175df7a0e6b4a94c148f599bc8010c550ca265ebe01fbeb6cc98e4f12a4bc066e6df8> [ 876 0 R /XYZ 62.692913 353.33685 0 ] <3427590d40842edefd93d57ad781de3167609f55d6b79c3956a68b18278ac80e6dbc5a0bbd0e4f31bd0691d6d4b0ae33> [ 402 0 R /XYZ 62.692913 704.24685 0 ] <48b406f19359e52bbb0472f91fa7ccfcf9af2c9b06e45125abbb4f2f48ee3e5d13b2d3f4ab68160f7608a83eeefac36d4651eee2f8d8e575dbb46af4dac5a4f652af9cfc11c4c7ffdaac5c8d1e99d394> [ 579 0 R /XYZ 62.692913 761.94685 0 ] [ 372 0 R /XYZ 62.692913 621.26685 0 ] [ 419 0 R /XYZ 62.692913 230.05685 0 ] [ 510 0 R /XYZ 62.692913 603.66685 0 ] <774848985dbdadff2b446ad37eca863561c9a1911632362f5f03062c2cd1120c9c9fd33149e0eac2d9bac78ac6a9d54305cebc103994cbc5ef1b180cc171aa1d6cf7fe6439f03ad42b024d71dcd946b3> [ 888 0 R /XYZ 62.692913 592.34685 0 ] <21484630503c3be158257722b623573abe8de67755cccf1305d7717d461f100d6660b140480a75a1844cdec3c2efe38b0a541f273c05884e44ce190de7173a29> [ 708 0 R /XYZ 62.692913 785.19685 0 ] <944d158ff4b92a00b37da40c37e215f2f390f1d941ce8940c0483c2e23edddda008038140dd79f387159a6d33a6c19ce919abacf9ed1c2c21a4cf266f5da039856bc5f5a33f524ec25623970659ee63e> [ 679 0 R /XYZ 62.692913 785.19685 0 ] <48ed3497a3e42d81ead396c33099ed00328f5efd4d5a7d014f68adfb51a7c4ed040d575aa2d1154b0e3437d4cfdefc714938abbff239e1d216434e488cc433ea> [ 240 0 R /XYZ 62.692913 704.24685 0 ] <562236f122974d49d7d184fb1ee5b5b52fea1b1d1dc524d5a8a75ca1eb876f7edcba60c5a123af817f92f4cc28fae0be0a418b6a938d07f06d58b95b588861b6> [ 922 0 R /XYZ 62.692913 497.11685 0 ] [ 447 0 R /XYZ 62.692913 785.19685 0 ] <8b475db07a7a608a6d7f413ab0c237da0cceaebd213d23f2d6ae618e49e92d369371f66a11fb86c76e70babeed5fa3ab61675685f3a9d18aec63e5bc193cf6af> [ 825 0 R /XYZ 62.692913 658.04685 0 ] <46e4115ed446a94055ebd6ff56e41534c7048883bf0165a15c12ea3c7b0f9f774ceff54100d6e78a35e08e8bbc5a7f0b5ce7019e7a9e4f038ee23e216e751867> [ 930 0 R /XYZ 62.692913 320.48685 0 ] [ 274 0 R /XYZ 62.692913 384.71685 0 ] <541e6dd6ce231d464f02ee188ca2ddb39b8d207a5a188999ee3535d7ba3a007044a44d17d734a6e37f192d2d2baaa10cb2690089d290fd88fad89bfff623ea9c> [ 985 0 R /XYZ 62.692913 237.18685 0 ] [ 346 0 R /XYZ 62.692913 479.51685 0 ] [ 481 0 R /XYZ 62.692913 621.26685 0 ] [ 325 0 R /XYZ 62.692913 166.25685 0 ] <074f006f1abc255a3fcd0b3c250faa585fb71cc716c318d9368cbc6624d9e09158847d98f17d40244c101c44653c6ae4> [ 112 0 R /XYZ 62.692913 398.09685 0 ] <98a530d4f1af0c6d26a07623c1111ca080a779d18c11a4a9ca6ee916dd4882e373b67b2d97c3f168ef5bc67795520330> [ 969 0 R /XYZ 62.692913 259.78685 0 ] [ 752 0 R /XYZ 62.692913 233.51685 0 ] <03d1fd18a44a71b22ff175b2a7ba2095a3419d16941af18878ff15fac3755f58f080db066edee06645fba84735cfe4a8b733f66fd5ab81d6c9a7ea708925583125570797fed7c6123259d65c50804a0f> [ 623 0 R /XYZ 62.692913 603.66685 0 ] [ 697 0 R /XYZ 62.692913 785.19685 0 ] [ 847 0 R /XYZ 62.692913 623.69685 0 ] <8ffe5244bad03a7ed6d003e3d6f054d6247366c2e1e8eaae722fd4322ab86ca4473840b7ed9e8d4a5d7adadd7e9b439e9c818f346462cbae1f0cd6e18ada11dd> [ 522 0 R /XYZ 62.692913 288.38685 0 ] <44bb0dae4ac656fc0989919988fce865e0d22329db33dcf1639f52763b7925c7d9316ddc9e0684081f4e5b5a83377f341dcbf906c94c55b95d1241f86426b2a345c1e0f55f913705d77093eef3b50164> [ 559 0 R /XYZ 62.692913 785.19685 0 ] <86e3822175bae915172e032b5e14758cf6697106192ae439448678c5c696c9bd956e0d9dc673c572620e7a05d390605bc09b4b41db7c8c900edd9ac855d9ec82> [ 475 0 R /XYZ 62.692913 182.15685 0 ] [ 391 0 R /XYZ 62.692913 358.78685 0 ] <7221a3280faf28d6de21bb0db70c8aeaba9413356a711a9e6e0238d403e32ddd> [ 188 0 R /XYZ 62.692913 719.31685 0 ] <519fd3e201fae6fb6db4d7c22cd8ae473edc9eed9106171652da0dc95528d13465e513961c9f2c0f9216075b37445c79> [ 188 0 R /XYZ 62.692913 653.28685 0 ] <656ada35dd5ad3ec56af0454a099e69752610d0d8d27b2d4720245e5f1d07bc4c785aa1fb56d871c8b261ff373560e53> [ 188 0 R /XYZ 62.692913 269.25685 0 ] <042bda657baad536256a8ecc6495ba7e1cc3df1f93ded05c9ccf7668f0f074339cf05bdf241375c9bb381ad68d54318c9c47e967f85211f148dd5d9ebde7d4de> [ 189 0 R /XYZ 62.692913 785.19685 0 ] <5fbdca1104e1464f9e3f775046abd365672b2cab5ec806abff05a7f9926270666830bbc3089c51d37b4936db5f10b303> [ 189 0 R /XYZ 62.692913 515.81685 0 ] <748a2e628bfa031686598bb1e810ce48b22966887b8969c3b62cd045603535e3a0c9b891eedadc61f8f20d7c46431a9528b62db4c7def66badac6641151831a2> [ 189 0 R /XYZ 62.692913 480.38685 0 ] <54bf99495c8de7fc865fcc1dadd07f76447bec30ee0edb85bbd5eb334be50658eb307f8160ad6c8a2f1373a69715880c> [ 190 0 R /XYZ 62.692913 704.24685 0 ] <8b3e161ec396463007078dc164a0d5305075654db98b7009f5313cb6aa063eca83bbdba5a05250bb3de0af70f99bbeac> [ 190 0 R /XYZ 62.692913 296.26685 0 ] <2727b4a509bb9aca8bdf7692a90c94f351a59b667007155c2afbe31f32124f79> [ 191 0 R /XYZ 62.692913 640.44685 0 ] [ 191 0 R /XYZ 62.692913 605.01685 0 ] <95fa90a21dbdf46c80ca924aa30eb47a7f6258ea584a21658ee1b0294ed08cd1ca7fb3ec16f3399f4f5714764cc34b99> [ 192 0 R /XYZ 62.692913 603.79685 0 ] <8df5e70f522042dee4f183af0b44499687012382e5645035e6c2de69d5669fa1b43891262a718b2aae1e252a088c0882> [ 193 0 R /XYZ 62.692913 704.24685 0 ] [ 193 0 R /XYZ 62.692913 221.66685 0 ] [ 194 0 R /XYZ 62.692913 371.59685 0 ] [ 195 0 R /XYZ 62.692913 491.14685 0 ] [ 661 0 R /XYZ 62.692913 157.80685 0 ] [ 914 0 R /XYZ 62.692913 522.71685 0 ] [ 105 0 R /XYZ 62.692913 304.89685 0 ] <699b5c667656429c349d549b5a6051dc995cb48ebcffad79335356da1ee2b4a5a95c2d8de38da54c3269360e2d732319> [ 197 0 R /XYZ 62.692913 762.69685 0 ] <5d5d076fdd580f113870b103abb0dcf55b44b93b819ef383ac370804dcaaea4b99d92ede2e88ebfccf388d9dd577dc179491341825645290d36e5f00055dc02d> [ 902 0 R /XYZ 62.692913 337.23685 0 ] [ 679 0 R /XYZ 62.692913 463.66685 0 ] [ 774 0 R /XYZ 62.692913 305.98685 0 ] <86a8ef1d27c0a03b9407fb5ce4f88cae1eea835ef287a9162f5e19814a7240a291ecb6195291a7baac1b0d6c8a94af670627a025389ad887a2376f7f4ec12693> [ 549 0 R /XYZ 62.692913 413.66685 0 ] <551b9a1b66349c26464275caab1543b18d297a14b725cfc3a2cbd8b935de3d6c1be11be8ac84a86fb38ca861250931b8eeece2637d505093c939827af062fdc8> [ 763 0 R /XYZ 62.692913 621.26685 0 ] <2a796115af6298fc3340dbe32ef0da5c2cc18238f011d9342a040771b24c437bf1bb6230ec48c94b4b541ecc500f6b32c0f3f931a9170794de70501cbd1ee60f> [ 817 0 R /XYZ 62.692913 450.16685 0 ] <337a09e7b87981988d8ae9f8da7f57d1cc3ea9b9f8c174a269c252d0ec1da7b43b2610025483a5ea494e76e2aa10e828d2ace9b486e3b40e8d9acd78b0f2da4f> [ 623 0 R /XYZ 62.692913 140.93685 0 ] <45e695a6a1d187550ca4c458afb10a14d3fc1299c6159353fa3112b246f228c323307a2bc473d6beb18211b89d63ccdf7f3b5ad650948fb8a88b26645e57c3ae> [ 447 0 R /XYZ 62.692913 586.06685 0 ] [ 831 0 R /XYZ 62.692913 315.58685 0 ] <25c8d326b84cfe473602322db1666a6168f3966bad826adeeec3052ed43fb0565b50fdf74dc92e24ef9f600738d851ab2f16f857b7a2e29c7b2e7ae7a32339c7> [ 730 0 R /XYZ 62.692913 396.53685 0 ] [ 690 0 R /XYZ 62.692913 537.21685 0 ] <49b94e715931b853da6663d44c1cf5078238577a06004ade06108cc6a8eca67db2f44dece7c7611ad0864fae2f40484c97dfea9339800cb605af44efd979771fd74a3bf4ead35b624f35a6cffe3a1e6373200dcace1985af70213bd33c2bf383> [ 568 0 R /XYZ 62.692913 578.06685 0 ] <04dd498599b152141b7c14e7ac009098a7a4ef6c3fe7c6d12bc78dfaa329dcd1e057f3f99470b3e0807216d0f0388e30052b3d89c592a378cb65478301902939> [ 930 0 R /XYZ 62.692913 121.35685 0 ] [ 601 0 R /XYZ 62.692913 603.66685 0 ] [ 807 0 R /XYZ 62.692913 398.88685 0 ] <70b77bc52740f039e46560a2561b5abee315c04caa6c85004a73f0299c130164c32a65f2f929064b3ce03eecd40046833249b247f49b30e5ed686d12c98bae22> [ 847 0 R /XYZ 62.692913 442.16685 0 ] <33aa88e57d5459e87c9e71130b3147772a17e00c5437abd6e50106c46b2d0b7d02f012ff06b1196a19fca03650a7f445c81960cd0175f61d8fb1ae8cc93ac753> [ 742 0 R /XYZ 62.692913 696.19685 0 ] [ 531 0 R /XYZ 62.692913 785.19685 0 ] <279d517d20ff30a308228db0abbfa66b8d412f649e995f9273d1ed66228f98662615e7aecd7f171eec74601612c4d312c202b8935dbc8ed00a6f17a545d0cfb6> [ 420 0 R /XYZ 62.692913 785.19685 0 ] <65ad1a91a65e464bd365b4d626707bc935423897f63be46c5eb84f2862941c344bdab6384a0d08e5ecec7ad71b525d6907a2dcbdbe0c0570651b4bf53b295a2c> [ 523 0 R /XYZ 62.692913 635.59685 0 ] <423783c763f06ee6b8c69c519f2c28c97ad99bf7e45c8fb96ad36a67dd57babfbb0318aa40a6981c912ba681510f7666> [ 382 0 R /XYZ 62.692913 134.55685 0 ] [ 840 0 R /XYZ 62.692913 578.06685 0 ] <34d32c5c11acfaa856769c70fdfa9908f3347f71bb5defce77494e12fdaed1708d594215248186f63503076113765bff> [ 970 0 R /XYZ 62.692913 785.19685 0 ] [ 753 0 R /XYZ 62.692913 693.24685 0 ] <6588bb2e9bc167b59e39de53b8f3f4ea0a398e3aa788c4b86174c44e611ac580a3e15792b31044463d4608311a956e41cbcbc934366aeee1e58a00f9ed07b9fc> [ 481 0 R /XYZ 62.692913 439.73685 0 ] <7ef61ba0e80a9143040e55fd3e5638ecd2bde6383e9600a40c7511285881e9caa595002b2c7f858bbdac54388a0b91e25fe67ee97955263e96a4bdd569f75c25> [ 612 0 R /XYZ 62.692913 785.19685 0 ] <50422c1af7f0538e333a3a4d7f89f5c01e639c8593ae3bf1b27b12d9901ba118a89fb044cf0e2c9b2b865550f9de021bc96a98578f1d6051d5ce7eea287a1aaa> [ 264 0 R /XYZ 62.692913 562.81685 0 ] <4c06778ffb1bf5249a1502eab20d27e66e13c27621a9ed2ec28827511a50901d0739e41dd8fa5bb346ae4aedfe6f2609e099df5bd3bfb933e8d77afa5cb05102> [ 367 0 R /XYZ 62.692913 586.06685 0 ] [ 876 0 R /XYZ 62.692913 189.40685 0 ] <31e7555e37afc4da7e887e38fa3f6433e17ec17dc283de71c25d929a92a6126cc3563b53d987dbf0ee228c6e383d597d> [ 938 0 R /XYZ 62.692913 326.13685 0 ] [ 476 0 R /XYZ 62.692913 592.34685 0 ] [ 652 0 R /XYZ 62.692913 470.06685 0 ] <1bad193f915aa76fba032fe8605a0ca7eab8d2f82777068eb9485b532311f9501db4e6d80291d077134f147eb04d81bbc866c3405581fb228ae358bf95591c46> [ 908 0 R /XYZ 62.692913 785.19685 0 ] <4caa2035caa340edacbe2284d233fa992abd3bfdaf394848a998d90ed05260d8577cebea158faa04d0a2d638ffb8fd2897aa228d8a6d7e977feff9a41b38fad914da6b661aca447c8b21648e13e296ee> [ 335 0 R /XYZ 62.692913 240.73685 0 ] <33a8fe4b5e1c3647277dcbe5d37916a8a0ed1f9ed9d4c346ca155f9503db4c3321e4ceb77c5e8f31adef887a41fa60436c933a564f0d5316c7b0551fbdcad828> [ 310 0 R /XYZ 62.692913 458.91685 0 ] <735158dfd8e9fad793e00bb3c8bf3083b068b97117c830c122f0c23bff2f2971240a429dfbdcfaf08fef8b45b83ddad82054f13c80f7c95fc236e92f2a5c6492> [ 468 0 R /XYZ 62.692913 704.24685 0 ] [ 720 0 R /XYZ 62.692913 310.23685 0 ] [ 590 0 R /XYZ 62.692913 603.66685 0 ] <38f8bed5e801b9004dddc97ad9e6c4ec579e1b9d879cef43994e1dbbd4578acc6c64d083a45e3f6446eaf7fcb74ef7560ee5d9dcf3af22af46a52b4bff6fbcb7> [ 977 0 R /XYZ 62.692913 378.93685 0 ] <605e99567b97182a258c4ecb7eb18e35e6960773bcd966c7f7351f889ae3592717d792cb5f1c66c0d57aa512d0c2bccf2664373d4dcc92e53519c40e6b1e4523> [ 825 0 R /XYZ 62.692913 332.61685 0 ] <32fcafc8f971abb1fd9d6b3aec1f8da606e945086678e840805127951952366aef17414e9d61e580763b57b40ff1f06d098f6c180200f627f33e7f98ca5a18cb> [ 895 0 R /XYZ 62.692913 231.18685 0 ] <953fdc4af2763bdeff2f6ba03e79fade2b3da877d0f6f01938de405c25b4a2c2acb37f30f19bf6d5cd283fa19d22e31416357f7f847db2b9e26b629c5a31be7a> [ 635 0 R /XYZ 62.692913 405.56685 0 ] <2f742b22b245f513b78794bea53663ffbbdc4f3ba870b7e8dd4ce7741030553a68c0217e47e98b3c71d5bc09c0f39dcb74285ea2a0abd9875ee9037cfdb23019> [ 785 0 R /XYZ 62.692913 275.93685 0 ] [ 559 0 R /XYZ 62.692913 578.06685 0 ] [ 240 0 R /XYZ 62.692913 522.71685 0 ] [ 888 0 R /XYZ 62.692913 385.21685 0 ] [ 286 0 R /XYZ 62.692913 410.81685 0 ] <1d50472cd9196cbb3debc6303713dc2af2b872c604d9397cd46bc46f8d4a932b03fbf8c6f8f0db1cfc41b67e304c346919a1a6f9a95c99564bef8371908808bc> [ 256 0 R /XYZ 62.692913 608.34685 0 ] <9e2e87cedadfcc1d33c414464b774fd4eb8a559438b325f5ecc15bdfe3a62a8f6e460e62727e0f28740da59b164ea6b9a66de6f9f441f0d38e6380dbb73cecde> [ 250 0 R /XYZ 62.692913 704.24685 0 ] <61e6716aeb67f0f4ba68dfb26de9efb36a8e73f9998fde73b65157b5ff9fe6e763db8129acbca2aac909aa3f461f1ec7e861241a7a57881550977107f21f3b1d> [ 411 0 R /XYZ 62.692913 320.58685 0 ] [ 644 0 R /XYZ 62.692913 149.80685 0 ] [ 671 0 R /XYZ 62.692913 761.94685 0 ] <06c747e04f4689605185aadaaae53adf21c9e6ce4aec4321af0095f7ef24c7de7b96593c915f76c6308c3eae47d55df974da46623afa35623d5b0ee1e73711e9> [ 797 0 R /XYZ 62.692913 194.38685 0 ] <159900b7a7f8572c354850e81d38fc24f3e3ac077fa3e75675b0278af2097c4427b6797a2d83c9ee2853a63f442a9126c187fb65c26ce71e51b2d796b2d2cd0c8ec367290895b13ad68ecf4476bdb811> [ 439 0 R /XYZ 62.692913 371.96685 0 ] [ 300 0 R /XYZ 62.692913 761.94685 0 ] [ 708 0 R /XYZ 62.692913 586.06685 0 ] <8f85527f82725506afa3cc2aa41af572efc48394937ab72f21a0fdf59a9a0c0fd1fe31d610f551d2b3819bd84590a268223f1995364de81565e4956ef2b19c83> [ 986 0 R /XYZ 62.692913 785.19685 0 ] <274be0b7d518d46712ab5dfba550dc8c69d81835b76c663b642413f4bfa44a8182c3b0c5cbe7f34ea65a6766c5105ef4f6e08005ec125ffaa0f358bcca8d670c> [ 922 0 R /XYZ 62.692913 154.08685 0 ] <2edab6a8ea57a9cefc767e83b6d251c6f4e80196ad0c19e436f731d5e9227f92d8838632888a1f6573c01c9a565581681ee94d05fc2ea621cefe3fac7bbc7ddbc9c1af3364d9ca49fa7e7957654d1663> [ 402 0 R /XYZ 62.692913 426.81685 0 ] [ 346 0 R /XYZ 62.692913 280.38685 0 ] [ 579 0 R /XYZ 62.692913 537.21685 0 ] <8073633ace2c137d1fc1c46fb0a0038171e31db3106e4f51bce1470f57ab3e394f1b854e36277c136730243ee6f60f01> [ 948 0 R /XYZ 62.692913 648.19685 0 ] [ 495 0 R /XYZ 62.692913 479.51685 0 ] [ 510 0 R /XYZ 62.692913 422.13685 0 ] [ 326 0 R /XYZ 62.692913 704.24685 0 ] <39e71c0f2f66fd165a997fddb9ddc07388d7f552ecda2bb2209f03146c720ab8b0c60258a339b4193518532c3c5e0fb7783a26a829a1877fe42b2feb57d067ee> [ 954 0 R /XYZ 62.692913 223.71685 0 ] [ 541 0 R /XYZ 62.692913 686.64685 0 ] <0351e270bfada120ff0d841eef532428e3daea1fc2b215f608f9a5dca7bf8a4f7fd55e8db91d5df71a89fff68c395274fdfcc6b3c9f0a324c9da2c7c680ce015> [ 274 0 R /XYZ 62.692913 203.18685 0 ] <76b793db905e4226bff5fb93678a72bd640554b46da1b08ff600edd773c829449760c6f8a402486930155b3f3fda02db> [ 962 0 R /XYZ 62.692913 415.16685 0 ] <746109c2623b7486a9670d8b1658ec7a32f8750a50ce0532dba198992566dece19f815ed45750e1a445a8b5a39c8f9dc42fad96f1ec51f64e4d16538da47d3152cd5ceb7ee773c6fd5215d121a6946bc> [ 697 0 R /XYZ 62.692913 578.06685 0 ] <0aaa50c876118e6c29b5693b003782d3c2962f7a56a53475ba1c9e24c535399bc3e921641769917962c383399a638885> [ 461 0 R /XYZ 62.692913 159.65685 0 ] [ 113 0 R /XYZ 62.692913 785.19685 0 ] <9ccae2d28b05c20c5278bc6287a8f0ab9ac5774eb984d30c1ec3b44c591c387566fd24eaf5aa2f9d2b3e10d311cef3a0> [ 391 0 R /XYZ 62.692913 223.45685 0 ] [ 197 0 R /XYZ 62.692913 719.31685 0 ] [ 197 0 R /XYZ 62.692913 653.28685 0 ] <3a0d178fd5ab2fe03b66556ee8264c361055331a0c7f143ead027a0d28d12ab18f732fcc5d843b81460974cdf7c9069ecb443587f6e18bdb2badcf70739e4bd7> [ 197 0 R /XYZ 62.692913 408.10685 0 ] <421322b458689bd37b337b2c34d95b51d372a52d9962c96fa98fb8a4bb4687834b7e46aa8fd6352fa910b96bff89cf6a> [ 199 0 R /XYZ 62.692913 785.19685 0 ] [ 199 0 R /XYZ 62.692913 177.11685 0 ] <3bf0d4db5ed650050900e392112527af1e38b3320bfa7c8d95df7d194b9549f98dcdc76e6a943a472aeb4b4b7462a0192caa69ef6fe8fcf83a210d39fd5f5956> [ 199 0 R /XYZ 62.692913 111.08685 0 ] <005f0ccac61b944363cf45714daa6ea271f674ce92af9a7d24772bd2966eb32be610103a5e6809f02666d237020baae4> [ 201 0 R /XYZ 62.692913 125.94685 0 ] <9c0a6a1bf41ea5194727d08fb9cd9bb7d7a8100e4b658440f5d6c7d08568f85994513d6d66a43eac8231e9e39ba6570b> [ 202 0 R /XYZ 62.692913 259.94685 0 ] [ 203 0 R /XYZ 62.692913 434.99685 0 ] <5661d57d0ab97d327232e3aa89daccf8ba3460b2caede2d31830d489f569f887542eb9d3fa336376453419acacd96bec> [ 203 0 R /XYZ 62.692913 368.96685 0 ] [ 204 0 R /XYZ 62.692913 203.64685 0 ] [ 206 0 R /XYZ 62.692913 658.04685 0 ] [ 208 0 R /XYZ 62.692913 509.39685 0 ] [ 481 0 R /XYZ 62.692913 258.20685 0 ] [ 690 0 R /XYZ 62.692913 373.28685 0 ] <9a8112d5325035b8085aa3978a06aa5f664ce6a16014d26fae5bbb5cfae60cfeb0c2e98680603b1014aa6dede154a5ebd8f6a0745e7c8084b05e79fc8b848b69> [ 326 0 R /XYZ 62.692913 522.71685 0 ] [ 763 0 R /XYZ 62.692913 343.83685 0 ] [ 847 0 R /XYZ 62.692913 260.63685 0 ] [ 708 0 R /XYZ 62.692913 386.93685 0 ] [ 931 0 R /XYZ 62.692913 675.64685 0 ] <0763b3ab8f53c934d74d1ddcd10ba83005cfb4993c1029d329bf180e4092f14b5bc4bd0755f69190a980a3a49b50a76af1127346dcc62f82c36c5535ebb18c00> [ 825 0 R /XYZ 62.692913 133.48685 0 ] [ 108 0 R /XYZ 62.692913 785.19685 0 ] <93dd7547a1902ebb52ca7c85799ae10125c635552b401aa6b0a99e111a0dc7cb5d43ac4854fd16c7ef682316657a92047839ce8dea1965a1f97f1fb4dda69fff> [ 212 0 R /XYZ 62.692913 762.69685 0 ] [ 559 0 R /XYZ 62.692913 396.53685 0 ] [ 440 0 R /XYZ 62.692913 785.19685 0 ] <1a28a47917ad56af302adac4e84ddf8cb04909a0db86a44b99f4bd0420043f773da9cea2f665a5b225b0c3b20ffbc3fabf22a015c50d2bbc667de4979264e8fe> [ 250 0 R /XYZ 62.692913 505.11685 0 ] <16918a1b2f6b3bc4fcb39cd6ab1ee8a890fd5d5aee9dd857464b6375a0d8610654a73ec473ebe67e7742d4ceeeef5e8a0a1c6f0d424f2d461becd6f965d64886> [ 531 0 R /XYZ 62.692913 603.66685 0 ] [ 817 0 R /XYZ 62.692913 233.43685 0 ] <3fa2fc82a5684376627863337f47340fab4c58e21e784e305b8426eaaf73a2dba85f1ee7c56060758ac06fb289a0bf5acffa032dbf14ada695d92b901dbc6314> [ 468 0 R /XYZ 62.692913 497.11685 0 ] [ 662 0 R /XYZ 62.692913 589.84685 0 ] <1f8e6926852b2e41faea74e5ffb14252aa7cac993554d246e7c53b891eb822a2c4125f7be817a5a2baf6f9e59e2a6798b64ad9f6bc273e0a35b967ff949c46ba> [ 300 0 R /XYZ 62.692913 580.41685 0 ] [ 652 0 R /XYZ 62.692913 253.33685 0 ] [ 730 0 R /XYZ 62.692913 189.40685 0 ] [ 955 0 R /XYZ 62.692913 526.74685 0 ] [ 962 0 R /XYZ 62.692913 180.83685 0 ] <27252f4fab8ea33bf4698f52e783967df428d6de34d85b7fd105bd2790aca4a521f466693bbdfdd89944afb95ea1cd1e62660935f77c2325069ce4bdfadd9bc161590bd1ccf68f43dcc5582b260c32d9> [ 889 0 R /XYZ 62.692913 785.19685 0 ] <6fc7d6561bfd220f5f7704a8a0c6dc924e0ee496fc4eb0f699ab93d65df3a6dc3aaae45f62565a854ba004bf636dfa37b3cd8d7a4640d1d985b4c8fd9018f7411d9164e43351b780b2186f9c9e29fd6c> [ 568 0 R /XYZ 62.692913 353.33685 0 ] <85e9a1f56aae1675d996975f1144e39b0b96fc94e59025444ce4b6cde5f3c8e525bfa447f158ba248ffb8e2175d3ba580c4713ffa204d4bbe7d2e11825bea12e> [ 923 0 R /XYZ 62.692913 514.14685 0 ] [ 948 0 R /XYZ 62.692913 449.06685 0 ] [ 807 0 R /XYZ 62.692913 217.35685 0 ] <0b0802b2f3e9f12b12c0223c423fadc2021ddf4e1eb6f902204dc31f5d0c8401ff19bc4a6a7f295afc1af66202fb481455e6f17429c99dbc5b82665fe2b90316> [ 287 0 R /XYZ 62.692913 761.94685 0 ] <0ff7472e12546278e55b263b980dd39ba19ce5dd65022ae1b2f956535412fe7b7e9e30797afc08fce0ea0e5fe5b280dd> [ 420 0 R /XYZ 62.692913 621.26685 0 ] [ 264 0 R /XYZ 62.692913 381.28685 0 ] <35aabde48f0efd81cc33d057eccafddd22fc5827f9d6168be4a2f9fa03ff9d43a4f97fce805df17f7a2b668e717129b8> [ 914 0 R /XYZ 62.692913 297.98685 0 ] <0db9be20df69690bb2cd175fdb3d73931ebf347c293a4a0f1109fd1b691ca779e0447e1060eb288124d54f935cd50a25284d520a5522ca2a3fc5bf5f4a14248a> [ 970 0 R /XYZ 62.692913 568.46685 0 ] <9b06cf5366b7edbe8fce508f0fbb2e3dc292495f6a5f5c958b0bfe76b4906967c59a2c254237e3209a8ce8df255fbd2b5b002d26670c5a82c96f4b5498b9973c> [ 840 0 R /XYZ 62.692913 378.93685 0 ] <2e8a160338f5fbef91bc4c07bd3d0c3b558cb263742b12844a505ed4153636a6c1cb716f147d0b89ae272a18c2e2b11f0b474300ef808565425577521527c6fa> [ 612 0 R /XYZ 62.692913 586.06685 0 ] [ 579 0 R /XYZ 62.692913 338.08685 0 ] <3bdfd8824bd28d9b205b512646f2ac8b6da17b8c2858ec4f704fed79f9160a358809c6c9b76b320516a89e9e9c261e275648ce872b4e325daf631bc1c33b2151> [ 240 0 R /XYZ 62.692913 261.28685 0 ] <0634a749558300adfe2fba535134badda06d75be0f789aa270aced7044f283f802c356abc4f0ea938b2128cadcd4ba819599718a8ded9a4637e486e5e87bf646> [ 908 0 R /XYZ 62.692913 603.66685 0 ] [ 938 0 R /XYZ 62.692913 127.00685 0 ] <5014b0bcba18d17ab842430b38129d996b8e2a736678eda9830cb8777b0829a5c82f99d93027fc2ae06d49f58d9ec8d3629784d08c294f5abcc968ff3ad63b70> [ 742 0 R /XYZ 62.692913 370.76685 0 ] <1a8318a5695e92ef58963c7d525be6a1c34674d722f9c86d32fd8b566ae889ca6bab597dbf54a1f8a28f6a40656e6d0bbee490057386f0e20e2dcadd2d8e6294> [ 549 0 R /XYZ 62.692913 196.93685 0 ] <54355f8b03f4494ab1acb876c96cfffb86cbd3a2cf9f4a445dd3677d48263d323dfa1970029c8b5388366358bc3176abe68221d8241751416bcd3d25d2adb713> [ 697 0 R /XYZ 62.692913 396.53685 0 ] <2e3ed93a4e904183691add7836801ac3ab6bc1fb3770a431990296071034549739e3f9ffac2a0e1e40d45b9b7e7ff3b4> [ 721 0 R /XYZ 62.692913 785.19685 0 ] [ 977 0 R /XYZ 62.692913 179.80685 0 ] <8e6641b272c10008c6518a1f21bee6a07b831d267aaff0a6a1d5bba7e3589bd22a8e0b7a2c412c8045064d3bbe3a7ff0dfa8b10f8343982d4925c8c48b7e9b60> [ 541 0 R /XYZ 62.692913 487.51685 0 ] <4f450d9c91b18ca38d3fbbfd2ef5c9f486cd01ba2561bb92914140a7f7a99024a3c9cef81377a49b040731a63c3515a1dccfdfc5fafd120fa80347404a2cf8d82fabb9bed688b6f5bcf68514a2505ec1> [ 645 0 R /XYZ 62.692913 675.64685 0 ] [ 798 0 R /XYZ 62.692913 544.34685 0 ] <58531e3fbf7d78f5e52d6c5a1abfdcbdbfe68f740b3852e253392e80be87c91038333e97904cbaee30223513190d75a69a2fe6ac514d5a3a06298eb2947d65d9> [ 311 0 R /XYZ 62.692913 785.19685 0 ] <26058eb003c99b87e58313d4987f5a1753f0cb11005293f0a56e1c1bd06f08c4f3a6d7e2e6563d18b5918b8b11ebcb72036beaa98a432e2cd05fe98bb9ab6c019c336d9cdb3d21e06b4cadfe644e0a44> [ 775 0 R /XYZ 62.692913 785.19685 0 ] [ 510 0 R /XYZ 62.692913 240.60685 0 ] <6d98beb615b0a99ed64ef825bdb8cadbfa3e859b14bdeac13070cb93f2409600b3159a8910101ba2abe79c17e7b61129f41f2ed35188e35a2ad8d10484e2b99b> [ 275 0 R /XYZ 62.692913 636.94685 0 ] [ 679 0 R /XYZ 62.692913 181.73685 0 ] [ 256 0 R /XYZ 62.692913 409.21685 0 ] <6de8c0485d7c899a451c88ae368742c526b05e21f9c787a1fcf722408651bd2768f50b26cdf37137765681497c06efe98772445f48fbc93345041db26ed102e7177dab8dae446e557b6896281b621d94> [ 636 0 R /XYZ 62.692913 785.19685 0 ] [ 412 0 R /XYZ 62.692913 785.19685 0 ] <6fe4a2038a0e5e753256692d368ee4d0ecd2ca7aa597ec9b12cb46310067522801afba769eb75bdd08516063346323484f80d2297426a35592912418aa072cdf> [ 877 0 R /XYZ 62.692913 704.24685 0 ] <4375a1693516d28fed198679b0629b31b3b5f9cb423cadf6546b34513295a511c40d966040090696a885faab2708dc37ef0a4cd3b437ff2a895226ebf2d4eb61> [ 753 0 R /XYZ 62.692913 414.91685 0 ] <838bcef65526b422c3378bb9b2efe7ebed80d6a1472acc965eaf800c5d51025bd077249904b4c9d750e8a5bddcaafd5388c294d4a080f25555d4127b13d16eb2dfa3f5014adc40268e3f8a73110a481c> [ 336 0 R /XYZ 62.692913 721.84685 0 ] <806963524bda49b497f1377c8420d2cae43c560e25cb6b280c13a660c0b0d0c4a4ab0d9edf3baa5101b2a8e0ea2a8d9810dc19f3c442e39911757637f686727e> [ 986 0 R /XYZ 62.692913 603.66685 0 ] [ 831 0 R /XYZ 62.692913 145.05685 0 ] <663a27956d100b11a97ed9ce885ef78e0f8cad44743c1e8ab4e494d9b00d10a53af03abfbc647aa12222fe3c2a76aa51cdc9c975fcc25682ee1ee23f23a1aeed> [ 671 0 R /XYZ 62.692913 580.41685 0 ] <840f8b64487f3df7f16b1280da9a4ec32d0f6334b7e5b5a63ce536832de78cd523a9baa233e92f8285dc726c18bea278d4ac6bd60cb4db0226821c3ec5b49d34> [ 523 0 R /XYZ 62.692913 436.46685 0 ] <1a9118009ae57767f093d2c1aa6f85260cd1ff04883607f82485b6912773d2a5b404c6218ae452d98a38de10b97d6bf2eda577c85f763b29d8655232b7128759> [ 383 0 R /XYZ 62.692913 658.04685 0 ] <6168f6cbbf31598cbd0975fd3b242bbdaac91af16b5fa48f23f6597540fc3686a0b69b72003b31b9445700a0d0e9494e8ce798b164feca2fa6596a97779ced1f588a57ec7d6af4894ca05e8d8f4217ce> [ 402 0 R /XYZ 62.692913 237.28685 0 ] <4a5a05a42e54a05d55835d9dd212f1ea5734ce2ff281ca62736c377ea3f91a5649941cb8eb739d8597e2cbcfa824d69d07777f6d1667bbb87d97d253d7a4b014> [ 462 0 R /XYZ 62.692913 562.14685 0 ] <7599e43b458fa0a83d9b12373f938b7eab8ab0e76dff5f9a01373d8570650e73969c63b00bded85442f19e736caeca0e9133450883ee6b1b91bd8ae9bd5c492b> [ 896 0 R /XYZ 62.692913 785.19685 0 ] <2ec98a41bdb02db134c059e04bd1e607e8435bae9d83824a0ee459f0c21196bc655ccb44236218a76c01b30a3a84a106d3bd2c3e25a636353668ef380754d557> [ 347 0 R /XYZ 62.692913 785.19685 0 ] <8d22bb4d769a1037e8b9aec353d570f460674b371759be85500dcbb37ed280fe727de4494006ac384d82290787effaf639158d4d3283137ea68adbabe871b5f289b52a0395e4dde2920a48d9fb7d4910> [ 447 0 R /XYZ 62.692913 369.33685 0 ] [ 495 0 R /XYZ 62.692913 297.98685 0 ] [ 601 0 R /XYZ 62.692913 404.53685 0 ] <04b5a147ab2a58ce7e15a624229df9cdd067fb42117ecb9e5b1a27a1b1207d9631dca25b5dc39f4cc9661d2157380d54c9a116d1e9f398a641649b3b6bd42140> [ 113 0 R /XYZ 62.692913 578.04685 0 ] <2765ee2065c2e848ec45d8fe9dadce04487308981db334068d3c94308012e24d198ae8c1046fcd08f036538280e1ac474a883a24e64c423279ee9b1295e0421d3e50d909392d5b0e4a7a19a9d2a7e93a> [ 624 0 R /XYZ 62.692913 693.24685 0 ] <296c98191343f858e2ed3989d1a419f9a5a106520ff7731ca43cce64e7fbc7f62f08b3329d8af317015d6f796b440a2d0831ba388359af3c274bbb4d21fdd585> [ 786 0 R /XYZ 62.692913 785.19685 0 ] [ 590 0 R /XYZ 62.692913 422.13685 0 ] [ 392 0 R /XYZ 62.692913 785.19685 0 ] [ 212 0 R /XYZ 62.692913 719.31685 0 ] <08d8297f42b8bac4e4757a4cb96c6d0e283e385497b57113faa4ca80903e08fab8880c32acc6bde27374569ab8d971f4> [ 212 0 R /XYZ 62.692913 653.28685 0 ] [ 212 0 R /XYZ 62.692913 222.95685 0 ] <346c714309bdfda33acd21471e3d69e6daeb6fc7eed1e0df44b7015f7e44164d5bd1eddf7140a3235bf255cb36bd2c4c> [ 214 0 R /XYZ 62.692913 312.19685 0 ] [ 214 0 R /XYZ 62.692913 246.16685 0 ] [ 215 0 R /XYZ 62.692913 417.14685 0 ] [ 217 0 R /XYZ 62.692913 686.64685 0 ] <854adbe5b2b35b2d02370e7f6d7247b0b99636d5c77f4d4048575e67e7ec3b833892158d850fc759df5c445f1ea8cae8> [ 217 0 R /XYZ 62.692913 620.61685 0 ] [ 219 0 R /XYZ 62.692913 686.64685 0 ] <2347893dbed65c6f7544ac6c548b1ff0ff1532095f10f6b100619d9f611a52cc2711864c2fa209bc2cb1123da21b8629412e54e508b53be6752aef400a04be1c> [ 222 0 R /XYZ 62.692913 632.39685 0 ] [ 223 0 R /XYZ 62.692913 318.34685 0 ] <51a100ca78c76b4103cd47e6275d2962654701dfaf3da87ba5e960ecacf7bdc324d1fb78df31b7ccc0ffbea325d305408d49964204a6d474b68b18052f1c616c> [ 224 0 R /XYZ 62.692913 356.64685 0 ] <8cc76b999163275dffbdea97161a52597e7b755c172b2cdab1b5d8c09eb250298b15bcb779a54f1fe26d48f8aa174276> [ 224 0 R /XYZ 62.692913 290.61685 0 ] <40a55a186aef482fb9e1e83bc8347cf322d02122dfd36b0f279c6e8c978bf5f877ff750af8d8cdb53c2687bff65b20bd> [ 225 0 R /XYZ 62.692913 240.09685 0 ] <76c16ece6f58f11958e8ccaba33addb99d08984a3f1f13a1caf7f8461a91a7deab54c41c6b03bc43bbd36db86b7ce8698ae27a1c34e4120440d868a29c80450d> [ 226 0 R /XYZ 62.692913 570.94685 0 ] <23b5c9ea0e9e3b6fc55cfcb48a473876a0f7eb59149a962028fef146a43b605b263ab16d37a1821fba132716d37f7308> [ 226 0 R /XYZ 62.692913 504.91685 0 ] [ 227 0 R /XYZ 62.692913 588.54685 0 ] <39e1c8624e7805c959b65e3874323fd46719f4ee5dc8b6c54c283acd99e9f3a66c5d6c8238676404610e6e0fb459456a> [ 228 0 R /XYZ 62.692913 622.84685 0 ] <3efa359980680903d9ee84960a94e6a23efa4c28c65ae18aa32c7303f17e9df721584a0fed31791af98e185fc7b41a0f5511886dc9a112913a7013d263814d55> [ 482 0 R /XYZ 62.692913 785.19685 0 ] [ 908 0 R /XYZ 62.692913 322.83685 0 ] [ 798 0 R /XYZ 62.692913 337.21685 0 ] <97ddd535e9447a2cb190eae25c80cae68f30740584101564018f6118f048e7d0e39e9fb329d4df7fa4070e4b4bc75287a64d7d971f549a191b6b4bd22efaec64> [ 347 0 R /XYZ 62.692913 603.66685 0 ] <1467c2972f2601ffbebd7caf7e3a57c75d4f2324824dab6895af376bbec59c284f43af307c00c6b8961cd0dca55c0fb9fc2e2197759b297576de413cce02d0a5> [ 915 0 R /XYZ 62.692913 785.19685 0 ] <8a9d07e2d9083e03b959dd8a7492acd7138847e07c678d003251c51152fc89c8fbc4694631935e4bd6f85b3a8d1615fcae97560f5bbb74bd41d9b625b357d505351d769ff30b1afb304cf50c6b7fa357> [ 680 0 R /XYZ 62.692913 669.04685 0 ] <3d16a69c72a2b3bfde04720e2ba2ed236878eab78dd060f9cbe5d8307bc535ce2ac849311ffc4229811433a2b8ddc4b7472fbc392679fd36515b4fdeb25f9887> [ 496 0 R /XYZ 62.692913 785.19685 0 ] <328d234e790e0db3cdc301a1d81718756542179eac13cc17fea67a465ac581751dbb602114dd121f72453d67337b40c9c4ab599a3b6e2853921d05fa5a642521> [ 326 0 R /XYZ 62.692913 197.28685 0 ] [ 568 0 R /XYZ 62.692913 128.60685 0 ] <0fe87eb5c9bea9c9ac4a320e565335ae07ecef76b52a6850b1e99685ac9ae5a1aca95b875e4354f8211bfc983a2537357f03eaabe2803a8d36f62a1341a5518a> [ 721 0 R /XYZ 62.692913 603.66685 0 ] <726ebca166795487d0026c14ed94ce857d80bff52eeb1b36b0840ceeb651176cc6019a7de8c3d77252952c9da86ce76308f245e475f416c6b3b955901d42f75b> [ 662 0 R /XYZ 62.692913 408.31685 0 ] [ 955 0 R /XYZ 62.692913 302.01685 0 ] <14790e8750361fa3ea523cb83895acb50277e5d0d79514cc9ce8c8063c3553204051801bf54e5bd2a502280469c9f538b3a06a7161f7be8bc6d752470d2e2f94> [ 986 0 R /XYZ 62.692913 297.63685 0 ] <261b2ab47cf4db610bd84436a0a116d651383c544d833a2290b742ca28985f69375d45f658b70e6b91d09adc078edce6877b1d5d18038ba433bb7cb590838e2d> [ 939 0 R /XYZ 62.692913 512.54685 0 ] <43ce45b65f164cfac19ef637f685b62ff123825b78a98a5a1613e779992a2cdb21871157cd0fabfe78a9eaee1bb4b975f66768d2ae80d640014065b904ea7a63dac3aefbce36f5bcf7ec660a2f1ccc66> [ 808 0 R /XYZ 62.692913 704.24685 0 ] <5f6d7a9541d98c5f3344f3460dd436668bae7788708d8cc5c7ba904fe564b465f2719daa6822028cb3fb3d250afdea5ec6c74134b1444b07ca04275929e4887efbf97da835728c3d3484983d04bfd47e5985b29c52608f79efbca770659efc07> [ 440 0 R /XYZ 62.692913 525.26685 0 ] [ 541 0 R /XYZ 62.692913 288.38685 0 ] [ 612 0 R /XYZ 62.692913 404.53685 0 ] [ 523 0 R /XYZ 62.692913 237.33685 0 ] <96c3512543a94d36b2bec74b8f9143c3b9be0b24d04fe5bf754fbc56e62619522ff08c3fb1b334bba6d9d4552964c7eba70046a161146d76f4797a2e1c902b6f> [ 708 0 R /XYZ 62.692913 162.20685 0 ] <2889e2a05b859228a09ac8d0502b74ff35c023f535cf35b454f9a4b028455f2e54e4f89239fc8b137ed8e42c34888b482a74f622139ea63a108795991dd21932> [ 550 0 R /XYZ 62.692913 686.64685 0 ] <7bb394b3837fa63bf518a85ebdbe15e3a8ed3a01fb327b6348eeae2f8ba4bb3eaf8bb54b232fcbfaa076547d136df74a4fade67715e7dee3d5860bb68b9beaf0128ba203fbd8694589cfd8e79e437798> [ 877 0 R /XYZ 62.692913 327.21685 0 ] <295c1cc7c4866091e77fee57b498b3a019ccf46dfa246199383e9ae04d4953f8b1169d1f509934836eab8da9a6682e5c3c225262db64bd2e3219245d33554259> [ 818 0 R /XYZ 62.692913 785.19685 0 ] [ 256 0 R /XYZ 62.692913 227.68685 0 ] <4120769dcd3225f9089ba557363b02762b1caacdc3598304301c02d69fbc8a7a765460408ae27c2ab3190b277ec932aa123640af2e8bd8c8d9e65c214940f9fc> [ 653 0 R /XYZ 62.692913 761.94685 0 ] [ 963 0 R /XYZ 62.692913 560.34685 0 ] <42da7e40fdeb031af2ca45e15619914d96a0ecadbc0684f150eb3fe560152b38b7fa596da9e0b9289892eeba0928d803ee70bf31a4b9a3d3df2ec4bfbe3f8044> [ 559 0 R /XYZ 62.692913 197.40685 0 ] [ 697 0 R /XYZ 62.692913 215.00685 0 ] <7c902d0673a9457d94bdf6a2e0241c857944304af8d1b26c37b0e86e295a9d341b78128074dcd2ccef149ae3cf2ecc8ed0452b62397490118b3c23bf249e324055d9ac2269afe2b6e72b41d1cc7cf1fd> [ 731 0 R /XYZ 62.692913 704.24685 0 ] <0b363f9ab2c448dcdd054fe925f9ab00c8e499de2d22be47767048f857a6ba75d50a8120cebd9ac576d5081366e283d608cac057636e08efdcec51d5b44b9255> [ 580 0 R /XYZ 62.692913 785.19685 0 ] [ 468 0 R /XYZ 62.692913 275.98685 0 ] <173be13868ea3aadd79e7fe1501a84059e15f3f2390d414b5637db061c3cce4b1e92d8898c4c76e8c133a37e68176ba793836904c8c44061a6b9d064330606c4b0dffaaab0e4097c2e664cf7aa7eb673> [ 636 0 R /XYZ 62.692913 560.46685 0 ] [ 590 0 R /XYZ 62.692913 215.00685 0 ] [ 896 0 R /XYZ 62.692913 475.76685 0 ] <1ec959fd87cfbdc7ea3439d8296638fda4ddf00ce4da33ab9a0b4e91808af7f5faa8f272e0ee5241c2929052217aed3b8e526db85662e9ac06252e50fe4ae260> [ 832 0 R /XYZ 62.692913 704.24685 0 ] <075a6c3dc66454a7f6af2fe8bde870cc23aa3e97f3226b919eaa32007559fd572413fd5beb8c2c022ea6894305c6b44aec436c315fb36948f8b325e59447171f> [ 826 0 R /XYZ 62.692913 658.04685 0 ] <20358c7744c82fe280e515ee9bae405a206f614825c865887e306e05acf759de6bee0e968915d58d91c648b4c1eb401daf91342807e15f95e3d1c3fe6d09ae97> [ 311 0 R /XYZ 62.692913 603.66685 0 ] [ 775 0 R /XYZ 62.692913 595.66685 0 ] <54572c7092c448255e9a86ff4d6cea30be5fdbb2ac9e9bbc4117cab981ad3df88704c3a12c700474bf0edc79c1a3fc99c0e704b1645bf2b976ca94596b4ecbc6> [ 275 0 R /XYZ 62.692913 182.01685 0 ] <42fb85011e85c535e88792f84a9f43c7dfcdcb451bf39f37bc57618a4c56fa402acfffbb49cd735d1f38f3a81cd95b07f69779699eaf7da14fb479a113279ae0> [ 671 0 R /XYZ 62.692913 381.28685 0 ] <3e68517a42f797da8ac312cdf0aa68c2eb648711fa9611b66dd9b1eee388b4358e5969b6156af4bd7d61d45e2c0c89eb0f42b9d3242c5a4d034ac33483c10bed9db01e140e5a3d2b8ea104604f95e297> [ 264 0 R /XYZ 62.692913 199.75685 0 ] <6cd77bb526b1a80ec296a8ff585dcddd3e942b733eb1b4ec720715931279b420d67969ecd9f4695beee2b03da3475e55884744b2b7bff921e7563b27be9c389f639b7c0cd10ec7260d1d3fcc88ab5ea5> [ 889 0 R /XYZ 62.692913 578.06685 0 ] <2cf5f4ded12580c39818f5ae46466767c5a44577641cb7b1d460d3877525f893e5cfe62cd6e1485f4e0d7c5ae2925576f2a9a24c14819e82ddc54d14d683b49f> [ 978 0 R /XYZ 62.692913 704.24685 0 ] [ 923 0 R /XYZ 62.692913 315.01685 0 ] <1acd52dcb0cfb76eac4f786625994cd06a32b2e916b5c932cf471ea1af81937b7b9152f2068e60453baaa493a6ed892f18e37c68a4a22ac5bd43185e17dfa771> [ 287 0 R /XYZ 62.692913 562.81685 0 ] [ 300 0 R /XYZ 62.692913 398.88685 0 ] <40d318395ada10848c571334c576f9d633d74bc26f86a679d4998af1848fb610d68e2e5707a3bdf275986d797d0e570aa9917256161021c28387f15e45619adeac7d2fcb96fca52421c5579bafaaff2e> [ 511 0 R /XYZ 62.692913 785.19685 0 ] <3a93305a2ebe223b67271a971d81b72e94b8f7f90950f5ea58d63d2bbddf576d927cbc1a18d7580cd08ec6fb48436f7c4b203195e27c79cf1c9daca2cb29a48f8cc68645dd6ad9f4f54563b77fadaca0> [ 412 0 R /XYZ 62.692913 578.06685 0 ] <62ee7a9b22c855938720d3c77ac6a73824721e41a4f12ea16a2b80e21fdfd23d37500c7740ee0b257a42d6d31fe8b1895e4f5a0146518f0744d7dc6e7e2f3c3b> [ 848 0 R /XYZ 62.692913 785.19685 0 ] <38153909adb17332cb68319d1d8597780cb483af48fbc053fb113871dd5183274c5ce706cd05a5b5db1c02508fcecf73ebd3280ab8b526ad5ab5b8d46487b9b3> [ 970 0 R /XYZ 62.692913 262.43685 0 ] <01a0c6ab106d76220be08bbc47996489d1da5a975ecd1059a9fc113fe29b863ee2edfc0edebb2d664c9d6b52e817966c422a3b51b4fa330f16f12d835dbb7a06> [ 420 0 R /XYZ 62.692913 439.73685 0 ] [ 763 0 R /XYZ 62.692913 122.70685 0 ] [ 336 0 R /XYZ 62.692913 457.51685 0 ] <33e22af8e9ec250f18300b11396991276e7cad3d270c683650e7548fbc891f41e61f86cc4a0713231e3d7d5c83c72b8749846232d46fd326706c307bf51608e8> [ 753 0 R /XYZ 62.692913 233.38685 0 ] <466a63d4b0b33e77a9d32b39f3cab335ae18c4d49ae4d1436171e106b392fb70f05cccefdb9ba56a567a3e2922377981bac382850adba63273e704af3dfdc214> [ 691 0 R /XYZ 62.692913 785.19685 0 ] <6f39d2b57b8edb03c2cb5557d0d16e94609ad1969125d0990367bde30c522b1ba8d945650c3e3504ffc5184165fe8680857b3d5362dee62caacaee3207fa975ca39e32ef092474fe8e74eeb269471375> [ 403 0 R /XYZ 62.692913 761.94685 0 ] <969227ffb952114cbe5503400157a2e654f20761fdd4ef3399e9fc897a8fa85eacc0c76a091e9db0defcc559dfeea60291a7a7098b7b6c059ac2e119f29c58f2> [ 786 0 R /XYZ 62.692913 603.66685 0 ] [ 840 0 R /XYZ 62.692913 179.80685 0 ] <923a50b361bd582a44831cb542c6e78577b183eb4892c6b3eb68aaa675bd4cd6d8491013611d3b323707e3902a980917c2223b93b3c833b8c4177be440ee1982> [ 531 0 R /XYZ 62.692913 422.13685 0 ] <1551b57b42f8fe194676faaed5f3598e72558926b16fea82c931582bedb657c54fe8d117eecae24e53c6cc98e1cb466708b8c73b8ef0fa4ce47506b397c8cc82> [ 931 0 R /XYZ 62.692913 433.31685 0 ] <6eb3e00760576993356ce3a36fa7f7b01d941ccabccaf02f1e238a235eee6c8ce7b933b9fdfc77aa2b2609dbd27b012d4acce5e59c71a0e3dd1a09f9078164a38d417e5c16c57047632dadada3926948> [ 645 0 R /XYZ 62.692913 376.01685 0 ] <4ff3075909c3984691ebe014be4320dff413d6c51c6375b3295b47e2cf9b875f6b30180958667eed4de3c56c313f3b121042e658cc5f6f5b0cf31dddb2bb35f70bfbfe99924cd627fcc2596f7e64f4b6> [ 241 0 R /XYZ 62.692913 785.19685 0 ] <243f43acd9179bc006370705deeaeff78dbd85f7d2df0010efd04366113a27792b7e9c740f94c5b0e998b9b7faf03d326de653d506143d8774b35c48fa78e0ef> [ 743 0 R /XYZ 62.692913 785.19685 0 ] <49e2d8229f2916033cad612ebfe33efe98f15397e600794b8af40a322799263c64c8fec6319bd480f807df8b5b0ce9f8> [ 113 0 R /XYZ 62.692913 307.09685 0 ] <406a757ee7ad7c4f691055208e7094928f63ed19e05869716c721230624e20d2bbde482113ea1eec30fe0ec23838195b18f1eb4a889f893351f243bc2c4b1da39d49ca16c769b7cff984e33122db2db4> [ 447 0 R /XYZ 62.692913 152.60685 0 ] <9f0ffdfae3cca995b4e66c1666ee51de73bce53ec1a82b9cc3a0e5f8615e4d87438fd1789e9d68b106e13ece183fb4fc> [ 601 0 R /XYZ 62.692913 205.40685 0 ] <021f599a3f8732c4a9c56d378600b053034595e169832912f49069c402bf437be1ee903e646ca645da7671c278586397e6a2d22033394af9878e986d1517f879119d5d5da02c1bac6189cd8da371a9f2> [ 624 0 R /XYZ 62.692913 486.11685 0 ] <6f56668ea13967178d607ee8d8fad70d7cbf004fff592b589800147c29577facd960d01804cab3afd943906a7bd5c1875de0a467fd29203fe40add224068cd3c> [ 392 0 R /XYZ 62.692913 632.26685 0 ] <64731f47ffced26dd4a5a80e5fb871f94f48e1c47fba5181327eb0eab9d43b4df203327e92878271170102f1aefc5f8f8a9175901fde74a2718f9f4296dd982d> [ 698 0 R /XYZ 62.692913 704.24685 0 ] [ 743 0 R /XYZ 62.692913 459.76685 0 ] <88ec037057d63a494dd9f6619d920937f7cf8ff8a3117fe2866cd948cdf52d379e0c19f292c4b135244ed65e9e03f573ffda05a76a326c61aec138d78f179ac2> [ 963 0 R /XYZ 62.692913 361.21685 0 ] <1c2e84dea531e1cc689a27a86cf9b95d7ca647b603036302d97be6ac61ea173c35cf16278b3dbe30d1c286f591c8ebb19bf301d4ac2a82522c2e4fc85502b2d5> [ 709 0 R /XYZ 62.692913 721.84685 0 ] <688285231aecd78ad9d6a57e20d5d118e03322c9c75309810b6e8941d7ef4c74af69a05b0740acd628537413becb3ea15fd14ae4ac3c104371523f9d142629ecae314629fd8005dd204fee4fc3ff4746> [ 531 0 R /XYZ 62.692913 240.60685 0 ] <88268744ea896eead9e08f666aab2fb4d6081660768300aeafb963ec00f12106b6a4e42de7654eb4cce0880f0106cad2e6dff6a8c689ad943bb49e248b9dbdaa> [ 798 0 R /XYZ 62.692913 155.68685 0 ] <72e65541f1b241dd764b26ee0946922732bb766245cfcce8e77d505666ef4b8df7fa3b6bd8f3d59711c59569039b9dcddb57af3d5e1aaf32b844d7f18e8f2e07> [ 300 0 R /XYZ 62.692913 217.35685 0 ] [ 327 0 R /XYZ 62.692913 704.24685 0 ] <98c6d29356119d366f6badc75bd1b78be7dac0e9a8928c3717b8d0c197ed5fe53cc2478f7357a1393eba0ec4c39559c6158522a17074dfa88431354af0a4fa1a0b01b97e90b80bd814843af6fa266c72> [ 721 0 R /XYZ 62.692913 422.13685 0 ] [ 624 0 R /XYZ 62.692913 261.38685 0 ] [ 663 0 R /XYZ 62.692913 785.19685 0 ] <348e4a3441c767e2c21a5cd603a3d69af6b6335fb6f668fc7f278d3286ec06af525478cfff847ef9b0c11464ee65e52c5795ec8848839b010f3c5190263382253d95d968e024a29b7d80be6c692416e4> [ 496 0 R /XYZ 62.692913 586.06685 0 ] <1d5a1eb5874780721f78ab18594590bd3499d2fbfac923169c94daded71ae4b365209d07d82b018f0cd7bed61f59eb2a30f0a3363d1a881690a6447610d958bb2290de07d1e52d417450ad9151646513> [ 602 0 R /XYZ 62.692913 761.94685 0 ] <657bc5631383536436ef8af46c456bcbcb36bf5143d1f2200ee0a83c071eae91f9c49478446989f87bb78d9accd03bcf9c6e6ef4dd2411c5130c28340c69baf5> [ 896 0 R /XYZ 62.692913 294.23685 0 ] <92720b468737e5587c6a7215f68151cc222262f5608e10ea0a9b81bc0b94dbe999b377a29d9a5418f8f711f84b4e6f888bde931e584a855dbd1524771a054e6d0b10651cd715427ebcaea345c6484f2c> [ 878 0 R /XYZ 62.692913 785.19685 0 ] <61a80fc87544854ecef90fa2a920d6e32d7a658801e1a29df80ac4fedc6d0695baa88af2bd658488daec3e3c212a78a92087a5698ace39a356feed527bc1b32c3834ff3c757ead88039930144f47eb37> [ 591 0 R /XYZ 62.692913 761.94685 0 ] <097798862d7f027bf21e11900f4a6bbd7ad6c21bfb6d392b421f85e43e1ba85d096541bbec9c8419cc7d8975fb3dd4f0f381348f290f32f83bc1131815b61bb1> [ 939 0 R /XYZ 62.692913 313.41685 0 ] <9a58f339e28824242a28cfa7fe646829103c8d7e59ae47322b2be6aea79ff6eb94f124299b72052889dbee28b4b11bdec8044f761e40d6cc54c796770917755f292f15d02ac70a1488562aabd3b33e5859b8d75a8824da531e01a04562c45eab> [ 841 0 R /XYZ 62.692913 704.24685 0 ] [ 754 0 R /XYZ 62.692913 785.19685 0 ] [ 265 0 R /XYZ 62.692913 704.24685 0 ] [ 818 0 R /XYZ 62.692913 586.06685 0 ] <3a0e0d30deae1dbef51128c75fffbbf1571ca40c170e75414af63ce4f8e51f1b11f5b8887722275a9c164a1bb70e6997c1b7384d7802b2ea94ca67f21aff52741efb26e0eae920e917652496e4c4b565> [ 612 0 R /XYZ 62.692913 223.00685 0 ] <4d8212a0e9901536da122624849d3daa2a3a06b5dc5935aac841f1b5edb99adfe501b9b517f3d1f1e5b828f2335a7bd57386ce003c68f2fb3f93929eceff02a4> [ 924 0 R /XYZ 62.692913 547.74685 0 ] <275332d0c6f43dfbfc92cef894293577bb4d79227f7ef068bdf657a62cc9c264d5696664b05e1d36f3b31b4d1ab33593eb1cb5d8f9f65284a07d9dd93270e03b> [ 786 0 R /XYZ 62.692913 404.53685 0 ] <74665668f4ba16b27e1ac1ea54a0f75d9531ae2b09bcba2585adfb418ece260a7ce1668fd994eb85e1d1224b3a5d74f0ced105cc28d3cb360bd807d8e39ce9e0> [ 931 0 R /XYZ 62.692913 234.18685 0 ] <2a6744eddfcf5903eabb86d08f069e4d02870380437c9fe216e7edd14f10cc60f0221e3071d96ffab3da8950253ebefc979436f23d3567b38287a312fe131892> [ 808 0 R /XYZ 62.692913 479.51685 0 ] <0ce47724179ddb2d0910ffb39ac73e265d039a6b26d0cb9e1048c936c55d0ac5ce02e374ce8a8546482be755eef897015971e5081b3342d7c6e191284011f9d9> [ 550 0 R /XYZ 62.692913 487.51685 0 ] [ 848 0 R /XYZ 62.692913 394.26685 0 ] [ 971 0 R /XYZ 62.692913 785.19685 0 ] [ 775 0 R /XYZ 62.692913 414.13685 0 ] <15ee1089e5ffe34b57ebefb8a1fdfe9c7afdd2597a2701d13245388a02972ec0a207c3dcb2a278fc4a0423b86750fa8450c815bf02d1a3af6c0717be86d99d5e> [ 569 0 R /XYZ 62.692913 675.64685 0 ] <8e0b5c6ed91b6e6a989cee61e3d7c7ed48d2cce0410d89463685af28bd5ccc9641299011ed7892c74634fbc14bce72775e248b775357269fa87f8789249307a937e9a7741292084ea4934d4456793875> [ 731 0 R /XYZ 62.692913 497.11685 0 ] [ 560 0 R /XYZ 62.692913 761.94685 0 ] [ 978 0 R /XYZ 62.692913 522.71685 0 ] <846cb2296178ef5f303c22385941415fe7862f7f12daf28ba78a113691de189c0119e3bf40078f72dbb1640d0fff62d35475750ec3ace238a27e423439a8e66b> [ 636 0 R /XYZ 62.692913 353.33685 0 ] <26d9bfd715e35bf8a956a580d7fb121d578a47f5af3953b6c28073ee13037441d4c78d03b8233a86917d557fd3a5d96e1d2a25332a363624a0daea13bdb33366> [ 826 0 R /XYZ 62.692913 487.51685 0 ] [ 241 0 R /XYZ 62.692913 603.66685 0 ] <374553ed0a20d69f08f79d5cbaa19c73cb988384d8cd0128f2b3c92ecb55042d6c40965b18ca697e028ebe8e0d6cf711f5e51c0f9cc0ec66c00a05bd1e538a4b> [ 764 0 R /XYZ 62.692913 675.64685 0 ] [ 915 0 R /XYZ 62.692913 603.66685 0 ] [ 276 0 R /XYZ 62.692913 704.24685 0 ] <5882f591310b8b53190a37c82010991e2b16366f0616ab9c3592cb8b36dc1447b49f49907c84a179e9a164d6fa4cbf9d> [ 287 0 R /XYZ 62.692913 381.28685 0 ] <39fc858ac51b4759990119e14d0fe53c0c3890a6dcf712d8e6b3d2cf6bddc3a712aaccbd10021ef6b57eae04b1bebfb6> [ 420 0 R /XYZ 62.692913 275.80685 0 ] <78817f521edf418dea0fa06dba9933a11bc3b51487c12ef716898481e1235838e65517d712e1a52795e9009579628c857c1f86417513d5c51fd3a6e8d0bde0682c84fe4bc05f2ab608a6c856eb0255e7> [ 257 0 R /XYZ 62.692913 704.24685 0 ] [ 511 0 R /XYZ 62.692913 603.66685 0 ] <6a4b02b06222097bfb59da801242533929b35879586c6ad6fd7a5b4d8bd8c4cf590c4d0f5501874a6157847df265b772fb4f77c1e5f6f9e6e5c484eb1878b495> [ 680 0 R /XYZ 62.692913 387.11685 0 ] [ 645 0 R /XYZ 62.692913 168.88685 0 ] <670de2db5c3020945673e256b86353023b6c0edb2a912506eaef4927e7bad0ce5aa5bc83abeeda8a4539574cb3243076631ea3a474888eda4ab1fcb5de9a938d6a245b27a71b48d7670d95e1f837dba7> [ 672 0 R /XYZ 62.692913 785.19685 0 ] <3d9ec0aa97b070727a81354a7c77d1be4c92daff5a01406ed3787d00763de637acc1ba8f9ea6bc4790facd4c32f8e9c7239bd852c1c8a7e7559d27870e24154e> [ 311 0 R /XYZ 62.692913 422.13685 0 ] <87150ed564f298857fb203cb4f971f4d27cc593818663afff64f0cf4ffefe08147b725f04374551c52cd3b5ab344897e> [ 956 0 R /XYZ 62.692913 785.19685 0 ] [ 908 0 R /XYZ 62.692913 141.30685 0 ] [ 653 0 R /XYZ 62.692913 523.21685 0 ] <20f0cf5e693b8e81c6190e88565083e02e5385728ce8c1bf1ce0cafa7bbdc4016b2b6bc3227e954c929ea4e8b921d1cebb28c99d348b3d98927253f35ff05943> [ 336 0 R /XYZ 62.692913 210.78685 0 ] <6f1ddddf2b862ffb8623e464dda2224b7dde4c1268b5582a87c8a839b738d03282ba407fc326273992225376bab82292a43d26d0a2684ffaaf784967bf27058d> [ 524 0 R /XYZ 62.692913 785.19685 0 ] <6d1f1516ab9607100759393b23d9397d57a0ef4c7ad6f1c6b10d2451f608c5f0b481f0e16aa549905256091659e5d7afdc1b633b07d01cbec28b23acaa62bcb2> [ 482 0 R /XYZ 62.692913 603.66685 0 ] [ 542 0 R /XYZ 62.692913 785.19685 0 ] [ 392 0 R /XYZ 62.692913 468.33685 0 ] <8cd6a98d24738d6d5b42c07a6fc0bc21dcf66e238075806027d04d8094ac159fd5571b64c01ded7d0078a960ebfbf574> [ 114 0 R /XYZ 62.692913 785.19685 0 ] <1560679974a4edd7d62d7c20aac76207480a4332616f5e8b5acb362fc9136912a9f79810c2a3637f02af264e8b4e80b0274b29970696624eba0bbd0a3da09e15> [ 832 0 R /XYZ 62.692913 505.11685 0 ] <3218ab280cac49a2346df10e3f7d704365844f29700122d37c8dc27ae0a1ca57657e2c7c79ee61f317a6d33a2b15bdac11bb2226678dca23bdbb669fb8077aad0ea9372c2022a376221909900c648b53> [ 440 0 R /XYZ 62.692913 300.53685 0 ] <3653992cc8d834b7dfcccae8d2a94397075f34635a1a0fb16a7cc4cf384b6e837c90d98d61b883b70913232cd551cb55d7cdbb7afd86bf741381539fabce3cca> [ 889 0 R /XYZ 62.692913 353.33685 0 ] [ 403 0 R /XYZ 62.692913 537.21685 0 ] <36e188ac2b30d006feb8fa863b8af9a651e229d6f1b1b8bec7742c3dee43088a4ab8e36ecf0fc3d927774cc0acfd8c6fc937d997afc5bcbacd81d55f0817fa29> [ 987 0 R /XYZ 62.692913 696.19685 0 ] <83c5765a186a4ed1e1528e0abde9997e4b038c66454bd93d45538ebeb71bed257dddbb845558c16b383ae135c21bafd677071a1e088f76607741714dbb9a6823> [ 580 0 R /XYZ 62.692913 603.66685 0 ] <9acc8fd31769451be158e79bfffadbbde7388a5b642e0fe8541bd0e0a523d49d95a54e6794a6ffcdc518b3b4bfcd1c1cd3a0a250519b0f07e9cd656fc88f9cc7> [ 412 0 R /XYZ 62.692913 370.93685 0 ] [ 347 0 R /XYZ 62.692913 404.53685 0 ] <7ea88b751a305553a74eb1c15a2c9429c090da594a75bc2f445af5fc7ebcdf9c158b61671989ce243d0981b1f2d7f0a3ba8f278c8ca37e95561c97ce6502a9e9> [ 691 0 R /XYZ 62.692913 586.06685 0 ] [ 469 0 R /XYZ 62.692913 704.24685 0 ] <997dd93843a00b94889466a129b06b90da3acaa90d244c8be15b6560dd99cd9caa63e413bdfb65daf6fbc19e513a60ddd8aa3f966ae983e67315f8f75397ea39> [ 511 0 R /XYZ 62.692913 396.53685 0 ] <7fb99a9f4a91ed50d501ea7cda8fa59201616f2d1e67c10e3a7c3a7664305aa951e8b86f72e5f1de1feaee9ca6d349e6> [ 532 0 R /XYZ 62.692913 785.19685 0 ] <769fa822f00b2e7ad97449b9d6ddd7eed45633d46bf58f218584c94bef069bfc6a9bdb08e188f50b6edc98976c07fc4fa96ad951620918849ffdd3372b0a8363> [ 743 0 R /XYZ 62.692913 278.23685 0 ] <9be6c114c1c1815494ea891c8c94e807398ab9f91634c93989d2a45c66367b8f0804f9a928aeaeb8c41947cfbc7c5f8ef45ec86c2e3041c29a4e83b3aa437025> [ 569 0 R /XYZ 62.692913 494.11685 0 ] [ 625 0 R /XYZ 62.692913 785.19685 0 ] <6574739da73643ea5ab84847a0cee75bfacb06eb8d46212ff8a0264d6b9363f0b883e307437e95abe0448e499f6550d02637f6b462b314b59735213ea77fed62b8dcf5d2726ab36d025bc8681315034d> [ 337 0 R /XYZ 62.692913 636.94685 0 ] [ 420 0 R /XYZ 62.692913 122.87685 0 ] <2df5b2cef86bbe2d0bc9b190e845b9353c450027c2730e3cb63bf8c3708241de6e96b20de4288d71011eb5f5321f62d36d94ee312170a6d0ac4b9ba6a32bd2e2> [ 709 0 R /XYZ 62.692913 540.31685 0 ] [ 403 0 R /XYZ 62.692913 401.88685 0 ] <858e8a7ce92f9f4085085a074a0ac61909b65a33e8fca891c6e32f4e51869b1f7422e66f0f0945cbf8518907a6b62d2aae5a5e903e7fdc6e9e80f9d0372cf305> [ 878 0 R /XYZ 62.692913 578.06685 0 ] <5e4c46b118e5b00e82cbb1bea2f46121941ee730327eade6f3e9081533371db2a94e490411ccd4285606cc3276a28d15786e5e672ff7a1e9d89c33c793631d30> [ 799 0 R /XYZ 62.692913 686.64685 0 ] [ 241 0 R /XYZ 62.692913 396.53685 0 ] <95fd7b8b4fc7c13f439d24199f5436b55d74bb5ca613dc87259f7b46342e60a7f610052af44426b83b98f56ea7825d99d33bd77bb336e4a96ba5e6193b459d3e> [ 808 0 R /XYZ 62.692913 280.38685 0 ] [ 721 0 R /XYZ 62.692913 215.00685 0 ] [ 754 0 R /XYZ 62.692913 603.66685 0 ] <956a60b81b6fd5b024f2ab11599db87523ddc6f125853f21e58416baaadeda751d71c55772f22d2c174cc6a3018c56270d39b587931849f9624be9b79ddb5765> [ 276 0 R /XYZ 62.692913 522.71685 0 ] [ 786 0 R /XYZ 62.692913 205.40685 0 ] <345815c6858ee45f951a7e1b2023b176547ec3e6e9f33c20ac2dfa4971ed4ea8a2c1f80cbc80cc101199ee55ae8a4f50> [ 890 0 R /XYZ 62.692913 785.19685 0 ] <1dfa51a49942e87160adaa14cb05831871e95d4308d91f95083aac9fb3695e7980e2d7584358c764baac2b15663031cd9a3b7600346ccd1dfd8d54612405601b> [ 848 0 R /XYZ 62.692913 230.33685 0 ] <7ec861eea82dda3a8026f169406231c2ae77d0c89a89cdbf82f564384f2b25459f3c692614e7d22142e79d3fd8b88b1ed8496638d842951f5f692f75ea2db25290cb8a7397022bcdf2193a4b370b9213> [ 636 0 R /XYZ 62.692913 154.20685 0 ] <24c5654a0b34dd66e10b63f5340a094959fbbbae8ecc86e797661a9de5de1a8119e13eea0fe14f391dd82b2cb98b3490001a18473841355e9b7812b78d56dec03fd3982bff5cd9b9ff738520dc10964f> [ 524 0 R /XYZ 62.692913 586.06685 0 ] <5fa2465b1b8313509e4e00de8d7890847ac2444b3f48629c9a707c59598b9322fc5ff1a99c86c8c5c1149447af84f355b65420ed1eeb4a43581b4c7126d84ebdad2f0347a58754672814721738d3a124> [ 731 0 R /XYZ 62.692913 289.98685 0 ] [ 482 0 R /XYZ 62.692913 422.13685 0 ] [ 347 0 R /XYZ 62.692913 143.10685 0 ] [ 602 0 R /XYZ 62.692913 554.81685 0 ] <3625ac0c3e0f02b8337b0672bd6c5b1bea611c58eaf0b2a4ecca669c44aa1164c4f199f3f39dec71f3ee8b99a1e6897d26204b81735ae7960697dc67c79d2fac> [ 897 0 R /XYZ 62.692913 785.19685 0 ] <1dd760a5dbcbe38b1d4dec37f519448ccbed32c97b8801218609effec229f3f89400261d3c494a0f183134e62fc8066fffae434946142b811b8de3f3132473de> [ 613 0 R /XYZ 62.692913 761.94685 0 ] <1ab4617fe580dcc4cb1d6bd1b424276c29073721619dd4492cbedac09b881d9785e03bd8c1e73198d01f736b91ececeb3d1e68e29de57b8ac5b5ee5eb0157924> [ 550 0 R /XYZ 62.692913 165.48685 0 ] [ 114 0 R /XYZ 62.692913 549.44685 0 ] [ 327 0 R /XYZ 62.692913 505.11685 0 ] <0fe8ff4e52162e35fcdc53f8a15db95c8147ac9bd443b85136aa6e17a11fbe83ef2080a5cc4684749b8a85ba3ee2fd54924f975fe38b31156c5963a3a47eb8f0> [ 287 0 R /XYZ 62.692913 217.35685 0 ] [ 909 0 R /XYZ 62.692913 675.64685 0 ] <17b8661f6259ceff4f228a9e53a75102468fa34bdb0ad4435ea2a78a465f6aaa82e2cf92b273c5e16aa45c2b07a54a033184d47a8a0eaa7140650772a6b957a5> [ 776 0 R /XYZ 62.692913 785.19685 0 ] <2dfc8581975b8409591947e20d588561f9eb0a02c3b816f89be1860d9d2d7624fe4d224247db7cb1b6932d4c497987f8494c865e8f5114777deb67c8b6c6a5b2> [ 265 0 R /XYZ 62.692913 522.71685 0 ] [ 441 0 R /XYZ 62.692913 785.19685 0 ] [ 698 0 R /XYZ 62.692913 540.31685 0 ] [ 971 0 R /XYZ 62.692913 586.06685 0 ] [ 312 0 R /XYZ 62.692913 785.19685 0 ] [ 646 0 R /XYZ 62.692913 451.24685 0 ] [ 956 0 R /XYZ 62.692913 475.76685 0 ] [ 924 0 R /XYZ 62.692913 252.71685 0 ] <1ba10adb77341e2654aada9976eee6a05c11ae2210b1f89fda37454c9ef4be3b4fe82f26f07e57e3d05755197ac5293b1f91450e8c205c0ef1113c9baf6b84da> [ 841 0 R /XYZ 62.692913 461.91685 0 ] <3ae52e473466e750eab71f1769032895cb9cbcf4906a345a12555ada93484e567ebccae3b43e9e853f2eb324e1f7d0fc3cb90a5e406c6493cc42aa230bd0ff8171e0822f315c74c717d9afcca93202b5> [ 680 0 R /XYZ 62.692913 187.98685 0 ] <0d8d9227cb3c3380baabc4fe4d667f037cf7da4b2b686e16c00bdb750f34b93d182ca9ca8e76bd6655f07c7a9b2c4d7ccee75f57e66ed4efe103c55db2b9065c> [ 826 0 R /XYZ 62.692913 334.58685 0 ] <7cc7dda2930be7ddb2f7a071d4eed3a3f467676ae9fac6af78e120568cd553c45ad67332c1851c807d3fc1cb45758fe564b75bfa8ad8e90ebe76a45ca71f1d20> [ 672 0 R /XYZ 62.692913 353.81685 0 ] <46640017389e418957fbedb0939443f7f1e8e192d2acfac65839693ed4c7b18b75d5fbd33b116344e773df4c52db6f680158d6caa186969edbd87c9162cc27b3> [ 560 0 R /XYZ 62.692913 580.41685 0 ] [ 963 0 R /XYZ 62.692913 162.08685 0 ] <0f506a79c892eaf2855422aae77484c1c716517776e8285fed6fbad6cff0c4bf0fb6329ab95ed68c4c405ee85d0e129523d633d56b30af5f9e6e8b9de040db2d8fada637e89bd520c4522e49eba17719> [ 496 0 R /XYZ 62.692913 378.93685 0 ] [ 691 0 R /XYZ 62.692913 308.63685 0 ] [ 301 0 R /XYZ 62.692913 693.24685 0 ] [ 663 0 R /XYZ 62.692913 479.16685 0 ] <6055d3a835b447a1a0e5dfe661c91c6b2f88d0eadf7c73988f834b8af744bc9d568e17d024fbca857cad520d335aaa25> [ 392 0 R /XYZ 62.692913 333.00685 0 ] [ 542 0 R /XYZ 62.692913 586.06685 0 ] <66eb21885dc85f4c8bb0974e2c7d1bd723810a1adbb180cb1358f10621d4a01cbeb015a9d04a14d04bd914ccfd955ea72fdea9682622f042288ef1461b3378e2> [ 257 0 R /XYZ 62.692913 525.71685 0 ] <7573027a8fe2a5db850767df1a584eba8e46ac52936bb8b99ad91742ba11467cb9853c8c6d8512c8d6259de108dea43a6e2fc33c0e9440b7a4c1c3db22d830b2> [ 653 0 R /XYZ 62.692913 316.08685 0 ] <9b50dd72e2be723f7dd187997955da3c51a3bf188469f7dbbe9ac0cbfc43252d53d8dffd8c5caed21651b65a94500995df438a53c792aea65c713447663663a8c8e1572e0a4565fc7f4405696505ef9e> [ 833 0 R /XYZ 62.692913 785.19685 0 ] [ 818 0 R /XYZ 62.692913 343.73685 0 ] [ 580 0 R /XYZ 62.692913 422.13685 0 ] <75c711ec6bed4f2bc68acc669087749e511a5fa4697ad6a0fd22beb245f44c5e7526de72b247f00136aba214d35df251a444bf5092d3394418d831ceeb868de599794651db49479bc582feac46093718> [ 987 0 R /XYZ 62.692913 497.06685 0 ] [ 412 0 R /XYZ 62.692913 189.40685 0 ] <13b043983ec671b2e3ebdce3887218cef160c82a7806fe28aac86679499bf461486a9e736016ea89212a4e8d13b4e4dd7b61e7668b6debf9dd3c5b262d909ce4> [ 764 0 R /XYZ 62.692913 476.51685 0 ] <2d0d2f988dda37f5aceb85f97857bd7a79d750616fb2ff4adebd5b6da44b6cdf68de62c00cf69c6d604b44d58b87ba0a8424a52bf6b0c4da8de5443afc6b2e4b> [ 940 0 R /XYZ 62.692913 785.19685 0 ] <58280fa490f2d5caa1b271217c6f5cf5da063afde04e55c8fc707299b815db1d4203c871103902786d10a9abca4f9a8cd60ada3da6a888af3f1e927cbe9a4cc614ba950d4676a161e560e8e0aa512683> [ 469 0 R /XYZ 62.692913 522.71685 0 ] <2b2d256dc5d9a4809b12e1136c891417441db8fd054be2f871695db4ff0966c05c798a5e214da5b58d02711b69467be8> [ 591 0 R /XYZ 62.692913 554.81685 0 ] [ 978 0 R /XYZ 62.692913 280.38685 0 ] <345d3e071b568410056389d55fb191ad2428bff2593efa12d99fc0e9a8273eb3f31b3f679351966ac50428f7a5fdd090> [ 915 0 R /XYZ 62.692913 439.73685 0 ] <20cc35d84fc090e35a2c25793970a37a20d32e87cbcdd8245b467a3e623b789864316f7b39dbc6efdcc9ccd4d5e5088325c8fc248dd2363ffac611ef93771109> [ 619 0 R /XYZ 62.692913 235.65685 0 ] [ 506 0 R /XYZ 62.692913 214.85685 0 ] <5d2cfe172f3290ebf44660b05aeb8206c09c056b3e605dc209e08ab23a0921f0938087f74e49f7f1346a28e0fe5bc41ba94513050941a4998cdda3bf8866b57b> [ 282 0 R /XYZ 62.692913 197.25685 0 ] [ 116 0 R /XYZ 62.692913 762.69685 0 ] [ 821 0 R /XYZ 62.692913 246.65685 0 ] [ 425 0 R /XYZ 62.692913 181.50685 0 ] [ 781 0 R /XYZ 62.692913 140.05685 0 ] <354a16d0f4983ce46d3eed551a3d18914cf64176b3c3cb32d053758b6633870a059c4f4c451cd480ccccd5b8427dd20a090158e0d7fed490676febc4a92fa1d8c0e90bf77b551c4a033d884362878e58> [ 384 0 R /XYZ 62.692913 288.45685 0 ] <27ed61b62458bbde4d5cab342546713b436e9ca0ed306116db411972b43a4e9ae4ef087736f07afe2ce15e918be6756d9c4e32df5dbb13e6afb47bb54fde2868> [ 331 0 R /XYZ 62.692913 134.95685 0 ] [ 685 0 R /XYZ 62.692913 245.25685 0 ] [ 749 0 R /XYZ 62.692913 761.94685 0 ] [ 704 0 R /XYZ 62.692913 210.05685 0 ] <1c81424d6bfab9bf47591f4bd1cc24fa8a399940121911ea6109963a446b98ddb605b7784e1efcd78297e94a812d0a35> [ 793 0 R /XYZ 62.692913 761.94685 0 ] <4be974c58e227e40a7ac60262302f5f83ff99aae2e5f8242f87d180b52321c56ec3c7907a72e5f64389c9bfb64afbe6f> [ 658 0 R /XYZ 62.692913 785.19685 0 ] <8c70f37bac4478e57853fa1105419c324126c4445ff9dcf973995d2c50000484fc4f821dcf8032eed1f8772894da8c98> [ 666 0 R /XYZ 62.692913 162.05685 0 ] [ 575 0 R /XYZ 62.692913 197.25685 0 ] <7c0be8de4154011ce0cc669fb55e05bdc5291ade703170d8b032b3a37ed6f535950fcedc1c15e3d32e0db71d08f6e52d42c8147cbc73dc7849583dc5141559c7> [ 981 0 R /XYZ 62.692913 259.90685 0 ] [ 972 0 R /XYZ 62.692913 298.30685 0 ] <0344bd9c3d36671ee69a794780f72730fd44b26a2af6e37212f2503e61bc030e35f64f50ae47b90f29697bd08068c31740aea2219e54d5d8fc6929995fc6ebb5> [ 379 0 R /XYZ 62.692913 785.19685 0 ] <3061b9702b77ecaf52ab9bd836be589721ddd5aa937bded7b9058efa4fe52dacc72cd0581b1de040b56a70f2a55efaac2216908971b914b1b026a7357d31ec16> [ 359 0 R /XYZ 62.692913 200.35685 0 ] [ 443 0 R /XYZ 62.692913 785.19685 0 ] <9615c6ce682d7a8194eda14c72dcd6f0df134da898f6c0044a4ec980e45a3fbf11a329496c48d24ca15805392e09d8f0> [ 597 0 R /XYZ 62.692913 235.65685 0 ] <972ad478298bffd953c84f7913e6b9a261014a41525faa349478170bcc0c2f91ae285cbd31e6cf6a4b3fee279a566b0b3b9906d26e8338c92b348d5a3d4ca13f1d1917f477ce302986f1656cf3a0c412> [ 884 0 R /XYZ 62.692913 206.85685 0 ] [ 564 0 R /XYZ 62.692913 270.85685 0 ] <06d6326e92d6e47f1fcaad2cae3cf5f99088a053d8e7a4c33088bc6a1fa8335057f7936c903d7c6900d69278bb71eb6fddc4ed80f4ccc2ec8a7bb3fed3734144> [ 246 0 R /XYZ 62.692913 785.19685 0 ] [ 933 0 R /XYZ 62.692913 378.40685 0 ] <6c090a3a156933f655767d80ac4a06413e14109d27fd76aef7fe80f18a58dd852632a7c420b64ee338a08176f1ad9cec51146741c22e1132e46b6e1758a8a8a5> [ 926 0 R /XYZ 62.692913 237.50685 0 ] [ 813 0 R /XYZ 62.692913 253.25685 0 ] <71e7572ef84392c64e7956ecbe0b94091842b7b4c53478f53e4564873db7e1a3d144e8e3d19bd4c307b0790806fc5ce396f7d64793fdbe774df6047c8c222e45> [ 957 0 R /XYZ 62.692913 164.20685 0 ] <22d1d1ab8e429ad81fb51505632abbc02cd3e56420ef4a105794edd347e9f37b87be96cfbb987f871f3bf420c80bcdca6baa7ee28d8f2c1596d03d0363a7b640> [ 398 0 R /XYZ 62.692913 214.85685 0 ] [ 949 0 R /XYZ 62.692913 272.70685 0 ] <400976197812669aecd11b3f0b18b781c4ee18b53172dfead4f42854c021eafb903e60500f0e14263356af17ea2c270f7ae2a09b67562782e7ba3cc79e548d2a5b4a73a1f9d009b452e360265174175803e28c8711b0fb255e3924397c8562a5> [ 862 0 R /XYZ 62.692913 785.19685 0 ] <2ecba288c3f468e078bc3837b0a10f074751e20c31d0d1e1e468134b7f8a0def5fb892590cd2388713d0f5cec2b97ed319b11b4378e05fd08b423f5074263725> [ 554 0 R /XYZ 62.692913 232.45685 0 ] <0c0296b21aa0717313814f9fe289f065b44815edfb830ebef6eeacddafcf6859bd48ff67ade7e8eda0fc749312acdca2406e9229768250f72073a8923e6de8eb22e9fd00239c9ac850e03673c0dccad4> [ 322 0 R /XYZ 62.692913 652.44685 0 ] <6a7d9220e4f8a88481d562aecaf26e12260814035b7a443f36b66368c7aa20686f994423086fccb323dd8cbb404921623117551298f60f5c66d93f99fa0a7798> [ 803 0 R /XYZ 62.692913 144.45685 0 ] [ 306 0 R /XYZ 62.692913 785.19685 0 ] <7c3c11477f3b970a259c5070c3e939196eec96553792df532afde60e9366e084e645bef560911091e93e916186922a9a9a44f0701d6363622e5be181444f97db> [ 260 0 R /XYZ 62.692913 214.85685 0 ] <7d6ff5773e9dbebef1f30de2b05ca1fd5b3caff15cc38bd42c1100f3a3b6300967cb18b402cbe8638ae4ab7ad6cf1bd70d777b73574af0523c87c0a75527cd8cdce79d4f082193b0de7fcbd6d30edb81> [ 716 0 R /XYZ 62.692913 214.85685 0 ] <2f4ee5e3c4f33a79464d26a3db3b1cc993d4db60084731325f22ab97a9293980032af3575bcd9198d605310af41b64c304fbc77518729fb5635801575fea2ddd008ee59392c65ab35102a07fca7a5f90> [ 416 0 R /XYZ 62.692913 225.85685 0 ] [ 759 0 R /XYZ 62.692913 214.85685 0 ] <2e52b5f33b99f7dc52c791c5bb8047082397aacb7b3a1cc9fd957f1a08157ffedd6e942e5879e0fe88dee3f934f712a0b9c8c489790b57d220de44a51378d550> [ 836 0 R /XYZ 62.692913 253.25685 0 ] [ 368 0 R /XYZ 62.692913 264.25685 0 ] <5a23e831c095c8710a71c9ab982a2ecfa2a0ebcc2d42bb1b0de6fb3f2baa677617abde3911b0118b0f60b6a61291e25fba1d042b1bc89df77806db4e695f8833> [ 726 0 R /XYZ 62.692913 253.25685 0 ] <1e00de30dc1a81db6be50c60471800dabd1ff25112d37842cf824aea5dda84eee60de7c3db10b90ac221194d926fd911d559a987f1029be5b329f0ba3a4847a8> [ 966 0 R /XYZ 62.692913 785.19685 0 ] [ 942 0 R /XYZ 62.692913 169.00685 0 ] <896b62275d9307ee25cb16bf3c07cb5df1e6e2ae017fa7ef18ff7d4cb7d73918702f78b4b1a9e96bbfa42c71a479f14f76cda3bcda391a8289b38d2d3cc005ad> [ 251 0 R /XYZ 62.692913 232.45685 0 ] [ 736 0 R /XYZ 62.692913 288.45685 0 ] <638c1535159058e01a0bbc47509776063f9e1ea2cc751b82488c84377b63892cedc20635635123cbf36f704e73913c24b69397f4fa68fc062b096fec804a8796> [ 363 0 R /XYZ 62.692913 141.35685 0 ] [ 771 0 R /XYZ 62.692913 785.19685 0 ] <31bcdb9f0af39468e0355c70bf6e97f1463a5a9007e88e361b8b1ca34f2c092673ca34bf3bf83d2dae66e093a88f28e27cbda8076034f3696f4314245ce357f9> [ 457 0 R /XYZ 62.692913 179.65685 0 ] <4eec50ab2e648dd1ea698d2b5c831b3d5b68c4ff122fda68ba0c7eeb385ab6d5927b5aa1fb014a085a652ffbfa1a4fe47b75498d9c27d9039bfbdc828f189647> [ 827 0 R /XYZ 62.692913 200.25685 0 ] <6ca761ec233dc589043a6a138ae28e8ff0acf11d042d4675c7fd5f9d58c2e96640dc740fb8df6bbe98fa173135ec410a16a2663c5440645fced89a2e123febd2> [ 463 0 R /XYZ 62.692913 218.05685 0 ] <46124a47308fb75772c615adf57838d907697e1ed47da75142d65e7c334b0dc58706720f6ba83ce6beeca1fab03964283b7b9c96211a04845e76cf41b03fff52154fe51400a06f645eb47f48c9d832fc> [ 630 0 R /XYZ 62.692913 200.45685 0 ] <95f6413fbd8c4e442aaafb172b2aff5c5b66011b1cc0fb590d8931645716ec8132d1dfbdf54c1be59943f57966f28ad1da035237c5d12fac8fda48e3df41590e> [ 536 0 R /XYZ 62.692913 263.10685 0 ] [ 471 0 R /XYZ 62.692913 299.45685 0 ] <7162c8bee07c86fbbfd42997824dc22df53e39c758f7518df0c043f58ec9d3166252064f095c35914a2f45c5e71880bef051ef241ce98ccb82c29acf24851212f06fecb9b9cafe203e150ef00b2d9d9f> [ 910 0 R /XYZ 62.692913 147.55685 0 ] <30bdbe9fb98e13017f9107846104707fefe0ecc3fa793a727dec9d49ca232d6a8482866c3c5e967acca6a25a08e1040914928abeb5ba383497535ed8e46fd8d5> [ 903 0 R /XYZ 62.692913 243.45685 0 ] <4a753ab96319681f0984d457312ca53acea8b30de3c7d7a6499dcd904aa614852f2afb77ef2425e10705ffe8b1eccd8b> [ 526 0 R /XYZ 62.692913 191.40685 0 ] [ 490 0 R /XYZ 62.692913 214.85685 0 ] [ 648 0 R /XYZ 62.692913 785.19685 0 ] <5707fea0dab01fe8f82f3bec1e11d16ac20c5b657ddb3a175c73d2d087a4fd6ff973d27a463ee4ab001b80b056241631a96a7930805f2de9b1b11e1f1e42663c> [ 898 0 R /XYZ 62.692913 214.85685 0 ] <2cd50520482496d4da9824c6ac9ab272c9aa43db06e4b40aac2bd5bf9f4e5ff4c8e8eb1c79533588a71e55d814cb1a7e24be781b89e03f9551391c05844c9641> [ 675 0 R /XYZ 62.692913 785.19685 0 ] [ 429 0 R /XYZ 62.692913 255.10685 0 ] [ 295 0 R /XYZ 62.692913 154.05685 0 ] <18f93bac4fca99bd96ed432bddd7287ef5ecacfc50f765d08ccdd3e8c43a2719caaf77a13f7ad1458b17443825b891c54f6c5fa40d4d50561e40aed928d0e079> [ 872 0 R /XYZ 62.692913 785.19685 0 ] [ 435 0 R /XYZ 62.692913 785.19685 0 ] [ 640 0 R /XYZ 62.692913 192.45685 0 ] <626c64b3668d9324f26837a3bdedd82fe9cfd7d9a7bcbb8083ddf5884f722997dfd597bfa1ddd794113c280cf9e8d6b56eee56a14eb15c6b60dd09d145529f55162dc953e48ec39f4cdd0343e7f86ea9> [ 518 0 R /XYZ 62.692913 319.10685 0 ] <4292ce120ae11986ae0177ccda1cce62018ff2ad6615cf3cfba5168ec101c7c00fa9d5709b6f35582798d994275856350e215d90b988a229a99cb6705eaa6668> [ 354 0 R /XYZ 62.692913 785.19685 0 ] <3908f969b5410a06aae99757751c67b5e79403823553b5835b56a4862eee759a3e1d34e8136783e962ea195ea33a50a6> [ 236 0 R /XYZ 62.692913 214.85685 0 ] [ 842 0 R /XYZ 62.692913 122.15685 0 ] [ 373 0 R /XYZ 62.692913 270.85685 0 ] <8c17accd2cd01cb62fa10a79d0b5d9338d304b6bf5da0609ead62e30f4fa48e2e5a26db62240e3071b3c71cb4209e089012ad4785b3e263430940d7c3c238974> [ 343 0 R /XYZ 62.692913 785.19685 0 ] [ 918 0 R /XYZ 62.692913 214.85685 0 ] <09ecf692ae6b1696f09a89363e7bd207a4f1a12e21a204e1af7f6f7b50a010edd887ca317fd69293aa525b9115db6f874b1410c8b2973b183a725ab34a4f639221266e8609ef0e601d59b83a24dd6333> [ 607 0 R /XYZ 62.692913 227.65685 0 ] [ 586 0 R /XYZ 62.692913 225.85685 0 ] <1e8a458c927d22734f4ddad5ac829997d6bf595a1883db8da1401c63c1ce9f97de4eda0cf1c154fa17e3bd9bea7c0ccd45242a59bfb1519aa7479f28669c97c0468bce9f6201f77d6bf02733a2795e47> [ 270 0 R /XYZ 62.692913 681.19685 0 ] <571f7150270cc32eb2a238787cfdae65d43f018f2184060e7b57a01511d1616bbaeb4afd4dc8ecb76bf4402549a361a38ab84bb43a5b4b2c239b719518b02af7> [ 233 0 R /XYZ 62.692913 512.99685 0 ] <49496ec4a93b8a42c3e38caa43f99093aba7080ccbb624e85754a2eb43bd3f994d1c834c95e60d6e1d94e55d917c8e50ab3216768204808cdd3f956dc18ac477b7a1434f33ac59c1d8cc11f472f9cfc2> [ 866 0 R /XYZ 62.692913 214.85685 0 ] [ 389 0 R /XYZ 62.692913 785.19685 0 ] <677eba657c65fd3b5e0e74d43a4a907f1ec6e6907ca4e025627c73ab7f7e664a9c88a0ee1889696330b1f8cafc97bb0957e6e0e2e0c370b87fbd6d6fc562f60957a8a946cec8d1ff9af8a113133c05df> [ 544 0 R /XYZ 62.692913 298.30685 0 ] <0f9f064924cff02a81b1079b06920acd80dd76938c5a19d35a7424d2a5be8f26a0e55443e44b1f8dd5a68f8607625b879e473d27415a3b749099f7b8493fee75> [ 477 0 R /XYZ 62.692913 253.25685 0 ] <957e33e80e0865cb7dc751e9ed3b180fad11797ab3734925c41c5e1e640273298e9ea7134ff99adc0be292f3e9fbb3b0> [ 408 0 R /XYZ 62.692913 243.45685 0 ] <30ed1c01414375a4a928b98f84c64861daadc1d455e08a141458cba15d41e15ab29f58c9b2e31ec4c4a4b89f0f105b3656d72c5fb816691088b9afd2108f3260> [ 693 0 R /XYZ 62.692913 245.25685 0 ] [ 891 0 R /XYZ 62.692913 214.85685 0 ] <298d02b32939c35dca48a88ce07d5b811374da3aca8059e0ad11556c39c6d807898e2d140607474eeae513ff6937f8b11cfb5f90640549f319a3a813c03c87c1c590273921a87af302842b6452ee323b> [ 709 0 R /XYZ 62.692913 376.38685 0 ] <0ec2fe44cd293e206ea0a11b2515829a0a0513628ef168693389c9f1ef0c692a12796da4e15e8c3cb7ff548460788e415491864d6a8f6d0ae527713dfec25559> [ 732 0 R /XYZ 62.692913 785.19685 0 ] [ 327 0 R /XYZ 62.692913 297.98685 0 ] [ 897 0 R /XYZ 62.692913 603.66685 0 ] [ 625 0 R /XYZ 62.692913 603.66685 0 ] [ 524 0 R /XYZ 62.692913 396.53685 0 ] <217fbe03943bc1852637d3e5d013fa811e279e2a85fbb15a04c950660e941f2a136e99b0f6e081dccdfa726447141315867db01632d89e6ef7f07d003f9d2136> [ 819 0 R /XYZ 62.692913 667.59685 0 ] <521878967f0e8d46a49da64b4bc31780c2b3ae544d6f07e829b4fb2fd2b64dc770ef308a184c6b3abdce893049709a197a7644fdf4cd26f8146dc59bf093d4b1> [ 312 0 R /XYZ 62.692913 436.91685 0 ] <9a777f9bd2a771c0e7e8bb416b07d66e9c5085199daaee442e0995112491220b07bea89782b2aa175b3b42b1b115d47f6d628ac7ea5cd9d265057d3a8e64ff16> [ 288 0 R /XYZ 62.692913 785.19685 0 ] <5cd87ef6fef32d7d56969d7a1322f3e0344782a11e230c48df843b17a0107cc3ab497a915864d241136bf66c6bdd22210a60cd5f3d72da1b3c31510d12ba8e43255e15a96ee639e38b5d9368c4c3a3b0> [ 833 0 R /XYZ 62.692913 421.56685 0 ] <4233cce20be3b5498acdc180d23311ebcdcb18bf43149b1ec4ca6ceb3773e126e2b1cab5cacd997683f887dfbeb4823e5b388174d10480bb65ae997b450247b4> [ 979 0 R /XYZ 62.692913 785.19685 0 ] <9417f7ccf22dbeb0c61accfbb30ed38475904bd752ac33d25db8de2fa75bbc9a2e72e21af96965b3724274efaab761ff48e688db5c86034a27cd9eee0218295b> [ 841 0 R /XYZ 62.692913 262.78685 0 ] <30df73ed1e87897ef75783f07494033ab3ba9ed3a2b2d1aa47a1129806f7998eb361fe17d18d0b77a779a80152531eda47d22590c3ad0f1dd4b6d82f8d640abcbe8e5f2c24321c9ae8a285754c0e5e27> [ 337 0 R /XYZ 62.692913 412.21685 0 ] <5437828f22f7b9b53623648d3b7865db0710a35700b98c4706c249411cc128ce05beed3bdc9ef290bef28f5a64471e29d04398efb1ee79bca532c834ecd8a3b9> [ 265 0 R /XYZ 62.692913 341.18685 0 ] <9b1af9194b7caa97d4fbc63349fafdf31f78dcf224a3e18615ba5a067085895334bef2b73ce7018a6b0dcc77acad26f5eb4b5a38ea5ebf826867e5f8f54d309deb766ee5b5f9f51d7e9a3861e65f8bdf> [ 496 0 R /XYZ 62.692913 171.80685 0 ] <18e5a1c8a28fc6cb1a27dce7bde7b55d43d100c7b9a0a185456b99a0dbaf6bc242d1a50606a5d1a6d1bd47885159af320915a49d3b215328142cf1f32c1bb4e8> [ 602 0 R /XYZ 62.692913 355.68685 0 ] [ 754 0 R /XYZ 62.692913 340.63685 0 ] [ 654 0 R /XYZ 62.692913 761.94685 0 ] <5aa50d2ef383975749a213f8c1e5770189114b54f7fdd1fa254f71fdc0dd3ca17d48fc26af393d15fd82d9c62c002af48d5287d4f935abb6c91814f012a08c5f> [ 392 0 R /XYZ 62.692913 197.67685 0 ] <2464b75aa91f5a50f8a897686678feb86d33e1a15341585164d79f0899cb06600da4cdc3c378772ef92542e33e4df6a0b4a2f0094bc6a414d00c95f2a12962c5e35322c7bcb055b9046cac2ee27a4426> [ 569 0 R /XYZ 62.692913 312.58685 0 ] <0d4c31a6f8874af1354d2f5576e0e65c94a5274d4536a0b483b51fc6b26b1330912433520c153264b9d3f2884d687aed> [ 764 0 R /XYZ 62.692913 294.98685 0 ] <088c937f06d1c7939c6a87974ad85518e28b65eefcc0cb42aa4e03093d04557e3a0ccb263d22719422a20d6f93d122cd82496f9cd31febee45afa0a85a81c9ec> [ 637 0 R /XYZ 62.692913 393.04685 0 ] [ 348 0 R /XYZ 62.692913 562.14685 0 ] <144d1cf7606fb9e5ccd24317997039ea42811d98163bc9fbeb037801d0d1d2562e9b835ef40f6f73675e31a98fe1ad11950423166e2ae62aeb5294e2d609c200> [ 722 0 R /XYZ 62.692913 704.24685 0 ] [ 964 0 R /XYZ 62.692913 704.24685 0 ] <20d5b9c06fc121137e2cb8cd301174088990654ad973aee7657ee0dde053c44904e326e441cb4f1228d19245491649b6c7c24af81114f2edd733366f154624952120aeeb599a9de748bb5bedf6145abd> [ 681 0 R /XYZ 62.692913 618.44685 0 ] <4ffeacecc3302f7b3e07be2f725aeec9573ffd572497c891382ffe8ff33641fddc5cce3cb9ac338645db60d965aa7b0f1baabdef1ecc98e9ffce253317b0bc20> [ 940 0 R /XYZ 62.692913 415.16685 0 ] [ 672 0 R /XYZ 62.692913 189.88685 0 ] <0ae8082a3ce09f42f3a46b12776abc3a673914a8d07c487b6333d85e6aae751ec5a632d33f25f02e17f58bd889a6090afccca9fc837bc53f99b25a8ddab2ef64697520354b449a2f22830ee3f6eb925e> [ 915 0 R /XYZ 62.692913 286.80685 0 ] <57015cd084fcca7ee6505a4193141d2ebca1193d72ba228c0c37bfd0b02f4c90bdec3f99ee1f6172b63bc6c1338098aecf0cdf48724f02acdb9eaeaea49ca95c> [ 613 0 R /XYZ 62.692913 562.81685 0 ] <6703eb2f797fbbda6143ba969cb0f110c4fd1b7a93d690a0c8f9c920aa0be0d8d58300a0102ad55c3f5bbda1e3ae26570ea176fa4b2723229dbb01d53b7a1a08> [ 421 0 R /XYZ 62.692913 664.64685 0 ] [ 849 0 R /XYZ 62.692913 785.19685 0 ] [ 692 0 R /XYZ 62.692913 785.19685 0 ] [ 878 0 R /XYZ 62.692913 414.13685 0 ] [ 909 0 R /XYZ 62.692913 505.11685 0 ] <3dc3a7a3e4b41a9e4eb2075136197dd07076a973b7f2bf3f1601a9155d4cd339ec8055c93c4a28429e7bbc8c3bfd3c4d23919a6cac9359e02a28da4fda5205f30e41dad811cb74cd8446aba547bf2729> [ 744 0 R /XYZ 62.692913 761.94685 0 ] [ 663 0 R /XYZ 62.692913 280.03685 0 ] <74354a9fe6259cb13677f3d0306fe2c19b76e18a8a11c02dc1e0bfccb3ae07e3ef77a25d801bcde244752ffe671ef5f6fd7757ab6ebcb8721d282659a888ebe1> [ 301 0 R /XYZ 62.692913 486.11685 0 ] <87ed82842e736865e3746658cefdfaafb6357084246c6381bb2a8e31221efed3055409ee7c79a2de48188951834a7a93a15d79ac2fc1cae7b4691ab70c3a2f5c72c249befb2cafb4a28e8d51c8131926> [ 787 0 R /XYZ 62.692913 704.24685 0 ] <4c74de1ae9050c345e30d26d47cfe89db2817e03fff699b5a9dae67e2a353dd27b47d012cb073a80744c10bd2ccd310cc18ab168797598c8fa39592d73c12e8e> [ 925 0 R /XYZ 62.692913 785.19685 0 ] <69e1ecf485b4205ad8861b5749f78a2ea0955f8ec1af20b0014c576408355f7d1cf9224496c20cae372e87d3eb402a6345f9a4deb0178f3377bbc7f9a88f3178> [ 257 0 R /XYZ 62.692913 232.28685 0 ] [ 591 0 R /XYZ 62.692913 373.28685 0 ] <39a4347c4a60eaf67871be0201e0bf7d5d9890a52bbbdf093a73bd8ce599aed4b14a03f491491ffa583125c8432214728b0ee6c4a604710adb4211074b71594f> [ 776 0 R /XYZ 62.692913 621.26685 0 ] [ 799 0 R /XYZ 62.692913 505.11685 0 ] <216a08c697d6fb5fb474f36a1a4ca5c19f2a151e30ecaa06f24da557a7a8ba25ed97960b387134df9edd34ba509b3cda216eeb4c26e3e2e374d79f812b4d712c> [ 532 0 R /XYZ 62.692913 586.06685 0 ] <3f66aeddc8612f267b1814a54c3c0285c9b9fbe92b043748f2583cd5363321c2890d8ec19ba175611bd593a52119c577> [ 482 0 R /XYZ 62.692913 240.60685 0 ] <621d1d66ec553778f4fc65ec55265a67162a0af11842feeb8e0ad30d548dfb55a6e3ef09342686c1fdb3c0ae6d1624ef8aa405d9266ac3a2ebaa286f30bde435e5dda0c92e2f37c51564d4a48d556222> [ 542 0 R /XYZ 62.692913 422.13685 0 ] [ 580 0 R /XYZ 62.692913 240.60685 0 ] <3d360ddf33c397dc194b239f495656642f4722b58295714d755a7a870103d283363863cc882d9697f00b457e1d9c59f9052c397a0965dc95bd3ce462dbcfb9b0> [ 971 0 R /XYZ 62.692913 386.93685 0 ] <04de3166f975a871c97ab5abcebec48a380427b3ce24b011446b6b8e7bae23fb28eaf3eb08f32532acf9a195ae48b0b173cf4cc8083ac03d530363f705370ebf> [ 551 0 R /XYZ 62.692913 686.64685 0 ] [ 276 0 R /XYZ 62.692913 245.28685 0 ] [ 560 0 R /XYZ 62.692913 381.28685 0 ] <347fa4ab7a26fe2064fb2115a1466ad4c8a0189e50dd0973afc7abc13932d2d21da5e59f978f1005b606fe6818d8e6ee1066297b0d8b0b2d839b3abadec412ab> [ 242 0 R /XYZ 62.692913 785.19685 0 ] <9e9a074e833f78cfabb135a398319d0683041c03f47f0bf055d13373638c628ebb148fd31c1cc612b340d727f2d23338c1c6aacb4b058d76e2a0126b532518ad> [ 511 0 R /XYZ 62.692913 215.00685 0 ] <24cdbae0d108ea600bc2a7dc481ccd993e17399ef40fff79570390265fca432668608ff2f6ef14dfd1e6b428996c269224431cb51d2207379884530d0e866708cee50b7141d031f090ca4014f70f3a0c> [ 987 0 R /XYZ 62.692913 272.33685 0 ] [ 403 0 R /XYZ 62.692913 153.05685 0 ] <7f401b8707acef6047f169c2418bc61dc2a781a60df4201b00e434d059ff90c35b2619f8175efcd63999b76e5794a8960b1c6c68a9e3d6739608f8053714a49d> [ 469 0 R /XYZ 62.692913 341.18685 0 ] <8b8fa86138e2704eef60686bfe825989bcdfe4d6d266d49e6b12a81a53ad5776737e1355dbe1ec6b98723b7f42d81e829413fa4a350a2ea68f4deae11771f6ec> [ 413 0 R /XYZ 62.692913 761.94685 0 ] <69146d4799df3dc336736b092bdeaf96cca13d06cce9f74cb2460480228bf6828c700681ceeccc9aeb7c511014beab60bab653b48a0d153ce9f2e2cb9c4ca9e74a3cc67906388fa6b26a6358ac230fd0> [ 809 0 R /XYZ 62.692913 696.19685 0 ] <61b76cf7dd0e2a363ff356865508c2c829550eca15e2e05ac5eb2f91cd41967733da54f05f67017303b7a5da654422e5cc0ec7c5fb383e2e077815881984d7b5a811f8b37457d00de80f0fbfa93d9f3e> [ 698 0 R /XYZ 62.692913 376.38685 0 ] [ 765 0 R /XYZ 62.692913 785.19685 0 ] <490f932a9ce682ad5429a9a3b1ad2d82bdb8c8fe37f544f9e7c108798ff6b2528ac7510c5b6d506967093f625730a65a65834581be220398945d6c2d11cf2a66> [ 744 0 R /XYZ 62.692913 416.81685 0 ] <02a58dea89b4d6309d01c73233a8933a3072913ccfd6052151d77beb5ca09a85beab7a189425b27604a37b1532bcc24c2283dab60007cee1891391c6a9e17bbcc0dec22e945025cd8054d8093d6e8d94> [ 776 0 R /XYZ 62.692913 422.13685 0 ] [ 483 0 R /XYZ 62.692913 785.19685 0 ] [ 348 0 R /XYZ 62.692913 257.51685 0 ] <09bd6c17773c9051274e6153d2bc20df06dad4d0ebbf89a4a16c01b5a75de574ad56e1e19fc7f4d389eb1a0e8042d20a3461673c3b5075e5fc3fc998b7b1f69067d86bf67d4eb3431a54f41790ab095a> [ 654 0 R /XYZ 62.692913 554.81685 0 ] [ 561 0 R /XYZ 62.692913 785.19685 0 ] [ 754 0 R /XYZ 62.692913 159.10685 0 ] <348f25620a4c2e127368607638b6fda661a6572011ab4115ced3c525fa69bb82f3e4f9b8530db5a0976559633138a41660f238fd9b30f0841f58326a5cd1b397> [ 116 0 R /XYZ 62.692913 719.31685 0 ] <3c4e67e7e31784e7cbf72257bca844fd23765c825ce805e9db3aa8680dd5cbdf28b2a984a69dcc7ce48a8686b9ac166a96416f4ae87d22085c7db5e98161d604> [ 637 0 R /XYZ 62.692913 211.51685 0 ] [ 878 0 R /XYZ 62.692913 232.60685 0 ] [ 809 0 R /XYZ 62.692913 471.46685 0 ] <93bba8ab8c10c1ecd5a70933bb8c5648f7913242057d6024320ebc0fdc26d3db305c077f92eddba4d2a2d3a71dd2a6e0452ed0746be920b56701f1ca83e2f28b> [ 591 0 R /XYZ 62.692913 191.75685 0 ] <9f50634c75041bbc3588842b38a5ee0fe29db80598671c9012daceefce6fe101b7b1665ee569327f75e88454209696d59e46fd3fb5e90e597a41456e1c7663d6> [ 800 0 R /XYZ 62.692913 785.19685 0 ] <8fd2fcaea2ac47628091f3c3c2c30a3e0b5c3700dd4daead81b3ef37810b62ff2956d3f347c3dd5e5f1bd2219dc29402eac6747c57a62dbc82688532a9af501a> [ 258 0 R /XYZ 62.692913 785.19685 0 ] [ 849 0 R /XYZ 62.692913 443.76685 0 ] [ 328 0 R /XYZ 62.692913 785.19685 0 ] [ 979 0 R /XYZ 62.692913 443.76685 0 ] <4c6e4fbd471a383a4ef72ff6645dd08504e2b25321f426555c62ce01987e195217ee5cbe183e9a66b4899edfabc04fabb64375db4641bed518bc55583231d1d4> [ 819 0 R /XYZ 62.692913 372.56685 0 ] <76205d53a01253b8cbe72b8e2fbc59af7f261db14eedc2b5973504a8471ac665e249d49bb86a79bd6d30d3e3e1b72e5493bf02d8712eb2513d86a216b7b523df> [ 393 0 R /XYZ 62.692913 785.19685 0 ] <3a23485cf627839ca2b4ff073ba762c1b4250791d94498dc12b781c39cac8a739baaf94cf371fe9545ce792af00529cd592233e4d5c42a4f96227265e7995ac2cc066ed484ddfccb261ada3b41fea13e> [ 603 0 R /XYZ 62.692913 785.19685 0 ] <6ad8ed1d4a6ec7efda130e61b3e6ea38a7ef1ce044103fe2c7be4821311c15c9a5929b7c3496a65c5c5b54a55ed8fb38c05a59e5ba491e191e61c853992502ed> [ 524 0 R /XYZ 62.692913 197.40685 0 ] [ 625 0 R /XYZ 62.692913 422.13685 0 ] <20d696238ab1b6e11234cc67eefcc954a30b05e051f9720c0f92130d8a474b2677f338d4e338b0c4a4ad5adc6dcbe19e3e5aa04ccc0f5fb108711111dcee0e803259a2e621a98d0959b09ea781f04baf> [ 570 0 R /XYZ 62.692913 785.19685 0 ] [ 709 0 R /XYZ 62.692913 151.65685 0 ] [ 613 0 R /XYZ 62.692913 398.88685 0 ] [ 242 0 R /XYZ 62.692913 621.26685 0 ] <45894b36c217c328a8a072243a8ed85fae0bcd037604a098aa9bb195c030680da682258b48ebb4b1ec50c6afd5f44ee4789c641f1af0b291cd948bd43336b217> [ 988 0 R /XYZ 62.692913 785.19685 0 ] <2fb36743f3fdcf2ede70bc6d8acbf3e2807655d426a0524c6d4af5f08078952cd418c3d548551e62e2056850adf85947639a7d1d4c9124f6254d62c252ab3ee2e75223a90c30ea90d2eb754fd49ddeb5> [ 722 0 R /XYZ 62.692913 398.21685 0 ] <9d02122c81825821a280ab3250d46bc0f4b47dcaf3479794883c35c115716be7d17d23e4a98a485d0f52d34916beef4892d18398a62997bc35df5801cd99a73f> [ 732 0 R /XYZ 62.692913 621.26685 0 ] [ 277 0 R /XYZ 62.692913 579.74685 0 ] <69ca161124961a4f0218ccd17fbd4d7b1f41e084627b01c3e50a88b3c6dd9d8f290078648cc42bd3fb9a06936124ded3e57362c792ac917644509097e4ebccf1> [ 413 0 R /XYZ 62.692913 580.41685 0 ] [ 692 0 R /XYZ 62.692913 586.06685 0 ] <7dff090256b65edc8d565d3d66454ac1693c8e1e1d4c39570594d159d1ad0766b2ac4b8c3f290668418e01f5e8a09431f9f69a10fa47547064aa7eb7833965c99c027690f60bca62ec5dc3dd3d415485> [ 266 0 R /XYZ 62.692913 785.19685 0 ] <1cdb756e8982c17eb3339e66e3e56ba4ecb90e37444263109256ecd00678c090e9414660396d0308618d7ad5ba76746ed18b8bc30ca63e0038831cdc9eb93e38> [ 469 0 R /XYZ 62.692913 159.65685 0 ] <31924429ddfdd3e3c3d3a0cd07a34b8781f9b28797fcece1bc43d9be7e8607c9563b08c49def2e6e27c80e89892c97b9657c0b3eca6dbeaa19e89a21e49b08cf> [ 551 0 R /XYZ 62.692913 364.61685 0 ] [ 312 0 R /XYZ 62.692913 255.38685 0 ] [ 664 0 R /XYZ 62.692913 451.19685 0 ] <3081666b1e3d730a1edb4ac0c691a1685b4feaba557d47099f05ad5dea8122428c1b51669ecc9bcbba13e17b3db5d72117998be1e3041d5f8c88808c161a9c9a> [ 421 0 R /XYZ 62.692913 494.11685 0 ] <9c03fdc88241ade966596213dd1a9b512cc6438acdb56cd0d4fc0f3ce9069a4b38f45c80397490c5fe15381def9d7beb66ccf535be061f567318fb0dc8621059> [ 833 0 R /XYZ 62.692913 232.03685 0 ] <34b0e4e706714c3dc2d05bc23eba25eb0e61c0b7b6bbd2e6cccfb30cb3506b59de5c575a43fb1f1cdfd28ecc1fa3b2aca154e5584b8e305e60b1c176194cc9e8> [ 497 0 R /XYZ 62.692913 675.64685 0 ] <2f7649080156aae5d82c6c6a02197851b0542ed26395aa3c5298cf3ecf963ad90efd749b8d2ddbf4102213fc5ba2a697bda988167e6a55655ed0c22785c162f0> [ 925 0 R /XYZ 62.692913 586.06685 0 ] [ 337 0 R /XYZ 62.692913 222.68685 0 ] [ 940 0 R /XYZ 62.692913 216.03685 0 ] <875a9a81c34f63ba6630fa2a036273cdedd644e8f4fabe6ece8bc2502facc93969bdd48e0ec44155d656cb3d3556198bcd3e16ea3f72ef427e976f12e3877adf> [ 288 0 R /XYZ 62.692913 507.76685 0 ] <64b408297dcf4c54d41a777253f6b28c94325a4e1d21bd0a55072e8c9aeec40ecc190f0716f5453c62a2ddf58cdea8c1cd48b150addafd0b8aa2dbc027c550dd> [ 532 0 R /XYZ 62.692913 246.43685 0 ] [ 301 0 R /XYZ 62.692913 286.98685 0 ] [ 542 0 R /XYZ 62.692913 232.60685 0 ] <54455c779d9bfffb674b3657010702c336c4bd804618d7dd5957a7cd71f6e021bbc237c5705b527680881f9fef62e245d6b96d14b3bd4cf17ad6e25e92ae67f889fa624a333fa2df83fbe75180cd90d5> [ 897 0 R /XYZ 62.692913 422.13685 0 ] <6d85d58a6b4fffe96c92781782eb3166ce6fcaaf43783783a41f55a59ad1aa8ec734e8b5c449594d21a9726cdb56a68b45e89edae01f67514c22b431a431d36d> [ 916 0 R /XYZ 62.692913 785.19685 0 ] <5857c264daf62f9aae0371019c7e5f3e1c64d5ee9f65f48ed2d62a92ba7dbfde8331ce6f79ba39a0704557a94db21f199e13a86ece36c8a04e7c978b5264e27b42bd4230e091623232f8a3ed81da4ee2> [ 581 0 R /XYZ 62.692913 785.19685 0 ] <46ad4e0b856fbaa1e193b3eda5df5150aa26c796069429c4092b1511e93a0426e3a9d145864f18a898757723ac30760ac308d9c5a14fe6135e49b48767d9222a> [ 404 0 R /XYZ 62.692913 721.84685 0 ] <332515e986fa94952870c726036a47a9068cc5078b45afb4020e220cd027031caea99457c7f0f7e1bb71f1e78d7a72052a5a0390b7534158aad1a1d649563ec8> [ 512 0 R /XYZ 62.692913 761.94685 0 ] <6066340e9e2054225e6b6cfc628ea22360d53a0a1f8669708ee2055ff4d9ee94afb56def01358fdc5f37d7c532a3ef25fa45e58c69219406e998a0b5efd96dd7> [ 787 0 R /XYZ 62.692913 497.11685 0 ] [ 681 0 R /XYZ 62.692913 411.31685 0 ] <7cf0334ea16a48dd8d8b8ad79fe080817cfcd9953801416f167a0852fe8c999b7b0ebc345d34965462da4e320d3e961999b0409b62640159cf73a191cabb8277> [ 698 0 R /XYZ 62.692913 151.65685 0 ] <302c96c6d7b65c87289484b5da468b5ec942a9e3052cf355ce4ae5534f28cd47e368c31529d0f61f7e5a5870bee15912> [ 121 0 R /XYZ 62.692913 259.69685 0 ] [ 122 0 R /XYZ 62.692913 512.84685 0 ] [ 483 0 R /XYZ 62.692913 603.66685 0 ] <84a4b0eae08f4d2fe5d5a84361cc5e4202e3637c4581effb70b2b70e8a76826b3d9c31f91625ac8fb5cb913cf9e47f413865226bbba0fd43dc169b4b3cc32279> [ 421 0 R /XYZ 62.692913 312.58685 0 ] [ 744 0 R /XYZ 62.692913 235.28685 0 ] [ 288 0 R /XYZ 62.692913 326.23685 0 ] [ 338 0 R /XYZ 62.692913 636.94685 0 ] <819385a7aeabbaeefe7b593dea59dc720f0a91df6ab8e294e9bc52b83aba243b456790022b39c5845222a2ad0fd144b506184807bfa39429b64ca9464a103bd0> [ 765 0 R /XYZ 62.692913 586.06685 0 ] <661b99a5909e796e360f111619f8e31df2882c46b09b7a7a3bc204fe1cb882c7df4d29596157370d60f64f25c472d8c0295d9c4295a886621d0b41766050954d> [ 654 0 R /XYZ 62.692913 355.68685 0 ] <2e6f4a425fa25755769678a30d2f8ad2f46f78120e99e8ed81be8d35860d74fd8e06c5ea14e0b46e1bef16f72f49003ff596c274317d81086e866c98d7b15a32> [ 613 0 R /XYZ 62.692913 174.15685 0 ] [ 592 0 R /XYZ 62.692913 686.64685 0 ] <58aa81b6bc5ab9f68f5204441d2aa5415a6dcb73b61028333310be56ee7f937b10c3dc2db94c1298c6c5d119360146a3bddc42a0a6f65ecc44b875ad9b3035f8> [ 834 0 R /XYZ 62.692913 785.19685 0 ] <3f4ea32bc36b77f8563ba3b137b0e9a6b67bb620501cc0dd0a66f82690f4712d3a61bffc32375e4c9e4f2fe77f633a36554de02c24d148738ceeb96794da51b6> [ 879 0 R /XYZ 62.692913 761.94685 0 ] <4a118e65b926b279a3d810183aab04b2d939e0b3682f9a15892ae737dddcaf88cee6ebd46ebecc106ad4b98dfb56c928ed54205ade408eda9fc16795f6e1b497> [ 525 0 R /XYZ 62.692913 560.34685 0 ] [ 413 0 R /XYZ 62.692913 398.88685 0 ] [ 710 0 R /XYZ 62.692913 686.64685 0 ] <03be714cfa3eec536871d259e6c4b82eb8df64aad685a090c7ef0dd9cf069ead9004fa9f154c49b74b2154bf93e53cd368f5c6ecf0d38a54ef68db155acfbb4bd9f05c662c87231e933595105faeb62c> [ 723 0 R /XYZ 62.692913 785.19685 0 ] [ 581 0 R /XYZ 62.692913 542.86685 0 ] <10cd9706450bacd9c4f899094594a49f59994ac3d4b64c0086c3944b24ebee73db1221b011e28f7747fc7f4dbf69b0178731090df75ecb635a1d49416b2f929c> [ 266 0 R /XYZ 62.692913 578.06685 0 ] <74b47a18c16ad98be77a057d3e6e5f69223f88d03179953e21372dbb6d416c6d85ff24aff51a066d3ab7981a88b6255d> [ 116 0 R /XYZ 62.692913 303.31685 0 ] <9f862a13fc44e95611b1c5ecc52bb39bf3aa97114a3cde48c5c18c77294c45699118e753aefc6b99758d6aa61f56ab217e8111be7fb298a6b0ef25729849c328> [ 692 0 R /XYZ 62.692913 422.13685 0 ] [ 681 0 R /XYZ 62.692913 229.78685 0 ] <60c99ac16342e98941292b9fff2a3c7ae0f664c014b2662084dfef977224e71991b09506c7fac73e9063639034e5bee2cf100c39949ddabd76e696e783d45d37> [ 819 0 R /XYZ 62.692913 202.03685 0 ] <3690d18febbe05a0736dbdc72a8acae7f3c8156e89b850f7c1437c492e8e6d7cbb6c7c883ef92124784e9f35597869679c38730c23ee7833d9f7746e759f21ed> [ 404 0 R /XYZ 62.692913 568.91685 0 ] [ 809 0 R /XYZ 62.692913 272.33685 0 ] <154cfe8cff1a833fdf554c0867470de716579667f9436976d2f994196e6ca84a3804efcebc806299814713b0de4f9f401cb73939b097c9fa010fe6880751de6843fd44d3f4096dbdc7bd130785002bbe> [ 543 0 R /XYZ 62.692913 785.19685 0 ] <3bfe73bd1f6c94e6df0a8bb6af6fe88d3926f7336679aff007774b78550a7e1f0db8f051e0c395df74df714a21d679383d1322bdd40155eb23b0c76d77fc6cfc> [ 755 0 R /XYZ 62.692913 704.24685 0 ] [ 570 0 R /XYZ 62.692913 466.16685 0 ] [ 732 0 R /XYZ 62.692913 422.13685 0 ] <74e6e4bcc62493938fc37036ea5f7638d814237fc2d6e9a387976a709b556b239eb6a1e5edf273b60d684ef0fb29b9fbb8b5fa0a24201ed9c64dbab54e84d2ad> [ 512 0 R /XYZ 62.692913 598.01685 0 ] <9ed174c98494dadc4a1e0efc1c3d8116a0feb779708969b3937906fa359d21390208e2e8f5c2e79a7392e5c2952e2bfe39b986d9c6c5c5543a8a8da0e2396e8b> [ 800 0 R /XYZ 62.692913 477.56685 0 ] [ 313 0 R /XYZ 62.692913 479.94685 0 ] <6e849e498e8e3e12e53e7af9cc4a359134bf05cf56a8a6e00380d422f5b36fc8c9008653b22626133658769ae9aa37bc8f8531fd578ab4170b4531d4297bef8c> [ 561 0 R /XYZ 62.692913 479.16685 0 ] [ 603 0 R /XYZ 62.692913 560.46685 0 ] <51a9eedb6dd48811ca1f2b983289326ecda3da995bd3198b27ed4c5d15be9677682767d97c115fe3f45a23cb765c43b3c2606cc8b25b427cd0f5a7d1f16972a5> [ 625 0 R /XYZ 62.692913 197.40685 0 ] [ 638 0 R /XYZ 62.692913 761.94685 0 ] [ 849 0 R /XYZ 62.692913 164.73685 0 ] <940fa360d76ef5e937db1b3bb9cefe19e81082099c97d1da2405a3ad514290a8212e034fe3c4c770f380e8d7960539e2616a124c9834ddcf09e68d2e4081834f> [ 328 0 R /XYZ 62.692913 507.76685 0 ] <46d4f5f7dcfb0dbf3966ab21e59f1c7e545418c3ba7a8ac95c1a7350314b4ffac4cb9a7696aca8ac4331934239785681c72307d5db6d66f022afa24fa0c41e906e4f38dbb8b933ee38dc2638aedf6d42> [ 349 0 R /XYZ 62.692913 785.19685 0 ] <415cc273a105db44aa0504818d10e587ffea3f45ab5426ad393e4b5318f5cfaca684e0e8dd9ac60170632e0b41c5ba68342043ed7a73f9eb89311e157f3b9473> [ 552 0 R /XYZ 62.692913 785.19685 0 ] <0ab143d6d51c4b7ebd3529747e477b1aedb3d170b969e4e7c48781b369981d9ec0ab6733d8992686fced45d3596cf045f706fd011112d46ab40a5f79aaadca8d> [ 497 0 R /XYZ 62.692913 494.11685 0 ] [ 242 0 R /XYZ 62.692913 439.73685 0 ] <3b7969b35a40860f04870c082fc5a4faa16c0704afb7aa24bbd68d80307b64c123236df68fa662c789d6a6a9610eda95> [ 941 0 R /XYZ 62.692913 761.94685 0 ] <0891a1e3457ea3270f9312e284085ef77e2b8bea2843c927bddce6adf82c9f19a015e070ea37797bc9872f0bba99003a352f5af383c43ad8e71337973751ce6d> [ 699 0 R /XYZ 62.692913 686.64685 0 ] <1a00c73fd7e8a23c9949b1d0f9985d44bf7ea532c571d7ecceaa7501e9653def737c2cc0e4157e9d30fdd0a375457ed788928840beecb580e1fd79678c1ae080> [ 533 0 R /XYZ 62.692913 633.99685 0 ] <2888f3e01da29dfa4c5c33b82631257cff74a6d35671c720df3cd9486b46ea5c75fc7e9c6aa9603dd67beb956738812090c0c3dceda47e9834787b5392f3da2f> [ 787 0 R /XYZ 62.692913 315.58685 0 ] <9049438844270cd54dfd89f6f28aea14f4551cc902ff1e0575cf1a6db060960a2c92984756dd2cae5cd7473133b9835bf8c8efefbe5cd9f0935140ebc9076f28> [ 776 0 R /XYZ 62.692913 197.40685 0 ] <851d1eb06513d4cc12632e3b3e8b59e5c5524b707b880be12e3dc0475579c281f0f1883c5eb8a058b1cc6812f0eb0bbab130a66cb3bca6318f3cd8a9fa55f0d3> [ 979 0 R /XYZ 62.692913 244.63685 0 ] [ 258 0 R /XYZ 62.692913 621.26685 0 ] <1818b8bdac51d1c909a70bc7a13ff88092fec7d6eeccd35e127c9da420127ba842b737a3543dcdf1e699f18577ff7147> [ 916 0 R /XYZ 62.692913 507.76685 0 ] [ 393 0 R /XYZ 62.692913 649.86685 0 ] <5a97da8514cf8aba3a12c38d1ce12faa85920cc09d5f08bd3f6255737429c9e02ef2553558bb9d207df07f49e88818e9ce2f2aa712bcdfeb16acc4131587f112> [ 302 0 R /XYZ 62.692913 785.19685 0 ] <03da57430323634464336a25bcc98db93b2d51074087f0c190d136cc0b5e6a0a66cfe04eda9f52d4ba3f7093106c2614ab5a6981b49a915f978c9b56940084ea> [ 277 0 R /XYZ 62.692913 270.31685 0 ] <12bc61ee42d980306bbe2a01594def45ae4b7151887f8f299661440747449b2ce9a570b852a022cf3561f4c298cc27e964d72262019e6693c76e6e1cab1ee506> [ 732 0 R /XYZ 62.692913 258.20685 0 ] <7d3e29da49eeb607ea574ce821a6ce36b6221d21aaa2f133d42b32e145b033d6045eeb538fa4d15a5674ae5a13aa9f5206ecb43e9c611632f65195b38ef7860d> [ 404 0 R /XYZ 62.692913 387.38685 0 ] <912f0decb4f8f4f3644f39c3aa6118703584f731ffcfe8a014a5e6bd11a9bf50ab10d6e87151e6e619bea409ce3e4134c569b166a739bccd223d71a2640e866c> [ 765 0 R /XYZ 62.692913 404.53685 0 ] <781f8555b9d0f1cfc2bab5d35257acec5dd67a2a2ddf58ecf61ca737ecaf7888da8ef2f306df4e0f7af072a8972a1abc1811e58f26d86ecff21b713171decc74> [ 755 0 R /XYZ 62.692913 505.11685 0 ] [ 810 0 R /XYZ 62.692913 667.59685 0 ] <0b58374fbd2f108ff956a4d8021a0d73192362c18d1d9c04756f322f04fa411e3fb986f63fed035816aa1ebb84b23fe593b9f66a9df97461ed8525f2c75767d8> [ 787 0 R /XYZ 62.692913 134.05685 0 ] <1fb24084203a5bf3971d8756a8eed58707e0fcbc3058544bf7cd67399c4ee442a3f0e61712c0a2691a346905a8a9f670225aa442d7e3812b0678366190ceb5e3> [ 820 0 R /XYZ 62.692913 704.24685 0 ] <64d1b7e512d18ee5f052e6de551b0b9a6851a71acbfa6c9775bc3d1aa790b43565c991c4c7d34b0db6057c345a6f98cea91aacb6e549730ebbdd20f28916afc2> [ 421 0 R /XYZ 62.692913 159.65685 0 ] [ 581 0 R /XYZ 62.692913 378.93685 0 ] <9787de2e350abd21770f9f58b18f56f5cd1f0c556aba782257a9d10c8e9ee52f1a6801569b4f3c70d2ca5d1a7cd5720a38edfc1f669eae9783eb5b76fe19640f> [ 393 0 R /XYZ 62.692913 257.03685 0 ] [ 614 0 R /XYZ 62.692913 704.24685 0 ] <2e25659173140c8436ca318d093c62f3a064cdd2ca0751350f0c8806612155324c12086243378e41713b39b95b0838df1ae595b62346154e749fdf4ba30990b3> [ 483 0 R /XYZ 62.692913 396.53685 0 ] [ 338 0 R /XYZ 62.692913 429.81685 0 ] [ 543 0 R /XYZ 62.692913 603.66685 0 ] [ 349 0 R /XYZ 62.692913 382.56685 0 ] [ 745 0 R /XYZ 62.692913 785.19685 0 ] <82ae7e073506d23751016dfe0ea9c7d82ef53949e7ca7b9ea786d3dd5ad561524e262db021fa0fa142701207a5f18d88acdde7d78f0f816725e8fbac57ec5643> [ 413 0 R /XYZ 62.692913 217.35685 0 ] <869e5c21d6cd2e2fea1f5ac5a7bf90eecc753eccb0eb0b74c4be141e8f50fbb21c94dab42413a2e06091f582eb832e59b95f6dff33845e8a55848ba3f05eb191> [ 278 0 R /XYZ 62.692913 696.19685 0 ] [ 777 0 R /XYZ 62.692913 608.34685 0 ] [ 850 0 R /XYZ 62.692913 508.74685 0 ] <8d09fd8ccd2c7653f5303c17269d085d4e1fb0d0ac8b5917e33b4ff27ca8e9431acc29e779e6707e2a03a2784ef73717a7da6e3ac0640372bd020249a981ecd2> [ 266 0 R /XYZ 62.692913 268.63685 0 ] <1b76bf50fa486e09d6c3f7396052a852546158ddebfc732fab627fe5d0b2829bcbc02306adf6939a56f0a1a39c532e12> [ 328 0 R /XYZ 62.692913 141.88685 0 ] <595b385887e0e311b309a575bd904c4ba7217fb182287c16343e794030dcfee1d4e8a9b3157ac3631408bf47c89d3ae0330b5ce9658e3382f2a6530af481100d> [ 561 0 R /XYZ 62.692913 280.03685 0 ] [ 570 0 R /XYZ 62.692913 241.43685 0 ] <196271fd103de329e00e2c8fd9e14288d5995ceb0cc84c9a8715325982683d341a9e191b41a4d8291dea5efbf358ceae3d19ab36710c51a1a8331ab9de403f92> [ 699 0 R /XYZ 62.692913 505.11685 0 ] [ 592 0 R /XYZ 62.692913 487.51685 0 ] <378c56c7e6a26f195ce9a49c933e1af7a360c3b826d17a5f39ed9220cf61e4094ebc126855c2fe56efc9ecd50b895590713cb1090fb78b789e577efb6bb3a55a> [ 834 0 R /XYZ 62.692913 614.66685 0 ] <1b34e392d925465d399388139f002403a1166c54eb3e9f8dd3ee404acd91bab383ed15e4d5b18b9d0fbefae07ec340d0892bc36fe6fc6d656d1afbd562acef06> [ 654 0 R /XYZ 62.692913 148.55685 0 ] <5e1954a73b7856bb2a529d39ce3cf993b13353a885cd3cf623b9ec51aa9796e2171eea7742b76c9ea65e628a66f94870c96c911d881c8476d6e503ec49aaa1a3> [ 916 0 R /XYZ 62.692913 326.23685 0 ] [ 626 0 R /XYZ 62.692913 761.94685 0 ] <9d5699afe337b2ac9e34d1a1f4f71eacefa0dd801fe9ace8e7ee5d3dad8f9ab31f55742d08bb9d80bda9e81419cae767baaf2be047fc18ccb7bcd744d484d86eb255f07fec5229641d9c9cfd8a721c74> [ 313 0 R /XYZ 62.692913 272.81685 0 ] <0bece0c2f93835df502919f4dab21c9387d1dbb0312a6d4d1059c44b5b8f430abfcc620865036db5a03656535e4cf07eacdbf0767e021efb567cd82578d13107> [ 682 0 R /XYZ 62.692913 721.84685 0 ] [ 603 0 R /XYZ 62.692913 396.53685 0 ] <4417ffa7d9666d0e9083ae1d6dd5c516a0b9d7bbdbbaaeca6cd56616177b4e467068b3db9efbac55f23fc9d75c447c4c20df8ef6c879099064f49fde6c9e41f5> [ 710 0 R /XYZ 62.692913 522.71685 0 ] [ 638 0 R /XYZ 62.692913 384.91685 0 ] <420fc82a49139440f6525674e104434953b1d6b84d9945213d8bde55d94218489a0383d51c04558a2743175f94d0482ef85eba60b23f1cb93c3ef93b4107b819> [ 497 0 R /XYZ 62.692913 294.98685 0 ] <6107d1e0f4628b22e5c2347b96bf80b570b7ce45f5f9650ecda11f7428f7e7c1fa878a6dc272d021b1f8e67db16f052c53137dd9339890bd9082b499899d83d5> [ 512 0 R /XYZ 62.692913 291.98685 0 ] [ 800 0 R /XYZ 62.692913 278.43685 0 ] <50fa300c2b22710f81e34c48ca0ba2847c514429bd8a02ee57b0308160c20e758ec27869733f7b3d56eaf016b46b8e0f51db6528ea44f417baf91fe4a39f685a> [ 552 0 R /XYZ 62.692913 568.46685 0 ] <04e8ff07705590d5819187c3bfe892b6a481d133bde7953ebfe204b4908a8a8e280528e5a1252c9a14c15aeb09410c3e2571ac79764541075dd658add65ea6b9> [ 117 0 R /XYZ 62.692913 401.74685 0 ] [ 879 0 R /XYZ 62.692913 580.41685 0 ] <09c074de093f21a877bb8ac7ff8a91f17dd9066c07b297ec4476de0d7e60d57a93f86796ddb52a1286dc69c9a097802475e1e50a6d4d692585c4dad4b2bf9468> [ 242 0 R /XYZ 62.692913 258.20685 0 ] <3d80ee7e962b982bcfe2e5a5067537419515510f48f895d0e8022fb63720a5cb71334adf155c9c98d2b96e43f2219ac9069d8d388b58fb737e279ad536484d33> [ 289 0 R /XYZ 62.692913 761.94685 0 ] [ 533 0 R /XYZ 62.692913 452.46685 0 ] <6be8e07ed4404b9662455a4ee63c92d2aebec391270dc101f2a3f05413c50e3df47f77e977f24a8400205d3ee5c666533bf083e514c75d990bd08a4f8ff54704d81d2de9d2dad1b1510058534110fa9c> [ 258 0 R /XYZ 62.692913 414.13685 0 ] [ 723 0 R /XYZ 62.692913 595.66685 0 ] <3bc06c364bfb3b18bd5203638aa786d956b7dda0c995119663128fdf577fdb8278a956a2381c2d5866abb99609177b41dbd890027b80045186f5935a234590c6> [ 302 0 R /XYZ 62.692913 586.06685 0 ] [ 243 0 R /XYZ 62.692913 680.19685 0 ] [ 289 0 R /XYZ 62.692913 580.41685 0 ] [ 592 0 R /XYZ 62.692913 305.98685 0 ] <6230afde5df618f9432488f038726c82cd559c5539fc5be9ae8391cf859c3d97106cc5caf72ac31f779222666cd9cc69b2c6e09494a0135441ec24b4c77ae472> [ 329 0 R /XYZ 62.692913 693.24685 0 ] [ 614 0 R /XYZ 62.692913 522.71685 0 ] [ 682 0 R /XYZ 62.692913 522.71685 0 ] <6291a3d56839f58fe1c8441e295781a6873bc55a06030c2f5f6d701f157b8e98c49c55aea0dc53c82c04d3049ac823a2075791406d3a97f0d3e4eade52f583f5> [ 422 0 R /XYZ 62.692913 721.84685 0 ] <365685773b3bcb36288e873e87479ce4907bd4672e4162e035b1cb456670370a736b660fad409f95e3285d35ffcb793deb0c0b2d2aa2a00de74c7b19ceb42099> [ 604 0 R /XYZ 62.692913 785.19685 0 ] <34a3fce9ab9457b452a9e42957172971e73778f07b9013e9560d1d8e27a8b2fc218220d8e71328d6845d14f799c0f664ce30d1b7243c0e9e625fc00b72e87a33f6656e87cf7541bd724166e3613d8968> [ 879 0 R /XYZ 62.692913 381.28685 0 ] [ 916 0 R /XYZ 62.692913 162.30685 0 ] [ 414 0 R /XYZ 62.692913 604.94685 0 ] <0ac126df5729545cebcee337897df372aa15e510e8ee8c7264be831bd24e52f7668bb1773a60283bd87938aaaea8ed6b> [ 581 0 R /XYZ 62.692913 179.80685 0 ] <93fd2c943edae3cc681d0be2a23ce0cb51470a7f67ce151cad5322184cc5b2654a5f152045903079ac81720d394dbe7a7c113a92cb4c85f41f1c746742480864840f33ea8d612b594a2927a93f5a539a> [ 339 0 R /XYZ 62.692913 785.19685 0 ] <20b111472aa7ae6167f12cd606b0af86f83bb366a1022e90ce69b33a9bc647c8bde8ef895d8edaceabda09b178bb2314108acfc23ace1fb29b4bf0e9a11bca73d60e3f7ec6cd4b09f35a9e03b4bb37a4809ea2f3602f39bd7965ea32f13ee5c6> [ 723 0 R /XYZ 62.692913 396.53685 0 ] <94571cfa86f29ba275e97411922f3fef816171a5d52f2114ffe8d3e6e66835059338fa9aed78498e9d540329eef55b1b16a175dd83b374a98fd6e04d95bcbae3> [ 405 0 R /XYZ 62.692913 785.19685 0 ] <8b4af8b76c6a91c2b683598be358161a081ae77731e6771098f7fa37b3d47bc9836f40dadd820c7766d08ff0793b5ff9> [ 850 0 R /XYZ 62.692913 231.31685 0 ] <8cf14c9f3d7be2268e70bdab9b40734b77adc17775ac908ef89aeb1451bcc0c2a138092f70f67aa7328e40994660f43e66d601dd31c1860077243bb3046cbece> [ 350 0 R /XYZ 62.692913 761.94685 0 ] <779ebe01afa30aea6a0224be685330ba174ef7b9f3bbf60bdb5bcce61a6829599830056bf14b20686615998b3ab542109a5614c170c4abd4dc033c35eb027595> [ 533 0 R /XYZ 62.692913 130.43685 0 ] <0bdf1b0a0502f7aa57e0ca061bc638540e4c630978cde608f9f442ec6643f345438b876ca67ea299d74f60c0e9741afbcad720cb8469d5017cadf7fc3f498fed629e7b574a4fa009cca6195df95b7cdbad436032449680ada3c1455d4cf6a231> [ 571 0 R /XYZ 62.692913 761.94685 0 ] [ 699 0 R /XYZ 62.692913 323.58685 0 ] [ 258 0 R /XYZ 62.692913 207.00685 0 ] <168e0348dbe0415f0e68c0387e44314bdea9bcf7837f610e127864aaeadbb60e577af6038a86fc2853b5ff7014eefbe15e3d8c8f5a0ea43eef4dea127240dcaa> [ 552 0 R /XYZ 62.692913 369.33685 0 ] <239702091b00b3a3ff5130fec1b2a16fb412bce0e54db3634c525e872b33c478fde0d526460182d4ae740e090592525fcacf846ebda10e50baa7f1ab71e0ccad> [ 820 0 R /XYZ 62.692913 505.11685 0 ] <24e40bd6e68f894000e3f7aae45a45fa70075a8565dd776a108116d5df2a2ad81191e07290e1d4c7eef1c5ce5427203ade8fb92d436790e980790cb66e579a4a> [ 834 0 R /XYZ 62.692913 415.53685 0 ] <5d59eb74e25c26f9926aa128fd27a80feda17c7adec1a45fc59506d10c91f339279a17ee2842a8aa946ece7970cc29f4e7492aa782106a2fc82b0352150d5912> [ 267 0 R /XYZ 62.692913 680.19685 0 ] [ 810 0 R /XYZ 62.692913 486.06685 0 ] [ 118 0 R /XYZ 62.692913 785.19685 0 ] <47490ed28550d109c3c41a180d71be93b4184b786b4a6305330ed59985473ad8ad89d2a3da4fffba6f16c8d4f9e21e0f2bcb34e7c318ec158ae1279fe1d75450> [ 562 0 R /XYZ 62.692913 721.84685 0 ] [ 655 0 R /XYZ 62.692913 675.64685 0 ] [ 394 0 R /XYZ 62.692913 785.19685 0 ] <9b6055c1ef6433672b6752f67beaba4023c630dad69856786266416b82759dc273019b35b34cac965b045ca00d6c3850135c6fb8095362fad8c600565fbd7eb9e1a1cae79f2e41606255f6ce5a6289d5> [ 788 0 R /XYZ 62.692913 675.64685 0 ] [ 733 0 R /XYZ 62.692913 785.19685 0 ] [ 278 0 R /XYZ 62.692913 532.26685 0 ] <74f91a175f3f87ffeb8646b7a3112957829c02afe5299c80a3d39a0dbcb645471c1dbbc9c24dee1bad3306706c21e1a386ccde8576ce6b33e725928c608a8511> [ 710 0 R /XYZ 62.692913 341.18685 0 ] <2c7027f4590f684897404e6b37ca0c0750b22cefcbffaa9fcf4cae02697649feaca3d4d5353222c3dc7361e1e99568f97c6b0be5cd73c373e681e5149b79b94d> [ 766 0 R /XYZ 62.692913 785.19685 0 ] <576d3e901723a8ce5c9967eda22253ab58907db6c4b0c5183488c0c085736892d29c65a876e461e0be0e928e3d6d0859b0aff8fdb4cae3169e543f98c2346b7f> [ 745 0 R /XYZ 62.692913 603.66685 0 ] <67c2d3b00633a9c5cd95b7362a73ea66239d632d4ec6c304f14d0549c594ccf4f0dc34ce8b11c05304f1e248c89f1be36656d53fbf4a1fdec42964e266828319e9d7d0ef75da5af488a3bb30014f4592> [ 314 0 R /XYZ 62.692913 785.19685 0 ] <40815c3a89202986e39cf36ddbfea2055b5c679e18b8b0eb2fc4cc3d8e118cfafad1ffe6db542c84739ddc65d8b5b87979dbffc9290cbaf155cc866f9681ce0d> [ 801 0 R /XYZ 62.692913 785.19685 0 ] <0ea9a1246a9f33766727c624fa23e363e1efa72382d971ed0804187d7179e1d5eca354ccd7abbf0b5e35d58d46ddb983c4819bdb9eddb6c9b807ae4141a7e85d> [ 777 0 R /XYZ 62.692913 254.31685 0 ] [ 626 0 R /XYZ 62.692913 554.81685 0 ] <2e67fde91b4e748ae6475f2a5aef7bc0195f3cddd981a1f0c5b6f1b1c768ede20f0d7227aaf022b76f3a4f6d951166cc51faa697f871d3a31382dbd660b6d88b> [ 755 0 R /XYZ 62.692913 305.98685 0 ] [ 483 0 R /XYZ 62.692913 197.40685 0 ] [ 513 0 R /XYZ 62.692913 785.19685 0 ] <3f9cec020b0bceb2f39451d04a831f1ae7b9d11d75bb244606cd254fa4a149868cb5203b3aeb419977c0fe87202d107065b863c4c7d66cd92f2c48e623eac24faf971e05e3e6a8dd01b0e9c702c5861c> [ 498 0 R /XYZ 62.692913 785.19685 0 ] <853b6d2028732d8ab074478900e521fb47c8de20d2828fcc606b1400d2ec2087a426c2d365d22d6c4635e009eed70baa3714008c5eaf86e167c4c15f8dc868e6> [ 302 0 R /XYZ 62.692913 386.93685 0 ] [ 339 0 R /XYZ 62.692913 603.66685 0 ] [ 778 0 R /XYZ 62.692913 667.59685 0 ] <067a48a87c15bac690f23f9133d7b92d51ff10036f6a26144e76fdd142fe5da219be7bb0debcd1f9517ee5963b652878e717f8b18868b11433ee0289bc4d241c342c64bb8dff1d76e1198c3474684c77> [ 834 0 R /XYZ 62.692913 216.40685 0 ] <5768885db4367ca70e3faa34c5b8477bf29558f5eba5f195d90ecce34c684799094db1436f9d25dad169c13ead011197016e7b70c8bd16f26a20b4f2b50c4819> [ 267 0 R /XYZ 62.692913 481.06685 0 ] [ 593 0 R /XYZ 62.692913 785.19685 0 ] <495ae24c42ef204f2ebe1fba6fd999efacc9f6299427571253665134f27f0ff29fd91cf9733747af9fc952d21a0a7b7a5bf645192f7071cc133e2689c943802f> [ 329 0 R /XYZ 62.692913 511.71685 0 ] <8127fc678c9ecf00da8d6c07aa4c90f1ccfe97a45fe7875f0551878150ae19e9ee3e8d5dd96bd20963a59337ed3f6e58c212c3fea26e97d8d55bdb6944f2d156> [ 733 0 R /XYZ 62.692913 560.46685 0 ] <4e7a50f234fd4b75b18695fdeb376a383aaec1a71d8cad470873e55949b278dcdf4655d74f4ccd7677a119a7ff958e37bf74aa09d3bae1adebe9438ba105b64b> [ 498 0 R /XYZ 62.692913 586.06685 0 ] <4145442a34e52596c2d1fce806155c6987aa741db6383177cbde7bb1dcfb5646ab6bd80c20395f4345448a516cca72c1994dd6fd902be1534e15c539dc9bce88> [ 534 0 R /XYZ 62.692913 503.14685 0 ] [ 484 0 R /XYZ 62.692913 761.94685 0 ] <4e2ce3b74b3aa8d81ff23c4006e0bd7cc0177b0d82c9f882b29e7ea7b6e3561b09571c7e932341e1f39ff867ce6bf577a18595dcb1d96549c9bdb4f459d35749> [ 118 0 R /XYZ 62.692913 514.24685 0 ] <12c1d8554e33df16e6068657044cbdad7fcc3a683c1bebcb774449271bb6af29d4c0a849a2bd8bee4c426f857ebdf3a4c175db204838fb1a48d6f42fa8c9d230> [ 350 0 R /XYZ 62.692913 452.51685 0 ] [ 879 0 R /XYZ 62.692913 174.15685 0 ] <74739a536299a3e371d70685af77afe1bbc8d714f013657f1e8cc4b0692fe1bc283d8867557b84de4b37804c827cab7e543cfb2a2b86bcb53af38c1cb1f88f02> [ 414 0 R /XYZ 62.692913 423.41685 0 ] [ 562 0 R /XYZ 62.692913 505.11685 0 ] [ 700 0 R /XYZ 62.692913 785.19685 0 ] <05da52bfb90dafd7006693d237cfd08ad50593c58e6b186c2f70925cb1d43d47699106c0b9dce75c14c51c33f89c31219579335f0676ead132461ca89962d683> [ 851 0 R /XYZ 62.692913 638.99685 0 ] <640cd6f3ed34c8c092287710a4eb05a16640d838e9acf00a2cfe33d4d9ab674b041d9e6fdf030723013f7ef44c541eb09d27b32bd02224d191737ba998b9c18e> [ 604 0 R /XYZ 62.692913 621.26685 0 ] <79404c313282b0df19543b4286b6670d8fa38bd47880ddb8e730dc02b10972b926b0be4b8c741d8e918460af8e5bb3a70a865214773f6331b43028ba44a0d083ac898718ec47fa118b267609785c721e> [ 394 0 R /XYZ 62.692913 465.51685 0 ] <94a6b69622b73ba6e2f75ff971b07876f27b541371f5f3364854e91600eb47f27b00e8e30268c282837616b436b7cb56eb5e1beb6495e6e1cf5df5ed968e3d95> [ 788 0 R /XYZ 62.692913 371.01685 0 ] [ 278 0 R /XYZ 62.692913 325.13685 0 ] <726b2d19aec524a647f67ed9380670cb0ac883ad911af163386bf2b975bec88e8f3d8965ab046ecdc2ec0ad6c5a99474a8d245b3e8895813303651cc7128bdf9> [ 422 0 R /XYZ 62.692913 540.31685 0 ] <1e0939f967d52f8fd0c2ecac5e3b9ed1f168db4452e663645d2b4de95f24d53865afd6d1ba9c069a646ec824849dac689f761476617f797ab23ba1631291513a> [ 582 0 R /XYZ 62.692913 761.94685 0 ] <5d495a8594ee3bde93a2399a8b979b695feb80129e5eec518c939f5d9d1531f0feb3c04e6073e2c79560f5cff4f5f943294f41b4d34973a205e5afc36257ff24c01fa19a493502b5ffa03964893bf7d2> [ 766 0 R /XYZ 62.692913 458.16685 0 ] [ 405 0 R /XYZ 62.692913 603.66685 0 ] [ 710 0 R /XYZ 62.692913 159.65685 0 ] <757798ef1b27f78c61421466dd62b87a00dda4bd1cd149f9c472e28344c9393cb90ebe41c5713b7c5c130894e2c44d56e26fa123fb922d8799ee07a67d644eab> [ 801 0 R /XYZ 62.692913 479.16685 0 ] <3d27b5242523acfda0568054fafd31fc23c3fcc6cbf6aa820d6dab546cfaa5c720508508c3859c62830bc50b6cb79316d16bdd53668c60a106de49bd3e2cb5bd> [ 682 0 R /XYZ 62.692913 297.98685 0 ] [ 655 0 R /XYZ 62.692913 164.26685 0 ] <43ecd9d363e8b3d234b1f0e872143bf93e73fcffa817a0dd1048114a93372387b2471b52ee7cbc3c4d5733c8ee0b7bc7cdb5ce74880cbfe97f9bf1361813c290> [ 289 0 R /XYZ 62.692913 398.88685 0 ] <45a15a31b403f8b4b435fea574c2c59b432ae04f71f5fec4189f07dcd07c08c42c62b9b16b30a6794426428015c3d726aa069804a4870612a94b9fa65d1d741f> [ 243 0 R /XYZ 62.692913 498.66685 0 ] <1bb79c581668f8c5688df93275e6789c78d31343f0f140c9cc7e4564635a3ef7d37478309829beb675f472063b4d6c4f8caa135f884c3d8fa0e874b3c8d634d018a66e4a1b23ec7929af1382ef65ad0d> [ 314 0 R /XYZ 62.692913 432.56685 0 ] <00c07b52fed7a915ad37caa5523760b87d0e6d6742cbc83d40dd7cc4d112e713802f70e17427fa5fca659dcdd3db76f4acabe5df810bf71a4abe4e69102439b7af914a0d476878bc799286ac3d8bcd4b> [ 917 0 R /XYZ 62.692913 704.24685 0 ] <691fc448e28e9ff426f04925eae457416dc7e61a0e6de58edf68ff5007af5a36680e725f8d1f2dcc3cee92defc51ba3b6a373f8d3eb141d1eff04358a25279e7> [ 614 0 R /XYZ 62.692913 315.58685 0 ] [ 745 0 R /XYZ 62.692913 422.13685 0 ] [ 626 0 R /XYZ 62.692913 248.78685 0 ] [ 810 0 R /XYZ 62.692913 304.53685 0 ] <970b3067557e407e17920504b78b26ead833aa4ddbddd8d7a3e64547e5bc4bd67a49c7fadb63e845e0fa79e7c51e287ff068f502466a393bf02c37be2d96c574> [ 571 0 R /XYZ 62.692913 537.21685 0 ] [ 723 0 R /XYZ 62.692913 207.00685 0 ] [ 259 0 R /XYZ 62.692913 704.24685 0 ] <422a4652d8b9759ef32e10c411a081ec9dfec34f7251bdd69ca922321e0eb156f35a216dd034f6c1e79aa28bb36993c277d7492438c10e4b825c179db8e6bd9f> [ 553 0 R /XYZ 62.692913 649.99685 0 ] [ 513 0 R /XYZ 62.692913 560.46685 0 ] <595fed949b6c9d214693f660130f1609fa88c9556e01b892a4d3018e8ff673245d8c5473123ee36bf380d979bfc23bc5c1fd234d29dbcd73e9279faa92dc1fd2> [ 756 0 R /XYZ 62.692913 761.94685 0 ] <16159986af1b964558773897d9a517a7b4410655498cd848b95aa16d55caf783f8b1f08c9f58ca2484725bd873aeb506b60334e100a3e223f4bc3156783b0f9a> [ 302 0 R /XYZ 62.692913 205.40685 0 ] [ 700 0 R /XYZ 62.692913 595.66685 0 ] [ 243 0 R /XYZ 62.692913 299.53685 0 ] [ 405 0 R /XYZ 62.692913 422.13685 0 ] <8c9bf62d1599bca4b85aeaa3d3be97e55d2214a751f5f6d82d9bcf7670b12719ed573d0377d56d54534eb9d50e878d66333dce65cdb6f11041d7cc3086283ff0> [ 289 0 R /XYZ 62.692913 217.35685 0 ] [ 724 0 R /XYZ 62.692913 686.64685 0 ] <971c8f3df866c713d2f9fde0e8612687ca000a9d10252a217c08b878e53fb7cc069e9b96107bb925503b15820acb59606e785f773f1fea5d5081523a55305fab> [ 778 0 R /XYZ 62.692913 468.46685 0 ] [ 604 0 R /XYZ 62.692913 439.73685 0 ] [ 615 0 R /XYZ 62.692913 785.19685 0 ] <3f15867f0b0ca0142f45495b1f225fd4f8cd00f97bd9942b051859ed458d5efc45658d74c9d72b60b34d79f995b966c41428724c3d51af59ea487ff7258047e5> [ 267 0 R /XYZ 62.692913 299.53685 0 ] <42cd463e5f4da0654129c3f4f205122451d30f4f25dcb904af7dfc35bc88c3786779502dfbc63a2ae8281a00552e98b712a0d3b71ddcf755cb627b11ea261562> [ 766 0 R /XYZ 62.692913 268.63685 0 ] [ 627 0 R /XYZ 62.692913 721.84685 0 ] [ 534 0 R /XYZ 62.692913 149.11685 0 ] [ 314 0 R /XYZ 62.692913 225.43685 0 ] [ 329 0 R /XYZ 62.692913 330.18685 0 ] <9a9228ef2f74b050c7ae1e97e9612ec597b3998e995757f5197d46a22555a0f21348c5f99261987938d66b36294ac41dc3cbd323fce1411d4c9b8530dda88bb78dc1dde2167d1e3dbd8ca9cb16bc1dc4> [ 789 0 R /XYZ 62.692913 761.94685 0 ] [ 259 0 R /XYZ 62.692913 551.31685 0 ] <0b2ba61c84c22687b23831671f73b95cbf252a4391845da14ff9b328ce181918caf02179d37ca86a8a756aa06461548f2a80fd44c47a874e5ab57853557e87425165c872f1ad145df76e8ea4dcb11f92> [ 835 0 R /XYZ 62.692913 785.19685 0 ] [ 756 0 R /XYZ 62.692913 580.41685 0 ] <1aea1d476e6bd488f6345026fa94b6beaaa70cc896d391409c49f629de393c43ecee302995e8ddebbb7462b153c04b290c27ae532f4281216e99c9eaf3b6a509> [ 582 0 R /XYZ 62.692913 598.01685 0 ] <95a5c91ad4d6571698017a0e3320bd2710f148fc75a1b1ba97e1855572e123e574df174f5a8d44e4a967072025061de1> [ 811 0 R /XYZ 62.692913 696.19685 0 ] <63c11e29c09c49253f906830648e367d45496ece5727e95afd7e74b6d149adac87d71a52a905fbf2094f4e9a2f1ca9712b392e40d902c3a55116da3ed690c81d> [ 350 0 R /XYZ 62.692913 270.98685 0 ] <23f45b4b4b656af57ed36d90c8cd84f96c00583260473ce105549a341cbc27c1fce384ed795993477dc2e5c64041be51e80fa64d62f149ebaaf9e80be40e953b4cf88bb611e34bc418f987bd0da736b8> [ 880 0 R /XYZ 62.692913 704.24685 0 ] [ 394 0 R /XYZ 62.692913 144.88685 0 ] [ 422 0 R /XYZ 62.692913 376.38685 0 ] [ 484 0 R /XYZ 62.692913 580.41685 0 ] <12ea7a56ecb0abf008a5dcbf694845301368013ac203c5eff5737c1a9cac3b04c348531e8bf9144236e7df203decb4dafc1b33de9a694eb7dbdc92af6bbabf18> [ 498 0 R /XYZ 62.692913 308.63685 0 ] [ 119 0 R /XYZ 62.692913 785.19685 0 ] <629773371a8b9296877d7fd7eb866180f7fd662a12ecd8fece5ac809e25cd25b62df47fbbb700af5713d708f99312538de343632bb3ddb4db8b4219b3e8fcf2ce6d22713a9f992bee1db93915ae07c07> [ 562 0 R /XYZ 62.692913 323.58685 0 ] <793367c12d282354b55f218217405458a82c7ce9b140d42521fdf41cadb699c4064da7f5dafd65c5173627757cee4d08d57382cd826f3082bfd3c1bebe259c120eb87875f51d76577fbbce0a7dced9e8> [ 593 0 R /XYZ 62.692913 578.06685 0 ] [ 711 0 R /XYZ 62.692913 693.24685 0 ] <39db6b870197f98ccd3f06a813c93eb9508ed1cad5b3895195353d8ad54e24520d09d8d5e19ef0d13847ecc1d7d68fd442d150bfe8f525ac55ba9e746ec22f04> [ 801 0 R /XYZ 62.692913 130.88685 0 ] <623bccb8379173a0f2cd4e912aacac83f1d755a742a6c16d76d82808bded3cc2d9d2e349bcd7919400bb86c522de79585d5c1165bf8fed9316d1f2ac0209cab8> [ 851 0 R /XYZ 62.692913 343.96685 0 ] <6a34189268afe304ca1530211345ba982e704f24077d25161d1b95eea535d4b6468287060567b861583708824e9bc56ab0ec11b909a53d8224bc4563d39d5991> [ 683 0 R /XYZ 62.692913 652.44685 0 ] <23f5e823b768517d289debd6f55901b4ba06774940f50c0f32549a30dab55c40231056a6f9b5115bf67724995a5d5966723bf33549c52d6bdfc17c6349e08cf0> [ 339 0 R /XYZ 62.692913 422.13685 0 ] <8c66dcbd04880b794e9a16deee695ca484aa249f82b0719926076af6b69edfa7435a3942b20d6a8233d31e477a61a4a491b9c52988896a8cc0dccb9cf0c07524a3e21e47d4303528520d9655806ce353> [ 513 0 R /XYZ 62.692913 361.33685 0 ] [ 745 0 R /XYZ 62.692913 258.20685 0 ] <0150dab81b6650bed74364bd223eacbc63f0aacbf7c318c31c8d20ad4a9ba170f31288130f180043a88bf50087713b5de83f193d4967b46d9f3557f721968c2c> [ 278 0 R /XYZ 62.692913 126.00685 0 ] <799d3504e42bc5a65ef9730f27f25a4bc40d4dd5f898de9a52b1c77652b5d0eddcf69f757206953f5b9c395c5820d5a11829f60def020ad61097b01234da2bda> [ 414 0 R /XYZ 62.692913 259.48685 0 ] <0581538f9dcda3515e87112712da10c76a7e2a6076dde4ba533faec4dfd815a04ba415aaae74e6b36973d531089e0b7ebe7f3bfc5efa5f973effc02e7b1cb1de> [ 303 0 R /XYZ 62.692913 704.24685 0 ] <43fa15b30a354e83b96cd1340e7e26c04ac1f5438a41b280f5c5487defa783d5f24994db91ee62b630fa063ef225f6d9edbcb90a8abbe253922b8cd0b36817fd> [ 733 0 R /XYZ 62.692913 378.93685 0 ] <38fc271546f253c3e9ff264d3f26e6cdaadcf5ea160c60f16a2ed8ffc80ba77011c120f981686497bace9af687dc351b7db55f3337f6d18cacc70ee33e43708e> [ 571 0 R /XYZ 62.692913 227.78685 0 ] <19d7cba4bcb703c9d67c0d7e627b057f415654b1a300546a08cee08127247594c30b34219ebdd9fa70c3b1fc3c59906bd67c72854492dcf0862ddadf0ca63c0a> [ 604 0 R /XYZ 62.692913 240.60685 0 ] [ 259 0 R /XYZ 62.692913 352.18685 0 ] [ 329 0 R /XYZ 62.692913 148.65685 0 ] <1756a32fd810259cad1afa680914a8cd83b02fa1faeacca512309f0d3d15294cc66d6bd3a2e8b82a475ffab145a9a26af110a3a3d2e715ced41eadca172f64e5> [ 734 0 R /XYZ 62.692913 785.19685 0 ] <99015fa3923e2c1e0118c35324ef0d85856fe442c731c7af084ea7b88e6101d6dac020f73ef574707f4f2146b95da4a19c55fde5f4db66b0daaca46eb8cb79660785ff5f2e38eb58c01412e36bfc525b> [ 513 0 R /XYZ 62.692913 171.80685 0 ] <7c16cbabbc9d763c670219a16318a9bc72a82fb0276cdd610e8e056ada9a7afacb97c9a7cf83188cdd898b20406dc480cf9258d6f34ebde7868f781d2f7f2368> [ 535 0 R /XYZ 62.692913 686.64685 0 ] <0e17597113ef5e489f5fc42a1b9e2dd5a127dd26cd44d9ecba4672421e07af1690fde4c0d61d459961bcf556eefd9519> [ 339 0 R /XYZ 62.692913 258.20685 0 ] <85b983acf74677d326637fcd2de9419446ca437cd7e01a720807b7f7f330b331ea89cf1eee730b3fff84a6973bb509669571deaf2d30ee84f45245c757e1394b> [ 615 0 R /XYZ 62.692913 578.06685 0 ] [ 582 0 R /XYZ 62.692913 434.08685 0 ] [ 119 0 R /XYZ 62.692913 250.79685 0 ] <5bb2fe359d8d217b339c0c4a5b43a5e2a2043ca02ab369eb51c846fb3463123dbc08363865246922f522d41037fa0234381953e1a0afca135a9bd90ab3e51ec7> [ 268 0 R /XYZ 62.692913 785.19685 0 ] <9ce45330a12f6a80e92b9f0f3e694cd7c779e80ecdf939d31aa3236825abe679f9ca96d11768ea894957980cc1ee23dc> [ 778 0 R /XYZ 62.692913 173.43685 0 ] <1f911a7d6aa2e9e324131619186d711d772ef5b92422d562407a824494c5143e48f8b255b12fc94ccabe62f745a7bb1a17cc62c71e3f7663fb2f8500148796c1> [ 835 0 R /XYZ 62.692913 632.26685 0 ] <4dfaee87aa348ac28d1a44902cb7e61402c148c9b110a1ee971f8eadc181f8f4d43028fe09003765bf79c13a6e155272> [ 756 0 R /XYZ 62.692913 416.48685 0 ] [ 627 0 R /XYZ 62.692913 557.91685 0 ] <15b75dc497d877ee2c4b9dfcefe8b90d03601f5976b0926716c297fcac02f42bc9000e9d170fce75027e52f396682db7> [ 852 0 R /XYZ 62.692913 623.69685 0 ] <26d34e8799cde31832fe598eca27758bc59ad4bfffc9499866fad7138cd66b52b5031170840be8e01d985ea438ef66a62c6fea7fcd481c11ae46982f9ea3e321> [ 405 0 R /XYZ 62.692913 240.60685 0 ] <7f93cca3a3bfa5294530c4f0654fe04587ed06adbc7c12a8a1154d9ed5b4027f5d3885814900c6edf45f1692c7e0e218c0c9ae8bbd98962b865cdd47c52adf38> [ 395 0 R /XYZ 62.692913 693.24685 0 ] [ 789 0 R /XYZ 62.692913 537.21685 0 ] [ 422 0 R /XYZ 62.692913 194.85685 0 ] [ 700 0 R /XYZ 62.692913 406.13685 0 ] [ 499 0 R /XYZ 62.692913 785.19685 0 ] <4931865e790dbc50cc0ce6736789736bedde2db4dcfa4edcc573522e86a876ae8315996bed657e55143361c66189cede917df2a1e8c2ce09623243e250694937> [ 484 0 R /XYZ 62.692913 398.88685 0 ] <7beb44f66c291098ef6488bacea3427cb3152025a735a12e8aef0102a7051b6651ed4ec9f82a83aa99de9cb15c91525497d3174cc757585acfe9d443dc6944ff> [ 243 0 R /XYZ 62.692913 118.00685 0 ] <6291e019f1f2b311bacc2328b335f9b8ae45a20ca1c88dca3955c6ff01344eb31b28979fea9e2e82678b20bb3e29000c8034fa17da97541a5c7012c22531405368c5e5a1c930b5b25eda43d1807402f7> [ 572 0 R /XYZ 62.692913 608.34685 0 ] [ 563 0 R /XYZ 62.692913 785.19685 0 ] <0534bb21f943bd33eedf748c2bc794a4c5799bb9727d28470aab1408cf91eabf8b2ec34205be29fd4281c7e937b81829fe6bae3beadfc0de786afb463867c3c0> [ 315 0 R /XYZ 62.692913 785.19685 0 ] <71af50e0bff59b16b49342f61e3df75fe4d4c1153b08fd528d201c06ab9803eb5f3142201470fc0346b997c3e4a6d9733e230ff2a3ece6932dbc977b7f235e76> [ 303 0 R /XYZ 62.692913 540.31685 0 ] <88b63d52a0f5977b5166f8168ae0b09ea6d92f85f0f7a7274073782e585af3c57b2ef6dcb0c906dd00e3780e581008259e9efdc5e6f442c834d0d058862571d6> [ 683 0 R /XYZ 62.692913 246.66685 0 ] [ 290 0 R /XYZ 62.692913 785.19685 0 ] <325cd4767962d544f5a4b3137b065e95eb28e58f40d058b044e970c3d1be7be2bf379266c2aaf053471792644c0dad7f4ea566d490e19d3fd6cf48aafb1b240c> [ 802 0 R /XYZ 62.692913 508.89685 0 ] [ 880 0 R /XYZ 62.692913 479.51685 0 ] <34246d93056bea99b5632af535e1084b26ed1fa6fab5d7d6bb67a674a4a13eb722d56d3c1da71479cf76a66c01af9e1a> [ 746 0 R /XYZ 62.692913 785.19685 0 ] <9596bb58e8451fb4a008975d911effadf5f791561b3463acda152cf09dc36e8be451f3f4431ceaf258ae0a72d1b2fff29299c1f25829902488339b266a0da51e> [ 767 0 R /XYZ 62.692913 704.24685 0 ] <4244a831a3150b4463d3aa4d24776e45e15c90fa753023343e58d89d6e8305da0473762740b24332cf2e0d6a0a61ed40> [ 279 0 R /XYZ 62.692913 675.64685 0 ] [ 415 0 R /XYZ 62.692913 785.19685 0 ] <0de0e7e3e6fdb381e7acf326b3953918193d4b95ad01ebe6e6c2469b02a0bbd16069e8c55e817eadd9209103f5d6c57bb7b20692e8846a6f1cea6ee57153968e> [ 593 0 R /XYZ 62.692913 414.13685 0 ] <8602479cb4ad694b1e6ad71c673d0e067f8888fdf50ffd60cc2015a087aacc51709ed3f2f52918e7fa1058ac9037a64d0da4ad0c7cc9f9b04c88cefe6903db7d> [ 711 0 R /XYZ 62.692913 529.31685 0 ] [ 351 0 R /XYZ 62.692913 696.19685 0 ] <7928da763061dd06b238d7c3f43bac1f3ec1ee493632980d5caed53b2e55da98d3dc31947ad20a681a4c364c1d78ab810ac644ff0e013bfaaa732f661d071684> [ 724 0 R /XYZ 62.692913 461.91685 0 ] <0e25ff4b9a69e250eeb19e465e071391199344885d4b509e6c20e57f9929ccbb22a9e85f5540d6036ec0d3e29aafbe41414d550aa23768e63963c8c0db4b7de5cff27200e7139421ba554a24705c298e> [ 811 0 R /XYZ 62.692913 326.16685 0 ] <1083d2daab932c861762a65acd36f672e8bc7fd40f0f88fb3a3fe926ab5221120d690899e633fc6b13fb9cc4c58ab93e4c90e254afdec4b033df7a211d72b78f> [ 563 0 R /XYZ 62.692913 586.06685 0 ] <592b3abd9f4fbb834a49ad7b34acf846682e40ed6f9efcf64943739f902a7cbd7dddd709677168260e1d2630b60847e92039668a845e08af190fac0c650883a8f3509c88c374246e6b03ed15ef0ae779> [ 572 0 R /XYZ 62.692913 401.21685 0 ] <0148f87b5f6c654fb461eedebf3c6851d0e285d917e0b6a0e5f964708843e5d9ff1be45c18713acfd442d7904ea72fa721e195c01ce8058310756470cad41847e2bae4c5df1571059ce74e504637f0cb> [ 789 0 R /XYZ 62.692913 215.18685 0 ] [ 615 0 R /XYZ 62.692913 268.63685 0 ] <9659269bea3c0025d9bb0837d4c758dd5449efbbe91e8e2699a7c13ce4b06413ac844bcfe3fd85359732d5be2c0bfb768622b90bd645dcb62a6e9152b8951eab> [ 582 0 R /XYZ 62.692913 124.65685 0 ] [ 605 0 R /XYZ 62.692913 785.19685 0 ] <7c9885d774b44c9b108cf444d1b444285837a01878aedf939482a2962747694bed48427f5c51947cf338006645f708edacd1286ed3a29a0ba088b0c19ae8aee1> [ 303 0 R /XYZ 62.692913 341.18685 0 ] [ 746 0 R /XYZ 62.692913 621.26685 0 ] [ 499 0 R /XYZ 62.692913 578.06685 0 ] <96bbc2317742166c18d831dd0dc1586546e834b7152d03e8c9b70f414d9794d0357ee36851fcadbcdb9e233cea62b665d26c119f14ab4439b2f57b2fa6e106a0> [ 852 0 R /XYZ 62.692913 362.26685 0 ] <75e70dea2dd3d8bce074f9702130f2fd614f6087c26e3a021218c181f2692bb8e0a22b963de8d795a9ceab97e3585f42> [ 290 0 R /XYZ 62.692913 436.91685 0 ] [ 767 0 R /XYZ 62.692913 522.71685 0 ] <273c20ffb9c6650df6dc54c456727f2ff2c625c1a0c4d3fbaf1a3c5e82bc4c9023b7af81deb018c39dc91030816693d28cfa9f77006f5929daef040cfb41c9caabce9b150d588754193f8260cc4de702> [ 244 0 R /XYZ 62.692913 675.64685 0 ] [ 880 0 R /XYZ 62.692913 186.08685 0 ] [ 120 0 R /XYZ 62.692913 594.24685 0 ] <156b058cabf42452d6b5c34be846bc50721560c5dfb3829fa2525b9898884825e0848f1bff481de89945a35577aece5103a32de9d93795af76da11077e871116> [ 423 0 R /XYZ 62.692913 761.94685 0 ] [ 484 0 R /XYZ 62.692913 217.35685 0 ] [ 700 0 R /XYZ 62.692913 224.60685 0 ] <01315c96025240911272495f894e98458b195654f38c5512bd7657f635eb7b542bc5bf4eeb549a6ae25afa114d320d11bae1867bf45a11b9ee8229b4ab1e4977> [ 351 0 R /XYZ 62.692913 418.76685 0 ] <852d11319888e8249bd620b502c92cdf1d3218ed37e21ea493e42fdcaa4de28c26e4442ef73989f6ba7db26ee3de00869d782096527ffe69e607fc73446ae2151a9542ebcab0f44cbae5701af6b03089> [ 627 0 R /XYZ 62.692913 376.38685 0 ] <72ce6a2fbae6d59ca214f9e43ff182090ef5ec3f35f73e71017c57eaf07a2b61da6ce442a00900a983784d6b625389e8ffb252b2e5b4c714b243059e64a8e120309116c30e14fc6a6393baac37d11d60> [ 268 0 R /XYZ 62.692913 603.66685 0 ] <809c3cf5fb82a74c7f31ce79358f46c30b15f1b75a69febe6e27de7e3851d2ef64d56a1d70c7d1adf7927fece56fbd71c425f016b753388235354fc0033e97f4> [ 756 0 R /XYZ 62.692913 234.95685 0 ] <514280d9245ae4c4ccb2d617b609f4f78e5478e9b9cee79f27ea8609dcf8724f7951d09a7c8eae7aedf14052c3fa1b2dac3ca7ca0c58095a362218d9e9d35f848f4642cb2ce95fd97e8d305298371e95> [ 812 0 R /XYZ 62.692913 785.19685 0 ] <5f51e4efa45b9f19b1c81c8866294324355c90416258a56fc20ca5e91fa377fd80b6e873aa6f6f9e18201edfb0a08098239e70a87a63ffb588dc6bd9fe195e8640f41d51c3b225768167a0cceb34b1ef> [ 779 0 R /XYZ 62.692913 686.64685 0 ] [ 395 0 R /XYZ 62.692913 529.31685 0 ] <62e312653d3b7e7eca1df27233c0eab661ac2b5da40b49f31161e6b8a42f53134958235ca8f4c69a61231cbdff79e21268f6c8a610991e6e34abb3e4064d36e2> [ 315 0 R /XYZ 62.692913 621.26685 0 ] [ 734 0 R /XYZ 62.692913 506.16685 0 ] [ 593 0 R /XYZ 62.692913 215.00685 0 ] <6c1e080b8bfc3af27363918e61c971293ef358f9924477af93647918e3393b0e2005f9e1942fb401f2b75c6bdfdc14117cb70e76fee01dc25f60c3f3484ce82c9cc9b438e086fa989b96359f01089c7070c39b7b90e34e407ef6ca4871611cb4> [ 514 0 R /XYZ 62.692913 658.04685 0 ] [ 406 0 R /XYZ 62.692913 785.19685 0 ] [ 415 0 R /XYZ 62.692913 603.66685 0 ] <2aebff561cf78801a82b5e87ac87ab0f8e074303149410f6e031c3245d038412a62e03ac3acdc1724404a3f40dce35ad53510445abf275af8a432f5216f63434> [ 711 0 R /XYZ 62.692913 365.38685 0 ] <40b06c0a3014f9866cfde039a19e8434fe97ef3899d39ff8d7de35b3b5c80ede9588664d0451d55cd8298c2a78708f10> [ 279 0 R /XYZ 62.692913 476.51685 0 ] [ 724 0 R /XYZ 62.692913 280.38685 0 ] <9bb0f23c9084fd7a35577089ea2a134d9d259dfb1f5ea0c774c17e1952f9d9ccc7ee034dc45fb465708ccb7f9f38fff1278aa8ae33d470349b85ddcdcd9a13c6> [ 340 0 R /XYZ 62.692913 621.39685 0 ] [ 734 0 R /XYZ 62.692913 324.63685 0 ] [ 572 0 R /XYZ 62.692913 176.48685 0 ] <7058235db0a8537d20f06ba7e97ffe71d982327b5dc6cd914afdaa4c28a1255b5f2b28c37be1db4574d5e2c5c5b64b610457ab2000757ec024b50507e245bd97> [ 290 0 R /XYZ 62.692913 255.38685 0 ] <1527553fbfe312e31148128887e065c80a6c90b1370e7ef71e2ce652904c0ba8dcbf1932b7528e54a474286b5f011c5490720efe75a8a4ea659a827d6e64752971b9e59313b319137b9314a076a8d6c5> [ 712 0 R /XYZ 62.692913 785.19685 0 ] [ 395 0 R /XYZ 62.692913 152.28685 0 ] <36c42504951fbf76d6093f83886f0630fe63549d1235a9a343932765add7bf7d41b08208e8d1d029a400d6efccd9d98f0951132ab3ab6bc2dcc6b431a03ea72187d2281f89eb92488d3bc824419de330> [ 514 0 R /XYZ 62.692913 450.91685 0 ] [ 351 0 R /XYZ 62.692913 254.83685 0 ] <7f72a000bcae1c69bdb34c27b5f686cdb2aff0fcfb39aaae121f24e374339f81f22f7d8b38f6640c40fad5d214c368a3> [ 746 0 R /XYZ 62.692913 439.73685 0 ] [ 767 0 R /XYZ 62.692913 358.78685 0 ] [ 340 0 R /XYZ 62.692913 422.26685 0 ] <79cb2d84d3473592794dcddcf24145eb16612b736e14e5b0c0858a64adfebfff165def9bd840275c6e5658d06241d7fbc6dc7c2496acf61145aefe90212a2d7b> [ 627 0 R /XYZ 62.692913 212.45685 0 ] <88aeeb7c98b3206eaab80b732d61c1dcb4fea993b1bf684c13197fc9df5ace95f7ae0b4cb5f9ac107236d02cabcb1422> [ 423 0 R /XYZ 62.692913 455.91685 0 ] <8f624519213f3c78fe8aa6201c26f4c0a586608b00bacf0ea899a8e69c575ef6d0d0356281c07c244a086e0171770bf7a4dd9fe4dc71cdf0d9ccc69283b59112> [ 485 0 R /XYZ 62.692913 785.19685 0 ] <23d62f588a205123051a5f283b34153b3896d7f2495f328fcd0f7bfa794492b282cd57735388ccde2abcb97c385d432b22abb96ecb7e55aa1d3ef6e48a706a87d1e1f1892172dcce651ff17a0de0fa96> [ 701 0 R /XYZ 62.692913 785.19685 0 ] <1542ba9c18c410b2320fbc680e52d1d5e9a351e42fdf854c5579add197d6b116c4c701f4e98a0eb068505a061f430dda2a7efcc085ad477da376b21d3538e19f> [ 406 0 R /XYZ 62.692913 479.16685 0 ] <024bc12852485f1fec807ae4b8d2161faac3fedc3d7d10deb05d067eb698134b97f66a7217997dada079e6d21968866d7a3d418559daa32391205cfb76140e7bedef5ef571b5c912c172aabe4f5ed3c6> [ 303 0 R /XYZ 62.692913 159.65685 0 ] <3a215814fe85b4f6f4dbd1571b4dc1f73b0258c69f38fce80926a6bf08df3a49125b3161db59b18b0d84f816bf4db732dcc059151827def13fad1c6dde41e449> [ 279 0 R /XYZ 62.692913 312.58685 0 ] [ 881 0 R /XYZ 62.692913 704.24685 0 ] [ 594 0 R /XYZ 62.692913 721.84685 0 ] <601ddc99c164a25e334859008e42531d6b28a3727bd8c70657f7ab93c2a79c8aa270d08ed6ca68b5ed67d65125a693ad7b4da859e0ecb85f2248fe97db3fb60a> [ 120 0 R /XYZ 62.692913 165.64685 0 ] [ 812 0 R /XYZ 62.692913 382.56685 0 ] <1b076b8537ee6986c5054df48429ad487f8bcd6f1ed27f5a98f78cf01f4782accda1156b668ae3adae35b20b237838e9a6d0d7930c883c4c281a0ec227c4ffcf> [ 757 0 R /XYZ 62.692913 594.94685 0 ] <0c2f29b062e389d606a9e7b22579ddb103c3a07d4a6870d8a38a23f6032de94351bb290639999c4ec0ad08e3b1e179e68adb1efb565004e494b7757630d9edc8> [ 790 0 R /XYZ 62.692913 624.34685 0 ] <3d22cce80b24b16c93306a68bcf5bd188c8d5f4602110e68ade5f233a2b17622ea6637abd158de29f67c347562a0d535a3980f88666a9421c7c833f2687bc2264bf1ed4875d69a839f502486c5493bcb> [ 779 0 R /XYZ 62.692913 226.51685 0 ] <1b40710a82b618757713e1ad2da29d2245cc039939067948cf24b8695535feefe5de7991c09318b71d6a9801519e007d91cee5d5ff9d5f0d7d872b9966be7ecf> [ 315 0 R /XYZ 62.692913 422.13685 0 ] <993e830e5b32c7f16c267045644947ef37ed00ec562c38d8aa3830e9ed95871646293ac3cd70ac0bc974504a4f06677a4497ceace42d9a78fb13d03b53870b2e> [ 415 0 R /XYZ 62.692913 310.23685 0 ] [ 853 0 R /XYZ 62.692913 785.19685 0 ] [ 244 0 R /XYZ 62.692913 494.11685 0 ] <4968d14fed91872ea11f3dd71f70ca1c32c5cf9e60ec8f9d4d09e768ac710ae1d6c217b7dcbe3fd3bd08f645dc175066ac3606e3dd4f62ae921f5cb90c3587db> [ 605 0 R /XYZ 62.692913 475.76685 0 ] [ 616 0 R /XYZ 62.692913 785.19685 0 ] [ 583 0 R /XYZ 62.692913 693.24685 0 ] <9675d1739a1def1f37dc90c091c1fbab668fd4a30dfc7bf9541c0c79461d26626e7f6136760ed5de890db5b205bac1f7b6ba80e041873a837ae99384fac65af5> [ 499 0 R /XYZ 62.692913 353.33685 0 ] [ 725 0 R /XYZ 62.692913 785.19685 0 ] <5726f201a06b1e0e78f1e376b51fe5db4d8de6c3263ef3b138115540547f7eea0626d53698fe89f39093739e72554b385c0663110a9bf3488cafa80219056457> [ 620 0 R /XYZ 62.692913 785.19685 0 ] <298ec2174b1b308ad340123877b1baef000bba04c4906792887f6f44f500ed94aa2d0575699882c4471eb744d2330a02b55c349290a5a7ed8a4126929de927eb> [ 478 0 R /XYZ 62.692913 785.19685 0 ] <7c47fcb0dc18c36a3879155f14867a44cc6d0a32a56db7ce17d746cc88044ff6691ebc3f8d6a18497ca29392210cdb8b7772646d0dd772a9b4dfdc09bb47c923> [ 399 0 R /XYZ 62.692913 785.19685 0 ] <556fa0f25dbf36290d5c9b9580e3ff86d2fb19849351d058568c06145e4ff1b85322df95f6e35de7154bbdd91cd1d58c1d86173357db4b007c1908c0c3a6bfbf> [ 822 0 R /XYZ 62.692913 785.19685 0 ] <6d00ebaf578c11a14207a4fa6dcfe4a1c6797e81b1018781882ef284b4a710775027663d3b7a01c45475e22186d91e6f21d91a67229e75dcefe98b8aae497de2930e7a126c1c987762d4ffeb89e72421> [ 426 0 R /XYZ 62.692913 658.04685 0 ] <861c6bc25c639cc9f95d69d544241d36caec16f3b62298d46e7b446b27f2902f00b3ada81ce706be1aa048911d6f33db53529342838887bea2a1d0ee9c64cf56> [ 694 0 R /XYZ 62.692913 785.19685 0 ] [ 892 0 R /XYZ 62.692913 761.94685 0 ] [ 686 0 R /XYZ 62.692913 785.19685 0 ] <5777d0d1de5dfbb7cd574db8d0723097a8f17553f2e67e0b8f68dda9e85e641d013a5faf7e74e2ed24d31e3d31eed54997d7adf2058eed3ff71dccbdff929e22> [ 430 0 R /XYZ 62.692913 761.94685 0 ] <4960380b430e17938dd558660f85517b59b36e2bd3158db4dc4c12164e9537625215c2a777b71af9131c9ab0c981d18a97821964ee5015732b2328b38c848fcb> [ 749 0 R /XYZ 62.692913 580.41685 0 ] [ 727 0 R /XYZ 62.692913 785.19685 0 ] [ 793 0 R /XYZ 62.692913 580.41685 0 ] <3580b7ad7e19b06a08035e8dd1c97872b1eafcd368375f47f910287aaee115db67ab8fd562ba5173172fe3c5a19b5896> [ 123 0 R /XYZ 62.692913 762.69685 0 ] [ 270 0 R /XYZ 62.692913 474.06685 0 ] [ 472 0 R /XYZ 62.692913 600.19685 0 ] [ 658 0 R /XYZ 62.692913 603.66685 0 ] <063ce6adfc830afb219019622df62620dea66d500a112d3884e7ed701767c6c659c3d6fee37d952d3c94cc1888f7180bba7ac49f407ca99223064ae70babd76a> [ 648 0 R /XYZ 62.692913 560.46685 0 ] <1c43cf450d6afe86d47e7b10641f51f1d173163268afa73a28ff429768812c5e02af8b91b5a89240b8b83fb1dfe62b91fe55fcc95c155c7fdd806694049d8c595ff56f2940a7bbf2080774ba28abc424> [ 982 0 R /XYZ 62.692913 721.84685 0 ] <54cd9826dd4d826583efee9271292c47aa98daa76e984c67904f309c4a8ff8e1d26eec798fbb61eb77367407201f288a35a7159af48ec6278abb4e88c216d3a3> [ 379 0 R /XYZ 62.692913 564.06685 0 ] <8859b29e2b3e74390a4c5bc65702789fc2e2a5cbc558d9841446837dff40af8b6ad5cf36643dd3281668a2cf95ec30e4855baef5258b044eac218484b76d575a> [ 360 0 R /XYZ 62.692913 785.19685 0 ] [ 443 0 R /XYZ 62.692913 542.86685 0 ] [ 943 0 R /XYZ 62.692913 562.14685 0 ] [ 973 0 R /XYZ 62.692913 785.19685 0 ] <37af4420b6e45d0c81d3ce41d2242e5cd465aced1a7456c46ff604e1dec338ddd58a66d8dd6ab0e5bce732ea36ca8df2> [ 565 0 R /XYZ 62.692913 785.19685 0 ] [ 885 0 R /XYZ 62.692913 761.94685 0 ] <9f7af2c9d636b5b41a94c03c63f30cc44b2d68da83d4417bf69f51146f3afa68ae1009dd411940e75cc7623efc3b19fbc8164899cf8f1fc61b9bbe9304d3be1b> [ 246 0 R /XYZ 62.692913 523.76685 0 ] [ 771 0 R /XYZ 62.692913 603.66685 0 ] [ 862 0 R /XYZ 62.692913 464.56685 0 ] [ 950 0 R /XYZ 62.692913 761.94685 0 ] [ 555 0 R /XYZ 62.692913 785.19685 0 ] [ 608 0 R /XYZ 62.692913 761.94685 0 ] <1c0c62093cec8d3524c6f5a0a7a34e10d4754da84d3059fcd082f45b219176c9b4681ab6809a0138f450738843d4a52aa87f51f04a0f522e79c4271a0586a533> [ 306 0 R /XYZ 62.692913 550.86685 0 ] [ 966 0 R /XYZ 62.692913 550.86685 0 ] <62c4e33c93d61424305eeac367f6d57263848f56b84532fc53dff04a0cad9f345b1d2a424cf6b75caf7a8aa28b817635> [ 814 0 R /XYZ 62.692913 785.19685 0 ] [ 760 0 R /XYZ 62.692913 785.19685 0 ] [ 837 0 R /XYZ 62.692913 785.19685 0 ] [ 458 0 R /XYZ 62.692913 480.89685 0 ] <01da9600c20ca58d3dc1b11f17c6757fc5309d0e7fc182b3c95710dce7653ff849cdcd912888ac1b8d5d0e28c47f72e5422ff4be51013902200ca1aceced819f> [ 899 0 R /XYZ 62.692913 761.94685 0 ] [ 782 0 R /XYZ 62.692913 675.64685 0 ] [ 364 0 R /XYZ 62.692913 693.24685 0 ] [ 828 0 R /XYZ 62.692913 651.59685 0 ] <20a883c1d1fcf8a4da5f46f76f334404039490d1b4b9cf05b14609aa05870293ab2dba30afc3c9a5260aaafc12ed4206de606e24801aa81ed0f509ff8168d03d> [ 464 0 R /XYZ 62.692913 761.94685 0 ] <3571cdfec46495602922069838959efbdde90a5b61e272c9cbb936e9f1cf3f9baf34043e7292c1db6134844ae7b19c92bffb79c9807b0dbbed043f93e58b2027> [ 717 0 R /XYZ 62.692913 669.04685 0 ] <78a8a9385c58bde079c359c0b794abb23527f50fb70b7c6d58b37e1704eddb57256d129a6c0ca91ceb849858748755742da72a0a4b2e6b0d23e448694ee7eae66b43a9a8e76cfdc6884498f4f1186f36> [ 911 0 R /XYZ 62.692913 686.64685 0 ] <89517eb5969c8c444817db212dbab4630ce04000ed7228989ee3f34082c3fc3ac251237cc818396a0e4b8e7ff21fa4606dc8fef53a3d98cda82bd065fc0784135ae4bafd3be1a8daec47bafee24d0b53> [ 872 0 R /XYZ 62.692913 390.56685 0 ] <789a02ccda1e952288f1578ffe8500ca094ed6c3da238752f3f9888b7cdb3673c903e2d540ffa9d441191435c78474858492fcaffbff452bcf7a59ec8a22b6ad> [ 491 0 R /XYZ 62.692913 686.64685 0 ] [ 237 0 R /XYZ 62.692913 761.94685 0 ] [ 867 0 R /XYZ 62.692913 572.94685 0 ] <5da94808f393febbec6c77b640f9b19195eaa5a96747aed6c4f26a2780b7cdcaee5eb113a9ae6c2377906b3f220bffea892ec86f7acc374f5208978ffcafc1fc146094f2f914d08cd8121d2b84fb2029> [ 675 0 R /XYZ 62.692913 520.86685 0 ] <480c437118f903fe9f6bba2021d945c570df6fc7e029200e26f5c418c445281468616a9eec71aea364e117017a2d4b0e4d40e36e03951ca6d7d7c18d71a395c7> [ 737 0 R /XYZ 62.692913 445.49685 0 ] <84ec74bdb8737b8146f573d3bbd69e55a7d9aa224dc667cbed78b03a7ea4c2937be4a3d56ae9ee0fdf6aee51e3640d5bd4db4b311e6caf89a682bbbced2b491e> [ 545 0 R /XYZ 62.692913 785.19685 0 ] <40e5e526acde53ed0ad8face4e62b11020892f4d3fa1aaa6bbd575dc98903b242017e4fcf806c598d03646ac77f5c0c5fa376a57fbbb21df4821eb7cd402f349> [ 843 0 R /XYZ 62.692913 675.64685 0 ] [ 934 0 R /XYZ 62.692913 696.19685 0 ] <593ae5d664d5060b2b6103cfc686b78d86cf996e851e90b694fa03496afaeb0df244ebf13cfd948f3ab4431620b2377766ed4c3abf83ba13feb1d66b59a8569a> [ 527 0 R /XYZ 62.692913 686.64685 0 ] [ 435 0 R /XYZ 62.692913 434.36685 0 ] [ 641 0 R /XYZ 62.692913 704.24685 0 ] [ 332 0 R /XYZ 62.692913 693.24685 0 ] <84a4e39a6c3957b3f3c688d884b48b2372619a85d0e86fcc001ec2d906e8681a5bb67d97e1c57b42affb144ffed244e938ae307651bf90f5808321e596f7e2c4249b38f19a3a7889a1af8750e047bacc> [ 385 0 R /XYZ 62.692913 785.19685 0 ] [ 322 0 R /XYZ 62.692913 445.31685 0 ] <30e2a32bdd80af9f78cb64fcc5bf70bd9cd5ecdf5f5cccfbffceb230be2bcb7244ce0acfdfbfa8fa425e0cf72de3b08a474dc6bd169d6634c3ab8a3112595399> [ 667 0 R /XYZ 62.692913 675.64685 0 ] [ 374 0 R /XYZ 62.692913 785.19685 0 ] <1b78bdca53e5f2c98ebaeaf0d3ce7c6e3d660c0560f5c661800773b906a470e33edce95653ee1ef3cf62179ece53211b7ae83c8a8f4372fb5729ccba6f65b3e1e7f0119a0725943e51c0d1514e7cf28d> [ 958 0 R /XYZ 62.692913 658.04685 0 ] [ 369 0 R /XYZ 62.692913 785.19685 0 ] <82fcd0b7e8d0a98d9c4f69025a06b55c63c07b7114e1f08adb0e130dc39f6d7c575ad1ab850054cdb6805fb86673fde774f593e590ae8fc848deafe6698da5a0> [ 927 0 R /XYZ 62.692913 704.24685 0 ] [ 919 0 R /XYZ 62.692913 785.19685 0 ] <55803a4e00ff3118f31a33d14231fe40e08e73fdbb441435e1259c08bdcc9f07a5109bf6bca687cfcaeb8a348bb0363b3a92fbd694cd8824a056b0a2da3a434a> [ 537 0 R /XYZ 62.692913 619.59685 0 ] <322cd617fc94bd4a3bb8e7c7c2b668ed9d30ca2362bbe6ad02d97d57730a0e5fbe849a55ab32a072bc4eba99a8ad80857941ed3f8c067383581a2de5928b4a0d> [ 296 0 R /XYZ 62.692913 704.24685 0 ] <96f5b4648f714f973e0c688bf63f25c5ebd0d733c186315e4b24f65b8866fb94d623360eb4f47a6326bd81c295b86da29cf537eeecbb717dbc74866650e6da02> [ 576 0 R /XYZ 62.692913 761.94685 0 ] [ 705 0 R /XYZ 62.692913 761.94685 0 ] [ 507 0 R /XYZ 62.692913 761.94685 0 ] <9d61a66a3eedf31161f0a0991e364a4baf8b499ebc57335d75b6a133de7a4a7340a54735405109e3a28d8f94a2bdfaea5ec9a369a08c904be079167f0c4bcd79> [ 587 0 R /XYZ 62.692913 785.19685 0 ] [ 252 0 R /XYZ 62.692913 785.19685 0 ] [ 343 0 R /XYZ 62.692913 603.66685 0 ] [ 598 0 R /XYZ 62.692913 785.19685 0 ] <30c3eae3f0cb23ff8c927d26b19d94da378935a0eb37a650c73380860fce3060a3e58cae3792b13d50a0f2b355960b1dbd630a976960f1e95922dc16b386443e> [ 283 0 R /XYZ 62.692913 761.94685 0 ] [ 389 0 R /XYZ 62.692913 649.86685 0 ] [ 417 0 R /XYZ 62.692913 785.19685 0 ] [ 631 0 R /XYZ 62.692913 704.24685 0 ] <9323bbbbfdb26cbf65cf11ecead45cb924ae938cfbeddb507d42e4146936392d88c82b610fe8027d5d53abca0e698dba> [ 409 0 R /XYZ 62.692913 785.19685 0 ] <1ebd0c42c0f7ded5a72dc3bb0cedb12b5acdcb729e0fd479ec4cf5bbf6445d6b5a3b26a9da5268176be335028e7f63d4d47b7a6f20ad50be308f24527357186bfaee083076ee65af9f734e5e42675cbf> [ 354 0 R /XYZ 62.692913 603.66685 0 ] <342403c880a85cefae806eac7344532dc5b7bbb2038151c4bba8f252df9de815691003e6e5a5a565e488b011c3468372a9d02d2491ec78d6c897c311f69c05ee> [ 804 0 R /XYZ 62.692913 675.64685 0 ] [ 904 0 R /XYZ 62.692913 785.19685 0 ] <8bf265b635ea0162361f4f9b5ef26b00ab410835172a63b9ebe6bbb3e81a9ee8814c5fc4724f81d79718401730be4aa4b7140e870bdeaca41afd7b7b8ab89653> [ 519 0 R /XYZ 62.692913 785.19685 0 ] <750ba256d2606e08ee5f02581b8ddc842236eed3d84b168729914365704ed57dcb0f84426cd5a15a966b03ebd4acf47c> [ 261 0 R /XYZ 62.692913 761.94685 0 ] <4a33208951937c7dea71dff151c05192654e5d314d0066ba982bd685cb176fe2f2e1cc36fd042ee9bc477b29d8f83d740f3f7028bf2346e8c3ff6efbe0c5c9ca> [ 340 0 R /XYZ 62.692913 258.33685 0 ] <3ede59946c1ed54b66beeac245611e596c7d8ce79e16c8fdba9f3bdcc1c971ff45927d471242e9f4fda175fdb9c7c14f> [ 315 0 R /XYZ 62.692913 144.70685 0 ] <468b22fab5562fcaaeb4bf5dc20576161dce0e9db449d8b4a8ed900a188faeb730d035075419dea1bf97f575a8dd79d9eb3bb1d7ebbf5188c85afd92035ec8aa> [ 514 0 R /XYZ 62.692913 243.78685 0 ] [ 280 0 R /XYZ 62.692913 704.24685 0 ] <9d41746f33002ff15419c0a938a78b9fdfb5d83023f2a0deba234a90177fd584c5913a086a05637c123a6ad3797d29ddb0d582b2b6fd9cb6af8aa10b55924755> [ 352 0 R /XYZ 62.692913 785.19685 0 ] [ 583 0 R /XYZ 62.692913 358.21685 0 ] [ 406 0 R /XYZ 62.692913 315.23685 0 ] <63e8fb66e30ac8df3172c33cf6accc41c6bdfc1b9083f8d6f407591a9d46e70d1a8408ea69f6b0fc94ed138197f60082> [ 485 0 R /XYZ 62.692913 603.66685 0 ] <61d3433a9d2a3c8f9be9bd606d0eafe8bcb8db9341f6c7de3eadc2c45c7243e884d2db6a02f4b17139eda30126b5552d891a4922c9d26114e2887c938461012894e039c880e7fe71b701b1a6058ab08b> [ 573 0 R /XYZ 62.692913 592.34685 0 ] <15748ccbe23936b99d37fe826e387ce7a19601e975ea6a413c477bd127526ba3fe9d21de18646e10cd8e1d9012360505a704a0ba07d387a3d7a8f7b60d11ebf9> [ 701 0 R /XYZ 62.692913 603.66685 0 ] <9eed40a8bc4295ddddc19a07c86f8b796c9a8e8ed8f89d514591a5a1b393a057af6f0f21f3658a50c61c0b45f3dc9cbb112889a0fb6ec176ea707bee67a8593c2ec83f7814f6c4ebd1d47641e574df3a> [ 881 0 R /XYZ 62.692913 497.11685 0 ] [ 291 0 R /XYZ 62.692913 785.19685 0 ] <7712141765acb20a295976d1f4f93f20da237cc07e61bc3458c295d441f2a5109dbbf874139be3f8be45129c34db6f4206f8f34fc9edcb8eb42890295c5e3a13> [ 725 0 R /XYZ 62.692913 560.46685 0 ] [ 734 0 R /XYZ 62.692913 143.10685 0 ] <9b0151902d675c7c8671197ef07b79b7619997c0d1468f973f6c43bba38519ed4d885a59b11d830034a6916be2efaa45fd9401fa3748d726866d81d323097b81> [ 780 0 R /XYZ 62.692913 566.09685 0 ] [ 767 0 R /XYZ 62.692913 194.85685 0 ] [ 757 0 R /XYZ 62.692913 246.66685 0 ] <23c4d0334affe0ded0dfb249aeefabc164ae3876749361c2ef6ddb83d1a3cd742f59869f82c0670112cd125c962869ce7c6056fd27987fef738810cbd3405d43> [ 790 0 R /XYZ 62.692913 247.31685 0 ] [ 616 0 R /XYZ 62.692913 437.56685 0 ] [ 712 0 R /XYZ 62.692913 578.06685 0 ] <62df98e5a3e818262b20b0f704c26d371cd6ae0647868e7b287c02dc2cc47b9ef61c0ada2227b926d886ce3a40a237c5> [ 853 0 R /XYZ 62.692913 621.26685 0 ] <89b3bfccaba0234bd9d7d82c0def44d188993622829fd879902981f285dfb58685b5e521109de300c9daa2130aa8ca66817ee954cb5a7cb5e582c7cba9102655> [ 746 0 R /XYZ 62.692913 258.20685 0 ] <1724a6293c3eb3c83749f9b802ceb5c597ffc78a3291f08a3b1362d26236a2401e2bf6a27d8938d27db0dd0ff6941d98a70c11f54130fc742fa4cb60d8d8d6ca> [ 594 0 R /XYZ 62.692913 540.31685 0 ] [ 605 0 R /XYZ 62.692913 166.33685 0 ] <14f291d4e22ed4b751ca2072ca2a5b7d71712c8fb3972e1f7a152b22c8fb218cb470db6bd59804a85bdd1fb3fdb5c37a8aa07b4bb71bb48b536420bdc08debfc949db419908cdd635a2890fe28b6a341> [ 499 0 R /XYZ 62.692913 171.80685 0 ] <240e7c9327a898c2ceca7f7e7b86a11770088c8bae22dda706d02dce62e3e6d8dd1eb2c6f8efd482ce65d1f5a8d0fc905dae5e7b267271226555f3227936b55d> [ 423 0 R /XYZ 62.692913 165.88685 0 ] <72032d03d2e3b17de8e7b3a14ffa7bffecd8e75651b2ee198284f10d18c5291fc41f0d670da143a6dcd56c3896e0e5757d8983a598bc6231c606d45ee520b3c7> [ 396 0 R /XYZ 62.692913 704.24685 0 ] [ 628 0 R /XYZ 62.692913 592.34685 0 ] [ 316 0 R /XYZ 62.692913 675.64685 0 ] [ 628 0 R /XYZ 62.692913 286.31685 0 ] <8f4421d0870ad9b3024260e5fc743f4e42c1e0143d6aeddf5c4eb64f292181189ceb15dd28e4a099e737e11b461efa80601ffed06c559950c7953c6331daca8be6324563c88210bbee6e72e295aeb61e> [ 341 0 R /XYZ 62.692913 785.19685 0 ] <569ea60bf4c92f62f2d96152f935c353a3213d768c7277677a69a5e3665a59ffa58c23616ca40a14443781c250fa288529384814053fe33f38cffc5a20afa9ad> [ 701 0 R /XYZ 62.692913 281.63685 0 ] [ 485 0 R /XYZ 62.692913 326.23685 0 ] <0c195815a220ebc44f71f6dd346b66fe7f1a87b37cfbfda2c905f2274fbcb6ee81d6e8df7c2026cccd3a5db08baaaab3> [ 123 0 R /XYZ 62.692913 719.31685 0 ] [ 594 0 R /XYZ 62.692913 358.78685 0 ] <65c828038124b26da1da8a793b043db867e408ac484d85de49b05d03f373d3944f03278a445e68a8dbde83df9787a7e012de9944100fa29f335897d472e5c80a> [ 853 0 R /XYZ 62.692913 422.13685 0 ] <85fb8cc75eac627bbdcb924d6830978ad932b2c4bb8d8ccc08d104febba457e38ebf8799dffd787d6edf0f0f1f072caae91617ca8a95ccc36743448c722d843b> [ 735 0 R /XYZ 62.692913 675.64685 0 ] <60e37119aeafa920a5575807a2b93fb8aef229dea89a723ec3db6ed47679bdba173dcd13b8425a9c392a1a172e2fdd47e68da8da1b59f35b97828b4ad2aff47e> [ 616 0 R /XYZ 62.692913 256.03685 0 ] [ 768 0 R /XYZ 62.692913 479.84685 0 ] [ 791 0 R /XYZ 62.692913 566.19685 0 ] <4922c99f15a9d402308e30a495a972bdbc1280f13358d688de39f1582bab114c5a14e51638de09b76f3a9893190892862d55873eebe00fccff14c190e1e4eef1> [ 396 0 R /XYZ 62.692913 540.31685 0 ] [ 881 0 R /XYZ 62.692913 272.38685 0 ] <6d44c88a11df63b2d9131813a9dcb93819d98b8964721e3442e7c85d07819d29915973d68f25bcb4be2c26829290e02058b359895f62ca046f76e95fdf86ba4a> [ 747 0 R /XYZ 62.692913 785.19685 0 ] <4afeffa53cf60f0fab9fc4b0890de194aed399c8bafb3c217224ec8ba61fcb3d9fd2ad9e7caf26c8291e96180521db81d31e7a9cc7cec8c0b01968f5c4cac9218b2f89c0a5361a939d1a74d1125da18e> [ 352 0 R /XYZ 62.692913 586.06685 0 ] [ 583 0 R /XYZ 62.692913 194.28685 0 ] <0ef76529b01ed0ee02340000dceaa89d4010d638d45b170995b7c3c54ee406364c6877070a5b5b81a89e6484f66719eac41cee5ca9b34bf99309229ac085fcda787d4de8995c0a8766d10395ab541874> [ 500 0 R /XYZ 62.692913 704.24685 0 ] <7909435494c6ba60be986475f946fc90a9b44d069193a2d26d9359eed2b245f4445a2c256212fb8476921b89eda5264e> [ 291 0 R /XYZ 62.692913 586.06685 0 ] <21babe424386c6930556b900bce6c9006f96ac5ca1795e111eaebb59e67daa3950b16c80758f84de7c46e2c3d32cc9cc3117bed9660f77510a53cef7182bca2babf09ce4e4755090793441f7cb9048d6> [ 573 0 R /XYZ 62.692913 367.61685 0 ] <3616113e210e78d8fee4ceb382cf450b26ed2d6758032d229b3a71acd7de503e914a4d061facf38e3af435685e1a276e> [ 515 0 R /XYZ 62.692913 667.59685 0 ] <5eee84126d0bc128018d1ef89f6e54ad15d6e329b7f7b84ffb48a2099e15872f7de897da7e4eb21ba50800d34f7742342e0a21610be9b8208c9b0e7ebbad5560> [ 280 0 R /XYZ 62.692913 505.11685 0 ] <10c62c6e0bfc4cdc569ed590b3f35cbed1d97970c93bbd8cf14c755c96036dd0d2dcedc9d487d9efdd4c427ae9b5345d241fd42c8dd6a3eb36bab2c9d4e0a61c> [ 712 0 R /XYZ 62.692913 396.53685 0 ] [ 316 0 R /XYZ 62.692913 494.11685 0 ] <67823db6fdcb1781d11f7ed87e5c7b8025a3718f242a68dd08f965570c875654d7474924795d8c104db35ca549616162b6a01215be9ed80f13e4103bd205bbd5> [ 515 0 R /XYZ 62.692913 486.06685 0 ] <65dc5afb70a7ba4da6ed10471368e576f844873e3973b56810273f691dc2d1e2a3fe39d31881f96994b0ff7964575cf89525fa2d52031b7b7be7bfa06aecd480> [ 500 0 R /XYZ 62.692913 497.11685 0 ] <2a76d5d1c6e6f7d9c003b580a7c90521ec0b1e03c2c07f30b820e0f77cce9fbb4b51ed7a112a821669669af3173fcd4c29221353124949022832098bd2fe4547> [ 485 0 R /XYZ 62.692913 162.30685 0 ] [ 584 0 R /XYZ 62.692913 620.94685 0 ] <5655c81ad37dce1936851bc39b5c43eb222fa0d959ed62fd27759c804b304a91523cb2f7534d5d30bcddbb3f718580624252fef22adab28d6933aaeb3c9719f8> [ 882 0 R /XYZ 62.692913 785.19685 0 ] <8cce1427779b893173824ee59e1b309030f4be6b496755396669fa04ff4095f7cd7ac87c230370b33db0b9d45178815460c8a9b3eac33a21b59d2fded6f2cd0f5826c8eb4373a7b8ff03dd3cd7f74eca> [ 702 0 R /XYZ 62.692913 785.19685 0 ] <8703bf5f051b09add193d46db6b3837a90511c4f18c6a82cb1ee86a42ffcb7f7eb4ecbf232cbaff6e60072af674e69d68d94e32218fde1fd5088f0d9183d8371fff58282e62c60b2536bb07b8df1d254> [ 574 0 R /XYZ 62.692913 785.19685 0 ] <2eeb03560046b2eacd65ad27642a45747db41aaea32077c88eac77f57854f2b626d9c2fc53b93e09ecb7f2bc60e2a144b59cddad65abc64da2b98117e5fe617e> [ 291 0 R /XYZ 62.692913 244.63685 0 ] <27b002b68d74c3f700b526b3422c68193ea6a989da15b2b5c6143bfb59c876889a759fcc588c36b22822e7dc59d80ae9> [ 124 0 R /XYZ 62.692913 485.19685 0 ] [ 352 0 R /XYZ 62.692913 378.93685 0 ] <92f90288342b8b80ee9807a55034ac999ece832f7b17c21f82a3561eb269d25ce8b8a2e03c7b789e3f10bb0c61cee1fec8884b27695438ffeef6df78b38edf69> [ 853 0 R /XYZ 62.692913 240.60685 0 ] [ 628 0 R /XYZ 62.692913 122.38685 0 ] <5e07c47f350a5969399496bb470a25b1ca070daf92aa7fc035a3d3cd445e28db58b1e79b9031910edc2e863a7aa87c4ee3a7bfa2262c4e8432991a6c1adc60a53c37dc4c98b36991c3b3d5d65c61710a> [ 617 0 R /XYZ 62.692913 785.19685 0 ] [ 769 0 R /XYZ 62.692913 785.19685 0 ] <0dc4154bae11e37c9e945a7d7e0723794420da48744569eb346249b0e235b65e28f6a578cafaf52efd2e1cf6e16c34009a2cbcbbe81128c1a8d69d8af03e4137> [ 396 0 R /XYZ 62.692913 387.38685 0 ] <1d5f0927a4cf0c1287e6ca1d0353c5d0d764647bee31bbef28811e5df9c69570bcc2100a112dc4f3160bfd56bab7c0444a191479b2e6ffd3489b27a4f0528b84> [ 594 0 R /XYZ 62.692913 177.25685 0 ] <295793a0853789a6255e555e7094687a11ebd06a0d31134b02e415710b2dbad78433a262fc98954f6c26a029673fbad3919b6cd7c058567a6f9f1ee47c65e6b3> [ 280 0 R /XYZ 62.692913 323.58685 0 ] [ 712 0 R /XYZ 62.692913 171.80685 0 ] <5a03a9262f701ac2cf645e1be413fa0031233e1ed2efe6054059d23ae2bf035cd39030059c025aa8f325a8bc640a752fc0257f6dd5f55692da99767a0df96778> [ 292 0 R /XYZ 62.692913 648.19685 0 ] <4ecc06ac264a91862768dcf23c122fcd445290475af16a21c3cd24fc119c9616b0df10498cb1d3ede258304527bb8b9254998c54e54e60dd2b4aafbce5b920442ce4cc919bae0119e7fac76fe631be4c> [ 882 0 R /XYZ 62.692913 578.06685 0 ] <757bb81da55173f149783346d5889b4bf69fce54190a703db6f75dedd364cd2b37f7a765946a373d449140fd3ec83081a4396aa71fe51b78ca803203256c176d> [ 617 0 R /XYZ 62.692913 560.46685 0 ] [ 629 0 R /XYZ 62.692913 658.04685 0 ] [ 702 0 R /XYZ 62.692913 595.66685 0 ] <6423c427884ecaa1a1f3a9be3425301f3cda5d6a11f2a348a62e49031e913ba358ed977efaccadbe4b1996c571e10d38> [ 125 0 R /XYZ 62.692913 704.24685 0 ] [ 397 0 R /XYZ 62.692913 785.19685 0 ] <4ad31ce0a720b4bf994584b277a22a34f4e55714620a4e73825a087f8bd4e9985212d93aeb3b1935f18b1f9aeb3c7ae25b76ed55a9c6e60222b9b0b0fed7aa17> [ 854 0 R /XYZ 62.692913 785.19685 0 ] [ 316 0 R /XYZ 62.692913 312.58685 0 ] [ 280 0 R /XYZ 62.692913 142.05685 0 ] <4a85aa458ba6fb31e6986f65ea8ab2a39840d3c6d5c477409475d93c8d112f32bfd766e0bbf229eeaf14ad963d66db5407a0820645dcd4766dddf3e361d5b3a3> [ 595 0 R /XYZ 62.692913 686.64685 0 ] [ 515 0 R /XYZ 62.692913 304.53685 0 ] <530dc276dbfe54723aa5ef8a5eb79fcf3773a55cbdbda9a7bdc62d8779096c46a577ef6b8568abeec59e9a469c4b5b634378b839b04f75f23113c8f94ca0b5b6a7c7b9122200db422219ea5dc72a0511> [ 574 0 R /XYZ 62.692913 595.66685 0 ] [ 584 0 R /XYZ 62.692913 314.91685 0 ] [ 486 0 R /XYZ 62.692913 704.24685 0 ] <177e8bcf94ba51cea0929f3425819799b7231d0378187bed0648502cdbd7256459b836977920dd1d61ba4cec0d6f506e49f5ebae7d12821f534d0a7c5d066e97> [ 500 0 R /XYZ 62.692913 333.18685 0 ] <9b0986b20bff29b08587b47a9e731dfc0074da58808f491c51e5fc0c8df4334dd4e2c9b2cb8ce528969d35c0bf55510fec3c53d92bfdedef27e17b5a5a7bdf574a9d2ab3df6923419b6f79f6fab8c463> [ 713 0 R /XYZ 62.692913 704.24685 0 ] [ 292 0 R /XYZ 62.692913 370.76685 0 ] <4be6eb9d833180a0436f13dd2aa11045aead48e036d1141e2a65d2514c38b3e3222e39a12315fb5182a2eea19223e4384c680625dd477f3ecc9555cbceba295b> [ 617 0 R /XYZ 62.692913 361.33685 0 ] [ 882 0 R /XYZ 62.692913 335.73685 0 ] <953e7d4731c592f251d7da49397c6198709afc6f8bdf49c0b8c1c7c6d28921b341b2508ef4833615f826e2dbd3c943e17aa8f289c8c523173a4b0eef012b3f62> [ 281 0 R /XYZ 62.692913 693.24685 0 ] <7341bf01ed3512bbaf3c7e99cc88d9826ddab2d25e1acffd6ad00843e224a704886d1fe9d60a3073ef447cae915d70dac9982732235c31d99c17143b5d1853cd> [ 500 0 R /XYZ 62.692913 151.65685 0 ] <2a3609aad4956b75136a470a5491565f70bc1ff662c2d9428daad3d41b9eb4ceed18a6d12b0deef38a48a9184fe6fb3e0df24c200143e091fdebf14d9f46bdc19c7ba8110dc831c14f6118b3a458b737> [ 574 0 R /XYZ 62.692913 370.93685 0 ] <12db430065dccee4a846829f04c8b1aab9b0d5010810caa5cfb5c7986ea852e941d391e135de11219b0fb78aca46c6d1a3d658ad4b13641d36205deffe61c0c2> [ 317 0 R /XYZ 62.692913 785.19685 0 ] <22b21324505c4f015c35b70744900f4d4f3cbf056196333d19e5ca46b93afd1622fb8889ebd99bbb9e3adea5d031f824ca07ff40eb3852d75e11db2462a92c2f> [ 629 0 R /XYZ 62.692913 336.01685 0 ] <1acfb21a905fb248ad8e7a098e7a1c3632ba7da721412bafe78774eca38b4b472c253d1d4965f6f33960170ae6d119ead69c37d01e5497d06f6fcf28926a729cdc2a24ee3a6383a00d63ede464e2fc71> [ 584 0 R /XYZ 62.692913 125.38685 0 ] [ 397 0 R /XYZ 62.692913 603.66685 0 ] <920c5c076fb43ea027bb6bfdceed95bb9222a572c0b860f1fa91345c23f5c22a155dd4c63d4bc2b050578426ad6afc392b3e6c8225c04fefbbce4adda7e8ba21> [ 516 0 R /XYZ 62.692913 696.19685 0 ] [ 702 0 R /XYZ 62.692913 431.73685 0 ] <85ad9b7fda891030346358ccbe4175f37fb52fabfbc823d6d56bf0d8f05cd9d1a9a21c97e96a33cd837eb5a19939c44e2dc3c2959dd09b96c667f3d5c0a4c7a9> [ 486 0 R /XYZ 62.692913 505.11685 0 ] [ 854 0 R /XYZ 62.692913 408.16685 0 ] <51ea31d9ccc38f6d731f9aa6ce3b8540fa582eb4bdf20bcc3ee2d1c8fae195aafa5db075e6489f060c40a75dcc4fee8c06f6f73b0443dd69af1941d79d2a0e67> [ 595 0 R /XYZ 62.692913 364.61685 0 ] <5ee6b6ee53c6b770eda8386c56e8892384cb5f6d852d56148d53601801d71e05215c2a6473e6af954e6627177cb76848b1614df2a055c56b08bd1f8aa354d50f0f80f6958eb185c33582a26553e0c5f8> [ 713 0 R /XYZ 62.692913 497.11685 0 ] <998ea0f37f5b5bd91e21626e2c30486a5937455b67d87fe3ced710eca3438cf92d7fbbc4ec3153ec2d06fe09c3e99a19> [ 703 0 R /XYZ 62.692913 785.19685 0 ] <109b725bb1fc39e47a87d90571f59b511d752be037fab45164c1f13134b90d85abea0a6e16174dbd1979327a850f650a56bd409b5f6221f51169065dabb211cf> [ 516 0 R /XYZ 62.692913 402.76685 0 ] [ 501 0 R /XYZ 62.692913 704.24685 0 ] <5171c2a299d44c359e4fabfdf547ce1de741a5da3faffe219dcbf9e1ec983cb7605faac3a0125503de7ebc853a0f4070e41be34c3022209cba8c0ca94eb0ad4a> [ 882 0 R /XYZ 62.692913 154.20685 0 ] [ 317 0 R /XYZ 62.692913 436.91685 0 ] <69d46ebc584ea755266ae51b0fc3d5f47ba75e7c3e87751b45d7451f1d98b1b49ac3f2164e9628694f0db30879535664116ba5a93b9465a5518f5eb8f623a952> [ 713 0 R /XYZ 62.692913 289.98685 0 ] [ 595 0 R /XYZ 62.692913 165.48685 0 ] [ 855 0 R /XYZ 62.692913 696.19685 0 ] [ 618 0 R /XYZ 62.692913 785.19685 0 ] [ 281 0 R /XYZ 62.692913 494.11685 0 ] <90bf2fafa041c96a15e5b505767b299f7d32b057598d00214d636ca95e510aa92648618d35547095a6f11b45ef511e2fbba1d080e4308be43331dcfeecd25d02> [ 292 0 R /XYZ 62.692913 181.23685 0 ] <4e91d40620e89a0b7b225a75541635d1e7824252da1030d38d274caa1f0bd53ef7854fba3b32ccd5affc73fb7cb62b48943704b623942cae6690dd49412557ed> [ 486 0 R /XYZ 62.692913 211.68685 0 ] <54913eb9824fdeefc99a964551578a872682e8be6cfedcdbbed54c444ce441dea96c2253ec37c959a59da027b51fec82793c310cd9e4ec44cbb66d89b5fefe94224c8efb04833cf7a69c9d56b28bf579> [ 585 0 R /XYZ 62.692913 551.14685 0 ] <74164ed4e880d02cb68c0a668286fb2738e3f17589efbb86e1a40dff2faf81dda456fead32a6cb0f4faacce8e993562590d04424fbca24cef8fe45b14ce952f1> [ 487 0 R /XYZ 62.692913 604.94685 0 ] <51d29b8e2c8a57c48b145e4e554fe2deff3f8609a4807033a84634f47337112bbc2a6c77f1e7b602997326458a873d64> [ 714 0 R /XYZ 62.692913 680.19685 0 ] [ 516 0 R /XYZ 62.692913 238.83685 0 ] [ 501 0 R /XYZ 62.692913 522.71685 0 ] [ 293 0 R /XYZ 62.692913 704.24685 0 ] <088c40df7f20f06e63656967bc65c3807b52503abed718efa61ea7565d76afeb4c93a83a29a3e99bb78f6f2f16c88b05d6dd5e172f5d7bb753f8e6146b476d32296c869decc0b7df4eb40cd2f6a3b93d> [ 703 0 R /XYZ 62.692913 586.06685 0 ] [ 883 0 R /XYZ 62.692913 704.24685 0 ] <0e3ae4553f7f734e96ed6ec30ecee6ccd7aabe045a4ed06921da98d7079280469bcea794627733e6e27b72a295839276a4ac46f5bce57a897995ae1a1e4ad089> [ 596 0 R /XYZ 62.692913 675.64685 0 ] <9f8151cafab4816e18623d208549a14d8111fcaa0f60cd947a4ddd40af4f2d8be0402a601a8db57d4da86b3b2a96f25f4e9c096102b7738ca8343f269d4d452d> [ 317 0 R /XYZ 62.692913 229.78685 0 ] <700d2300988134d81c3683451498f7a4938d617a352fc9dd476aca172d07340826956e4c72992496ddc7c0bb7735dcb50ce3da31847400f66828a0d8acb51c11> [ 855 0 R /XYZ 62.692913 514.66685 0 ] <96c4723cd561a8aa19e2f99715b14e79c35782348d5797e12b6ac34996667bf1a088960748e6088977364afcdd1d6561> [ 714 0 R /XYZ 62.692913 481.06685 0 ] <7ed372e571ac5d3ccbb423a74bd8795f4db0c10bb11fd4dc098e81d164f336592b8c816f279168b8bd12156db144d6ea7fed9f5d686cbcdf4e0f5d3a6f9797d9> [ 596 0 R /XYZ 62.692913 494.11685 0 ] <233e6f72c7e2cf51ed8bc2927d90755a50b2acf2a25932b0f4d560c30f3c6cbb477c914ca8ebe206a28c3e2ed4010af2b7aa4546db97390365ae604161e0ffe2> [ 855 0 R /XYZ 62.692913 350.73685 0 ] [ 293 0 R /XYZ 62.692913 522.71685 0 ] <2648ed7ccfe0169dfede223490572a11df17f5f33bea3d40f1fbe56c2971d2774a90044f746ac31a571ac8e8eba52bebb59d1c89bde4b2f2d6ae0bc068fd0cc6bea44aa806a470d3b84bd6af0a39ce6582f76faae19982c821f68864f7349b75> [ 517 0 R /XYZ 62.692913 761.94685 0 ] <9817d0ac3472fd2fe21e99dd8d094054157d9439602529d1b24b828f935723cd8ca4326b5696fb251cc1ad35e90acc5d298a5fa3afb015a25109749200ba7ce4> [ 501 0 R /XYZ 62.692913 323.58685 0 ] <12b05498605c15211dd992aee95b8f695a20ce60075e13a019bd6d812d307b3c0c0e169b59e068cec04279c807370178286e8689b5e893d4b22125a0c842c54cd9d3d02df87040120285d3700bb2f2c1> [ 318 0 R /XYZ 62.692913 785.19685 0 ] [ 487 0 R /XYZ 62.692913 441.01685 0 ] <217a2910bd44b1ec90d3e7012a390db2e668bf3a51ae4f3f1628d7ddc0119201e5557f179c375d8e9d2915e69e4dcc3248dd0207e430b26f4df7b74fb723bbf511354445a2feb89ebed56989757656ed> [ 703 0 R /XYZ 62.692913 378.93685 0 ] <55d4b89b2e81d23f669ca0f0a8de1798facc41d94351cd022b49e94547da489e25a53e8806022ede86aa89b62cc53b141f3beb5079feda5225631932035953af> [ 318 0 R /XYZ 62.692913 578.06685 0 ] [ 714 0 R /XYZ 62.692913 317.13685 0 ] <0cd89788679d5ace62e5c206b8d1f8bff9e6d5c10f4f64848aa839f1b1e83deaadaac4b9a696a539b1361a11efa21f3a> [ 501 0 R /XYZ 62.692913 142.05685 0 ] [ 517 0 R /XYZ 62.692913 537.21685 0 ] <894a96c7ff9948e49a7f9237292f886e1033acb88cc720ed510c839a1fe845079ceea25795801d3c290e66e494d4aed6> [ 855 0 R /XYZ 62.692913 186.80685 0 ] <07b331941605cefdb88d92afa7e806d42ac8bc127671157e11d6e8d357db55cf332b0f1eb4f9e8d1c916444f746f4391e0e490e13231ff499e730d281a396177> [ 293 0 R /XYZ 62.692913 297.98685 0 ] [ 487 0 R /XYZ 62.692913 259.48685 0 ] [ 488 0 R /XYZ 62.692913 785.19685 0 ] <99b3c64849dbc97036b70705097e70c25d5b3800747a6f26023b1f290234f6d6a5393bcb0bccf288be905db5b4fed0c479d2ec3ab144c1d543f80b8867c652a7> [ 856 0 R /XYZ 62.692913 620.94685 0 ] [ 318 0 R /XYZ 62.692913 396.53685 0 ] <6fb402f117077f2e73f2b29fef75a29547719b39e8fc8f4d45e53a45c5f353b5c2efd6466cfe2edc37f90a2f94b5d08fa3ca7b8bc17ec8530f30fb752d81a67e> [ 714 0 R /XYZ 62.692913 135.60685 0 ] [ 294 0 R /XYZ 62.692913 785.19685 0 ] <0f1cb252070a89c26b606d81758ffa060f65a55d3673830bfede2fc290908aadcb1e6d004ed5e191e143d030fdb052db> [ 502 0 R /XYZ 62.692913 675.64685 0 ] <4560eac56eaa2a3c9dd15f243df639173dae213ccdf94f8b5008f542ca75229448dcfa8309515a2204a1cc04fa7bc471cfcd02d57f88eab6f9d2f9f8f4f5d386> [ 620 0 R /XYZ 62.692913 603.66685 0 ] <267ed5c46a11add8d98b69c9a5c33f3e66fbd253797a2dc12b75a4182ed1e80595bc8c1b4bda3fe12ca6545ad475959a01fe5451ca50623f0fc2ec305b7db6df> [ 804 0 R /XYZ 62.692913 494.11685 0 ] <9fb70e952cbae71873c2d8ea76625641005f55a4c681292445da3d28ba3edd341fb5706a2cf01c35810347f6ffcb5e284178ff420224108ed86891dfee74d742> [ 867 0 R /XYZ 62.692913 348.21685 0 ] <77365f395a4b74e09015dfc0c0846697ce4e2cbb8d4ae6c68c7d7343d9558d1454b87622f4485791f0490616a9b24630463de708d2e8ca2218b6b8adc6a83ea3> [ 545 0 R /XYZ 62.692913 465.66685 0 ] <89e16b7f9eaccb48ac507438fb221457d02db92f7183d5ca3208ca57c8b9632682828449cbacaa80b67fe164fd74c98411264384a32d3fab6858337d878db2b7> [ 675 0 R /XYZ 62.692913 313.73685 0 ] <8a41c31ad44d9c8fdb6eafacf6ed068f1c334dc988de4c1bb6ea4e151b8ca7e2b2e7bc999216b38134435240e56eb98e1ac760348d79be6a9788e0e04c54a79351323664fbe88fa18e4e6c0b11000f7f> [ 426 0 R /XYZ 62.692913 380.51685 0 ] [ 385 0 R /XYZ 62.692913 560.46685 0 ] [ 966 0 R /XYZ 62.692913 351.73685 0 ] [ 738 0 R /XYZ 62.692913 623.69685 0 ] <80e1b9c649f0df4cd61081315ee6f5e67561abfa5b4636599d78535cd4ccb5ec271c7d0ef1737ca7d4e6a494439a20c4bba569973fa984ef36a7ae95963f9661> [ 360 0 R /XYZ 62.692913 568.36685 0 ] <01ba8ab201830db516782d58bc0e446f904253a173bc1ef85368637fbde59efe9e72336d932e7a0e31f6235f7dc70d3fbe4a57f6bbfaea8b037054b4813083a1> [ 686 0 R /XYZ 62.692913 621.26685 0 ] [ 749 0 R /XYZ 62.692913 416.48685 0 ] <0dc9f78f6e8f06798ec8b18e5593d698e1f6fa8646b85210bc6bee547fc86207db083c688e6723e91533697e426597411510183e22159c0a75d3721c50f4c5ff> [ 608 0 R /XYZ 62.692913 466.91685 0 ] <0bb62a67d37a00f3960ab1575813aa96c37baa06d9053440fc090bc7d51959cb4a9000eb437fb44ebb3d9899edc24104> [ 793 0 R /XYZ 62.692913 269.38685 0 ] <82193e30df40fccf6d267dc634335b2d7ae5d12c45d3e6bf33a0db4cb9bf9856e4cf7f4a6b472e654c360147c86ce09a310de39d3c82245603e858b36428367ed2f571fae85424fce1eb4c1c98a47c11> [ 537 0 R /XYZ 62.692913 438.06685 0 ] <9b53b187a3ee451e964b0082786b1506ceb893604e6114a14ef69c4475fcb644ef118c65df5d99b29203b5d0e5d10cf4> [ 472 0 R /XYZ 62.692913 418.66685 0 ] <098cdd329aca7edde4d2314cb5ecd51f5f1483d4a87411a7bf85c7ef902b23085dc51cd19a971f561fcb4a7a4502d392aa5cc4704bb4011d40a37bd139d9d684> [ 658 0 R /XYZ 62.692913 328.33685 0 ] <653379ea09f8cc384fa07f42f669a0e8a0ea14f6251870dfd478c3033b3706c3d882b5dc8fc142a299159eb173eabfa2bd0041b6391dac87c676a9a6bff0ba2247b742251708dab9af6b3b1fd4677791> [ 648 0 R /XYZ 62.692913 343.73685 0 ] <8e52374dea8a8917503e593fe6b07d279e2709318a4f35efb759099225f18792af5838b5cc25b4de4983b31063f1cb2975dfaefd5645001c9e6ab99c8236dce2> [ 598 0 R /XYZ 62.692913 507.76685 0 ] [ 973 0 R /XYZ 62.692913 621.26685 0 ] <4e20950d0e295f2fb1101ba1ff046c5ddd1fb3dd2504bbafc6aefc5b97e9cc579780422f5b252d54be4382c153ac284703eb668d9b94748f9b2e8cb5b53fa43704312137033ea651cb989d54f5b64ac9> [ 885 0 R /XYZ 62.692913 554.81685 0 ] <426b37499dc1d232ca81582127cd54f77986008acf2ab6c652ee3dce050689938be0adfc06f43ac3831c3607a49ff867360459acfd031455f978b8347d1fdabb77c83c0071553d56848b0ccf2a40af6c> [ 246 0 R /XYZ 62.692913 262.33685 0 ] [ 982 0 R /XYZ 62.692913 497.11685 0 ] <47d45d199e2da79c1eebf961447fe48b264f2de8f88e16e7ad73e360f869ec0e7f0b958e5f890992d3e58006dd8849c1457c12b00ec1d16c3515497ae93e330734d9f47a18e7f0613d03111d6d9d1d62> [ 576 0 R /XYZ 62.692913 580.41685 0 ] [ 126 0 R /XYZ 62.692913 762.69685 0 ] <53ffeb9a5bdb4f7e6b08096bb7d2ddf34cad64c63eaa4753cbdfb715d48e41d7b0a66bec9dd96a6c7519e120634fa811> [ 555 0 R /XYZ 62.692913 621.26685 0 ] <92861c34343e11e80bd29bf5571bc7a1c48b3df5a4c8e0bf36c5d28bef25ed14d5777ca10721700df1f51d5be29370d9d9018e37074b06d96507b98fe46a3957> [ 911 0 R /XYZ 62.692913 505.11685 0 ] [ 822 0 R /XYZ 62.692913 632.26685 0 ] <71fbae7bf9ee5c2df8b1fd3df1f7a78e2ef166c9efeebd5c8540aa4bed022d6dca79b24fb1583c13dd12b50ea67ceaa1c21aeb506fe11c3aba1a96b2b5d1e1d3> [ 519 0 R /XYZ 62.692913 411.96685 0 ] <25b3e98b0d5ab6d1fc2a5da12479442aa7f0fc7f5ffe50c8ef3d5d6821f63d8195ea8728304836a6bcae4be34f04df91db54a4c0b3e4160c576f4f404695ec04> [ 478 0 R /XYZ 62.692913 621.26685 0 ] [ 943 0 R /XYZ 62.692913 337.41685 0 ] <7d15ff7b13949c61678a8b012fb1a4bb0733a7d5aa8dc17986216dccf85a00115844e6992ba5c9e2d80a1ae2c592e8969523e734f9354b33197287338204326b> [ 837 0 R /XYZ 62.692913 603.66685 0 ] [ 332 0 R /XYZ 62.692913 340.61685 0 ] <734d6dace4c5049c3ebd7fd5e31756bc47485cb906ac90bc4a9963fdd8eca6b1986005e3406f1740af6ad2d73264103f8e9983f6f2e14974e76128a57b6f3433> [ 527 0 R /XYZ 62.692913 332.61685 0 ] [ 899 0 R /XYZ 62.692913 433.31685 0 ] [ 296 0 R /XYZ 62.692913 505.11685 0 ] [ 782 0 R /XYZ 62.692913 494.11685 0 ] [ 283 0 R /XYZ 62.692913 598.01685 0 ] <898463954d5b8cc893e475d9924ea754b7e5658659438d170c276c717c6d8a0a1f93eee688fecf4bfbd3f2fdf4416625edac39589db8f857d4d858696d0d7482dd3cf828d49df544efaa4ab0405a4cc0> [ 364 0 R /XYZ 62.692913 514.71685 0 ] <9d336d1f6edba5398a561b0940203667bc8ae88040de73c2f4973349091938156f7ec342dd37c3641fd2db4fb3a713a600f81fa85cdfbf79d7a0031e352a1f29062389cd374661150299c1a4b06595bd> [ 507 0 R /XYZ 62.692913 554.81685 0 ] <8d609dafdbfddb25a4fb97fa30ded98c141c8b115c29867e087af63864eadea680fed9ecbabf64f83b683d9cab13c7d6591e4468af841b93304abf2f9983c62c> [ 814 0 R /XYZ 62.692913 603.66685 0 ] <7b0a67138b0589b39cdb713f15a415bfac41a5a65ea07d1225ff2ed28c7ad7e0d2af03aa49c86386b1955b5c0712c006> [ 828 0 R /XYZ 62.692913 470.06685 0 ] <850358bc7ab9c995ce9b3240d050cca6c9d3453a226f0b7eb41b3fef4cf71b98209057cc4844b2989a2a133eb98a199256c974b72d64baac727bb87c94cf9b09> [ 369 0 R /XYZ 62.692913 568.46685 0 ] <330cbac92dcbf546501f9e3f8e82661bfbb70a41f025311a94f322d560c78b89335616add91461e52d3156279a409df4b67c68e41ddadb9b47589bf4bae5d72e> [ 464 0 R /XYZ 62.692913 562.81685 0 ] [ 252 0 R /XYZ 62.692913 560.46685 0 ] <6a520fc44246f280d58046cdbabd1846a0a52dd8a5a76f24944d2aa7dc356d69990970582a62ed968e21788f4053ae124752b13e1faee8b0513e006c3fc74508> [ 958 0 R /XYZ 62.692913 450.91685 0 ] <34f5b22b324f5d6f622cb24192bcb4947175a8d707d3ab8a3d5ae27f76cec843abfd7122b9bb1fccdde997c55708c7cac554875c2e3e6fa1b7562376a290ba2c> [ 417 0 R /XYZ 62.692913 603.66685 0 ] <89bf88659ef902a55040edd90bac772957a7f7cbf5f46df8ae5aa8bc11eef6a9e5413577fd45f9fbef78dceecd5b04867f4460860b6979cf3382fbe6a38fe3d9> [ 565 0 R /XYZ 62.692913 603.66685 0 ] <6e29ec2319c42f30214593fbbf5a11246c7d0e0321a0a254dc530b59db1d767693f4fc22a5c72fae5b5dc87f33fb9b0ea8f503c2a531b6d6ec98e8061e1d629b> [ 399 0 R /XYZ 62.692913 621.26685 0 ] <2fe8a5b26f8d611047c791f4969bb93b392810e7256f1ce846e7c875e596678a60a6fc640ac7f421f9dbec0b659e4f47927dd955c46995169c375cb0ad491238298e098bb003821e8930ad3a4d46cb35> [ 306 0 R /XYZ 62.692913 351.73685 0 ] <299e574655652bb5ce73c90e2f4f49bae91cd544f274464d986a9fae7c45b2ad7d8a3909ac8b0dfa3383ac13e410a09b9541fd1b15d1f69e5b4d94baf40ebbcc65e14b5e750dc119959f829bafda6769> [ 872 0 R /XYZ 62.692913 165.83685 0 ] <9758efac9a788e93c3abb22c3a85a96038b8718391b407665fa8a906bc404aeded8dd8a2a4a9b63e227d01ca31b240bdec6a5bb95f3efb80e667ebb735ba5589> [ 950 0 R /XYZ 62.692913 484.41685 0 ] [ 261 0 R /XYZ 62.692913 562.81685 0 ] <2052dcf6ab7931d2c18674282d3fceb618030957f61d0bbd1d3264e12541b8a32102de2bb0d3f172b297ab66d7ed55f4ff1fda6e0afe8e51ab44bd0acc6d1f4e> [ 458 0 R /XYZ 62.692913 310.36685 0 ] <8886e86b5c77dc4a377cf8729a3e756a253d31bf6754028948c48dea927fad3d078f47e6cc1bae116b8965fdc519d3bccba18ad20f62271435ace16193b2cee59ccc4ec5fad451f566d0d773ff699727> [ 892 0 R /XYZ 62.692913 598.01685 0 ] [ 727 0 R /XYZ 62.692913 603.66685 0 ] <9c2ba4813566dcbd7f28c5088ef1cf09ffa2b12de6b54972b0454650478bef17ddfa414657c05631edb4c50e57625445ec3bea224df8095f37c7b2508d9455eb> [ 443 0 R /XYZ 62.692913 318.13685 0 ] <95022b015fbc2ae2ed37fdefdd13359593da98706bf9c4533dbe049a91dc1ef3fb6a397114f8efacb9f71b90c305abd940340e94a93d5f29d7f8e4d1b76ff255> [ 270 0 R /XYZ 62.692913 180.63685 0 ] <0225fd1495a517101521bc7db2b6e0c06c4292199d1159d899b81042a0a9639ed257b6c645e475ad4564bba827d4d6c3e1105cc891848a1749c30b3aa5ccefa5b530e7a3197ec0aba6c9345f319828a4> [ 904 0 R /XYZ 62.692913 632.26685 0 ] <8ee70d31086cad23accbbf2c3c9ffa32a148ae332c23ecf68a17f28883eca9847010787a3e819226e4250ac70e7d949078f92caa0175cf381b429c210c6a4fca> [ 641 0 R /XYZ 62.692913 497.11685 0 ] <325770ef1a7b5517a483a93a4411e5416980b2b4d4e19f70e371a96bba62f5da46196ee18799e6db317463d459833ef6> [ 927 0 R /XYZ 62.692913 522.71685 0 ] <14469986d39a631f11f297689f995d6c93970f04ccd82cb74f5b4265996d0966416781535dd59ddd3a754cd9cb517a80> [ 771 0 R /XYZ 62.692913 325.33685 0 ] <615f7b40b3d120f05ea35672d3b99e828e10ab70307800a04cf943aeb79746ca32b18c33b521aa4c2bd43e600304e138b7da89cb1e99f6b0d7b3355a97557c7dd41538eb68807f6a3d5e80f73dc80be9> [ 374 0 R /XYZ 62.692913 603.66685 0 ] [ 862 0 R /XYZ 62.692913 143.93685 0 ] <191301023f28707180edf46301573dc9420d6f0f070d6581d27647e887567b4d26ef45e85366e71356f3d775c3cb094f55d2c3cb2e3c2deccd53c7b107bc5463> [ 760 0 R /XYZ 62.692913 621.26685 0 ] [ 667 0 R /XYZ 62.692913 476.51685 0 ] [ 843 0 R /XYZ 62.692913 327.36685 0 ] <0a6a7d5e4ffcdf49fff8f5d4e238c9e742114cc6a8be91ba517a1b5f040c722e9576ff29b832a6b4471fc6f0b1def53056e20f8d6b513125628b58b2c7de8dd45406d100b97f92c48084a69015302f53> [ 435 0 R /XYZ 62.692913 209.63685 0 ] <3f23fcac3bc1b3dfff024f91bfa12b1412ebd4bb19537a5c3215c49aa272ebfe47bf117fc77e45fda676ef9af6dfd8f07ee42a3bbbb760e8f7784ff065193e00> [ 409 0 R /XYZ 62.692913 649.86685 0 ] <1a8397c6ffaa0f6f9190b41405aaef219eab4f1e9eec6d8484e89e716f6c262ee6a2263f2e38f6f410ac4d8b427f6ee7bc5cc390c1ff44f0655381beee6d7a19> [ 717 0 R /XYZ 62.692913 487.51685 0 ] [ 934 0 R /XYZ 62.692913 261.66685 0 ] [ 343 0 R /XYZ 62.692913 422.13685 0 ] [ 919 0 R /XYZ 62.692913 621.26685 0 ] <5546036edb600c85bdb067e9bcb5e7b316917a75afb09f7893bc4db787a77c4f9b8bdd551c514584aaa08cde6d23c02bcfaabe9a8abca481490f3e3773179dc11cb621e5ec918a8678ffba2ee9b6b2e7> [ 491 0 R /XYZ 62.692913 348.61685 0 ] [ 237 0 R /XYZ 62.692913 484.51685 0 ] <7d8b2ebb38e266eda3ca88ed5a845180ea685e6748409cd694c81f51646e2b8c70b815646c6a86e195c4da84aee25e64473449eb50632bafba4b86cab91fbd043c0cb36fd8f6b19aca411baa9862a8f1> [ 705 0 R /XYZ 62.692913 562.81685 0 ] [ 587 0 R /XYZ 62.692913 479.16685 0 ] <222cf2494c5cef869ef5f012a7696b96cccfe16d6b91e4ff0eb72a3680cde5145ff2c7c4dad23a3f3d34cf038ddc789eaaeda48556f35906cace79b4ab2000fe319e7549dbba5992c52ac48bcf65c7da> [ 430 0 R /XYZ 62.692913 545.21685 0 ] [ 389 0 R /XYZ 62.692913 496.93685 0 ] <204e18d1864a8beea3dddd71c192b3b22ef8a2169161f45f5e492c58f3c188d2c04339964d5ff450ba484762f7c0e55b589e582114628b37430ea32f699af2ee> [ 631 0 R /XYZ 62.692913 505.11685 0 ] [ 354 0 R /XYZ 62.692913 249.43685 0 ] [ 694 0 R /XYZ 62.692913 621.26685 0 ] [ 379 0 R /XYZ 62.692913 364.93685 0 ] [ 322 0 R /XYZ 62.692913 263.78685 0 ] <2ff1aa2d4d1be189d247ea9400131d86664a4337c414158e989202a2d87d8cda928425b03914b00184a533b39846a686> [ 318 0 R /XYZ 62.692913 197.40685 0 ] [ 856 0 R /XYZ 62.692913 439.41685 0 ] <97fcecfd50a395615e714a0da37d80a10bc717256138eadece66dc4fb72646a26e49287039a85c47a9d95e2dab28c5488189152d747e8e357c3dbf2c4d38f816> [ 715 0 R /XYZ 62.692913 551.14685 0 ] [ 502 0 R /XYZ 62.692913 366.21685 0 ] <7091e728451d4cf64e2c3ad341d002bd19d897233e520473b82ec7cda09446759f7e6b33d9396440ea38aa76a4f5013c> [ 488 0 R /XYZ 62.692913 578.06685 0 ] [ 715 0 R /XYZ 62.692913 369.61685 0 ] [ 856 0 R /XYZ 62.692913 257.88685 0 ] [ 319 0 R /XYZ 62.692913 704.24685 0 ] <964bab31ec0354d6e5aae579cb702531777f44cad5e84784807beccb4a764f54e0dd4c08d23344548e6ded0305a9c912392cdbde3aeea844c0b189495ca1a285> [ 488 0 R /XYZ 62.692913 396.53685 0 ] [ 502 0 R /XYZ 62.692913 184.68685 0 ] <0c8b82f2bfbb35374ea7a35f5af3079a88e9874aa95cdbe178e0ebfa7f5669e4fadbaf22581785ecc3900461a634fe85> [ 126 0 R /XYZ 62.692913 719.31685 0 ] <5ca9c36a3966c9b539671423747fae7efd1f2ea12311c464a191a85a241b885540f23931ced274b5176b238b9b7c58e2> [ 488 0 R /XYZ 62.692913 215.00685 0 ] <0a472bfed2ce5936d8545eeaf34d0dc862d3ba05ced727d5f7c583e74abe2df7c21f8e1839938f7e072fd1be8b9ac12b> [ 319 0 R /XYZ 62.692913 540.31685 0 ] [ 857 0 R /XYZ 62.692913 785.19685 0 ] [ 127 0 R /XYZ 62.692913 409.49685 0 ] [ 503 0 R /XYZ 62.692913 704.24685 0 ] [ 857 0 R /XYZ 62.692913 603.66685 0 ] [ 319 0 R /XYZ 62.692913 358.78685 0 ] <4deeaef1e0494e662dc8339c630065b16a7366c196c5c96ac4b40f936c834ee558364379ff905c27091479622d256f3d> [ 489 0 R /XYZ 62.692913 761.94685 0 ] <7606f391bed18ee91b5870e8b4dc6fd8e9267125310152a2c24ed84d38072b4eac8428cfc7c341aea0a12152a3682e5004de73449107366acbe3a458be8f3dd8> [ 503 0 R /XYZ 62.692913 522.71685 0 ] <9b8de3d294431330c1646e92110675e8b875468338a2e8d1b9cd8a0057653cfd509770f84d3f9210bdc1687082a0cc565ad2ccc918b17da3ba74bd21993e8aa6> [ 128 0 R /XYZ 62.692913 244.74685 0 ] <5ab94cefe1928ac0c274b4bb370f08f62bd3c3e2fb28fd1a6a67e1a93e6c6a7be608e97286afa5a31ffe6b6cad09b1436c5213b521f3075fab43e26e9e410b9e> [ 489 0 R /XYZ 62.692913 580.41685 0 ] <0bd8a5db97f9fe82e87d5c881b62b2f02832d2ebf5e540073d197f835e928e5367bd8c97e4fcaa18aa57e1409d43cb1a77823c5d544e7210651c13aeec340fe1> [ 503 0 R /XYZ 62.692913 341.18685 0 ] <526820be013d196d1ec2c8bf22fc41f942a3e1c3cf91167852cc8778bdf90e62ea1f7838ceda067218a14b20f7b51c430751be2f5861139a7bc8066ec8a228d2> [ 857 0 R /XYZ 62.692913 422.13685 0 ] <13340935777d1da7f1b514a658e684e214070711d12e8d42e2d030c033fe6c497759b160f5d23e36dcf43ad61f5a33fb> [ 130 0 R /XYZ 62.692913 552.44685 0 ] <270f1e1105b9a8171adcf6380124b37fe4e0f750cf34288efa752186ccf9916c8dd83908a4ed32ea960181fbbf02743dcf9ad304f3cf111b1667d76ecc48ed72> [ 319 0 R /XYZ 62.692913 194.85685 0 ] <4b6789fdce858b5760615c618f1de735d9b1e965d1b92da1bdf49cf3c1b0bf283a6afdde9bc6baca8892b23bb04b13c1a05103a736ad3892dd746f854f25757c> [ 858 0 R /XYZ 62.692913 785.19685 0 ] <9b1593108ef05823ddcff530594913e42226d750e51d1e1720b3fa6f58ebb2a2854856787244abd9fb8889d916f5cd47ef70ba66ba2b6c587c12f4691f68ffd3> [ 489 0 R /XYZ 62.692913 398.88685 0 ] [ 320 0 R /XYZ 62.692913 675.64685 0 ] <5fdb680dba40cc2715db96e9edfe98a9c828b68d6d2f70d08151d95f978da16e4b29f8dedcd579b27cd6af9ae8b5f917> [ 503 0 R /XYZ 62.692913 177.25685 0 ] <768e523f2ab0a2c75c2061a06f560ad7d94566201b61b6129c9c931ef69910ff3571e8d7eb0d767fc7ebcf4e34d769218728e4ae76491fc8d1e248b2aa2b6d3f> [ 504 0 R /XYZ 62.692913 588.94685 0 ] <6a020baf66c29076c263bb72a3d7d5e1ee975cd8c249805098a6679c1427c2b0a388855c76eebb9af57f38b93672766b> [ 858 0 R /XYZ 62.692913 603.66685 0 ] <5cee0ecf22f1dc9ffaf869705c03bfa91bc0750e38f450a4d6553f1c68a192b315e976e2138c5a93e1f25165db09e18f5aed4f1a3469962ea32dec5a27e442b7> [ 504 0 R /XYZ 62.692913 389.81685 0 ] [ 858 0 R /XYZ 62.692913 422.13685 0 ] [ 504 0 R /XYZ 62.692913 208.28685 0 ] <86e76bd155ac944378748649d1a9eaca964c2c46714b55be22b6daf46f3b6f64a90cd289554bac01dc73e686b2ca939965d4e5b7d606873c1282001a1abca450> [ 858 0 R /XYZ 62.692913 240.60685 0 ] [ 859 0 R /XYZ 62.692913 785.19685 0 ] <4176a03ddf145411768c3b4593a25fa29efcde4efbbc29f43507c47e3babc6e93917ff10ba3ce708f9f80c5b802fb51f> [ 505 0 R /XYZ 62.692913 761.94685 0 ] [ 631 0 R /XYZ 62.692913 288.38685 0 ] <17c721e693686b90127cfa78da7a24fb6a86a7ffcac0708163efed54a3c338b669e77cfbcf009172509e56cbe3cdbf302cfae0020f5a1bede16119586ffeb517> [ 252 0 R /XYZ 62.692913 267.03685 0 ] [ 271 0 R /XYZ 62.692913 468.59685 0 ] [ 379 0 R /XYZ 62.692913 148.20685 0 ] <3c221e1a33166850b642686edb5707d47fbd92746c222c72fa3efa67750f114b4a75f5150ea200040774546c34ea55fe1386edd50d7273df1b7373cca78c51e91b6fab1702c0bd32bb08f4481de517bd> [ 472 0 R /XYZ 62.692913 237.13685 0 ] [ 904 0 R /XYZ 62.692913 154.38685 0 ] [ 659 0 R /XYZ 62.692913 785.19685 0 ] [ 436 0 R /XYZ 62.692913 686.64685 0 ] <87cb17a9de1f9cb4125937301cb908329efb6e5efde4667ea592932d12dcde75b24db6d2cc7f5a91955319a4591a482dbe74255a8d865710be28aa06de188024> [ 444 0 R /XYZ 62.692913 785.19685 0 ] [ 919 0 R /XYZ 62.692913 439.73685 0 ] <34e58a44a1283f7a21abc136852b7e7e673f4741d1bcc4f934475f2bd38fc4e96ff15c96f52dec45a0248b867e5f1e4e2b5cbf43aa9cdbc9defca0fbf5ceb046> [ 537 0 R /XYZ 62.692913 213.33685 0 ] [ 843 0 R /XYZ 62.692913 163.43685 0 ] <1beefe0d276e93ca18dd4aaa3e3bf6a5cdbdee12247e9a64a5784c88b149b0e203b999e39de0feb95c900170b75863b5c51fd81b905429424741b27687de8cc9> [ 749 0 R /XYZ 62.692913 153.45685 0 ] <4bc7309a0b12efa7bfb0cafb97aabd107a74bf32a5f5c85af7200f6217b94b39123d3d7d3217f67eb7385374e5c6064ea481eda84d62f5c7f157832099b8fe4f> [ 648 0 R /XYZ 62.692913 136.60685 0 ] <880bcda92bfa63bdf14d3f5bc5760d40bafd847ba44d4c17d5172e3fae713d57cf0322b0345a5b4d7ef2eea39e6e1940f5f4794a0e7db7a9ce7251bee7058cc5> [ 459 0 R /XYZ 62.692913 785.19685 0 ] <70435ce62e5258e5bb2b7a3a9003b1c86ed2ad1124b82347ed018b887474ba9a129b040638274cc887d1ab86147db50c> [ 973 0 R /XYZ 62.692913 217.63685 0 ] [ 885 0 R /XYZ 62.692913 365.28685 0 ] <72ad5fdb7d722485ced73fee8d50d6305a66918521322f07000baa41666a6c3a55b21ad1b5817a9a87075a94b99b6ea5c29eaeb4ef785bc9f90c20025ccd099517967f24a02358ff9a8c7976c7d1d19f> [ 828 0 R /XYZ 62.692913 317.13685 0 ] [ 385 0 R /XYZ 62.692913 361.33685 0 ] [ 555 0 R /XYZ 62.692913 439.73685 0 ] <9747c3653bdbcb244b743c729823fca1b7f3398b0ac068c52aab7c5eb69972861bd8c2b5a7f449a499416148f8086b40c437f32ca243c355f4e915d68af8ed26> [ 911 0 R /XYZ 62.692913 352.18685 0 ] <4d19d45620e5b7e1e5a77356dcbf689fd612c41c509b814515966baf2a615070a5a9b9a98bbca52b9f5b0937cbd810419c20473d089b139e0238348ba8af8f96cb206a526b3de303d94e6b6925634439> [ 822 0 R /XYZ 62.692913 407.53685 0 ] <185215c9fcf72af40476c7dfa8bbd6d65d91ce9dc0972fff93d6ff2f1fbaca3759736a2eba82e5e202a198b5a4f5c8968b08983d006b85d72670a29a04a9e72a> [ 837 0 R /XYZ 62.692913 422.13685 0 ] <1ad70cf99bd74b937f08b79b93a5a74a3b43d7191b1345da9ec1e92f90a7ff1cc08f549ecda758b79431da682043298ea404d78f9907335d9d8a4c501fc9ba81> [ 374 0 R /XYZ 62.692913 396.53685 0 ] [ 587 0 R /XYZ 62.692913 315.23685 0 ] <0e12bc88235bde51a7ebbb57e11002a4f0379d950fc8831cb56a5d991a24d247633e6da6e5d682b151550fd63cfd1b58c255d40d2d005df1987ee63ced04a0d2> [ 794 0 R /XYZ 62.692913 785.19685 0 ] <2584a1b32c62a81488660739a084e6f8e5d2efdf0739ea1dc4ac31a4293fb6dda5af777330f60bbebf93d8fd5076f5fa57a5532b2bda765fe33bc6cc315b74c97a39ae5f2863efa8ece5e403b294b75e> [ 427 0 R /XYZ 62.692913 785.19685 0 ] <5f73c53e3e5dbefc00cc26b0a101ebd3c2085607d89d12f5f44c461261371947db10586e608355d6ac05207cc56e6655d790a4a0a78dcbf1f34a72fbb9a1d184> [ 982 0 R /XYZ 62.692913 297.98685 0 ] <37d06e1dd949ea08887b6bdca85a825c971e2324427b677cbe135a1bf40c6e4c8dc3154f3f9c0b8237a3134b63d5b2b7bc557797d536049c1ea463f6bb6c2230> [ 527 0 R /XYZ 62.692913 133.48685 0 ] <08bc2206265fc7a8474b308de0941565765cc59b7237218733749623bdefe60e08eef63d6903bff3b85beaa216f6304a3a75a46618426762f3e6a778be4ec01d5c08ff881152434aea9b7b8abc0bfbdc> [ 944 0 R /XYZ 62.692913 785.19685 0 ] [ 717 0 R /XYZ 62.692913 288.38685 0 ] <42f1966791d0137544edc021fb2541ec4507d3b1115c90c587c4e25f4c938152d9242e5257bd00a1c2d2fa73be85ad62> [ 927 0 R /XYZ 62.692913 323.58685 0 ] <1ef5b1845e0d3bc52f9b46683058aa7bc40ec468bcd2cec3155ef7664c497c568655f35cc01eec3210f6b3459d2de8f8> [ 598 0 R /XYZ 62.692913 308.63685 0 ] [ 867 0 R /XYZ 62.692913 166.68685 0 ] [ 296 0 R /XYZ 62.692913 305.98685 0 ] <7bdecd2a85c966d968761763e563920d39f4a4290b9730c4bc529a0919729b3d9d40789d99cb1561a06e73253c7250d70935468b926045ebaa1c5289f6a62c85> [ 545 0 R /XYZ 62.692913 159.63685 0 ] <16835fc93d84991602214b843f5b56203b1bd61c301796c188726e61a8d1dc1953c3698a10d453d46efd0e419d749ae7d654603f7d2dcac8b0171e48e4282276> [ 641 0 R /XYZ 62.692913 297.98685 0 ] [ 364 0 R /XYZ 62.692913 336.18685 0 ] [ 676 0 R /XYZ 62.692913 785.19685 0 ] [ 132 0 R /XYZ 62.692913 762.69685 0 ] [ 237 0 R /XYZ 62.692913 302.98685 0 ] <4b9905c93078fe34c7c6c5827cbdfe93853abfd73bf9a34e9b00e10b091eae8f6d3d885cf2554e59bb2c21ad17a3b01050c0c7a3a0601335eec006bf2a20e246> [ 958 0 R /XYZ 62.692913 251.78685 0 ] [ 782 0 R /XYZ 62.692913 312.58685 0 ] <638dc29aa211ddee9da950d127ff8184831377d95215f014c6a549d1f9b5a912f817d411f3afd6cb8443a4fedb513e9f02df9a1453ee4adecce2594f4a5c957777265e396c240a2cbd9d5e4f3ab7ec83> [ 507 0 R /XYZ 62.692913 251.78685 0 ] <932c3450884aa8eb6f33e6b4dc79dd227f2acd5b2062002a676bc2bfe185375b415e9dedac0d4259e8e0c1f9a8b7b15fd5339bd50580cb00fe46808d9edfaa18e8dd6e7d1f3b0be45188526cd79a70c1> [ 727 0 R /XYZ 62.692913 422.13685 0 ] <003db68b94a704374145fb06014b47f458a54e6758dfe25d3303471ff6f0aa80ead126a203dd9df0e38bc7a78580b88dd803160a91d71f30fd30984fcd97d400> [ 814 0 R /XYZ 62.692913 422.13685 0 ] <039e355bd78fcdad460021039c62f198d6a351a73677d6abb4ac9c076f9981d8039b94ed499ac3aebb9a51a09adb7ebc82f7e982194a787f955451ce75251312> [ 464 0 R /XYZ 62.692913 363.68685 0 ] <7d1b7c9d65f8c252fe31c4d71c1bf1a9be7613870a4aa6d19022948d6597aa5a44dfdfbca124db32f7fc89cec4f81bd4973382f994ebdff2bb4b84e91c8ad299> [ 738 0 R /XYZ 62.692913 424.56685 0 ] <0577bd1062457f46a5eff485492c7e4623d99becfe2b941abacc1c4f3e9303f54f1ede9b4d6091ea88a7d45a002457b76261a29dacf792ac0fcd18465d70f21f> [ 283 0 R /XYZ 62.692913 416.48685 0 ] <4c117679628c6cf80d1b4fac2b2844daf55c79b9c684b4ea22f1ea7f295ddb2f855e65bc6aef546cbd9924fbaacb707303435835ab515b4613385912abdd37eea91515dd96a2048cac4d4924aa595bdc> [ 705 0 R /XYZ 62.692913 338.08685 0 ] [ 935 0 R /XYZ 62.692913 785.19685 0 ] <3e629886539df6ef848815a436469d40cbc286cc71aebea8e83301743ab53dd9b36a924b98083d717ca135e020486e7a94d8f0de4e0ae3b9d7ec177760e8c1e5ac2cea26ec9096575604182ef7cfa659> [ 863 0 R /XYZ 62.692913 675.64685 0 ] <2fbbfe41e79adb81d67c4618d5480ac05bed82b6c5926e4cc5374056bab689139f30deda4c6fb7de3a82d903d302cf7bcf6e3e1c3416012ac98b524ec92d08cc> [ 892 0 R /XYZ 62.692913 390.88685 0 ] <8958daad8f29c1d7dd91643e7eed311dba766efc3daeabe819ea2449625ed78e49f2deb35579cc75d0e506408aa8f5a1f5d932691bab30b90721d0bb6e90ddbf> [ 399 0 R /XYZ 62.692913 457.33685 0 ] <5abecded61a492da21ccf31284df23885dee0b296f5adb987662def32e4c45a40c78f3dbf03b9e32bbaa235831d46849a42db689faf4e771b4f10094b67bac3bb20e84f463e4f9971bfbd01515116269> [ 307 0 R /XYZ 62.692913 785.19685 0 ] <63b36a8b5ca5faac8a46d83c49decf2597c2f6f05d9bcc1896827ccde025c5381a6bac8a581c6026cf3382cfd2745f1ff56f3833a1cde901d506ff9179a1ce45> [ 519 0 R /XYZ 62.692913 212.83685 0 ] [ 430 0 R /XYZ 62.692913 346.08685 0 ] [ 261 0 R /XYZ 62.692913 363.68685 0 ] <098c546dc7e40681e9e08f31335428c5962db65412593dec19a3bd812839ebd4d9b1cf59b8e5e5567db01187f65af2263ed1a4853afb2a3fe51f3c54202516b71491987e76b335b8dcf29390173fb27d> [ 369 0 R /XYZ 62.692913 404.53685 0 ] <03555cd0db72b53241bb654442f4cd72fc16c3b80015765297cb4f955a3023f8a89ecae1c85c65d6487ca99062cc16a15cbf1e00ee9931e66e1b23715678e075> [ 491 0 R /XYZ 62.692913 141.48685 0 ] <25311e052b2ce9943f644e1cf40c9a71192e4fc48cb7e00d4b1ebbabf5d380c6b139522b0041c21a7c3e01261534ee66ac900bf33c4eced447e574ca53b2e259> [ 247 0 R /XYZ 62.692913 785.19685 0 ] [ 899 0 R /XYZ 62.692913 234.18685 0 ] <37749f60c0a50015ca7a1c5c7695d1fb392017b92a2b0074c6a0de62201a453c0b4b343a4f6aaabf334ebe564aab30a4873af56833c5f742f4de460abd044475> [ 565 0 R /XYZ 62.692913 404.53685 0 ] [ 332 0 R /XYZ 62.692913 176.68685 0 ] [ 608 0 R /XYZ 62.692913 267.78685 0 ] <3097f42e54624d35606e42125197686ed382747de33b27ea3003edfe8825601c5003f8425c3f9ad5909f265d3630d77729308618315fc4c8a53d6ce277271d9e> [ 873 0 R /XYZ 62.692913 515.74685 0 ] [ 686 0 R /XYZ 62.692913 457.33685 0 ] <38bfbc5647e74c715238ec641e3a8dff4b077aae0b7f172f2a3d399af92d58edbe65cbc588f09dfe7f686b23dced0b10eefeb260383b0e99711f60b5e08699b7> [ 323 0 R /XYZ 62.692913 785.19685 0 ] <04ac16d3babda7711b5a0420d19df6ccabcade6d77afee6592d4381436544be487f51c1d1da336a72fe943ae28a2461af41da59e2bb6e466a7d9cf8de2e55e3e> [ 772 0 R /XYZ 62.692913 785.19685 0 ] [ 760 0 R /XYZ 62.692913 457.33685 0 ] [ 950 0 R /XYZ 62.692913 302.88685 0 ] <80768312e36a9e25decfdd21e1bbd8815151b8bc56f1aeb6e236c04a664ea29c5f00fb6c5b55b75cc0be43054ae92b93ae26b725706aecbcc8115dae9804e8c9> [ 576 0 R /XYZ 62.692913 373.28685 0 ] [ 667 0 R /XYZ 62.692913 277.38685 0 ] <02ed3adf5430c8996d97799869efa0697aca439bec81801837da149b58f8c9856ca479c379a3e403276a46f0615aa0aefc495b7cd9800018b38cb9bca518e109> [ 804 0 R /XYZ 62.692913 277.38685 0 ] [ 409 0 R /XYZ 62.692913 496.93685 0 ] <0990de86bfbf27b458122f437a0893300dd1ae3e6206544c5cf92dc66cb86df837d66c8578b57be192a103a95d3ac8962b25f38c37877462eb8c0e4e235d5b6a> [ 620 0 R /XYZ 62.692913 422.13685 0 ] [ 343 0 R /XYZ 62.692913 240.60685 0 ] <095cace745637affdbffb596054b2a0b8cdc3c8b4407d58e384b0de98ab16b4c44007b464786068681cb87ad4365dac59956fa83f6e73b83eb07a8d68722b457> [ 478 0 R /XYZ 62.692913 439.73685 0 ] [ 360 0 R /XYZ 62.692913 369.23685 0 ] <708df55ecd1437f86768c211688752b3beefb6794faaed5aba556977b03df5be872836fc25a9aa8b76eaedc189d9502ff934faf09d503f22f1ba6829eebb8039> [ 389 0 R /XYZ 62.692913 344.00685 0 ] <681904ac9102ed758f12275248d138846f6a61bd90bad87c1793b0c437d4c61d8dad393dfc4794ab8cde8cf838db12e422cab6b8308fcdc68d9ecdfca002c7a0> [ 966 0 R /XYZ 62.692913 152.60685 0 ] <434a4b0fd5b312588eb0d374cf84f41b587508d6d8ee7c07109cd2fd760279986121ca08a43b99ab5221df89cda1707883871b97936883415f68d03467df687a> [ 355 0 R /XYZ 62.692913 621.39685 0 ] <128ae18d9238745b56b4986f7ccf90ad8adfad05b0ddabfee71012c25633a1e4342dc758e9998bb8241e75084b10dec353385d4be4409385d2121ebb507670f7415512a300a06c16425c09ee7666d6c1> [ 694 0 R /XYZ 62.692913 414.13685 0 ] [ 417 0 R /XYZ 62.692913 439.73685 0 ] <78c04b00150efd95aa0e0f2de3a428afa40e7d4658a95bd151749469226869f1954d3af632721e7a5ad50fbbbdac5da80292c1b7c0b99bebe4321b76af803db0> [ 859 0 R /XYZ 62.692913 603.66685 0 ] [ 859 0 R /XYZ 62.692913 422.13685 0 ] [ 132 0 R /XYZ 62.692913 719.31685 0 ] <20379454f2631822cb46774948619e8be91fb3132c06a61a0f7c2e741df6269336d581abed2ef0170e4bd57e2ff50e04> [ 133 0 R /XYZ 62.692913 272.54685 0 ] <820909d8db7f0f8a089ad589d77601051068d0e25918c0f3d603b5d6312b91e49b8a817c98f3958e9b881ac345773bfb> [ 136 0 R /XYZ 62.692913 622.84685 0 ] <036516a831e21ae3ec94d9309f9646cad008bc88b99953172528a391329d5da0fde4fc0adb5480be20cf218df82f3be5> [ 859 0 R /XYZ 62.692913 240.60685 0 ] <32a45d0b7e400fe32a53d29239b8aac21423489621d2714395daa556fbdb203426ae51839d7237808d9f6e359d4e21d1df6719e7252f041510c32af775cd6331> [ 860 0 R /XYZ 62.692913 785.19685 0 ] [ 137 0 R /XYZ 62.692913 761.94685 0 ] [ 860 0 R /XYZ 62.692913 603.66685 0 ] [ 142 0 R /XYZ 62.692913 414.84685 0 ] <44780b72be28d0026b8319ca9454eda7ab476662f14e94c4104b57410b58c1a04495f3fec24ac9a6b655358b5b9a770ce88e3c7d969531a6a8d3647dffeeef86> [ 860 0 R /XYZ 62.692913 422.13685 0 ] <4e2bff81d9b1f21184a7f055f17c08479321bacb75bb4beca58d7473b5547f8cfeb27a3fbc97f407fd2ca84b43ea5d3b69084dcacab4da43c338a2ffcc1e7180> [ 829 0 R /XYZ 62.692913 785.19685 0 ] [ 253 0 R /XYZ 62.692913 595.74685 0 ] [ 772 0 R /XYZ 62.692913 568.46685 0 ] [ 983 0 R /XYZ 62.692913 785.19685 0 ] [ 565 0 R /XYZ 62.692913 205.40685 0 ] [ 333 0 R /XYZ 62.692913 761.94685 0 ] <78ab7c8f21a15aba55b5354d1ec4a79f914b4b60bbbec421bdbb4fdae49f5fc67ea96e82eeb2dcc78eebd38e28f6612c> [ 738 0 R /XYZ 62.692913 243.03685 0 ] [ 750 0 R /XYZ 62.692913 658.04685 0 ] <0606d374336f21d061ca1b48f149995e6b9e9badb6fa883ff048533b8f940b07f55aa2c8b64114f040937811e6f37cc125d67ace4990a29775c9ca614b68b36c> [ 436 0 R /XYZ 62.692913 469.91685 0 ] <1c660bd7eaa45480fd67475cca25ee06498a955e2a0144b86ef6c2a3a8227caab34b6d955d641488373c0da6a5f61b3bcaeeeea2614c3a08dba8b9b0d4b9e916> [ 649 0 R /XYZ 62.692913 675.64685 0 ] <0a61c2bf84624c9ef8391b64ea68e3e42f16fd0e71d706ee384e95342b9ad3a563b7b85122a04ab35e50d065b5a14c879e77b0623bd0a2c7a368ddda7548a3a2> [ 659 0 R /XYZ 62.692913 546.46685 0 ] <251c7c6baf94cf2b9d59132d68d8ad47a66f7e97331cde2bc8f7da9a29aaf85548c2110b6c260b9b8f2ffcee4af713ac2024cda8b3780c1bfcd602a9014c108e> [ 874 0 R /XYZ 62.692913 785.19685 0 ] [ 959 0 R /XYZ 62.692913 785.19685 0 ] <607c90420914c1fbab65ceab689ba3018b9659dc77dc3162ceb9e07f9773ae1a901a1ffcb357927b0669d17ceeb5b101f32a6aac3ea54048c3be765508d84926a3a825106d77f191b8afc3a5dd238fcd> [ 369 0 R /XYZ 62.692913 197.40685 0 ] <93eedc9360003691eddb5437676eb4d08dba69f9e28bc782ea7ccb414338fa9249b27ca9e8bedf0f991b88588a7841ff775e466470c2a2ef25186cd268d20e4f> [ 951 0 R /XYZ 62.692913 704.24685 0 ] [ 380 0 R /XYZ 62.692913 704.24685 0 ] <10b21bf5f2e9c2523ae3163966f98d518243175df179c101537bdef9485b7644162a63b0cbedfbd92bd9dc8961317e11942df57311f2b02f2b0e61e2e029a2d12da5c15dcf6efdbbb9087af6d754a625> [ 928 0 R /XYZ 62.692913 785.19685 0 ] <306e713931bd5ca6a2a30302929165821b5602e7ed04be767bed98fe234f678290fccf46428d02affde149203947105d4771f0acad1cd559c62438c610e54459> [ 885 0 R /XYZ 62.692913 140.55685 0 ] [ 598 0 R /XYZ 62.692913 144.70685 0 ] <726561069eb253d68b2a9c5bae2e4032a5fd721e9777b7fd1792c138555d79fcfc1d3399c936f385722f990e74dc19857599b122b69ca4a922b15fe72963b173c4af3df64945d42d125a6cad7235a2b6> [ 967 0 R /XYZ 62.692913 704.24685 0 ] <322d0131c518c425ab00a246906bb197aba07136348804e852d9d28cf2dbabd8b8552030a519d394a9df3cdcc8cbde1bfe5ada2d865db35b97f02d104f5688352d31c473ff108527e24b764f5034e5e8> [ 863 0 R /XYZ 62.692913 450.91685 0 ] [ 386 0 R /XYZ 62.692913 785.19685 0 ] [ 555 0 R /XYZ 62.692913 275.80685 0 ] [ 911 0 R /XYZ 62.692913 199.25685 0 ] <13e3eab7c21cbe21e9039745912ded5c710d61032124b706a6d7581d3fdeb095357abdfc77447bc11f029e05e0516a5045e324ef94652bbfaffd7ea7204b1520> [ 360 0 R /XYZ 62.692913 170.10685 0 ] <3f83ed2395c6911a4c81c5ace25082d087f33bc3c6dd20e8fdd5f2ba1a331823e16531edd8d274f7f185d620778dede492eddef74c3e6c611cd4afc4ea6b25ce> [ 760 0 R /XYZ 62.692913 275.80685 0 ] [ 632 0 R /XYZ 62.692913 589.14685 0 ] <87f6134e661aebd763afc2b8f9cbb2492e76d4ec9a43059c3e77245a953992d8d65a85eaf58a31c9ef4b38c11e437ca1813098a79804e23ebeab3637bdd971d1857bec2134825af7f4d8527b8549b1da> [ 868 0 R /XYZ 62.692913 675.64685 0 ] [ 546 0 R /XYZ 62.692913 686.64685 0 ] <745ca3eb66aa56c8ee5971f81dcfb105aec75f553740548cf2b91b21985aff01bc7c833828c66702f22f4a2d76db09bd352b01388f55571109da61bc4fcd8e99> [ 508 0 R /XYZ 62.692913 785.19685 0 ] <17b34f47f9b7efbd3bb8b29e0f2072527275718e91a9b606e94c16d5ee8b6b602f20f5a9967aad8f889622079f650171daf1f8e7684fbdc90027c921629fb60d> [ 620 0 R /XYZ 62.692913 128.70685 0 ] [ 706 0 R /XYZ 62.692913 785.19685 0 ] <08c74472463d90af4b1593e8c2e606f7083e8079ba5e408754aae9fc845c89ad46c11b26b352db44ca0bcd81dfb517f6418e522318a482da51d9f86be73a7c06> [ 822 0 R /XYZ 62.692913 182.80685 0 ] [ 814 0 R /XYZ 62.692913 240.60685 0 ] [ 538 0 R /XYZ 62.692913 686.64685 0 ] <9e7be3b89e2cf2fc2b4172a58d9e4e30f226be0ed754733e6e5f8c96d39f87bb04bd2425a0ebd17eba7f822c32339b2bbbdc6a81166e19af3a0a4b48852e4b07> [ 271 0 R /XYZ 62.692913 175.16685 0 ] <56c484da02ca13ba7ddb0edbd3a8068df97122eb3d5f4ff2ec9ea9809db066df9a0c053f738db445d2305325e909fe1ede9c924ee96348249d23823665e21268> [ 974 0 R /XYZ 62.692913 526.74685 0 ] [ 676 0 R /XYZ 62.692913 542.86685 0 ] [ 687 0 R /XYZ 62.692913 761.94685 0 ] [ 718 0 R /XYZ 62.692913 785.19685 0 ] <59ca4103cf46129512edac64be45d35ed8abe0db0a25f2afa5adebb5e773751a8bd5a66de75eeebc574cb09427a838714d9b4d855d14306a1666833cc1977bb0> [ 344 0 R /XYZ 62.692913 785.19685 0 ] <147d182f45fa5b52b5d240206e21f0de9da2ab5f523ebf904f7e530199de5fa53ea841561b90808def3139b3521612da90f28771d26fd6ac0852b171d80d655f> [ 364 0 R /XYZ 62.692913 157.65685 0 ] [ 783 0 R /XYZ 62.692913 785.19685 0 ] <0414cdb7e7053b68b430345d979dddcd99e8c5cee2c38d012b3799a8c6cb890d1d092fd8d6d765740d2cdc556398dbc209e808456c7099e5d47d828680b0d10e0eac9cd118a46e6722fd0e334b44ebdb> [ 668 0 R /XYZ 62.692913 785.19685 0 ] <678a81b0d697c63b3550885d7d11ff092c5dd1e53f5088789a82fb54977b78195e2d752bb58aacab60cd8266ad33bbba6651bb37ef4d76d63f30c23a5516e8bb65830295c0c30752aa7252bb8faabf5781868676880ff8914446230a0ec61e71> [ 727 0 R /XYZ 62.692913 215.00685 0 ] <16283cef5a9e18ee72959b71b075105a5e5963b888ff93e7210c6661443c899efe58f657cf100011531c41bd65e1764f2d0505391901c06bb97a93a8c452329d> [ 283 0 R /XYZ 62.692913 139.05685 0 ] <4a490a6dbc2a0c78ee4b605253d350182da954bfc96e96ded6b995b2105cca042b08a65912267369a293b581daec5bf6> [ 464 0 R /XYZ 62.692913 164.55685 0 ] <02a9bc27e3aba759ac3e6c7065d10ba687dfad1335d89e6582b4b506b6c2be429142d053dc0f9242f59e50c3aadf0415e2eca52f370b1f50cf798397b53e99a7> [ 261 0 R /XYZ 62.692913 182.15685 0 ] <9a1eb8cd9a271f9e8404d5956ac13ea5ee306da53b455f8e0403fe73d9c022505d0b4c30d68d1712c361cb3e355fb0da7d23fc23666790b96db8259785c0f217> [ 837 0 R /XYZ 62.692913 223.00685 0 ] [ 431 0 R /XYZ 62.692913 785.19685 0 ] <66a8d439258af208691188c959cc08d2259f42fad03d5f6865fb9eccc95e657afe248452b6e63319466b98528b48a4dfccb2a734a6dd1d0cf115a5eb67fbfad739338d5d9be87125d7e0f1cb403a365c> [ 427 0 R /XYZ 62.692913 533.26685 0 ] <2b4c8dbbce4a9270af190a706fd69192e519629f5edab748ad2b81ad56f28c1c847b48339819d6577efb946eb170074c0b8f431039d5a927d03ed95cf1cff1ec> [ 794 0 R /XYZ 62.692913 474.16685 0 ] <3dd4122baaa3cf29a7227b994f51a63411227977510cf779240059e3c9d30cb23e2d48518a31591aedc89fe5885c155b95dc648245feb008090bcac7f1773a5b608c8fe635a3c2c0ed02f8cd871bf17b> [ 935 0 R /XYZ 62.692913 586.06685 0 ] <5f48e25407b741e9d7adbe99299c5f825132b0743b8598509c57634075b0465c4e41414541ab5db712908e3d275c4548d3409fbc9b193e7527673198be5a4bd940bf90d9a7031bf1b31ab4d8ef91dcde> [ 355 0 R /XYZ 62.692913 439.86685 0 ] <4d5b23c423c08b1405f47ae1401d6830a05bb3dfa712d371eb32eb64c7cfe98f5886fca27e9ba0862cb9d92d022d757f66d9c4a826de91cfe0967e31367b7302> [ 238 0 R /XYZ 62.692913 696.19685 0 ] <022f2b17895f0d92c93d4d76d91901bd1c80345529f9d68cac4b564b578c9c3c37321dc0556e99af71181cd32dec1538fc1adc33c86bd927934261b632a9195f> [ 694 0 R /XYZ 62.692913 207.00685 0 ] <496d4f5e7a6fe1178aa7ebd33a19bf20718f61a14b0f0461537c2565a0cb2d4da56ad57b575930f675a4927b7f3a0f0e36551808dde3ae79e661fe9e92903c44> [ 576 0 R /XYZ 62.692913 174.15685 0 ] <6ae5130b548e03066456590f80403a71fe8478c9d3dbdf8cabe1e9bd4445312ae4a3fc6e4f2cbe50c268eeb6888b64ff133a2e94e0851dda10e4a00b1140af8d> [ 528 0 R /XYZ 62.692913 693.24685 0 ] <0c555c46f3c4938cf93e401aed4a7431a9bdeb1710702b48a328e64fb826e6fc7b7428545807b3c1d0f1651a25f8914e63b745a5b6e3d91b7ee6410699515e6f> [ 473 0 R /XYZ 62.692913 442.09685 0 ] <756abe99c2a119924aeefcbcd9f7f27ee0ceef1b3f60ab7cb02056ebe3414719d741b439ef5fd45cec88d5d099d7ca0774876e8b6d3f86af9da2b5185a466664> [ 444 0 R /XYZ 62.692913 568.46685 0 ] [ 399 0 R /XYZ 62.692913 275.80685 0 ] <3de0360a09fdf09d3d8fd9ffd0504834171748de15458d1a3c94417c11c5206c4595ab09ac7ad6823b11ea341917fbe57c2513f7d7b5aa1c74d5881d789f47e2> [ 297 0 R /XYZ 62.692913 761.94685 0 ] <967f4815dcf8115649ca979fdd5a9a741cd8f84dde027e7ff72827748b19bd5846990e93bc34c1bbd9f93ca4bd993a0a6beb039cb2ff32cd6b481e5e1b632645> [ 642 0 R /XYZ 62.692913 785.19685 0 ] [ 478 0 R /XYZ 62.692913 258.20685 0 ] <0fbd6cb30961810559211a35264837aab93c61b6b6f52044253d1c438bac1772577a991708db8f847faa2496511736470e7df6c44f1f33d4bafd9f25e4173bce> [ 892 0 R /XYZ 62.692913 209.35685 0 ] [ 900 0 R /XYZ 62.692913 785.19685 0 ] <7eb73183f7ef62a0efcb83e005f0cd3e9a807ced8dba340c0d75ec4cc9977b36> [ 144 0 R /XYZ 62.692913 762.69685 0 ] [ 844 0 R /XYZ 62.692913 595.74685 0 ] [ 520 0 R /XYZ 62.692913 761.94685 0 ] [ 459 0 R /XYZ 62.692913 586.06685 0 ] [ 417 0 R /XYZ 62.692913 258.20685 0 ] <53750e193ad9f5204aa39c64bab55d3689d1b6edf0cecf225bcf35ed530a5e1c6261d67b0f844fa03bea806eb8ebcbc07f3c4cc3b2db5b038f456900f4c8fdce> [ 805 0 R /XYZ 62.692913 651.59685 0 ] <9b5a976462c1a8fe1655a2e237886d857a1a82b4f9d71c43a4ccb67b367f282a2a2969729310ee9ba822dd3fb8d16fb6c73cdb77e76a4ab0a488f9aaf0a52870> [ 920 0 R /XYZ 62.692913 785.19685 0 ] [ 492 0 R /XYZ 62.692913 503.14685 0 ] <0140f2d17eefaa088e74e73d120c31103127c98da0ad082995b9b4e7636806319fa9248b9b6e32aad1bb649fc5948658> [ 587 0 R /XYZ 62.692913 133.70685 0 ] [ 389 0 R /XYZ 62.692913 208.67685 0 ] [ 323 0 R /XYZ 62.692913 408.16685 0 ] [ 307 0 R /XYZ 62.692913 578.06685 0 ] <26b5f916e1c05fb8481a62b44b78b6ee033f09581447c38e7bb3db0c52193d5293bd78a35967e6f3f79cb4e304e1dc28680c4f0d875f6fc90386e288bf939551> [ 409 0 R /XYZ 62.692913 344.00685 0 ] <496c88b3b4cdc3c5939c7849605b834da29e3efac5fde8971bd7671bb6c125d295f7fec45856537305116c65280cc4647ad89930447e9bd9b90d56a4fcbe2eac> [ 247 0 R /XYZ 62.692913 586.06685 0 ] <6878f481004e72870a8533acf90ec3d8587afa2ec901c80dacd0c435e5302f5932ea26281ab98c829d93c2b288f62733> [ 905 0 R /XYZ 62.692913 704.24685 0 ] <2ab9527c96634c476a7a250001a291b64ee400920d076d78cb33c9453262c1e56731e4f9bdf44ba9b322dba53f272aec> [ 944 0 R /XYZ 62.692913 274.46685 0 ] [ 609 0 R /XYZ 62.692913 785.19685 0 ] [ 374 0 R /XYZ 62.692913 215.00685 0 ] [ 144 0 R /XYZ 62.692913 719.31685 0 ] <638e1db67b34d40bca089ebdcf2ac26ef27f2a63962e14349a277f17ffa277035c36621c0238f5c51444472f0ed66116> [ 146 0 R /XYZ 62.692913 409.55685 0 ] <021f2b334c5a92148555241d6c07e79ff12651e5540668b7d625c50f2a583be01b0e58cd454f7cffed13401a2296b6f9> [ 147 0 R /XYZ 62.692913 785.19685 0 ] <8d9f5a639a60569f1d28705ad5653575bf1504cab4e575b01edca3fbeefc99aceb5d320d59129bab836d64e7fdff00a53cdecbfe7c0dced6794f2088794e5488963cb21ab2cfffc0158f6b4b84c76fbe> [ 829 0 R /XYZ 62.692913 586.06685 0 ] <5a1130ff56d3c0bf12126107a199e09edbb3442c581f20175257b824f69c08b600feccc57ca3a7078cc0d65acdbbfb25> [ 323 0 R /XYZ 62.692913 209.03685 0 ] <63adeec4ba5b3a261d58638e885d00b76901ebed887ed50ebbd586c459a63ed010791fcc2b1344319347a0bd4b4bedb68b1bb0bcf7f46fde3fe6e7cf311baffab2ad94aec96ee8427fe8d569ca64533a> [ 528 0 R /XYZ 62.692913 367.81685 0 ] <89c5ec4b1c0b8242a8fd175ad34c8e8610af8c7b5c88c564f9a2f53f8588210a7e3d88887507e2b9430c2a8ab480fd4d> [ 772 0 R /XYZ 62.692913 386.93685 0 ] [ 473 0 R /XYZ 62.692913 289.16685 0 ] [ 928 0 R /XYZ 62.692913 446.96685 0 ] [ 959 0 R /XYZ 62.692913 447.16685 0 ] <19c49e89f1fe6b8e470a54aa8ad508a079c3f0369a0a2836306e0f1a4a8817641133412916d690f61f28197bc35408674c008077a73d50bbeecf89976fed849b> [ 844 0 R /XYZ 62.692913 316.71685 0 ] <8fb93ff84ba9e8d8235baf35a2e746a81c2d9213710c0b220db43e72dba42939fd2b8e6b48f2d0884b3f7ed21e6ccca757093a92c62a5a254adfbbf4b664d7535313493a67c68e088c5b944e731f7516> [ 247 0 R /XYZ 62.692913 386.93685 0 ] [ 706 0 R /XYZ 62.692913 621.26685 0 ] [ 750 0 R /XYZ 62.692913 476.51685 0 ] <4efabbe0b68a80bcbc52b33246d619910df9e20ae9a6bd22ff7646ec1d4071d0cf719ceabcbe14e5f662533b4bea74e9> [ 380 0 R /XYZ 62.692913 522.71685 0 ] <12f15be4d9e67b0830916d832ee7ef87f62e13c24463ec581dd2e3374845002796a2e8431ae158ac211fc992c0dcd20ba5532f0ec45f913e381ca584f65820d0> [ 649 0 R /XYZ 62.692913 494.11685 0 ] [ 459 0 R /XYZ 62.692913 386.93685 0 ] [ 886 0 R /XYZ 62.692913 675.64685 0 ] <6810e2d5e10472328e987feb53af08d16b5d92d2149ae3e193481a4a15320274052b2ec8eff7e565b1f8b31f737200fbcaf8df00c386f8632117bee9c848510d76969ebdfcd312d4086d5ca3f47c5766> [ 668 0 R /XYZ 62.692913 485.66685 0 ] [ 431 0 R /XYZ 62.692913 568.46685 0 ] <8de9db76de5ab908b077dcba59b014aefaa5467349bb42a102d57ca161d62f42c4612b752ef5c94a22d44846b9860cf5db133cdea163a7c6b9429e23825b848f> [ 967 0 R /XYZ 62.692913 479.51685 0 ] <0ec3ca730599e7a423bf16abb752dc5367ade6dde782965074f6f46578a8e2da72572c1477790c3bf1cb5812d1f2f0b7> [ 805 0 R /XYZ 62.692913 452.46685 0 ] <0f8539aca55151fe51956d8128a91a403f3cbea89a56810457f8508b891e8eb18b912a6bca3aaa9f68643b45b79ac2cdf18f38d3fc89ec34f7ba772caae7101f> [ 386 0 R /XYZ 62.692913 560.46685 0 ] [ 556 0 R /XYZ 62.692913 704.24685 0 ] <28f5b05f4e18e7e5e687977da6843c1ac2a725c899e6740ede298136b987d9ad06fc74401c08c67191db35cc5359e901448d71081043afedbdb75245f519a387> [ 912 0 R /XYZ 62.692913 785.19685 0 ] <081265590159d2a22c2688277b3b414bfc27bb8c2c746528aa1a89fbb6a5654dd43578be8dd0fbe6126957746502a0eec8ea25b0e7d663789cd8299621342ead> [ 761 0 R /XYZ 62.692913 761.94685 0 ] <12a8b66af9906d405fbd9f1ea51b4ff61a8cf9e2c6ce7c235fa8e0250c30b9a1c701844b69044ac8ca8a39822573061e09f876d7747201ca54b2b3b511080432> [ 253 0 R /XYZ 62.692913 208.21685 0 ] [ 935 0 R /XYZ 62.692913 361.33685 0 ] <6046a8ce46765ad2e1b1a58b0d16398b6f850573f55cc7b6d3aa847d776e5a363dfd25d511d636e6d2be7520818c5763cfb6eb181395dce4dbde2c67b6f70b7c> [ 436 0 R /XYZ 62.692913 305.98685 0 ] <0cb748b217194103b107f0e47803b997555c905d687e4303d9f5934546900792a9ccad81b0cdfda42ad90a1ade86a313a68088b96ea396ebddc68585a8bb2c7e> [ 621 0 R /XYZ 62.692913 675.64685 0 ] [ 538 0 R /XYZ 62.692913 487.51685 0 ] <59aafc8ae2f35c64ecc4063939be998c9660d5bb0d5119b78febe7e352288e48529d61e6b93e92f2129b7f5019aa05b890370ee24186687e369eb0ab49df4f34> [ 951 0 R /XYZ 62.692913 487.51685 0 ] [ 659 0 R /XYZ 62.692913 347.33685 0 ] <2fcb8404e9c900c2e29cb6522b3eea8e0f3621ffec747ceff56a3422fcff2d9f3a567dfda7fa8df8cc716165eb06fbe2> [ 974 0 R /XYZ 62.692913 327.61685 0 ] <5d25dd90bb9d4a543dc643d21b0b755a3ff8d79df8818f936647ef0f70e3c80f39ab5928d34c8e91033314f5b1c6248dfdaa169d3a9d5e867f9905e8d970d8fd> [ 823 0 R /XYZ 62.692913 761.94685 0 ] [ 284 0 R /XYZ 62.692913 675.64685 0 ] <0e87874357337e35284ac2bfcb428ce4def48e3dd052851bd6ec547a4435c68be53b7711212452f42ad2ff129d9e7fb4a22acd18c81a810d540f75db2da9e9777ebd69710b7f756c96dfee846c99227d> [ 945 0 R /XYZ 62.692913 785.19685 0 ] [ 577 0 R /XYZ 62.692913 704.24685 0 ] [ 400 0 R /XYZ 62.692913 785.19685 0 ] <053e035e4dc69b3af639b4fb9ee2adac657544b007ff8ce0928d79d8661b0fe12252434592d835a7c531bae8feace88cc26260c985bf441fd3e6414e8ce394c13a75ef62b67ecc23485b1834a0538490> [ 365 0 R /XYZ 62.692913 704.24685 0 ] <7d1eec71f72b4af6aa33090039987d150edf41ec4304f3976f4cd1494fa466f1c78d05426028e6a0b8545bcf89d9643b236e5c0c1c9c92d628dc4e6aa8ec461c> [ 900 0 R /XYZ 62.692913 560.46685 0 ] <9280bdc2333f00453d70a4da5a825a5eda9ecd5c7e079b5a54084defea0de530dea41795391bb2dc279a714a802ef788504961dac6ad9962b7c44ad363728b65cf1d178f8606098da4d0e7be86c5efb7> [ 728 0 R /XYZ 62.692913 704.24685 0 ] <0c69b8219d5be982b9df3a72e7d03e5c92fa8f718817e6aee5a61307b00a7b681d8d88a99ed592cc3c75fab3e56975fbae4edbaabda00b2f9149fa0f9da940de> [ 427 0 R /XYZ 62.692913 351.73685 0 ] <00ae7ecf06ae856218e637e2cdb06140827e116975955f5a2608410b5855941557c1cb1b1fedece0329f16b37bb3e44e9435a948658e31c19b63738f83ae840a45c1b97077b70746d0f5bd6e6380ae2a> [ 520 0 R /XYZ 62.692913 580.41685 0 ] <5e56cf1995f3ac8b795fb6171ddd12f1c12db663faaaed33baea02fb50db862560d1d3700398940604c14c55bcb36787a93e6fbbf51b0ed3fdf1548588913c59> [ 983 0 R /XYZ 62.692913 533.26685 0 ] <63d566665b13dc8695bb56bfd4e829d2ba0381a78428a8cdbe33b2cd4d37c251bdc9cb00890d0af59dda91af0be605d9f7d0b2fc688f63f45ba9170bf459af6c> [ 465 0 R /XYZ 62.692913 658.04685 0 ] <9e6de40676fb3df1ff2d51b9762a0e443b422c62b9740e385c633ff50bcfd5ff68c8f266570842a4a6c030b709fba97733aba991f5545fc393121239bdfebb53> [ 370 0 R /XYZ 62.692913 704.24685 0 ] <31390a9e39ae903cca3096e3a09d78f3d6a60b6555f3cefc7f8d12f19ead5a21d93baa1eefeca9dbb6a624534de510d488e999a9f102c281231726a9517af871> [ 262 0 R /XYZ 62.692913 704.24685 0 ] [ 739 0 R /XYZ 62.692913 680.19685 0 ] <828f8c26d5ae4cb7d4fc8865ef8e2c78992a5015aa8975201b7117e0577d6e8392c35747918bd630954432f447582f42b68063f9e65aa83c5e82a82bd327bf3a> [ 794 0 R /XYZ 62.692913 292.63685 0 ] <18acfe0529ab8f9d9eabd2535ef1b4d750098887803071c7946aaced6a568c5fcc428685b6d799630398fce05d01829b071f315375b78037d7755c8ddff0c1feda3df0ed2d413f8e8a8875b168a373a6> [ 868 0 R /XYZ 62.692913 468.51685 0 ] <954a0f869bc497d50738b5cc56aca90f4e4fbc00d65b1d44ce64dabaf295224a6c50d40fea6c048a4dd1f99645d3dd4d18e45a57370cd2bbd1106af288f3974ebce938cd4ab25f11e2752437f547f935> [ 355 0 R /XYZ 62.692913 261.33685 0 ] <6125e5d1348ab62cc20de5199f8ecdbef9fe10acabbb663f9880f4581b4a3c1f20ef02d5c5ae8ac73888e48f377528eef5690441e236885ffab8fd07df4a9d03> [ 479 0 R /XYZ 62.692913 785.19685 0 ] [ 642 0 R /XYZ 62.692913 482.56685 0 ] [ 238 0 R /XYZ 62.692913 514.66685 0 ] [ 838 0 R /XYZ 62.692913 785.19685 0 ] <4bd4e981de053d43f6d84dcf5282343994acd0a61377c68b11b2f75883ae23cbbf2234e89e067798ec47c7effb89a1dad3039d52217dfdf857467d395c567e1048cd24df0ceded8bd0f65b34eec254d4> [ 874 0 R /XYZ 62.692913 568.46685 0 ] [ 687 0 R /XYZ 62.692913 598.01685 0 ] <15f8eeb17d9a57edad9544d6b60db75590da192b711c3f15240185683c448d45a242f32edad10b6e4e22ff8fcee20394b2700663d79ed65082bdd1d94a67d800> [ 546 0 R /XYZ 62.692913 353.41685 0 ] [ 344 0 R /XYZ 62.692913 507.76685 0 ] [ 893 0 R /XYZ 62.692913 704.24685 0 ] [ 676 0 R /XYZ 62.692913 293.13685 0 ] <42a2b9e798ae019cb80e7554c66f7f56e26376651dfb34e964173261424a22e7> [ 148 0 R /XYZ 62.692913 762.69685 0 ] [ 609 0 R /XYZ 62.692913 603.66685 0 ] <10c7b40be5585bce37a5a58621b40ff26cca39f5d5326e516a976a7914b47eee9aa1b5820d7b1d16e1e93377963fa6c8> [ 492 0 R /XYZ 62.692913 181.11685 0 ] <20291336a30eb85a0d9dbb82ac6318408ead1344b79fdb81cb27e400ee56fef1b77b04644d577be1226d8638c3f7497151d22594d70f5039cab5f9a3fd4c5e5dd332a6e08aa05bc55df8c7cdf4d054c1> [ 333 0 R /XYZ 62.692913 554.81685 0 ] <8c42266c59ef6d210b3d72e1e7512abba376ee75c110c9566ae148f6039ab285cb36e7371d46d1925f99df4cfd23c939bbc00b855c48bf9fd5e1260ebf2add2c> [ 361 0 R /XYZ 62.692913 721.84685 0 ] <3b79bf2cb1e71e688637718c13c9d492e75fbcf375619c71c1fb859ce37ae244d5623c592651d12eec03634f9dae66d453315d2b0c292f654c3762db36af1b22> [ 375 0 R /XYZ 62.692913 620.94685 0 ] [ 588 0 R /XYZ 62.692913 675.64685 0 ] <1c1456a4b7dab866ed2adea2479b9e6be38725f4fdfdd1309adfca4ceba4744a586dbf1c043e9463f5155de84e0e7a2f> [ 905 0 R /XYZ 62.692913 522.71685 0 ] <45cb35645deddeb4d6171ce4ad4a0804491e2bee377f5cbc19b4f38bdd7287f1ce47202efe86b483da1821acba971fd31d23752812a8a99b6f2774a13f421981> [ 783 0 R /XYZ 62.692913 586.06685 0 ] <934aafce76b7fd5094c4bb4fbd643da32508fe3cdcb377712e7f5e8fcf8338e892717387deb4eb77c079f6c92f7dafa61b6dd50b4a82c724e83eacbd66858678> [ 920 0 R /XYZ 62.692913 603.66685 0 ] [ 272 0 R /XYZ 62.692913 604.94685 0 ] <7c3c55aeb0c1d1bb8415a32bb846d822ed4ca2e4c02c3ddc0022b73ea8434b880dbc1150434448e9bc239dc7b8a08258> [ 390 0 R /XYZ 62.692913 785.19685 0 ] <47e64932152a1f052a245713ff3c777b285c8f32a9c2131d0852431bd6320ce8e3d258168ed9188a5f6ebb72abe1eb25a0349e5b49f5b21548a63c0d8083f8e1754639ee7c3277af11c425dc4d532c4c> [ 718 0 R /XYZ 62.692913 578.06685 0 ] <9f12b8a15fa6d15c49ee73eb563bcb03a5c6ff73c3a32af9b6ef4008d7113a407c6569f3f992852711cba1483f1b41ba03b5c7b2156f16dc89e7e4cc19cef50e> [ 632 0 R /XYZ 62.692913 292.51685 0 ] [ 863 0 R /XYZ 62.692913 226.18685 0 ] [ 566 0 R /XYZ 62.692913 704.24685 0 ] <63585b8ad03a6b91e6158d9798bd07599d74848d110c36060fe76a998f86ec0ced95cc1bbd8062a3096b4376e302af1f1b9b7f88713cd83af2f96776242a4ca6> [ 444 0 R /XYZ 62.692913 351.73685 0 ] <739df06ed860e4551932c245450616b9e52d7ac0c8b118d41e20433cee46f54e3232e3185ba0bec01fe5df860445c743cbe3db5dd7524f9ce2baa928ce5fded6> [ 815 0 R /XYZ 62.692913 785.19685 0 ] <352a54fd8a703de2f92990da7cc0a5546ac8f6cd130314a00df2819177abe5af2a146d9db1452057a5ef06ad2e1da474185fbc060efa79f53a6f1b1de2d9ed9c> [ 599 0 R /XYZ 62.692913 693.24685 0 ] <162cbe7adeee7f6862b094ea0f9fa168e08e17fe897212084e118145806dc495c506be4b469df6b9451eb0da53a26852221868bd683dddfde83e2c2c0e22786d> [ 409 0 R /XYZ 62.692913 208.67685 0 ] [ 307 0 R /XYZ 62.692913 284.63685 0 ] <6734caae0e365fbe455cd734e6da7e1d7378fd054cd96a4f6f500196e1d084ed48425f0ddd54b9c823207f210c1db15b4e349826c22d4e908b7ddc1811abf29c> [ 695 0 R /XYZ 62.692913 686.64685 0 ] <27207b513a20f44cff2b5df6305b5fa230dd293300c8bd0b5534d0ff7c6b2f5224a5a44c45e8ce6605885c50b0c27c329708a26cfb297bdc8f976f6425f792c3> [ 297 0 R /XYZ 62.692913 367.31685 0 ] [ 418 0 R /XYZ 62.692913 785.19685 0 ] [ 508 0 R /XYZ 62.692913 603.66685 0 ] [ 148 0 R /XYZ 62.692913 719.31685 0 ] <7f29cb96a8b1b23acefa29519cd85bb4c3c5e827859237284de57d506767db102e3835ae213957659b9eea1154ff9e63> [ 149 0 R /XYZ 62.692913 177.84685 0 ] [ 151 0 R /XYZ 62.692913 587.64685 0 ] [ 153 0 R /XYZ 62.692913 761.94685 0 ] [ 465 0 R /XYZ 62.692913 401.71685 0 ] <660ae5665e4ccb5630060b5521b9e04d264c1408aadb60b48d915c32f116b77f27cd1312cd734af0fe09e2b8bb2424ec1e6085f873a4b6f5e4149fbe5f268fa1d6e2a82f8addf9f24c370281737db198> [ 508 0 R /XYZ 62.692913 439.73685 0 ] <1d962d0306d3a06ac7938caa4b1d839d813c844b94b5f9dbcf7ab0f58079cb19362f5bf80dd48a348b08eef79ab57c6f> [ 155 0 R /XYZ 62.692913 762.69685 0 ] <9acb101127afff54dd298a18e67167d53c6da009186b0678fdc5b9fd9347e0e8612a4e3631801b108a705e9bc131a242f4debed656ec776a391ef1c8c44049e6> [ 344 0 R /XYZ 62.692913 343.83685 0 ] <79af079915b197467074ff43f1a3c7ad865f08c45eedaa03ebf128cbeb6ddf585e00b7b2f563b72f0a713981df780f2792b1f0e4ebb973e27ec9e91ed06b4603> [ 706 0 R /XYZ 62.692913 439.73685 0 ] <5c5431094a0889f9a7c720c30e6c1ba5ba338bc929e8c87bc72c8a92e1ff5b1c2c9441df3edadeca0e5ca79cc022927e> [ 772 0 R /XYZ 62.692913 223.00685 0 ] <7227c71d10f0a0001d67ed3751c7716907ded5f488f7b23831c896017fda2a0c58a73e476e269495c2e648dd596a2587> [ 460 0 R /XYZ 62.692913 785.19685 0 ] <479ccc199ed77b6b1f488954388907806dfbf07a8f3e2322fa955b9f3ae68be8b3f223c73d98a343e58fd894980fb6d12205472d4c3cf826ad1f5c3fa169b2c4> [ 886 0 R /XYZ 62.692913 511.71685 0 ] [ 262 0 R /XYZ 62.692913 522.71685 0 ] [ 297 0 R /XYZ 62.692913 168.18685 0 ] [ 795 0 R /XYZ 62.692913 785.19685 0 ] [ 920 0 R /XYZ 62.692913 404.53685 0 ] [ 324 0 R /XYZ 62.692913 588.94685 0 ] [ 750 0 R /XYZ 62.692913 294.98685 0 ] <2924adfb4a1325bcbc9c86a0df518dd987a62471a3df0431ba80f691e884c55fa987db7b20cf1c5c7ac9d084e4f9a0396ca329eae1432472453dea7019c0fd94> [ 649 0 R /XYZ 62.692913 294.98685 0 ] <8abce1d0b096f6a1b6067f534cb47a273e8330fb44b4452b126cf3b4d47f5f9a05e130d2c7ce18c1fcb40bca93513f889025f6a8b7c77ea642a93953ccccc5b81f7a176297d34d6ec41fccc438da56d5> [ 370 0 R /XYZ 62.692913 522.71685 0 ] <2a7b2f3f0ac2b45397724604cb8b7c85193148850e17faefdd1a7c9db3902bcfca34213aebf0b21780b798d863582af259ef3687f251c4d58deb6f81054c108ecc6de4f22d35db857bc0f726eb0a97e3> [ 783 0 R /XYZ 62.692913 404.53685 0 ] [ 838 0 R /XYZ 62.692913 560.46685 0 ] <234cbd25b9df2ef5c137447bb33e56f3ae578164dfb296717843506c18cef690fc1c6e8c470b5823c65e0aa34fdf4aa0> [ 960 0 R /XYZ 62.692913 785.19685 0 ] <5bfc57e1656632814931874c0bc2d6f36f1bfb6ea36dcdfb23d378cc2923420748ea1c94644e147631442fcefcc3b0d708bbc0893dcba0c7be44c024492e621c> [ 945 0 R /XYZ 62.692913 472.56685 0 ] <277658ca2d254f6e0c2bd3d5ba37a226dfcb7dde7e03b17e7e8cd0d6ae1f231518e6fc5c634ea04525ca066ffe34b668b7754138c8749ef734c16dcbdabe3bf2b3e3a95259ba32ff3ee8209396984ac4> [ 864 0 R /XYZ 62.692913 621.39685 0 ] <662e5ef7b6cc2965a307175a833dc30c9de3d48f331801f24dda9e772fc90d79aa968ccfd74c11e4ad87ddc4088a93a397ac584f7683485342a01c6c6252af23a04b90442fc95f4979199c2dc9921929> [ 431 0 R /XYZ 62.692913 369.33685 0 ] <2a5682fa4c6630e63a9421141e6bdfe494f4ea68fe3013d1f17de40c9f10d2edfc842592f2e341f3daf787040ea3dd70> [ 805 0 R /XYZ 62.692913 288.53685 0 ] <43b654ebab12c756f636b45d061f868df1782b84230d9f1acd457ffb6f42a5baaea6b3fdc84193fcea8edc85e4889b75e4b7c905c3fa98756ea271933f23df03> [ 815 0 R /XYZ 62.692913 603.66685 0 ] <373fb73d445e731cd0b50db95ebde6d81d002022cc96c4c7785baeac1f733546a005f80caf65e40b9136e29247479c979319a0c88dbbaf79bf5962163fa6d6ee> [ 912 0 R /XYZ 62.692913 606.66685 0 ] <753d95d9388ebf6773edcc70ed18b3ab173896b689b0317f9f6d1975ca24815c0f9942e51e439bc98479652faa37d15e> [ 905 0 R /XYZ 62.692913 323.58685 0 ] <69039dbd35a02298759cdd1d2efe34070dd29c4ebcebb154b70086dd92e8445fce4a7554a6371d66758d95ba9b7b5db90b8026175e901c7438c298605d3968e9> [ 556 0 R /XYZ 62.692913 522.71685 0 ] [ 951 0 R /XYZ 62.692913 288.38685 0 ] [ 599 0 R /XYZ 62.692913 511.71685 0 ] [ 375 0 R /XYZ 62.692913 439.41685 0 ] [ 829 0 R /XYZ 62.692913 386.93685 0 ] [ 677 0 R /XYZ 62.692913 785.19685 0 ] [ 761 0 R /XYZ 62.692913 580.41685 0 ] [ 254 0 R /XYZ 62.692913 541.49685 0 ] <72485f50b068f7117d0a5431fc6dea1d0b5032aac383bb5a8412d70dbce02f18e99d6bd1359b94f6f7c6254bbb5f77c3> [ 900 0 R /XYZ 62.692913 378.93685 0 ] <24aae2be72d14ae8925fb0f228e96b967438a382704e64072a57c36fc2753193d5e0c3f96137c4b86a1f2fc0d5e56c10379a408fa3a4acf3aa4459b88cdddd58> [ 479 0 R /XYZ 62.692913 458.16685 0 ] <8be8a9f823e5b776e421740b475b6a82e95e215e289b6ab3c772d4935c079077bec66e4c4d951c2681862821738647a338b458255f16e245fec79cee7879448c947021f11d858b1d5586a6c98b1a9f37> [ 621 0 R /XYZ 62.692913 476.51685 0 ] [ 633 0 R /XYZ 62.692913 617.99685 0 ] <66a7bc8fd7ee51889e55cb1399747621cf0ef0007a2dd082d91a454ac7bb0d5375c99f8b52176062647734c36d9a1f2d53e54686aeef44ccfb11de8904ead9c4> [ 845 0 R /XYZ 62.692913 721.84685 0 ] [ 365 0 R /XYZ 62.692913 525.71685 0 ] <39def055fb2e4d98d17ea7b84648b13dcd59d10e1df084fe78d0d7f47a4b4c341028a358dc9d452a730b07fcb45f7b1159b753be1e7d76efead283a5ec36511b0b313eab9eff2d1cc5933df29df9afb8> [ 728 0 R /XYZ 62.692913 522.71685 0 ] [ 386 0 R /XYZ 62.692913 361.33685 0 ] <0e5096901078c352840a5c69cda849f18fdc6d902d77cdfca6bc9d9dd38dd061eed4d17fe5d5bf58ffa6801f6f207ded6239115c1e58f8e4559ce85c4fb4b77f> [ 437 0 R /XYZ 62.692913 785.19685 0 ] [ 659 0 R /XYZ 62.692913 165.80685 0 ] <775cc347e8dcef243829b4d938593aa82f4f6fbf0aebe7169b6eb95241b4b414a5ad7f0a7889340dbd5b416e2b6410c2f623df6a4aee37de140e8ecf67ab196f> [ 538 0 R /XYZ 62.692913 288.38685 0 ] <0abe87dd4393bfa27534d4118919ee82b327184dd54807f70fc34b91b0961a6ff2ac2f78a99398ca01929f899b4ed152407367d0213dfe49013cf537bad2e07b> [ 739 0 R /XYZ 62.692913 402.76685 0 ] <91c50f5b47dde38857dffc72dc9a8d284167c271c22ba9e1c2024a4a19e1398a1810bc2acb5e6dd47fd012b1e474b96f6d4cd882cbfdb45096cb13f5c8e2ac95f5db2e885e5a30723ae7b95bc3d336f4> [ 874 0 R /XYZ 62.692913 351.73685 0 ] <808aa08a4ec76107b231f1c7cbab3e6951ed68b3c561130c0d7536d85ead18fa0c3a16f4ab97221bcc2260c91ecd9dc0aa9ba9848b7c067788230d21428a1e92bd053fb9fd2c95d84361a6f5f872af04d400a143e0ade4874dab468b5932125b> [ 687 0 R /XYZ 62.692913 373.28685 0 ] [ 428 0 R /XYZ 62.692913 785.19685 0 ] [ 520 0 R /XYZ 62.692913 232.78685 0 ] <5d9aba21bd03350aebcd2ff818c6e120eecf36f9d8f1720509fc2b095c24c8878b0bf759ccf1e21798ce7101776389868147aa6ab6c292a48877f1cdc7a43767> [ 974 0 R /XYZ 62.692913 163.68685 0 ] [ 928 0 R /XYZ 62.692913 222.23685 0 ] [ 247 0 R /XYZ 62.692913 162.20685 0 ] <76b419b83d2731420c4af57be1813ed4135dd4efba0086371ac9e1c88b14ac17e51d9b81463c0c6da6a54601817304a9b325dd125e362c69dea814b731615741f51d078e39ab86b586afab5f30469db8> [ 642 0 R /XYZ 62.692913 186.63685 0 ] <2f4b89fd8dd797486cf3ee9009b82e0cb1c844beb7c02e1f382362a6625de3a6c9d426a9560d01e168fd93cc020f4114005fdf0771ed3c74d7d5b8cdfbed692b832b86fc934f9d9eac2350366e03821d> [ 445 0 R /XYZ 62.692913 785.19685 0 ] <34e4b4aa7b5948edf176c3e7eaaaecb1c9f4b9a2ae0931d50a8e47e4912a66524494a301936868f47aab96d58a82e5e0> [ 823 0 R /XYZ 62.692913 450.91685 0 ] <8d44f803a295cf3f2e309f7564cf295a3f0f03c4d94cd02097ab59f790cfefaff7994d9cb8d3d7703b3f24bdfad30987> [ 893 0 R /XYZ 62.692913 522.71685 0 ] <02081278d7d4b7a40aff5a2923b952b9f28be37b392859f8bd5c11c9148c95906cb3e7cf82384c67703d64db2d85ffbf0f5e81a03de1fbca7fc9ecf221b363377a2618e04590ddbe1029b440d1406a39> [ 474 0 R /XYZ 62.692913 785.19685 0 ] <7714e5c4f21da6731cd8305671d88864d74097045f86249db0ea3911f5c649bf785aeccaebd4490324b7139e2695972b> [ 400 0 R /XYZ 62.692913 603.66685 0 ] [ 493 0 R /XYZ 62.692913 624.34685 0 ] <0e7f8449e497412caeabd3f844e8da2644d0b8287948812d5adc0eeb19ab1b1276bf6805b2e481e4e25ae13aa69eac49> [ 528 0 R /XYZ 62.692913 125.48685 0 ] <2d07c6386d97eac2457ff106de3f27e76ad972a435fc4ade6eb0b1951f528846045cb4d6e5693821a913e73928a7c43e> [ 418 0 R /XYZ 62.692913 491.76685 0 ] <96741c4b6fd2e59c4c3444c678b46332079d076122a70d340fff016b87e71f074cb751ea21b8b1300142379854d25361> [ 380 0 R /XYZ 62.692913 323.58685 0 ] <1f08579ee8d8781b7fbf0cf35a6c25fefbf1192b113cdbb196cae9b90838d04cd5b82d5bd439db142119665cb75b253e0eb002c5e866d9657ca64625e018f54edf80165de83520e5c17b9880570ba020> [ 361 0 R /XYZ 62.692913 557.91685 0 ] [ 588 0 R /XYZ 62.692913 494.11685 0 ] <863c7672e67dcf98a7952124920d0cdd0a3ab12c50b4c0e406935813aefca55ace443e7036cd2273bb8464d7b0439fba185780ddd442c8996a60afbafdb9060a> [ 936 0 R /XYZ 62.692913 785.19685 0 ] <201ea7da2d1b8953c18eff3e9e1e03a83dc3edf5f47ef9a555caf63057a311ad4ad476e5323c07ff22e4911aff9ee0d80f4d81cfa25c264e20d0d9ae32e974da> [ 238 0 R /XYZ 62.692913 333.13685 0 ] <48e3a1713ee372007f38c22b3fe65ece1184f3bce3c32bf4031661906f4301a278d60230f95a20e867dd9088f9aeb49e588be2cb4e2d60e9d01c3d30f78cc101> [ 390 0 R /XYZ 62.692913 632.26685 0 ] <7e9ea1a4cbbba4729cecc65d57b3567af5286f29215069ce04c69606103ba82b59fb8d1943bb6fe29d6d8380de5b34e6edfc072c843ff7d72f1ecf49068541c4> [ 868 0 R /XYZ 62.692913 261.38685 0 ] <9e5a26dd4d6894c31a397d327494d1967926b79cdbaa3ab9acbbc6f4c5d0f91e0ae55a8798b0f21545f0bd7edbb5d810cb1a6d0547ecd4fdc088c4f966470a9e> [ 566 0 R /XYZ 62.692913 522.71685 0 ] <2435e99e57673e8f35e3247b31bb22b0cff04d990b214ec89cfa9371d02678c4f0b43083a9f3aceefcbefbb346bbc26e50e1191347b669c4d45e4c01f600eb4169764c65f3773faa33cbbec863201981> [ 356 0 R /XYZ 62.692913 761.94685 0 ] <8ddb4a5641c1dac701dcfa6426d69488c1c23c1301fc442d253c250d135bba7b8254fee73ba300f0f1d4d7783f381bd23ee72d3e8f9bf3f8419140d163d66e5c> [ 284 0 R /XYZ 62.692913 476.51685 0 ] <0d5f15fcaeecbf0789d8eaff9fc24f6ce4580def619850358bd4ec7b9cf6f685a381f0f3526705f0de198ab41238c81361f3e83717c8db9e83d2a71f74d1550f> [ 983 0 R /XYZ 62.692913 351.73685 0 ] [ 577 0 R /XYZ 62.692913 426.81685 0 ] <0fca4303ead9222cdbce6991faef032bbc7f410bf4a39c859ae3d9238db8a2ff7161e27df3e2843aeee7ef95beb2b3a3864f561142e5fc90e343c2fa067a0c93> [ 967 0 R /XYZ 62.692913 141.48685 0 ] <14c96929e39f74147584321b8fee140b603b00d866d04b5073fcbf902f1ea7df02aa71a8e98d35bd62b82ae34619d3716ffa92bf97f2ea8745cafe02a9541bd5> [ 547 0 R /XYZ 62.692913 681.19685 0 ] <67d951446d34d6294ea85e8bc4b9ba585acdbd8ef0142f5575636d69885072c70ae8c6bd97807ebc427191d050995401592320123bd4eb39702102adc2f4c470> [ 308 0 R /XYZ 62.692913 785.19685 0 ] [ 333 0 R /XYZ 62.692913 330.08685 0 ] <26ed7ffff95bd04ca1b3fd04bf848f892858d8a9fdb9fa84efeb6a53520de94719aa18fdec35dc4c6021b9aac4c236feeae482a8009e15e0dbd3a56f5299ba0c> [ 695 0 R /XYZ 62.692913 505.11685 0 ] <466cf3ce9419d9b348585fd8df612be9dbbc5e2f0f7198f82103c6b5b22546e4799cba0b0e2bcd1b9b4c5e0c2b830476> [ 272 0 R /XYZ 62.692913 423.41685 0 ] <15f493e8815b8accf91530054a28ff24c4e12657e3cc88268ec92c55f2d5941986a287511152bd46017c75c3f3073835b9795e93a7b1058d4a1996c793176019> [ 668 0 R /XYZ 62.692913 260.93685 0 ] <5d04ea983024f9439871f26cc41636cdf19316fbc099b02ebaf4f94433748fc9085cab94588bcb848e71854e2284fa97df1a2da2cf1d2eb3dff94765cfca6d8f> [ 410 0 R /XYZ 62.692913 785.19685 0 ] [ 718 0 R /XYZ 62.692913 353.33685 0 ] [ 609 0 R /XYZ 62.692913 396.53685 0 ] <7e13226fc883b4df5002a71040f70e08c8ace0c0576cac4ee6609acdc11727c929e1d867cc2180562b38b2dbb87923f4> [ 155 0 R /XYZ 62.692913 719.31685 0 ] <937748cc2ad63f4ffb34bf5af34ff5ae8f342fa4c9379ddd2ba03277d3802163fedd6537f63421fac272af690999aebc> [ 156 0 R /XYZ 62.692913 686.64685 0 ] <9e9d771d32645ba82891779dd442050a93e173ecf29bb344b34f15de66caf5fed15e6e5963c1fc44acc94057d454d5f0> [ 158 0 R /XYZ 62.692913 463.69685 0 ] <1d3fe0f14d05d775b181c3ba0470bcb3770fc44205ca7fa31311461d7e97813553e8af59ad2db1f0ee4f60e9805d4f61> [ 161 0 R /XYZ 62.692913 549.19685 0 ] <4993ac8c429372b262f76276514c3e654ac60e6e57a13fe627c78488e2eef575ffb06e5af8289b1ddc2c0ef67d940454e3c3da10d26a459b574916e9030358e9> [ 344 0 R /XYZ 62.692913 162.30685 0 ] <6c3444ce690c37d5b9467e466878fc3258242babe73b8761c29f178354c8e0f863be0a64e15ae5b11a0a838b5b13e6227dcfa293c6f8c79d2964d3377926a47497abc1d2213b587151b8257329b1ce60> [ 381 0 R /XYZ 62.692913 785.19685 0 ] [ 375 0 R /XYZ 62.692913 232.28685 0 ] <78ff2e929a58a9eabaa9b3755236312180022df66687ced74ab59509c755c5d3392277d8debcb4722ba52ccddb45a5cfc9c70efb9a30bc1f954870d373ca992b> [ 960 0 R /XYZ 62.692913 586.06685 0 ] <5a953a6ca4f7cc425247170e65b33b0f04f58b589645b8e8cd4173728eed1eda91b9953c7e246a7c244a5c68e643ed202707262761691fce7b267efe2d119998657b3550571f06c9796ecf83a19791e5> [ 262 0 R /XYZ 62.692913 333.18685 0 ] [ 566 0 R /XYZ 62.692913 341.18685 0 ] [ 445 0 R /XYZ 62.692913 462.96685 0 ] <398bc79b0a1fda6d8de47eb3a063532fa05c5cd2fb576131b5af3a30b5d77b8ad614c1499ec0da843da6428cd3bd870536f70b65736c4119e6510891f38b3e2e> [ 324 0 R /XYZ 62.692913 364.21685 0 ] [ 936 0 R /XYZ 62.692913 586.06685 0 ] <3e9eb9a94a6040a3be21205be2eb848b485bb2d6855a84196b600ace5935cb5f735977087a73c588c9307f2846a9334d> [ 751 0 R /XYZ 62.692913 785.19685 0 ] [ 238 0 R /XYZ 62.692913 151.60685 0 ] <2f7091538b9f3a57218c7895f6eacf1b489e318c56474738e05f776aca89bf180c55d2e884846c6a79b50ea8014823b1beaa08bbb644080d49c8e84f74d53dc8> [ 695 0 R /XYZ 62.692913 323.58685 0 ] [ 945 0 R /XYZ 62.692913 291.03685 0 ] <79cf24302bb1700e358d69d0adea8373e362192b8485122f9ecbd8fae62b50110a21383c8aeb756354765dc9973a9c4e143388f34bbc284b07e5cae5ff32e77f> [ 921 0 R /XYZ 62.692913 785.19685 0 ] [ 599 0 R /XYZ 62.692913 304.58685 0 ] [ 687 0 R /XYZ 62.692913 148.55685 0 ] [ 864 0 R /XYZ 62.692913 396.66685 0 ] <99926c10ce37f21e0f6aa74e88c92831677ce0658c0abb23f605e16b9dee56f7b9fc817e84cdfab703d061ede5c757ec27d0d3d6c851de1d7b8863896e049c2e> [ 248 0 R /XYZ 62.692913 578.14685 0 ] [ 806 0 R /XYZ 62.692913 785.19685 0 ] [ 845 0 R /XYZ 62.692913 426.81685 0 ] <55958e183d680cf431b28d77b8c1ae609eaf64935ff12b35e57992bcf82f8545e8090003a3baa620076a545d03f9c70a10cc36e86fee32b1374cc75afa25088ccb795ba580859fa391da4f5ff44343bf> [ 356 0 R /XYZ 62.692913 562.81685 0 ] <19f2d077f67f253a8d6161732a2f55b508785ff3186b48baa1a66cf2c38aa8354c7dbdf7892193c56c20d675d833c664> [ 912 0 R /XYZ 62.692913 329.23685 0 ] <5ccde999f59731618d4aade5c20d7500e7fba8aeffcc55d004a95ae716437899ae3f64195cfe4d1851354ec71e9bcfcf3e4c787cc21536fd02276e458b97a4eb98ed2117adbdfdabb200f2892f690cb1> [ 556 0 R /XYZ 62.692913 341.18685 0 ] [ 361 0 R /XYZ 62.692913 350.78685 0 ] [ 621 0 R /XYZ 62.692913 269.38685 0 ] <25543c184a8f0d1552542ca4115a18bb3f290c6d3607dcaac6ed0678a69309c3963c1255ac28f013d2f1137ca57dd8798762edeecfa92ad67c3310d734dc1566e2d76355fa16e4ae318262092e3b6315> [ 428 0 R /XYZ 62.692913 568.46685 0 ] <4a29e3d008538a83be61dabe5c4b49ae5392711939aef34d86e5d6b1fac55407736664e46a723ba923c5ac5a138e84fe0f086f55a6562673fcd1e6188c8ef8c2> [ 706 0 R /XYZ 62.692913 258.20685 0 ] <234f7879d0bedb551041e7554c809d6588c9a4070380b5f3d8e3b133688810a4db8daa9ee739c8000c6a5d21029b4f1ff6b32bf9ed0abf5a24ad2380bafcead86f17d75b60eddca4a725c40d7aa21f6a> [ 370 0 R /XYZ 62.692913 333.18685 0 ] <98730902d6cceee47dabb13c164100097d516aaebbdf879abec8e29834955e6655dcead675f50c94c22c3fb286ac085d990b9820f3cb68076102edc7597ce8ed> [ 643 0 R /XYZ 62.692913 704.24685 0 ] [ 521 0 R /XYZ 62.692913 761.94685 0 ] [ 929 0 R /XYZ 62.692913 761.94685 0 ] <6719975590076a0abf5771daf37f4dc3a783ded005503847c43735f16a4db28842f797790aed6079df2c66cf23c5b3db87174cc716c6e9e6f3bd96aa0849e90e> [ 761 0 R /XYZ 62.692913 316.08685 0 ] [ 633 0 R /XYZ 62.692913 418.86685 0 ] <40eadaf07063971d995a4792c8b4a5057cb15639efbd4d4895a29ff403ccb9a0c8e01f8fa99b41e7ce3362f526c4ad2c49a85177662fdb554b8205c2cfcb633a> [ 893 0 R /XYZ 62.692913 323.58685 0 ] <534f3e88d3358b49fec5bb75005055920fd6eec264b3dfb99f827eb6ce10ed64afb5c3c9a651ba2552c8fe99ba6c84b52e527ea1f30d0193f9e0dee14d4fed39> [ 815 0 R /XYZ 62.692913 404.53685 0 ] <73eef804fc9531698706b07ecd8d9d7c904367a2f78520a46c82be9e703ddda1e4bf22157aed570ba0a91d959f694fbeb4a5135aa52ae08528433f52e4662018> [ 968 0 R /XYZ 62.692913 531.74685 0 ] <7613865361c5c197cff5422dbf35ef821e1711fc272ffbb6dbbd3fde1f027d8069e313f66aa4956016ea41607bfd3ef4410a5a78afb08dc4aee4d9ca3ef13f42> [ 718 0 R /XYZ 62.692913 171.80685 0 ] [ 529 0 R /XYZ 62.692913 658.04685 0 ] [ 975 0 R /XYZ 62.692913 544.34685 0 ] <46fcd8d8f83f9eb79c8714489f11b94196e7cc6aa4a59a5475be2dac575b218e92e28704952d9332fbd6844c86ed1f6fa660107038b46a3bf05a65939d1c1e56> [ 400 0 R /XYZ 62.692913 342.23685 0 ] <4b18c5bc9b7193fd65f30b04042ec07342f90240121ffbf6ef23a7748e3a14e2498fbf2233239edf064bcd304a1cc4f066baa156bcaec9753385a16c7dc7d817c51e33a0c98a2defa940df63efc24f14> [ 669 0 R /XYZ 62.692913 785.19685 0 ] <236b751b402ca17270bd4472806d34a6b0cb124dc28760aaf2cfe973b51929c53de71c8a540f2bce059f642b347045db302087d064232edc473b341ebba85ef8> [ 437 0 R /XYZ 62.692913 443.96685 0 ] [ 660 0 R /XYZ 62.692913 686.64685 0 ] <867391146675a8a348065c0a854924051a62213a5aa4cfd69e24c7e70d660343c535371df4a96a587b249c231d4372edecf7ec225a382eccdcca8b63ce2caac7ba4dcef94116894765ef3bdda7b885ee> [ 677 0 R /XYZ 62.692913 546.46685 0 ] [ 783 0 R /XYZ 62.692913 165.80685 0 ] [ 650 0 R /XYZ 62.692913 785.19685 0 ] <31d1801707c54a9139373c36ce58e8184a9252d6ac2662982e9e80076d3594c24d6eccaf7e7ddff74b77e3b70f2c5e29074361a0a76a320a05593a48778bbb3f> [ 983 0 R /XYZ 62.692913 152.60685 0 ] [ 952 0 R /XYZ 62.692913 667.59685 0 ] [ 284 0 R /XYZ 62.692913 294.98685 0 ] <2dc632f2854a0e5d19c04036ba3dd5928ed1e355260dcff711a7163926c81f3f4d200f27119600d55adc35e88d82965f> [ 163 0 R /XYZ 62.692913 762.69685 0 ] <3bb9cd2b77f49d47ba9eaa851c22a467d9dfa53000e6d0cf28fdd268edc77cdcbc4c59ac32cd489670444255857e989d2b4c76f5af61267189e41f36b613a3e6a98841a73c71a48bf579d8780b8d4c9d> [ 875 0 R /XYZ 62.692913 785.19685 0 ] <0363147cbc5058225084fd8881557ec9fed708ea0fa669864b1bdf38b8229a16a23708db58b38462b266e59126bbbdc1861783e68c06ed115b5a700f6ed7d3496044fbfd4449c8640b93ad4ede5ea249> [ 823 0 R /XYZ 62.692913 262.78685 0 ] <94f8288013ae33037f2a9e7548717a88d29533534259cc373f8137d2484cc6c83e86c1ac9b2a01881b3f5a668db9a7d3f0862715f32798b324fe996058fa9608> [ 728 0 R /XYZ 62.692913 341.18685 0 ] <0f863387581f31685674a64dd7353fa1f9bda0d39025b87cb7e4e5becb0a0d623e7d7b3d9e13a532f031e2476c5c370b1702f2cf2f610c93e49197d365b63292> [ 333 0 R /XYZ 62.692913 148.55685 0 ] <0ce3e56e57f4ef50b506d6be96bc4285e6558fb85f9ed5766de4e0a9dfcf9cd6e5e3a33bc8fc0ef6888bf1e980d8f6ab9d9f976f097992331f92ce2a7b3dac7eebd9f2c9641f867d9f9e2f24991790e3> [ 298 0 R /XYZ 62.692913 576.34685 0 ] [ 773 0 R /XYZ 62.692913 785.19685 0 ] <8fc514c79a9d41a27fb7b471867af469dcc06a335fc4ac3e2ad8de25045147a86b65717a19d41419f4845235fef326e7b789f85e33246277122644389764bbe6> [ 588 0 R /XYZ 62.692913 330.18685 0 ] <7a0b3f91776d77e724814f376beb87d444a37d334fd9927c985295de76220d06915573018f732e8c7f3eabeeb03330444b000da6eb889cdf8a07014722df7c12132cb5c0ba5749dbe3b9aceb86efe564> [ 365 0 R /XYZ 62.692913 390.38685 0 ] <9fdec1f9ba86777d673be8ac3bc06be1bb82f724523fa84a5f0cad1446f44de0f12f10895288b5e2ee00e75b6e40cbfe4bef51d43d9fd62aa792b3a0c256bd49> [ 740 0 R /XYZ 62.692913 785.19685 0 ] <6531e04e948114d159742ba486f2ed123aebb6286ee384c0b199de0b9bc920fc3ceeb3b78ce13383bbc39ef08dad3059f9e0a0da36e38a0d892256446472df58a126d9f742046ff3f1c5a2f94dcb750d> [ 431 0 R /XYZ 62.692913 152.60685 0 ] [ 886 0 R /XYZ 62.692913 312.58685 0 ] [ 460 0 R /XYZ 62.692913 603.66685 0 ] <51b841ffb741a19a18915583c31ab31f4a82af09b7e99653047ddd5a66164e6afd2fcb2b0b1f83bb9ce62f143dae2d1c0b91c32dad290ddd6d12159d492242fd> [ 254 0 R /XYZ 62.692913 377.56685 0 ] [ 577 0 R /XYZ 62.692913 262.88685 0 ] [ 869 0 R /XYZ 62.692913 785.19685 0 ] <9d5780332e799bf9e28b51f51736754e65d9bbb9ae6f13daecff747739774ed94d27b87ee3038904828ad63fab00fbb122d07c9e5421f34011ac355b2845c89f82f56515eb01325ff0206af9d6675e78> [ 829 0 R /XYZ 62.692913 162.20685 0 ] [ 479 0 R /XYZ 62.692913 276.63685 0 ] <29b3e10b7368d204a1e516574ad69ca67fe02765de0ca35ce1b9489004732447acc76930678f8613bfc7c33f5dd45411e1b76281173c71e04d735a3b71ddb064> [ 474 0 R /XYZ 62.692913 498.16685 0 ] <6f9ece5e5b2d0a9184353088787e4416769dd6802e05110b15b3dd14153e2b5852e7a357e8615f13341aad18905549e8baf17ad0942b48b9a6c874d571017428> [ 795 0 R /XYZ 62.692913 603.66685 0 ] <5d5a5eb22adcb7b71eadf6ea1d7486cca9b69a90f718924da964301f30548b164889025859eef930833a973c2b72756b> [ 390 0 R /XYZ 62.692913 479.33685 0 ] [ 547 0 R /XYZ 62.692913 464.46685 0 ] <133de24b2d94554a28c66ccf30078b0a35f920507c776bd42cdcefb53a4cf62aa3e2727cff22213d59bedee8814d105d53407508a9a1bc3fb581992d1b709b3b> [ 466 0 R /XYZ 62.692913 696.19685 0 ] [ 906 0 R /XYZ 62.692913 785.19685 0 ] <5812bd0de404223f06ee8980980aa877b54f929d5bbf9cce0d44ea3f2c5d75060321d7fe74bae65636cd1883d0061facc07bc8b680e83e1585f47067e953af522233cd838d4a36d7b44364ac948d0114> [ 386 0 R /XYZ 62.692913 179.80685 0 ] [ 539 0 R /XYZ 62.692913 785.19685 0 ] [ 410 0 R /XYZ 62.692913 649.86685 0 ] [ 308 0 R /XYZ 62.692913 603.66685 0 ] [ 900 0 R /XYZ 62.692913 197.40685 0 ] [ 493 0 R /XYZ 62.692913 334.31685 0 ] <4a9eb203e2d53ae098f2efb912fdd39dacd14e4d2b569c1c00b5f644e61d143da1a9b36cc2fb849bb12fa07b376ca3d10b0e786ac13760f1997e369aa24af096> [ 418 0 R /XYZ 62.692913 310.23685 0 ] [ 272 0 R /XYZ 62.692913 241.88685 0 ] <22ef4f63532cb5799f1e2344b2e1fe70f23348c4970ae959debbef2972ced78a3e1d2c4ce8e65564be03eb644df2a8c0cbbf0792e78660a0f30396c73a572b80> [ 838 0 R /XYZ 62.692913 270.03685 0 ] <0f9c1f48ad110e11a50ced3220f318a96aa83f1c575f0f1242e66db4e3af64df57e0e68d3e377a627e4351520e1f816c5ee6ec7161022a8fe8b5f58911c77e96> [ 508 0 R /XYZ 62.692913 232.60685 0 ] <48e67448be2ca322f7e4df7415c3776d0c947a07d1577ed37e7c21dcf3b83279b22996b412d036ed84a6d970f463b886005f45be067052673c1fea58de7001790e0b66a2c8d19147d293cef6993558a5> [ 609 0 R /XYZ 62.692913 215.00685 0 ] [ 163 0 R /XYZ 62.692913 719.31685 0 ] [ 163 0 R /XYZ 62.692913 494.56685 0 ] <96daf676e716b67ac04020a6d389e592a58a5b19cca0fe8550816ecc104513d937d13c447bf1f3b278acee19d9e1747afff72844578073657be5437346d0a55c> [ 164 0 R /XYZ 62.692913 669.04685 0 ] [ 165 0 R /XYZ 62.692913 704.24685 0 ] [ 166 0 R /XYZ 62.692913 330.49685 0 ] <93dbb0d628b61a5ac08fc9fbaa4436d3b2af12e4bef23babc7d78588084c8947e12a0d18204a3e261e18536d07f63bbb> [ 167 0 R /XYZ 62.692913 293.64685 0 ] [ 168 0 R /XYZ 62.692913 559.04685 0 ] <13433ac42ecf044f5cf24b5b2979dc543d9b9f863ba3bd9234d72447ccf2669e3f5c5b65e4d446b56dc6f17479bd017902470ded6081f78885e18eb992f4ae14> [ 169 0 R /XYZ 62.692913 669.04685 0 ] <6e5a162bfc4111b7906d9ecb9d7970b1bedd63e44b0cacbf99068ee8528aad482ed3595cd70f7938207462cf55e026e6> [ 156 0 R /XYZ 62.692913 222.30685 0 ] <3a2c101c5c501326adc6ea783a740eaece47d71ae57fae06af9f13e1431a7b93e895028d091caa0588b23e710db93190b4957ffb62acc64ae2b41fd791876189> [ 165 0 R /XYZ 62.692913 668.81685 0 ] [ 449 0 R /XYZ 62.692913 403.90685 0 ] [ 76 0 R /XYZ 62.692913 292.84685 0 ] [ 630 0 R /XYZ 62.692913 769.11685 0 ] <78e320b7afccb4a4b698a6ca4322acf5e0297c2b5d1fa38b92609526cc25c490d58cabb47940af74c273361a04c01e2c76b62dc9ccc976447905d11db9885b51> [ 138 0 R /XYZ 62.692913 324.59685 0 ] <9eaeca7e82ea9f66a718ef920d31d7d3c445af1f82031cbe2b91bb2583e97126008e6d18b80aad4e8a9266f50a1bfa0679870502bf5f173565088e2768dd7cd4> [ 42 0 R /XYZ 62.692913 785.19685 0 ] <237d4ecc6e5998cac77742a28f426d9c1d7b75520f6717e81a5686b30a401f6afb736abaa75d6e1919bf1107acfe6451> [ 53 0 R /XYZ 62.692913 769.11685 0 ] <0513225fc77a30c310c56b8cc2844172c28cf0fc55321a04763a85ee2c22fc6859f8620f18b9debea48bb6d7ce6e1922> [ 55 0 R /XYZ 62.692913 769.11685 0 ] [ 57 0 R /XYZ 62.692913 769.11685 0 ] [ 59 0 R /XYZ 62.692913 769.11685 0 ] <95f4d7929319c4a700b5aa370c90f039e6130eb65c3297d704028b2dc266930538cf3985b6826df6f7d5c09bb22096a2> [ 61 0 R /XYZ 62.692913 769.11685 0 ] <52218793a9ebf7c879b4985d1ce26d0d71a7ce2581823d6e29651b01b1444131bae38c95ea43629e974c053504a0d3f2> [ 63 0 R /XYZ 62.692913 769.11685 0 ] <82e73344c4e16c27e5986cd969d935b79ad82e9daa11b27b3632b0705b49c9f30dc6714e963e2e36a5953e816b8415ac7830e2c9ca78265bd4d8a734f3759ea2> [ 65 0 R /XYZ 62.692913 769.11685 0 ] <774cbe49f2d816f429859ce68506a258c1da0b141a5618b708d5fbb3b1ba53a28cd82cd58d4df099c540c502b21641f31b7566c4922e4e40aa13edc7926104e8> [ 67 0 R /XYZ 62.692913 769.11685 0 ] <648a91c3dac6698f614b967ff4cd77e277ab31d32d831be124d64f926e5670b5e26882b9aa4f58829292040b6c6fe737de4083ab3472ecc15ce4b1ef4dab9cf33a3c5cece23a94b11bb1823194933bff> [ 69 0 R /XYZ 62.692913 769.11685 0 ] [ 72 0 R /XYZ 62.692913 769.11685 0 ] <9b8aa9cfe4e995f68f916d88422c1b6da8d700ea800144f854e809318408d458ebaad9a487b018ab10952d248d6e05ce3195ee0c36ffbb707e3b1515059bf452388b69cc90c97fd8a23eee77a3109487> [ 74 0 R /XYZ 62.692913 769.11685 0 ] [ 84 0 R /XYZ 62.692913 769.11685 0 ] <6454f04cbd17e08c078b62914f4f13f5117e067eeb83a154737e88a0001c963b0a06a9790529b60e5638afd444616ac4> [ 86 0 R /XYZ 62.692913 769.11685 0 ] <14c40129a43638599cfc241bdda5c9f52f6f4c156a581aada9b30a7a1142767c4d5488e3fcda6b34bb88fccf95166dcd> [ 88 0 R /XYZ 62.692913 769.11685 0 ] <07c562a545909a7d8ee7fd8a84a812ac46562fff0292dd16712df98d5898fadc60338246c9275d10b8abe9cd4ba18d62> [ 91 0 R /XYZ 62.692913 769.11685 0 ] <5e6c9fd67434cc53014cc4fd7e931f0c4c9c43e4a7f22195308a2c1cc2c9b437967b80e016a720d5fd055547805bf257> [ 40 0 R /XYZ 62.692913 577.24685 0 ] <4ccfd34be71b02791a56ba4102e90bc8a5d64269487acf253e0a96d46d025a9dca32a71e925ef8d200ad08ff12c3b240> [ 53 0 R /XYZ 62.692913 480.22685 0 ] <58262cfb20651fe2b5727c1ea85aab72881977325bf1625765d3a91681e57f06704ab535ae430889bad6828ede9df33c> [ 55 0 R /XYZ 62.692913 462.62685 0 ] [ 84 0 R /XYZ 62.692913 462.62685 0 ] [ 86 0 R /XYZ 62.692913 462.62685 0 ] <2a6560fba3409d142fe0fd3f6938dd316200fa51ae7db9eb5c26c1824aee3429fd515428187386bcef61177dd08972ba> [ 88 0 R /XYZ 62.692913 462.62685 0 ] <2c1e7211477d941801106ae637480af66b9ec01811a80b7e380a00c39d0f6f198e370abf03307cd287073013e9e6b3f1> [ 57 0 R /XYZ 62.692913 298.59685 0 ] <9d425eb94cb1c897ababf543bfc183b3ef0ef287de46967526fee3d6a798af7d1b3890aacdc889f953af8251f8c51d6b> [ 59 0 R /XYZ 62.692913 480.22685 0 ] <90a82ea1bf9a910a7dad40bc2ce96751045dc1cab6cf9dc38192e50c14eefc2ce676325e6f9486d7131e0f1b38a105f8> [ 61 0 R /XYZ 62.692913 294.24685 0 ] [ 63 0 R /XYZ 62.692913 462.62685 0 ] <38ae403a181f7b0aab35484be4bd9ea1dfc0242bd200b881b2a891b8b1bd2e4dd8ec91c40638517217f980491e6ba7bd> [ 65 0 R /XYZ 62.692913 424.22685 0 ] <6ca62a67678ca1d666ecd63e6c8ef2df4d9a596c9b6818c81721ad244588a23ab1c933d1684898a3cdef16ad41811b88> [ 67 0 R /XYZ 62.692913 424.22685 0 ] [ 69 0 R /XYZ 62.692913 389.02685 0 ] <0a6c1af4b01c138591d7b09c26f1bf45d00685b406fc52a28813a7289978ada2b250899d3637456836f9fc208ce50ff9> [ 72 0 R /XYZ 62.692913 325.22685 0 ] <4860603c4542e2c70d2f068ffbf82ba249c5f7f23e711ec09b71e5355cc39e43e3581a4c752dcadfc01c06230a2c286d> [ 74 0 R /XYZ 62.692913 117.66685 0 ] <811263ba64d3f98415d3436c62cd75d199f9a729525ac47c7bf78d9694f1090a19904191067b582ab0004a0113b262d4754bd0208cf1f0975766b0e89283897c> [ 231 0 R /XYZ 62.692913 769.11685 0 ] [ 926 0 R /XYZ 62.692913 769.11685 0 ] [ 76 0 R /XYZ 62.692913 681.94685 0 ] <12d094624dbe61ff3be809254c9ebb9e4f2815b787555a1253978484fbc00af5> [ 61 0 R /XYZ 62.692913 462.62685 0 ] [ 50 0 R /XYZ 62.692913 785.19685 0 ] [ 813 0 R /XYZ 62.692913 769.11685 0 ] [ 68 0 R /XYZ 62.692913 727.49685 0 ] <760d4896dc1cde6939f85b03199c55b4cb186b4982d6773a361e944f2b8b27ba> [ 70 0 R /XYZ 62.692913 709.89685 0 ] <94799ed24d9f77b9952b2363df23ab37a06728ba7cb7fd32f5451ade9fa4903a> [ 821 0 R /XYZ 62.692913 769.11685 0 ] <1dcb560eb7d639f630ec964ef34496b8b5c02a598eec94cd24c4394788092de8ce1f49fde90534ab64141a66e805c6b855d28dd356d6bf308d9bef11d606b55f> [ 295 0 R /XYZ 62.692913 769.11685 0 ] [ 305 0 R /XYZ 62.692913 769.11685 0 ] [ 449 0 R /XYZ 62.692913 164.67685 0 ] <1b52a68f5747f25bbbd53b272b0c283462d772e8771b2685e89468f604f00add6601f0cfb692005b559fc7f38b492deb624acdfc4e84e53d53334ec75cd72d5b> [ 321 0 R /XYZ 62.692913 769.11685 0 ] <3cb973d302a84eac962cbc2e25adc969c5db60631c071abe2737bce727f731c9a9fdbbd0fe1b530fb449369d758b8879f92277e042c02a6c023f6ca44d2ede85> [ 236 0 R /XYZ 62.692913 769.11685 0 ] <5ff988ed84359a19d3db421243546aafa7b479609b10e1837ce1f7789427ea1198b4bd7b17992b3408825d069e674f3fa41ee11712e9f0436626a0b5170f4223> [ 331 0 R /XYZ 62.692913 769.11685 0 ] <291aeccc8d0c9be7faeb31b08dfb67ac0d86984a286d45e44eba8c13b828d7f0> [ 450 0 R /XYZ 62.692913 531.84685 0 ] <82e4255d6fe272f29edb01c9f534feaf7e7a20fb51622b72b82c0224e2342c6a9d78e0272a04ce3d3373c7e28f497ce2> [ 49 0 R /XYZ 62.692913 642.21685 0 ] <07762fbb0ab3e5acef7936e874e6676bafea46f8b4359f63dd617c415283fbb2a4dd3d56308f5fe70d3e9ae179a1f348a396264df2b7f26053f7702cb27a6616> [ 378 0 R /XYZ 62.692913 769.11685 0 ] <0bde8c8cc198830c0f50ddc00b58db96124ee477125918a1501b55c2dd6939de68b4143ebd6b2159c204818d2d1721aa560cdef1defcc335d05d068973c2109f> [ 234 0 R /XYZ 62.692913 281.49685 0 ] <12805cfe1455ad6215662c3ce7e3dbc6512795d668f8e4130a237bf75357e93c1d1ee24c52cb1c95ab3b4429d814dd8f> [ 693 0 R /XYZ 62.692913 769.11685 0 ] <44bc268dfe7a0cfd90259a820405808f6ef38b7f5c2589f5a81779aca1b1011a4bfa84cd4ff2bed8975311fe723b2580> [ 165 0 R /XYZ 62.692913 242.93685 0 ] <1090c71f5d3fcde3bdb6cb9625398a9c4bc3fe34ded281b8dd0213ebe4983f30> [ 49 0 R /XYZ 62.692913 474.66685 0 ] <00ee9abe7f4739b3c8b2491119a837e1563fbdc2404c81f49e53c320545aeacebb7381112312ce9c8fb6399640b81763> [ 48 0 R /XYZ 62.692913 300.36685 0 ] <66060860839287a96c9a90925918ad1e40db33e78740c2b8219b353899dd67fc2090988c6443839531249b10e444700671deb61775426a229e72f5257f9797df> [ 759 0 R /XYZ 62.692913 769.11685 0 ] [ 38 0 R /XYZ 62.692913 476.65685 0 ] [ 40 0 R /XYZ 62.692913 286.53685 0 ] [ 40 0 R /XYZ 62.692913 360.51685 0 ] [ 49 0 R /XYZ 62.692913 741.81685 0 ] <6f093170341a45f608489a73bc4fc4ab54e4589a86ea04714e99acb537f38b4c> [ 94 0 R /XYZ 62.692913 785.19685 0 ] <17c21750391bf6119b237dfaa856b3c25c4264da46fda04d61d8b0012892384a7798525e80570644cc42f1bfb237c5fa7bd8bf06bfee1743993545c51123e764> [ 861 0 R /XYZ 62.692913 769.11685 0 ] [ 353 0 R /XYZ 62.692913 769.11685 0 ] <337da75bc318d8539a3984dd744febad22cbf32024585c349203a74ada1c8754> [ 48 0 R /XYZ 62.692913 520.61685 0 ] <9071995c6b28c4d69e8c38510541ad055f5575a50274d6da94b97bad1be6a1a3529bcd7fa330400978a2e9b5a66655aa> [ 640 0 R /XYZ 62.692913 769.11685 0 ] [ 41 0 R /XYZ 62.692913 453.71685 0 ] [ 657 0 R /XYZ 62.692913 769.11685 0 ] <3e7b893f12cc66f8b27df1df5801107b2f22792bd32a028b957aef135ce4158d9575b2d02576f2e8b3185995b2874e8d> [ 137 0 R /XYZ 62.692913 482.34685 0 ] [ 647 0 R /XYZ 62.692913 769.11685 0 ] [ 139 0 R /XYZ 62.692913 686.64685 0 ] <6deb32797291dfcb651354af87d06cab9cdc146f48c12529a29c8e1d124586f690a25b694464822cb2298b391783d4ab37dd9a081fd593bed34f2f3b6c3c428f> [ 141 0 R /XYZ 62.692913 351.21685 0 ] <823511aa94722ac86a571ef40d6f16012670de0ba461377497a99aaa92ae9bce47ed649c26129d146b5bdd967f4b36ba1a95a94be1b2229d7e7b42684eacf244> [ 827 0 R /XYZ 62.692913 769.11685 0 ] [ 38 0 R /XYZ 62.692913 664.78685 0 ] <4bc8c4f495b88650e6ea203325a0dbf2a205c77f93e51160eeac80f95941407403b515287d1bf3dfb6e8e424da4de630c8c12a119affb41115a934e5f0d4bfb3> [ 554 0 R /XYZ 62.692913 769.11685 0 ] [ 233 0 R /XYZ 62.692913 761.94685 0 ] [ 234 0 R /XYZ 62.692913 512.84685 0 ] <8f1c93e7f68df715a2bb6c14db1f69d89d0e5d1c4a716679157e5683e44f75eed9dddc6796c5ef6f25cef2df0f5bf6dbd80b5e7706859f4968bce6c0dd8d6b9a3572b6cdff76545ca9631b16557e119d> [ 933 0 R /XYZ 62.692913 769.11685 0 ] [ 74 0 R /XYZ 62.692913 161.04685 0 ] [ 45 0 R /XYZ 62.692913 360.91685 0 ] <661d4d88ae441523c5abffb492891e29f77b2657dfbfdbbacd2789cf48b443fd2551f966b1cf9787a00be19e16be888a> [ 359 0 R /XYZ 62.692913 769.11685 0 ] <8961d39317b5514602f373815647bd9066527880b09adf3f99e3aa6f85550d8e> [ 53 0 R /XYZ 62.692913 133.56685 0 ] [ 56 0 R /XYZ 62.692913 648.91685 0 ] <365e7b7dfe4b0f147fa59909dcce458c6354d2010d6498e376a6c1b1c0e332f6> [ 77 0 R /XYZ 62.692913 384.06685 0 ] [ 85 0 R /XYZ 62.692913 524.21685 0 ] [ 87 0 R /XYZ 62.692913 433.71685 0 ] <10b0f212d04f410ea4b564078dac2331d8156350cb574f4937e9e39de5a1bd65> [ 90 0 R /XYZ 62.692913 719.44685 0 ] <6e5843208f4ce72c1078923187b63be2b91a0d6309471aebe2462ef5d157afbd> [ 94 0 R /XYZ 62.692913 424.81685 0 ] <5d37a6c828332df050e71fb544b2651a74c0bed7db2321f15b4a5907de0d32e1> [ 58 0 R /XYZ 62.692913 631.31685 0 ] <91fc788ae55e5b7c80d85b89ba5cfcf7e73502c4cd0d2a9e38c0b51bffc06828> [ 60 0 R /XYZ 62.692913 666.51685 0 ] <64148f7f2a701d80863b7d4726bc763c370415a38f96aa1e2d22af8e8f6ffad3> [ 62 0 R /XYZ 62.692913 331.13685 0 ] [ 64 0 R /XYZ 62.692913 676.09685 0 ] <2817ac7da4af2addb0796924f03de5cad33317b9961055d841dab13dd525bf98> [ 66 0 R /XYZ 62.692913 320.43685 0 ] [ 68 0 R /XYZ 62.692913 214.73685 0 ] <9be6aec6a232201fb2600875d039a12117bcb008585f09acb334d6661be6df26> [ 71 0 R /XYZ 62.692913 687.44685 0 ] [ 73 0 R /XYZ 62.692913 631.31685 0 ] [ 450 0 R /XYZ 62.692913 206.36685 0 ] <8baa6dceaba6a4113bf7daef7f445bdfe7c968cce132ba8095efad25d41e38a118bb8030edf9e4133d85f073106b8e0372c3b34df3fc6a3deb6a5908cc7f3a0a> [ 871 0 R /XYZ 62.692913 769.11685 0 ] <80701c18d5407b6e9086f4f4f484afa2e4e06a387d4bb13146d223c0465b912c126f15afcda4a10fe4c089a34256d2c1> [ 564 0 R /XYZ 62.692913 769.11685 0 ] <0cfaf1d1f1787564b9e72f5076c2d13c7e31aec921dfb6043381d98e61402efcf4aefa10e9b606ff8bac936db2cc861e> [ 42 0 R /XYZ 62.692913 168.10685 0 ] <054a9f4a2ef240d4816b4140f9daaef187247476ccd60eb634f640e9205c309c> [ 53 0 R /XYZ 62.692913 664.78685 0 ] [ 231 0 R /XYZ 62.692913 367.82685 0 ] [ 233 0 R /XYZ 62.692913 384.41685 0 ] <5f99565134da06bb96fbeafeb88517d8fdf27e5f0ec325d7d3434fadd800c209> [ 55 0 R /XYZ 62.692913 664.78685 0 ] [ 74 0 R /XYZ 62.692913 626.38685 0 ] <0ffdf59cf4b0f5b426660badfc6d07627398c93721c9fb8d87ac4c885016b135> [ 84 0 R /XYZ 62.692913 664.78685 0 ] [ 86 0 R /XYZ 62.692913 664.78685 0 ] [ 88 0 R /XYZ 62.692913 664.78685 0 ] [ 91 0 R /XYZ 62.692913 664.78685 0 ] [ 57 0 R /XYZ 62.692913 664.78685 0 ] <34d954d399812b864899fb16a15892478ca5be44e7df8cc45f0c72271c536aa3> [ 59 0 R /XYZ 62.692913 664.78685 0 ] <739d31920b5ba897badfb9b239bd6a5c991116f7f300283930d39b896eff543f> [ 61 0 R /XYZ 62.692913 664.78685 0 ] <90694e6f73bcb60f22a92b1f66188baeaa994afd2b34aa340eeba5ba4a7aef02> [ 63 0 R /XYZ 62.692913 664.78685 0 ] <9db83f980c08734cae8d8623759e9ca49b55d02805fb89ee17c92b7c57e625f9> [ 65 0 R /XYZ 62.692913 626.38685 0 ] <2ac151977e9d9aed5b84a5af02b8c293e35b3c28e9d639a3a90b9fff62fcb9dc> [ 67 0 R /XYZ 62.692913 626.38685 0 ] <2adbbcbd673ed292e0e4aa88769b7d85828e861605ae67685ee4ed35d7aafeb5> [ 69 0 R /XYZ 62.692913 626.38685 0 ] [ 72 0 R /XYZ 62.692913 626.38685 0 ] <5977305a0ef12a7e5ecc95dfb90be42cfea24d0cfeeda9a0bb775f8f2321243faaaa35633ac41682da6593d609b8dc495649f5061da7da4823429f34a70a431c> [ 957 0 R /XYZ 62.692913 769.11685 0 ] <2674b7a0178d2f6530b1f58f8a826e7c77f22a90b34c68670cfd9411d6858b27d50e5916d700bc059dabf30293724aaedec9c5bb4cefadce2503608865c65ecb> [ 269 0 R /XYZ 62.692913 769.11685 0 ] <7a25d80ac76d9440ccf73c8033cb441ad7e4605961eadb8002f710c59d71791b6b18e93ee79c5141dac199ea3464994b> [ 144 0 R /XYZ 62.692913 624.68685 0 ] <840abe3adfc7d68e7e24018069f8e6f1e59ea51236d055a2301e77a6030f06421b9d8dc45d1afbadbfeba3d3f5891a2641092ffea476f326d52b4b24994ecf27> [ 748 0 R /XYZ 62.692913 769.11685 0 ] [ 145 0 R /XYZ 62.692913 586.53685 0 ] <625619178cbd2b3028890f9076c53df723a9646d56835a1ef63f184dba1f8a26> [ 53 0 R /XYZ 62.692913 563.70685 0 ] [ 55 0 R /XYZ 62.692913 563.70685 0 ] [ 74 0 R /XYZ 62.692913 490.10685 0 ] <0b5f5c331d0df88cd84ec1fcc1bf5fe550fa6606ef1aa92e587dbbfbf4d6a5ce> [ 84 0 R /XYZ 62.692913 546.10685 0 ] <4a4bc2314773ee873649c9b250cb3752f863622049a55ef0cc9c65389f9ab6c1> [ 86 0 R /XYZ 62.692913 546.10685 0 ] <81107d19f769552214373ce9754d39843b3b29ae448f177cfac5bc5ed16369fb> [ 88 0 R /XYZ 62.692913 546.10685 0 ] <9ee371ef5d5d74a7c8e0fe4fb3567a1dd3282eec8a87a43b7f226287af93ef4a> [ 91 0 R /XYZ 62.692913 563.70685 0 ] <4fc0b7acf118fcce517f2f3f19d08f345b41f6243493891bdebb7dea3faf71ca> [ 57 0 R /XYZ 62.692913 563.70685 0 ] <41dc434550799057c60e461af153176ac6fe001aeb2b8e623f9924bfc126b018> [ 59 0 R /XYZ 62.692913 563.70685 0 ] [ 61 0 R /XYZ 62.692913 563.70685 0 ] [ 63 0 R /XYZ 62.692913 581.30685 0 ] <25717beddd7469343d261cb16510cbbbe99e05294ddd84ac46cfbb5b0f94ff92> [ 65 0 R /XYZ 62.692913 507.70685 0 ] <275f06bbd0e4886741035cf05e68457439566315cdd315d67ed178debc833251> [ 67 0 R /XYZ 62.692913 507.70685 0 ] <8023a2111cd12ca807809efaa99de042a68ffae2fcf0c3fff99705f846310ef8> [ 69 0 R /XYZ 62.692913 490.10685 0 ] <9d21473a92be864e993e1220ca199a659dd99dd1f8fe714d98375c3b14a7bf64> [ 72 0 R /XYZ 62.692913 472.50685 0 ] <4363f9e6becf2d552027b956901878e1209b05cf58e0732d58a6481e8c1778af0cf4a790d8e9edc803543505b9d2a188> [ 836 0 R /XYZ 62.692913 769.11685 0 ] <3f9eb09eb6e851242defac09eea2919532abf91cfe22880b790eaf54dc4b4680d6d90993a3702d68f8e30f36b3ff35b57938f673f0556126dae0c4774cc5f075> [ 575 0 R /XYZ 62.692913 769.11685 0 ] <239cbfa0c1fb4230136c3486290626a5350c2e385fc9a7a2b9ae9824dbc96a738d040ca026672f2313ab4357af244d41> [ 158 0 R /XYZ 62.692913 369.06685 0 ] <0d3a1c65cf2ac0207b254affd7b3916da0e84954f52b88222e4f4a7637780cff59be333595b5eed4ed1edb00806b49c5> [ 6 0 R /XYZ 62.692913 769.11685 0 ] [ 45 0 R /XYZ 62.692913 317.53685 0 ] [ 45 0 R /XYZ 62.692913 126.23685 0 ] <48cdc8e7ed1226a85ad5d17fefadfd5b92b77aad1181e11fdf819704645eb9ade3330523e808a45ade4c5922376b55dc46dbf8ccba430b60d08134c2ae64cb546fdad62da03b2d766925651e65f81f01> [ 949 0 R /XYZ 62.692913 769.11685 0 ] <04ed9d37f5f5eefd2faef7a5c4ef032a827960285a9e68df3780b4ecdef6c58ab77ae67e3499523c6b6fa1d413dc8cda65892ccbde6f25365c6812b939bae1dbfb07a638350d761018ebdde588fb091bd3af393c2a6b3087da8c8eb6e498d4ec> [ 425 0 R /XYZ 62.692913 769.11685 0 ] [ 156 0 R /XYZ 62.692913 432.91685 0 ] [ 451 0 R /XYZ 62.692913 646.84685 0 ] [ 41 0 R /XYZ 62.692913 785.19685 0 ] [ 463 0 R /XYZ 62.692913 769.11685 0 ] <001a5f2e7dc11ea5090a92cff72e2d2aaf8a618b08907eade56efeaa5cc8023f88283a813a0c2f0740a8674c3d4300f7> [ 60 0 R /XYZ 62.692913 785.19685 0 ] [ 66 0 R /XYZ 62.692913 520.81685 0 ] [ 62 0 R /XYZ 62.692913 515.51685 0 ] [ 68 0 R /XYZ 62.692913 431.11685 0 ] <46b2cb7f6f010a8f16d6a42ab4f8a768fde4c910309923a884c72dbe6ae99f847023ee0894d2fe781bc623df9b430c35> [ 70 0 R /XYZ 62.692913 333.51685 0 ] <96dcd11bc96508741268ae95bafe3be6171569b4b894c323b949b796b3de40dd0421fb44a9d2514d06dda81a7722b3dda205dfff54fcb9f0ef7105badcb1e482> [ 666 0 R /XYZ 62.692913 769.11685 0 ] <5ffcefbb995e92cd4e572fcb6a92b33082c2a18928e942ae8d0e3c77e54dfc2d3aedd0ef24feaab22619fc7b44fcc687> [ 43 0 R /XYZ 62.692913 350.14685 0 ] <1fd367e477bba89e7584e561699deb856d244354122f2195a72a953e6a3c57004e9d5e2f7882afdef69a2502c1ea82db5ef198292d2bb0e6eb763f9b5eea56a8> [ 918 0 R /XYZ 62.692913 769.11685 0 ] <861a5b938724b2082915ff0584341c5c9de5760558565ba8e970fa376c360c97> [ 39 0 R /XYZ 62.692913 241.08685 0 ] <4f8ac41b081c5de0fb295dd882fbd7e12854be326328d93d3c323b63f85387b2b0ad40b35daba48774fc5596a2200cc6> [ 704 0 R /XYZ 62.692913 769.11685 0 ] <230b2aa418b5786c91c9a50059824ebc1b34085288262c44b0f1879870dda0b4cba4d889b3d5123b9938c39adb4ac7a2> [ 146 0 R /XYZ 62.692913 785.19685 0 ] <4759bdcd05f4f005d3162e7e9833abc5fed0ed9a279d04b9d79489fb9ee126cd421d472350506301b79cbd37e38a3133c59191486e39f38f94b1d1cbee87240493411e881c2de7de182152a41cb736e2> [ 442 0 R /XYZ 62.692913 769.11685 0 ] <5e0f9dd40c5461a5af4b1d3dc1766fa235deb1a56eea4c9210eeea147343b6e9be0073f3e0a7be4a21030c7cc19f7810ed7f2941776eda951b7713b5853d40f3> [ 884 0 R /XYZ 62.692913 769.11685 0 ] [ 373 0 R /XYZ 62.692913 769.11685 0 ] <9aafa3b9b9faa7b55b1059572ea817a39a5c727872de17e2522b745cd1f28c63e136d45120d93958212755a50d105e9255a9f662a02973f9ac268cf4bdb058f1> [ 770 0 R /XYZ 62.692913 769.11685 0 ] <35f56fb9babb11f05e09460b1a1ecb80389bfd730903e3e63e482fc7b183985d9d6b927f14c3306a93e2e787e98b13ba> [ 942 0 R /XYZ 62.692913 769.11685 0 ] [ 866 0 R /XYZ 62.692913 769.11685 0 ] <310daa4fea87f8af8b502b690c66e3126bf7d6a747e4f2decb003f8da484daefc322ab278cde92e4147ce52b0fbf0be5c901957ed2ad997536b6cfe543dd8de7> [ 429 0 R /XYZ 62.692913 769.11685 0 ] <3d283c60adc3aa45c694e7cfbd3b5a9aaf20ef6e69df61dc9d523b23e2995491c5061a8ce396a2da045aa09f3947ab2ea4b9fd648505ac6ede3a33e84e9a7e63> [ 139 0 R /XYZ 62.692913 418.76685 0 ] <77eed398f3e149ff5b48fdfcc13df6f0a79e3f311a05fc8f0ea9487842757dd3> [ 75 0 R /XYZ 62.692913 666.59685 0 ] <6fc2d37628ed9e2fc67e4cb65f069166e73bde65734cce8e270261cb1316c73e110528027932db49d5242f5e5b6a04a6cc23e0cd8e58cc8def3f69d3dff8378c> [ 245 0 R /XYZ 62.692913 769.11685 0 ] [ 139 0 R /XYZ 62.692913 177.98685 0 ] <26abb229a5b2cd361b5390fed54988d2ccbb86effa9c0c18a47ef79e3eb118e9782be43e5db1008744b06d6bf03b2f88> [ 363 0 R /XYZ 62.692913 769.11685 0 ] <0d4fb6e479c251f56b07bef33c2d20fbbc64eb5236faf3baf41b7e3940efda282aa2a8c93b79fa8200a824097a962e1d04426e1e12f701f0b6c4c4215930e053> [ 781 0 R /XYZ 62.692913 769.11685 0 ] [ 282 0 R /XYZ 62.692913 769.11685 0 ] <7cc52a36d2b60f0000cd5a4613fb1a16c4e2f859fe7470ac95e92e45fbead88c> [ 41 0 R /XYZ 62.692913 497.09685 0 ] [ 451 0 R /XYZ 62.692913 407.61685 0 ] <8d675a42ef9dc32ff304cc6a9ea15472b73ad8aecf9643939253a38b00f88c063b7808c4a635d1eeadac9b48b6ebe7a6> [ 159 0 R /XYZ 62.692913 551.31685 0 ] [ 471 0 R /XYZ 62.692913 769.11685 0 ] [ 53 0 R /XYZ 62.692913 269.84685 0 ] <0b19cceba478ea9d75b03b4298ba35a5f2d1580501206df4c562d91eb5d5a6a3> [ 55 0 R /XYZ 62.692913 252.24685 0 ] <8fa5884632f1961661baa6c353a63925c9e6b0f94b5083cc4db120207501ffc7803c8e4bdafd0cc8320f301d1b044244> [ 84 0 R /XYZ 62.692913 248.99685 0 ] [ 86 0 R /XYZ 62.692913 220.24685 0 ] <36dea2a3bc24ff1370c0f1c1ad4700a39b29aa8cbb3f390f672c69126cab629ab920deaa2d39f745ef35747adbdff5f3> [ 88 0 R /XYZ 62.692913 220.24685 0 ] [ 141 0 R /XYZ 62.692913 616.19685 0 ] [ 58 0 R /XYZ 62.692913 785.19685 0 ] <5adb62aa7c51942afc9a556006782ee26f11219872db7a4464f25b4fa6f301ac> [ 59 0 R /XYZ 62.692913 241.09685 0 ] <3b5511166e9f708b0ec71af1e835ae22509dec5a2a8dd2f83d0c1ac48986d25d> [ 62 0 R /XYZ 62.692913 785.19685 0 ] <3382e95bfdfb6e072376760e0b490ef2a3ba909eb90b9b7027142fbfd04a47d1> [ 63 0 R /XYZ 62.692913 252.24685 0 ] [ 65 0 R /XYZ 62.692913 213.84685 0 ] [ 67 0 R /XYZ 62.692913 185.09685 0 ] <45fcd39d847bdf5b96782807794ceeb3dda85a3b5b0c18ec40546adaf10dccaa> [ 69 0 R /XYZ 62.692913 178.64685 0 ] <34b94a19b290645edda1a9d7eab8dc9099d275954c8c26329363bca3022588f9> [ 73 0 R /XYZ 62.692913 785.19685 0 ] <4d76265831244809a974dd6e9dbdd474f1ccc55df7f7a6c348d38fb3065964a2f34c2fec81bd0155d32dc26a1cc85a678ee3715213d1c6a8ea8a88967e199511> [ 586 0 R /XYZ 62.692913 769.11685 0 ] <588d225c4cca9b1e26c1ab515abed53cb0465a78007222f342ae40a37e2c4effab11a78e113f1f4f58dd6da4f718d6f3045e95be6c303ec0b55c4344e5fa723c> [ 674 0 R /XYZ 62.692913 769.11685 0 ] [ 137 0 R /XYZ 62.692913 230.91685 0 ] <1a56d98bcedc1f0f62d4c7bce97fe04d84ee37892f8ce119ed952d221c638b294e70f04fb1214129e38129c59646e22dce8765c7cd00c5d9004bd88923401d73> [ 251 0 R /XYZ 62.692913 769.11685 0 ] <642cc4a26657c00de8755299ab08393103b31acf29c6caa4a164cafa74de2d3c> [ 93 0 R /XYZ 62.692913 439.06685 0 ] [ 891 0 R /XYZ 62.692913 769.11685 0 ] <0251e11f54ee26566b8652c33f8635ddfc1a90cffa28c38482e97ec1ff4a46c2d982bf83795ba05ee85c1212cdab0f77> [ 46 0 R /XYZ 62.692913 719.39685 0 ] [ 388 0 R /XYZ 62.692913 769.11685 0 ] [ 51 0 R /XYZ 62.692913 653.19685 0 ] <6486eae3729387cda8ba340fe686a13adc92d0f4169fc56aa561f5bbe87012f9ca9db0e3204ad356808c40080d57f1afd1185e8691bd28d3081dfe5c9651a014> [ 792 0 R /XYZ 62.692913 769.11685 0 ] <4ea69357c7d0b6cea247e244b9216b869315065870902a0c9f204e8cbbc1e118898300d3ffa166b6ea215363a80319a9fc0cb222e93f3559b51fb4f1112caaf1> [ 965 0 R /XYZ 62.692913 769.11685 0 ] <935a88f1a12cb60dfc988b06108d22a5c600ffb1ee9c10c5a5880f4f82cfae6a37914c8df50b453c026996f1e1ed0bd296e0ed3778c64f9524973b25c41457d8> [ 434 0 R /XYZ 62.692913 769.11685 0 ] [ 898 0 R /XYZ 62.692913 769.11685 0 ] <3369882d4c9820e5f1dc4f0d6474589169d56a7af3f9585a57697d52a3e24099> [ 451 0 R /XYZ 62.692913 197.13685 0 ] [ 45 0 R /XYZ 62.692913 652.84685 0 ] [ 42 0 R /XYZ 62.692913 259.68685 0 ] <4e350ecac8a9b0e23ad4bd96645444c8f99e83312494d762efebda4125bb7663d37260ee50793b7725a9056b0eabb60a> [ 45 0 R /XYZ 62.692913 785.19685 0 ] [ 44 0 R /XYZ 62.692913 685.19685 0 ] [ 518 0 R /XYZ 62.692913 769.11685 0 ] <7539b5015867633e9d40a5a939a86320df92440765263e8287aad8e03ee75a5f> [ 452 0 R /XYZ 62.692913 675.59685 0 ] <79ba9725f59cdc3d16cfba0e86f00f092e36e077759e312887913c7d4ee43b0b86aabb92ce50ff7e17a692b998075d3381aad60e04e2bd286800262d51f64e50> [ 398 0 R /XYZ 62.692913 769.11685 0 ] <19081a9a22051e5b0323a2af8dea441a734d3101f313a78eb01aa073763fa5c9> [ 452 0 R /XYZ 62.692913 436.36685 0 ] [ 45 0 R /XYZ 62.692913 475.66685 0 ] <80df970aec8a72040e26daf9c61d3db75882310585dbcf3cf9b45140f9babf9b6eaf4e96f63a9527b229324fc6abd7aee94cdf77d6dd47d3853208eb00c947b3> [ 716 0 R /XYZ 62.692913 769.11685 0 ] <9d7772b34f08a5b38d5616a18606e30dde9169b5a7e6af39272b1c61544bb48e> [ 453 0 R /XYZ 62.692913 785.19685 0 ] [ 260 0 R /XYZ 62.692913 769.11685 0 ] <8dd8f74f1fb18cc672216dd95016a13cda59047847b15075e62341119062f2f4c23d84c054c9b362f5575f08855f1693> [ 597 0 R /XYZ 62.692913 769.11685 0 ] <09cd230e9a72ef6b69d826033274ce5f5ddb02f55f79fbbe25c392ba9fa68d972c6edabaf69317c276942129a3d5fa3a> [ 726 0 R /XYZ 62.692913 769.11685 0 ] <390880cb64e74fcf09960973f97a403191a042ed9c4be5ea5f93f621c2200a76> [ 45 0 R /XYZ 62.692913 609.46685 0 ] <0884608b2736c9bdc5fe3eff7a83133026c13757b850e505601ac6db07281be7> [ 108 0 R /XYZ 62.692913 465.84685 0 ] <7fe089fdc4fef408dbfc8fa1cfdb1de0918dd19b58db6b6f63e2cf728bd2a80ec5e2e0b60e0bcd307187c727987d93895595b7501ed042e77195ef2d2719b1df> [ 43 0 R /XYZ 62.692913 216.84685 0 ] <60d3c0a150d061dfcf66ade3a104c1c12595c67c3a7b58c58409f5a76dcbd301> [ 89 0 R /XYZ 62.692913 674.69685 0 ] <6f04b73d56b48d212c57b1b05984d904731be112e54aed093fa5a0cbe3cdafbd23b5c6a803e7e61d1bbd471fab4dabf7> [ 158 0 R /XYZ 62.692913 269.40685 0 ] [ 607 0 R /XYZ 62.692913 769.11685 0 ] [ 903 0 R /XYZ 62.692913 769.11685 0 ] <6b429bba452af5e1dde0555141de9c971c6be3f3adc524cd6fc23ff2ddaa3ab18ff538c8e692e992f68ffa3221f6771627a22ba19559c335ee60f84483f14d9b> [ 910 0 R /XYZ 62.692913 769.11685 0 ] [ 453 0 R /XYZ 62.692913 488.46685 0 ] [ 685 0 R /XYZ 62.692913 769.11685 0 ] <1ef027d009e74d646b33c0ff8bc8a8429fcbbdd1bd9fefe62c0620b0bd469732ba672ba3ed26840799a98a9f90440e60c0f5adae242a7e9db7d9be3861fac9f2> [ 457 0 R /XYZ 62.692913 769.11685 0 ] <82688527864cea7085595e036c2963caeee50069c54f7744ea1d9f3ae2f57640a0a0a2cad270758bc2ee0c889c2d74af> [ 74 0 R /XYZ 62.692913 371.42685 0 ] <4f3d471c8667ab5dcdf7434f0b5488f2bdc68a0423ef312f9d1a966a6afd18940a2f4d456096540074fbd3abfe397a80> [ 187 0 R /XYZ 62.692913 508.59685 0 ] [ 210 0 R /XYZ 62.692913 190.21685 0 ] <672ae73eade432f19bf840ee4712090420b62fb87f1f2dbd945b6511e4407bd738c04efd0b63425570035b9c4f95c5b28ccfc36efa7d08b9e2c3beb4c71a5089> [ 229 0 R /XYZ 62.692913 323.09685 0 ] <6398420af0bf7a05dba79619b1ffd0a86b354a81fdc8afd8c38a0183e0aafdb020ca173fd8bc4557a4a8b24b49169980> [ 51 0 R /XYZ 62.692913 429.14685 0 ] <0182756e91433b8fc54260cfba2242ee6698abe719f0e1225002f1c3fea3d9291fc1f5ddfdf4754119d7fcb85f872453f8640a91f571d2622de2dbab2687c34e> [ 972 0 R /XYZ 62.692913 769.11685 0 ] <077c8d63f3761ce6cf26f0df6190d2b8fb496e7371284f4347c796964b7e900757b5c424e198d2d4e8519bfc8da497b4> [ 49 0 R /XYZ 62.692913 431.28685 0 ] <29cc7b232543a6a8a2a990e5e8ea78cca3b9c5d6abc364a915d7d6a30276bfefb56fcc4bd1d96f8f7566b63a31e8a0c5> [ 62 0 R /XYZ 62.692913 741.81685 0 ] [ 111 0 R /XYZ 62.692913 428.53685 0 ] <4383bbf2957a00025cffddd730b77ccdec00191c91580921ddc03e8541b6bcba1315935e17d302b6df27d331b890af70> [ 111 0 R /XYZ 62.692913 202.02685 0 ] <4b636469d5c1b9aa2ecba9aa9ccd1bc6c2d4812bd0cf0d9bd97b7c963b8e46faca54c6ed4af62fd81b6a2fbc7292695d> [ 63 0 R /XYZ 62.692913 208.86685 0 ] [ 64 0 R /XYZ 62.692913 785.19685 0 ] <993429c968b8dd93c7247f595e1853c580846a4c4ba2d2397ae7d5a166b97c3d1b05074957a18ce31efbcedbcf060dfe> [ 56 0 R /XYZ 62.692913 785.19685 0 ] <7132969f8e6bdc7d0433efb2668472a7cc29a4ac82f6dda228845fcbe60aa705ff70551074ff65d89ddf246799e30cfe> [ 51 0 R /XYZ 62.692913 269.61685 0 ] [ 453 0 R /XYZ 62.692913 335.48685 0 ] <7d2b0471d3964a664399ca3461007fa64f3b6b20c71d4b74a23bff439170f32e85e94d5d34254c397c22caebaa16add7> [ 44 0 R /XYZ 62.692913 365.44685 0 ] <1b529867f66580a930d07440c89bd9b962a8d6538f9b07541d334debfdf928ea> [ 454 0 R /XYZ 62.692913 785.19685 0 ] <474a18c0728d14f586d2907b7c579cb9c788dd1339b9ce9dffa07d3ef736c9c0> [ 736 0 R /XYZ 62.692913 769.11685 0 ] <896f4369b5d8abf428d8caa46827a0c9f2405fb54b31d9bc53b24295f9805851b99d3a46f11fdaab4a3ec47cf06ea904> [ 57 0 R /XYZ 62.692913 480.22685 0 ] <1dec9277d402b8775428238a2b04f58694d008457a7a4ef81c3e1633a8b18c76caf318feacb1687abf7a72c2e01b3a22544bb15f8d65de05e1f6f87ba97bc894> [ 48 0 R /XYZ 62.692913 637.81685 0 ] <460db9eefe49115591fae425ddde1bdaf3344ac348787a3c0ecd22032be5e80f> [ 42 0 R /XYZ 62.692913 561.14685 0 ] <1dd2d8102e4186a229bdf12792761eac8e3dd58f26cb53fbe02588ab2145b1c3> [ 454 0 R /XYZ 62.692913 632.21685 0 ] <18983708bea22db9edecc77e45483d0816a8ba67dc0fb441a21e9a470035ecb7> [ 42 0 R /XYZ 62.692913 419.21685 0 ] [ 454 0 R /XYZ 62.692913 335.48685 0 ] <26ac40b9e63374e6bd1a61bf9b0cc601ecc242625977ccfff14fabc7336cd830200a9708f3dd6a5a581f3d50afab96b3> [ 115 0 R /XYZ 62.692913 761.94685 0 ] <8e3c2c28537ec2748bdb04f4bd077081c46ac2cd91592f0072f1039d89e77437a64deea0c6cfccab595833c295b1fb49> [ 173 0 R /XYZ 62.692913 276.04685 0 ] [ 176 0 R /XYZ 62.692913 306.64685 0 ] <620daacb6fd24daa266c2609be1f68e20292a7a5cf3b53772a4b4ccb7ef785246b6166d178f943bf140e32611cb2f84c> [ 180 0 R /XYZ 62.692913 138.39685 0 ] <227c94f76a60189550461a72594fedb6140875175c9299cef236e2396750cdff1b86cf51e509dcbe99f2242bc651a542> [ 186 0 R /XYZ 62.692913 266.09685 0 ] [ 195 0 R /XYZ 62.692913 220.19685 0 ] [ 210 0 R /XYZ 62.692913 785.19685 0 ] <8738521dbae297c7cc1d8b3c91175e73967a4c738c0437ef1138f1be33f9cc248e2cf4faca8af44bff153405a9db1241> [ 228 0 R /XYZ 62.692913 335.86685 0 ] <7ab9c606e1ad4299a02e02afb8395ff600396b8968621673c8d0e76c85d318c545434a8c5aca32e12aa37f8bd076871e> [ 122 0 R /XYZ 62.692913 149.49685 0 ] [ 125 0 R /XYZ 62.692913 161.99685 0 ] [ 131 0 R /XYZ 62.692913 122.19685 0 ] <25c24139711fc75a03e924d529c35f0a793b45ae0bfeaf716ce2e8d2e91f950b2139fcca45bee4a18d95add002647401> [ 143 0 R /XYZ 62.692913 442.44685 0 ] [ 147 0 R /XYZ 62.692913 479.04685 0 ] <4468c000f65a39c032f7d817ff370496cf1db3d53ecb71e6b49041b47a66882d1d043abcb50ec9ecf518bd55474ed66b> [ 154 0 R /XYZ 62.692913 686.64685 0 ] <37b2e1c821a4a2bf64419eeb57df3cbedd2516316c77016f4f27c651e6258775518b4fb71712c20844a91c07e44df572> [ 162 0 R /XYZ 62.692913 506.39685 0 ] <0600283f3736accf39588ac71743c803d2704a1d6be29ed05116ff4a1cd84f0230294c36fee28e6792df9a1c8d8a9f26> [ 169 0 R /XYZ 62.692913 147.89685 0 ] <068c5875c59563ed538b0c963b4dae19d505151c47c542bc02073d9be1e9d333> [ 455 0 R /XYZ 62.692913 785.19685 0 ] <1cfc9a3d3f855616a274b27019497d411d4026d6d2c65a8afd5ab39b3763cab1a31faf4f6cad04df99fb6fed40e32399> [ 842 0 R /XYZ 62.692913 769.11685 0 ] <69512cab9968215d07b685a6ce71f3df2c80fb0ee30f32d77114121ed8e94912> [ 455 0 R /XYZ 62.692913 632.21685 0 ] <4d7bef38e6d9dc355f73dd1c1d51f69c285e6c120b5f0d53400eca73d3125ed61e31ef93faa6cc7dfa29b997b11f80f0> [ 368 0 R /XYZ 62.692913 769.11685 0 ] <61f523a5bee42586449239980128a10ca94d26549fb0ff5a16bc5351143ac81b> [ 62 0 R /XYZ 62.692913 624.61685 0 ] [ 49 0 R /XYZ 62.692913 265.98685 0 ] <91647e91569ea56cedace8fd2d6f05036d0a5f25a3d7b1efd8929ea5639489272eeb45abed36d0d983c67a01a4a8dca2> [ 95 0 R /XYZ 62.692913 769.11685 0 ] <6618db5f4fae0879e36240a21e6bac4d73119ec23c978e90dd6222591e734899eaaf33388c9b11131cd86843c6853efe> [ 76 0 R /XYZ 62.692913 401.94685 0 ] <0c610ec26d691f256ec6b89d38f7081d4cbf3acb385742c8353d447d55f4c13d> [ 455 0 R /XYZ 62.692913 249.23685 0 ] <5c81b0d6023ee72cf04782ae4ca39122cff9b91c4ccb6c8b283b96630c7ad8b1> [ 38 0 R /XYZ 62.692913 769.11685 0 ] <218a7187c63d96d3cc7fd1d7be492ce277fb1c1b63e44f18e17280de2a67da0a55c77e8a7a16c349a1ec79af35ae2128> [ 47 0 R /XYZ 62.692913 769.11685 0 ] [ 43 0 R /XYZ 62.692913 570.19685 0 ] [ 526 0 R /XYZ 62.692913 769.11685 0 ] [ 342 0 R /XYZ 62.692913 769.11685 0 ] [ 50 0 R /XYZ 62.692913 241.56685 0 ] <7b81521b44b5502a28c7d75fb832b13ab73f037ca9be2c8e04096cff960584ec> [ 49 0 R /XYZ 62.692913 785.19685 0 ] [ 536 0 R /XYZ 62.692913 769.11685 0 ] [ 231 0 R /XYZ 62.692913 603.13685 0 ] [ 77 0 R /XYZ 62.692913 230.18685 0 ] <18612417b35af6b008db9c562a21aa7a7ee050bc204f547d117c4aba8762a7820cea980beb5fc6c267e40cbce510ad14> [ 619 0 R /XYZ 62.692913 769.11685 0 ] [ 232 0 R /XYZ 62.692913 785.19685 0 ] [ 233 0 R /XYZ 62.692913 188.26685 0 ] [ 47 0 R /XYZ 62.692913 589.38685 0 ] [ 110 0 R /XYZ 62.692913 785.19685 0 ] <2c665cae2a1bf6d292cf02ecda52a4f1e11c7707c76ea2da2d7f6d50f027839ab509585c6c69af83b84e938712a685e5> [ 77 0 R /XYZ 62.692913 531.34685 0 ] <1398405b4da3f03d184a5b9fa8c15d6708e07021aaa88786a20cb2c19814a24a6b298494ba48ae4db921501e1a17c2fb> [ 50 0 R /XYZ 62.692913 198.18685 0 ] <3840e637b52658d112cf89b093fc8e6a631bc5c1740cb9a5fd5a37975e024ea6d0af3bf6c5bc79acf22c17e901a50c63> [ 91 0 R /XYZ 62.692913 480.22685 0 ] <697d7ede443465cb31e4e1c7c9e441003a4469e36710abfdbc01b1aa17861469> [ 48 0 R /XYZ 62.692913 681.19685 0 ] <960e1d7f4a2ebf24a4938e561fdf03ef99d2861d8be34170bc6e846515dcaed1> [ 66 0 R /XYZ 62.692913 785.19685 0 ] <8cc5b39e860e2e6ae0faf6159c7f624660dd8d2965cf280563e3f7fd79801a56fdd5cdd3ee9e82127996c47d83ddf552> [ 89 0 R /XYZ 62.692913 193.71685 0 ] <85e727f9e42283eb7862895768007f52e3550adb9cae3168d08cc8a95d6d83d665744908f69153f36ae8c92c637a4c09> [ 85 0 R /XYZ 62.692913 785.19685 0 ] <53ccc2d562b8e383e03128ec62a5b1a028ab69afdfc48a5d94cc4cbc93b3574ca3a1eeb151876748ede718b9037acf87> [ 235 0 R /XYZ 62.692913 761.94685 0 ] [ 981 0 R /XYZ 62.692913 769.11685 0 ] <9401a6d32fb6f51f67685ec4e3161f04068ac540bff4405c9ce43d0c8f565540676f9b49162b32a7c9edf81e9c5b3d0a> [ 477 0 R /XYZ 62.692913 769.11685 0 ] <01aaf5bc8fb06cf74cb56c269b8f5a9bd466d3d0101e72af144fec02205ddaeadcb0bde1440bada22958942a89289be8> [ 44 0 R /XYZ 62.692913 585.59685 0 ] <0ce49367f11252b3fb6ec24c79bda814aecbdacafdc62c00b9e1f1d0bb3d6bdee4917cac2c490337a748e75691d34799> [ 146 0 R /XYZ 62.692913 604.13685 0 ] <148ef5492584cbe0bb5321548d7a7d80a629f017fba9a2ae31c556216f6cf5ee1e00ee482b76c009c78d91f98ac6703d> [ 6 0 R /XYZ 62.692913 675.21685 0 ] [ 95 0 R /XYZ 62.692913 569.33685 0 ] [ 490 0 R /XYZ 62.692913 769.11685 0 ] [ 449 0 R /XYZ 62.692913 769.11685 0 ] [ 92 0 R /XYZ 62.692913 642.84685 0 ] <4522577b2fcfc89ab8d79883a637fb8e2f52a43c727ce197ca8d32c8ef4c0ce818e9f9346fbb32bd9cbb2068b38baf3d> [ 93 0 R /XYZ 62.692913 719.44685 0 ] [ 92 0 R /XYZ 62.692913 337.26685 0 ] <8c76394ae012c0301c52522a4690fdd2bded91c14c99bd0b3f8608d223ad1735fa2a22468b2c0d1a3e8d41f249674d7a> [ 91 0 R /XYZ 62.692913 241.09685 0 ] [ 544 0 R /XYZ 62.692913 769.11685 0 ] <13ce4b12e6bd7671450b31942d2aedeaf944a612fd2ebc7dfea320ac575692ed2a5f474fcd2903b0b8e7f7aba46ae04d> [ 44 0 R /XYZ 62.692913 174.14685 0 ] <780972c6e206092f175b1d4a13a40ccd50e6744e583dfbb88ad85795d4de4270ceb2aaa848f4736c1d78bdbade3427a3> [ 39 0 R /XYZ 62.692913 522.71685 0 ] <3c834aebc21af73ad55f989626fe05d024041745caa86666e2a5bb735eea2b6bedd8fb02d330cb97c1cbfa355ef964a9def510865e52341f76d7e1461843044f> [ 408 0 R /XYZ 62.692913 769.11685 0 ] <44f28cd9d34a3bb7a38438a8504853652aa42330955141759d36bfc7651bca7e> [ 39 0 R /XYZ 62.692913 785.19685 0 ] [ 108 0 R /XYZ 62.692913 277.71685 0 ] <873698b40bf43ee446c8aa4a12726dc3c5555cbdf2d1e3f337918e7ad962aa4cad45398701a9362105e5f2b35430ac2c39e348639126884c0d06d6821f0f9ddb> [ 416 0 R /XYZ 62.692913 769.11685 0 ] [ 145 0 R /XYZ 62.692913 785.19685 0 ] <0411f968de8c43b52fe28bdb122aa3db46f10456c6ae40ef6e548cacb7670e3f12eff0f23856f65c09c86fa24683ba6684052ecee301c76fb45dd9bc5dc5eaa6> [ 803 0 R /XYZ 62.692913 769.11685 0 ] <0ae5d6e5bf764cdd8ac7c4fb3e57b3de4b542849be4b29b984c55fe927107018228e047fb8cffabfb726b3bc2f93691acc1d106dcb88861a932fc1862d923102> [ 506 0 R /XYZ 62.692913 769.11685 0 ] <72b215d540bcf5d5f338d0d18b35deac5cba811351f5d1790fb02049198ddbc388f3e498fb73e07f22040970d4f35a79> [ 384 0 R /XYZ 62.692913 769.11685 0 ] [ 449 0 R /XYZ 62.692913 608.78685 0 ] [ 87 0 R /XYZ 62.692913 745.09685 0 ] ] >> >> /Outlines 3587 0 R /Pages 3 0 R /Type /Catalog >> -endobj -2 0 obj -<< /Type /ObjStm /Length 128400 /Filter /FlateDecode /N 3608 /First 41671 >> -stream -$lr{$pkyY_^`sVcmoFǻ#wq -. =Fj-tXy -wRa)Tƍ+ҧƧ&êP?ZOEa(C8 I) 3QJ'F|3i:,P;+[<$ʟ]Wnw6 ~slc#fĠ n9u6ԲMu2C&V~PMwhjj6s1<:zl'eZF&5RUfIsQjS_4oR I&d*OB4E;qWqcF룐q-NJBC}Zg80̃_f"t!Q%nf`_=K/GLFh2ϵyDQ#'R=yK^*FNu`wnX --+˥4Y%!I,YJV\+=/l<Lza%dwc jI`e^-Ӽ<>c:/ѐ mrG9meq̙ye#sdD E򶌟+Nla,uw؊ߎN(()Xm 3r#7[:{b!l8"&d?t Lg{FLԄF rvsV v$uyʉK^9^ ryR6!5[ܡC \hӶ[Cz}ǯ[BKcИ p@]~n`#PQ!&bKڮ=Mtrqo&Bb١LmXB;"U}*30vCǵ3) RuUPM('ˊ]Y{|싘EI?N/4sD .[\ua OqG=s$J9[G: V:G@ -hKڛ]ylmg/>B0)$qc7Z^o_Sp [-S&fo O3Qe;%y0."a&pm9'?XQW]FښҖ\$/#hIs&յ0,05T Un6l'Oj<u<ػKY0'Kqp>4UnN&iDU <>݉jȇ_~"{} v8ʔZ-r+EPu=(ɺS Ұ "@ N\7Z8QNBO*8G=R> ([ְeM:QTb3.UO3'jnZbsDͲp!b(P^5%l^pLHJzt#"cót:Ǵ>eZPlV|gkB-JoJQLlݐ:@Wd*M'C9HzՓ]6M}pAHR죸P{Du˰OAvKA@mI<ܟ~8r3?ʧmR -F0CD浨/a3Z(5F){QXz;㈼HfcH1Otrr=c/M-ʪQHFVtQZE׉VfĒvl'L2nꀄڑWdCڑ MdrKy%˴sbr|ٵ$ܞ){ pgN$fxkU3#ldx'mR~+r0-I餣ꏨPMC滮bRuX~#@01\eOr:pG -,M/F)DRɒ!+m/l !7ymf!`E|7'͸~hRM=ob:GhuV{_=OnP {*{A7|`w}u>62(t@D\ZRv?c M>f~HGR ϔ_  tMC2}U ڌg:M<%*{\90h8oIG3QK1]0K6h\5ӛ`&7ls +n?f$dZ}w9YYលmhyͩAߝ;VIJ17HVt_ٷV&1;WOI8zG| eoti<;"VB-s @ C[N q[c, . rrkӗqŋ* 3t"Sٍ 88"QnNm8?r9e/31o#YT'zA$=C͹fϠ#s3l4{iiߦfp2> 9>۾Ƞ,ŕ5cg@hee)'xGFDhm!c}~w(De@:]{UozEHCR5gi -n0GbT#k1Dx^~Pr^C]=eIq`>_.0} 3[ <wW _zmߋd3LW>2n`@v s>𶗃2XP*M'yZW?J -$ξpF1gy˚Ξ+zfv_]`%|LJXXTr O-B 7|R)Q nNz6굴(S8R>JEl%Sg?ĕ3]@[ fQ1>8$6CN\qOi[ Nڊwwd&PHv'}F7_ k587NZSe,C;PlбOgfR4:Z -n溿`TjFAA{gE013A)䈷'쬒6`PhZxe0]U5S>a ,"dEj'9[=3h8\PTW{ʌkI)RQa:{' 8j;E'a_ؾ:U I -/kEz<҉aq:tw TNS<}^j3a`i`gWR$r%+IW|}upˏc]M߀$1Öj -~CH s(r R@/;.thUkU(KLu>A|-=ۦ=ye;xrIRn-liQpt__Ϸ(쯆ƞJ#w^ZI:@ E@߾FDN7v|):"I #Vn|AYi|)$fcb,qZc;^Ey3Grli]|6e&m~1bWKts?$" dM,dqԃto-T2BtN*dg:(K( HL/q781N'͆>cRlfOFP)ip }3z-1QQ}oP c4kpbZrRh}x-f8iN'T|Bbdŧj$s8@#_ f-Ó2_Zo")3*a7*rcC6q WBg j+ʁ*Ȧ8#ϵ.q['Ԗh]~ `7oG6Q<p%Wi}_ekkS4(Є:Fyq[qӝ\6`hj\c FwEvК5gPU4"8ퟅmߞ )Gy2n&Wܞ\!z?K.^GJ+>'o)']I\N@+GCdR=@O`iV DO2OQG9)vK(߹VKq5ÔwwP75r_L4iyDѳ[%8t8F kOqSWM]ȺJx&`*BaYfԢN*2n۬N\)cPPѱ&%-)\LJ) xו"trފmvޞ -w*Q  -b> 픖xN=1 K;cPsdԈ0?kLa,"s@*Lgzܑ9Ұ%U&g<'} *uC[*(_;:#mO:`WAy~eHe%hOQ2x]TcaLJL5L]~nLJ V@+1?@ -2Im=(_PXFY!嬟^ߋɇ׈{20e8kw@EhfX,;nۭU(VUr:UE)Dϓ`$`P ^Cr#¾ $>`PGOP &-oRx?a C;+C[T"Dݑ/r!DzkY:[;Q10Ö+kyTfwA.*>:,a9cLb=&Z|JwD*qvt/hl2({VbEL@K~y2LUIkbWa/8 -Q`RB]#k \d|˝M{l@P9鳎o害)d͢o;__ lWY3Fnpxkg@ؒF.g΅ -z&JBg-\[w*ӢrXrcVݚGro81UΜPL}e;<(a\l ֏Z42$Sx yY~B"4t0M"bKz;4x|- r%[(6AErgs|Ѧ7|v^?FPvp -6GD\Հ m, æ2j'vXk -^vfITyZI1 #@Fh %SPֆb\Y t!O̥J]QWTG4"!ݔ:ߥ-X[]]XZ:50(ϡJݼ).֮zZG+ +[.y袵eMwH?s9_5=NPRLi{S!Y)X~p?X%ώd";͜`t;37z)Tt8?|p 6 zpSGbXsf7&O"g+Ah^tLcBn ?q<'A?noRLԹ`u/r<{mtnPL%p^gtHb<&g^8bYS}i@8j@al~+vyQLvŤn&AGM9мXnɃUrQM:$u$0:3B0}7W8)ܭӮd.$ JL}7 +P`C^'c/@RDR|\p9NMbV E)UhD${vmyY_[sձ$=[lHgxbbk;Eo%%ĶZߦ䗦t[]:F׀NӅ_OqZrQZ4`_og+[btPXD!waJ{%6u -?TE%"_eWs'P,7S:~Q &.+!,}Rnsծ fӀosz/@*̐! ;cHm[\EM3g EQ$y_KA5ꪫ1QwHofTnY8d#c<ڬ`PVwBbŘZɱ#_a)\)uRL=a$sȯ1ԄiRT)Iiˑ -bAq39I(gXg9dʏ%% -u*~.]x𪪦'x奣q{w1 8 G繗@l3q"oL OY+c-~bGM$ TqEh5rA&ttU'œӧ-uVU^gwxسhfH*2mDe?Kvs%h (FMF>C熌 r+Oy֝\@i1S tlL !O<aOhܴx7JJP]7Ync&K 1@df%YON1J> k -B ٘f7ԱDslQ֟R)톿烳< rR G?2[`-|J.}|{ZUG@g t)B|iu:1:/3wDgNZ5hR$mCOfc)iĵ'_(^:LPƂV>76Vi]?]"˸6 -*?Duc*)V5 E|v0b2\'㩒1N r!(nI>R>=;Lu:wPP9,g ) - VJKZ/cl]LIﮪښC7V~P*JN{4dPaP ei'nw}}/}D̝޾Tޡh_\TѤgv%lrqD3%dG@`S4{S+CCTgP% rX[D-w^=K[j111f?w-0ˣ9,]Q&}q$R!{خ[5- o¨rfiak;/۳!wG){\iZFUE^۽b*Y#Vi%[lسVZT2n<AHxo `1#%ޢ^)Sƽ SZCV),.NKT$:$drw)`scABN{LÚٖO((̷|е]z6,k/.]0m#y$gl%Caxfz6aQtZD|7P8nQ -N|*_翳fd - ~0ֺʶ;xժ41l؈XKh&Q+̾]Q[袎?M$uHjƞe-{+M.YѸo"bо#M5vYj\BK ԧ%`oXMϓ>h-aKbjF#Q?lu. '#O e3<{P7X[Wk2(쯸7@˅C|x؛hdwd; ƚp椐a ]" 4RWR%#J րPeS=X`21 ڸrz#d 03 -3JX-fPFbJ>'Oo>rB$7w?5^>iOW‰ -+ָ"iaGar0)…G#: b ^]j.RO*f%!YnNl"RٽپCAA'.VI4[u8l!I$^b_mf`|lّ{Cĺ}~0͈8 -w<85 -; QՉ03E̦<ّ -YVnsm}y_*?Oh*; w;4P:IŦ$?S6öAehL)>m kt_v-0= @/P`ި'\28pG+P24,\B U}{nnxRE$yy< -?>C#lϋ BR'VfSEѴ96cOsЬQL YNBd7 3]$k̶=^z)>j^f!蟜֕VPqUe2a&(*LjY}#:xMlrƴӫf/ \.fX2T -' ~+:c|>yF9] JZsDԠ~FxfLwMn3$Mc;Y+Z"q leHm߿D/(brpLOJ-mxOWlRס-h&xUYE{ 驦=,BV*XU+uɞʰ7eN",\9{߁ =u)(pNV.?S ݼ'DZ47gy8"/ [ATkyU|̓v@*Ut- b)PCײ.tE%e'xHe(w9秳S0>$ i@劖( 'J9T<-%++&?ÇB1>[~*f%R~"*-]>#ۯYO#tz([G|>mbv#zD1f"Jƹ6h -'ςq -?@o]]8eYO -<ɹtjOdzh\Ȓl_CJۚKXsee],GJͫ,Cؠkw;o* 2f |TkK?9+/iIU(仈=+D  -xk@t<*MIAԳ k;law.|ZdEE|Gҫ pMZxWjݤg9K}DG[F:D!ap>!*?{Oz/~B_|[ -p"*VEu/066!<.ΰJ"ĐR:%n!CyzKS2FͤD& /tώtnsYψ̅0U iD@./>N)Tj% ƌML䚉{ o9g' "AZNjg wMX'RѳvIJDa&Ftv>14ڝ1 /;uߟ# ۝JkW{PUSḞ3d@_Ťg=0kgbwt{gn1lŀ .uFHI3m;NY'>d/QK%zkcyUoZW&|aPa)|S-M!St*rwҒ&W"nRY.SFΎFjLr}4 W&3w"Kw-$.}+~ɃyM^_b}bH0h8eA^W\ -װaeٚdXtDR0hZ&ǦTڶy8>vLl -~#DB!|B?HUL4mQ\#ܸ|'B͊pJU#W*9v~-p-3 Ixed\ݾ M~o:,~򅤡eHxCG?Y5ݔd4Ip$cJdL !% -ټ婲N=2o U3_M p7ei|^})}Z"QgMU}vP#DBxp$ԏnte"RK팋?]5KDn -)b>iKR5Ou%M}7uwcooLTh0{>Ց|;gGͷ9fCgх{9]/)1C:CqrZlLMG[[^hMd8 ]yn-K,/'kI4^JC({ +W=ALP1,()wUgt{fO/B5~LSHI #I=}`SFt˱,y, -Y7L=Jb=XGc'̄q1z=n8If_%lk4q].{u0Bq7 0!/zf`C1/5P٨Ԅ2=Q"}mS6he' ->2ǂZ02Ѹr0Yƛ@Es9!8Wy(rS рf CEt%S+^l!B*,Tl! -0=o.4Գ<.cHN;{=_DC%/OmQCߜ^W.#%WIft|3`aK>ؘҙd4xt-ܑ\$D-x/u ,Q.Z7,1U"}e:|E^xSb`?%<­a^@\}Зp -ڭ-Euy -_T - @0Ho2IwhGR*͜.B$U9=kfp- %F~]$l9r_UL ǰd_n5"8=IȊ=={²_J9:K%ǻMwII^'>=QQMh՗A; ówU†%pSjgpqpV8p:>t{?r]ygF즚1pGR;&WYiS.B^ %j 21Ӳqo(3} ?քcZ $ִ9)Tȸ`O= JJ*_[X~ -BC~qMVqoTC\Lp&5QHi^z`\ď @M7t+Ƶ->\=AGUq -Q=MnjLXb~twc~W -mt -vU퇄d,$ʂƤ/\N3K|)jOwZHiIEpJE% -fT2)c]J֦=Ki(tLid~dLD*_S=4~E5Pivd?O r 8EˌG ebDTM621eYU4UD5)o6W^nsɏꉧL`*QH6 9Ebv﵁QyA[r5 ;qp9f_8f@NJ$Mu` mBƹ@%* #F 1$EJah -(A*ݼ >L1]Vi)v -^55tո&8>HPn n\XUպ ,YUAVFhj-)p^;c̱Tˋwwdy?Iwqn/X*EGvQQ9vʏґuagU^ðN `Cfcw4#j'Ny7ė5gv(:]N0 -#q u\I)]d |]^oWD (I2qcџs?/l| ?xz<9T8f p6NB~7J3Xe3߻0F!Lhtj`bYpv2|jˉ*jWn;qr2$vFļ۷ĩ ,<"rN[pB<4G.*bqoc(ZwF!*FMJɅ;)ptEBw`0H+ԄXq唁y)6 w1#|ZA]:(#W8gTQi?i~+/Ш3T2aU4TzzOfeRE!B̩R,^ >4_ա):,Uh3ԙU}JVÕ ?Yp%!FJKf΅E7!HZ}RT ݐ OM@"5]LX"Z襪ha7`<)V:ILa+d5&!~8{ >$F7ֈZZ6@}Jge.&@?8}-Nր4 ܩ))LM9()e4ώbbUqK%&A. J:В*qN_^&<:B0efN&[``E"?FVn\概3>  -z.D\fD’ܲOEjI}91pխ!u'hIXzta;b#9`l"Ƥ\X)5qoj,m2͊mJu2Іޫ~FM7DM eJ,mH$`c۬ I?J-,orĻW8瑣֙Nϑ^C$ ﰞ^-δjN SEN*Oi52뼉xh6ئߏ%#xr&`V-H"Ӯ!;MVo%Je6w70؂$+9 M-3MWOky՝VnńEGF@֏XMPz\R.Nk 5x)J%'vG^BlI9R,XlPĹ :x=os, 8O.(|U"長$ ;曭%&sz*PsTv'I))CZoʾ?oR$5[#@GxϠ=L8JM!sh2P.en{u$澨HM -<0}%# b+4QL:4LK 7"''^ϼE-ih&:9/R_R'r5lS?B0*s{T(˲Kch]25*F \sT)V-hnX= -c +LπTko[*'SdJUvTdUH? A*":n*ddU6lT%{L}SeHJ5^a#Rp dW`Hr'Ĝ~ykA ѣLbLMRClbtM[$V{KP ZOR=C7Dg.wBI2BVP0P>9]ɤUOzn5p4Kha–A` Fq)}-mrVv|ij97zʘ푽Zq^ZJڮDlnʵY2yH'Ǜ +uݼ}:.+K[0N]z80Y2mX(Sx2bB.{1(0\4 2ֽxvM0Jpj A=!u܁Zm,'W -#lL=nl+19o|:<ۦg&BMXБ|^qo^C9Uv㘑:.z9ʊ'qV,'C/ #Zs306Bj#l5MxRiRa]~3}/qrDjIF^}3PI ⁸V0kd%Lgfa+q:GEڰChW,w7c:j̶"~]քHe6yI% -Kom9E;qYWKힳ|A?ftscQ -76V {&.bRľf G:VqۯS;VGv9 -ي]/4##ՀvwֺG;o6~([}ca81@E_e -bu9R'!]G@kx[c͓8΢!a.#Nlv(yPY?: n^D~c^J0U"U5 LؤG7.zE)ZmH -`ZhEHJ*80u}.~!{9B <[n2z i`=-ZPv}"b&)<a TecvxKG+4bf8^*@ O`#T|د^DeZ/"^ 8hyO4/`EV3軤d#7\D!%1Ԍ1$k{ >Zz8k@r-픶T״\zqHǡ `{v[^ƾZ xr\d6xy9͚٢ ^O`]X g^YܺFP:v-zzYE;Z!}=9[[sFOp10vC^jp&ゥy5%@3G -ETfJTHbt֍Evx62kƓ7lZwMk.l{6+ѡp􋝲IX&L-ϝhⲕn UR؛vU=-sc~Q(x $\^GE+n89ʣX=vkf[B5apȡ0b>AD|&®[V/jį9%LZ4IaQXj20S>cjwio9"%W JB@D?f?`%BMt$֌ m#{dBzKu-*t+B&g0'_ ddd$ɱV CWs NBuЮH(E 3?3we{#QѥD[5nܺo[(nHi|@%'a&T>4Eyy!BRž]֓u -#/ָ:JVh0_IBJLV=Fsw _z`p@shWr̘AK,<],L63[&qpX␏+XGIyWNBT -T!KWP[*J dq<菦jkrͿI5gD9#uj`RJ!cøbt"tugrF|5M%ő*) 0< #BY7kdIHeݢo 9Fr+T뗛}'3`ԣ5h8S`/PǸu.ޢ{ }\/B>z`"E_o%ꈸȸN -Bx%R%*z%y~¼ APmFYkDpTX}wۏ^ѻ<{Zj^k}~]Hr[L%7V:Sw`]` CZzXZ8]93eA@BiХrB|V;c(0hVߌm/v|pܢojoO_+,lpA΂o¡yGl~E KZ8a.Bo^,c& -K2zGBSCOL,ǣ™x'PlM5UMW+3> +CබdkKq@Arẘ.Nr_E<Ȅv ͵CeAS_MUxM eE@Y)+;jSe!$2:}~2n^nqޛ( n'MOp.Ձ@}<" -WHյ }27%mTpC%\FiL&B,IspC>o~ 'ǰic-휉] -{h"!`<%* oQCfϵ:Rk-& X)!YeJ6\ZAW{V" Qӏ =(Ggjg.J3DfDzm>C> Jp.KS)>(tOwC2L Yו^<߉WrЦg ch&g|3rJnFyw,|`f*dPն5?NTb}wi+FvE!>:'_2l{c?X!Q(sIo12T+&b4vwy`j3y ?h2[ a% n -bZd 5 )WVgݤCK":&|*ԩo@42W1M\ 'HΆ򘔝j6$?!rCw 'WLi)`mH=2_BpG+ӱ魶PC2O\1W痟oL:͡tѵ O%jPXWݻoHjmx^QwZyDHv#DR(FR^'$:o]/!YP4*|@ g/rsrءZ[8"$d{ -9v!~'w-Q*ѣo}P *[|t}|zl<~'JUxhQϙec+{|g-B:bK6&.qU:M%lmCciQf0BxmL4EA},m~- $/= ?*f/.R;zlB+`8>mSlZ<ȴSw=t<.dA܁3(|s$UkQy 51c랯RQ=o$w" G6{7ʺ`[]@fW`+LGeiPD W|D -G_]-+5zф%cGV]F@DT_i [nt϶6ycĄ}e$e ƙ@[XmyVmf ](66%w'5F9U*GPkKeDןDrfñRŭ'WHV/ >|`CƱV[x5>fKμUtA/\gP1_Ix(~ړ(?$P Z_k9"2eeb7rY 4uEN\dXrsSlX"w&M/%q9RȘ"'G -ܠ 9-єqCTP=xk~8ܦ(ڎlȖh@1=u n]5ҩR ]?gA5z^?a 8qϕJ/@W@5YE湊(lDadM!#6:E)o/҅SVby+Y/c7]xKӣRnEQa hL0}Z뇌B*w2d?q|cpGDc'6E_궂~2ٚvxed[@2En;G?iuðȁV.66q*-'#k$d !<;-{{;3lÓ!~mfV2y/\͈)P21𞡀oRMzGÍH0MȥX$ehd޲0ZB|1A9_hr˳Gշ+J=icРxs2ijV;r-2џ[VIv.MJ|vn o0$1d>?>=X6]tYm96RDA~kR> ܬ@u4.qpB(8e_IciA>EdΘ*KGv00QR+e>4a;q_S BG^n14:갦Ϯhd}r5񼝋e'5f=k|b0t.#+?'mO0#KqP(z=BEkHMnP)^Z2@ |[-P؍~o'pfCUˌpKm/[PR[bA8{,!E#q[f J —|N?%ܴZV$J=w|4N;43-Kh咽}eҌm$O߫VSBwz}@Ѱcce6?Xbg9luT;)PIB=ű¸} `:!GRot k!: Y N__:)PMJ$'nH6I09aV -yt!زfm?}Xb M5f(C/Be\W\>Z 9ZA/>i"Bv /lP1 - 4LBJ{c٭<8zaG9{(aV'Ps\0ck*'yV}q3[}ap<¦STslt oҠh`}\f> lqlƎ: T<ߴRpoU:>4Gq&rCXd2 $Ƽ`wj2҇_v?hT2tj&Θ{@KW\+tF i0Bi>Bz0O"5$(Ϧ5v -#:a2coYjE/u,%{Ku ٛP(LjOQ߶+bQ'袂#4-vjvKTu|- $=C;5@Cm+E77OzdwzGglI)Ј^Ϋ\BZ=̰M}h?uȝhnycÙ*Q-k+*!w¥J=q< Z Ens Ӥ7jiqBlT=bАKRXnѮꟅYJ8,1z9x7$)hQ$Dnn6Xjlڰ˧UmS.6-@+_'潼("@K+ t8:S|'rAU9ׂ[#V:Ԭ;Sȥp h^g+S{4aB5uE٭|i^Μ 9OT,_ 텵elR;)W'pMUULi֛{>^F*#"^Wk,(7[%9.}{أݱ:4=u&WA:QK\U'Zo($(W凁,jJB6A@D)qYT0ޙkjXZX=nR|R! um3PkXnUz -,Q ӧ=.֏Rio*aH+yH Ly0869SpdOfCC> GGVrYr[ YlMYˊ%CʬMumwG.5GNş< : -Mu-yC@6Fuy^&7j IΫSOӗfyY.h7#]/` ~Wpr&SID9IFvuBXI[W *'х/ZQH^hӔ=A̽.%RXK$X5T-{$Xf=ֿse#a gᱴ9z{ɓdn61)굍*GHY0e~x -uʼU r~_&3S@33Ͼ7ii#H-Ѻr1/P*7Ǘ< 0X6I2\Ǻ>>U{[^ᨻ}>(馂N )'ωӛh02h)oB>?IHѦXv͉CF9JJnMAZmC33<('MD\\LW:n J۞. 'f=P>n_`pqjQj멘nOBl/2uVu5jIFa8aǗiW} }ܵ@>eA3ي45 W"D{B{:\auVrqi d铅~ KgLeDJ %-b n X\EM]?nE5o%օS ۈ-G˙2c7l 7?zJf__,"V*sla46imV'XA'F G9ء={pv9 Z`Q-*JkQw^p =[Vw2Ҁ]󱞭͵59u9Pl<+W7W7h *SlSڢ!XU`=g7#}q@z|?E}pӉ Z*<0G7GÀ?}{JCz|[Ojx(Ħ>h gbxza-lWZ\f=wVB VQY(`(g[o5  &[({ GHWM!'vܖ^k\7S?KТۛwj8F-5FV,%?tgW ~V}xE֯v$,UC~)Rŕ~$Kҷd% q]!0yMұ)O4*d Li?/+,lWftt'ہ\ AVCOQO5euiT2CMa RҹS ݗSBr%BI(UEǟ>QlA?S@#|fTEUX-cY,? `kk=6g?L . \t>`d慮lDgPFaIzO -L0ah -r - D1E nC.Cv8h_N:(L=ÿpn^mT.ĕfyS\y|&{7HIO?ѡeq<͌v {Zk -У5Z$:e/S7U%@W?~SCQ=N"1+g!$J>d{'ANظo?K %7|,/Unο)l\Fb<˝VVgӒ56=¿UzX0b)ժ]9k'{hצQ+Y*75H\n+=.$hrGܷ(ܱaGhWAR=QON柚Y4TZ6~n}cs֛.5{TkcjF 161WN9:m"%z€cK7f&|Of$"KQ婯tʼn: -FB/\x P (d.8Ӂh5D9:4k"Qמє[o=u3D|UsuzM_jixڷcv(:&?L+Rq=j~i|8FӻXYWd9ppfszWQ.qQagI Дz%w yAHfMjpUOq(0ICm8§Q}Ls۳oL?e& -Ġ׵##ĬtC`4eNJd2a4I -BaEB[_KO5QQ#?^x>i1XG^aV^pmt΀&zA$z rgܧEy`%r}/qJ.fpE -呒Zn$>Z尶lؖ1 -9YN^*ucM.tJB'% ^44dz:)%NQdѱNZzP~f!~Q1u%X,"a kol¤ Htillz_w2^1ylgf3. -pQ 1Ƣ~l2yp~ȍBfy&T.kMR -Օׅ%D5Lܗ4-")DhQ$Em \UhD.d#+czc86lK)1֮) uCAIVwYSm0bw2Xϫ%/^[y/Fz.s3DPh+q|5_vC\KL`p% 5M#:Kw-cKM"TbTacn<*nm#X3Ehê3i3PQ%9RRz!0JRl N~??=j -s ߉DZaȒ:vV SxPpMLݍ.Ÿ3*n0xSkY.^hj17;EGW΂+;IdySAF !7䔩Ldžy -BtrI) -ضԩ2 ,m Wo=6'4)8Y)dՓz=^GI,t!J4#Ixɕo~w=7W$s_\_rl]5fR}I-pio1vƷȽU+_&%ܙd{/yz)h -eOuoxfjiܛ([3 5D쮅v=nPh`R*@X\+ˏ /xuԨW`,*+&Y@@ڛn0M3}[lzc:v[o_c)SZ8[u3yT+kڳ3?(lZ%8[;u -®V4>49^fɻs#_Wfm}"Ҭ{Á/awfWEvzv Bd&K-Yr1S2CË [*kW8mYnz!:EO:% -bـŮ^zsc^H #2q -h(yLX; *"v(pBs BZyno@hYciGlt)(yDbGӀ]s(?JtŖnZk/|gWf,i%/Μ) 1QL߹|vempC|2SH#*RWdmM+3J&/fvu'ڠeUcJud[0MM̲+ڴ# AКnf -}L#jmxRF8k'2B=uxZܣw3=i]rU!z>|+ %U7"%ۏu:abEy XbF1F+9"|y+Up,衼`BXҏ`k׼LAKG+IsK;8'ioM#u9$dz{{+OCIٌ4Y,o$C0"D;usxMwe^t& =wu'bAg^P﬊C j#+ȅ -㾞NV=Pe@_D ZsxMQ9ڠXYjY XGNБe478Qo_7V$,=fq# :m, g%ۙ⟞"rRxǵ5xp15>ٴK=XYIDp릻+#/U;e"zCi0oЪT;jo'LA20f$2Oz>GZȽ:8k7p']zГ9:qS\4YGQ3d[uMIq =k9EޅMXk*fb˃fUX[W0+vd"ݒ4w«IdowVQރ^qZų6/Ӌ+ -asl$e@BYNx^1trC4hR2S'?\d>/ -/yڷ/=緕#z11xjh l"`d %c5Iڛ;n]0B?(ߑ 3yLH _Xv[u@,gtoI ( muڬW} -H /<SRl=*k{<#&wG?t pj&>h??#yo}J - 5Cfr7Xm𠀁ԣ\'vPW|jym!rD503C-WRK×K3Mk-+lHs_x?#U}[zl1@mQA _w٘RBWʋ1n%n+RJV6gaLq?+L.gLyPqo*nz^g?_2PPļa?%h2ԿuӒΉ:%!9oCzG-ǽ ;zȧ:,ٞˣn[+9 7|pdsR^<.dD-{=ѩiM}\9K5~w ҃ -a5uH񣏞fU#rNCp;ML4|F04<1{yL/?YQRC^|$O`\ 'i br5ϳlzȤ -LȄҁޕ5ZІdri$z^H&u2*+$jf҇J+@9 mq/!u;Z}jH!ՠBfy"ݔK+[Ҫ&G+v9͈6a蹷ga;$-m Gy.Ɔ,uGOd3]R1H,Aƃɽ1j+@}9m92Bڄ9o*zuO|;5ʼV3qE#Vb2%X4\WQF[Yn(!L\dkoMoA_!FnXU*Uwg긄ƌ_6 Qp'K_[s]I$|dTu땡BeN!\ QӉ*[ -\b >'ĶxĀd|dS1J34rj}fȅ@HÝ;LxljPgסNwr{Y{ jI:l-1%a= =83jSުh#0+kwQ`5( yU]UjMJDΈ#2}>"2 -#3h׌Pς#װƂ\pV>Ub̬tT\v1:D5cx[AzzPy!4B1=^/)5 '1scP ,):dPY(u 3@+(ڵgK 9hh>`ԲDPLn,LCgT&Oa*jtsKVNi#iǍ彑v=8KKe( gZ|fW{9 [ɕ 9@Ou,NgN_'uYb 9?9#ȫd/xe }*p k;>J8cV:NAGICc~e)<)3SZHwW|FnhS2ܸ -fϷ!Qyx넣fXVc |$;B„GD;& 95lW ¥+p4h4rklG\_`bt*Ca A)K}|s;wwup&}^HV*C<"$=_  t[#6Ѐ]76XW*YЈuEOJIӝ|+> J$))n[DȒ8w(?p~O7и~>^ĉ;g#' aK6@(2 M&Q -JGJٲK/;LNSCuk7زiPӥy/e ?φf;piVb*1dAONOMBKF)U,ack+3 'q^]"*vUλ@M;]T+.!t+'52cJZu[5OV¼0m&=\2y~L!gɮ> =>kZ:D K ;h[Z{\2s &N(۴)&S-׈dɆƟd?K~QW!\M s*K - f Hh\De{yjRe*5duA'3qplqfXswfrH/|-)_n 7!N"۷1Da*yqmq=?b:b$}yḘś:U{#lD,}&w -0L,+52 R{zX(??DzWߤin9q\m\F(J gy -iI -a%Ox{i%!}AZ N8nU="CaLx2WId~Kfyq{OE暽bR~ߴ") #(P$%B,b/CPX6ֳc˜PBrĝw}m69R aAAp2{Cq'( o`~Kϫߔ}X$'WwR9ebęfТDaKX}ǡm\:|aqaa]]__7p{V!V[9w:YrKG"?o.xK>3WRe>u7q.a;kAKE]Hz&SєOvlM hPjeo mQw~Tvi-YEWdaC:%R\j՝ڥ!,Ѝ% Wɦ/ @s2H3$oQX6FKnu=rCPuu}=Cm/kXKUkd>iG i֒rWgr[XJA6@$`@^U,mfw$Ig45剥id|tԛs<IyA0hBsS\\^,qʈ'n^fΎ_[\Ö!v'pĕ[,Fq]@zepy(Q򟯟z/uGI`RIȭ,1ey -MgA̹\bš^Ԫ@>Q/{4pA6efФ}va)CZKc3%[*6̪N3ƊX z2H"3?N_7bnPyjN;z|[wu,m?זE_غ1t -/x̣:M 5 tj}TW5@,{'(6 % #4ߎG` bb,%GJ+W>%Z}# ?;W?A2.X0:H7p0 0)sYqaRۑ?FW$[$I+W9a 8 -;O MrjTL%^ A- d 3xNM[-4bDv_hv Ok Ao@,BJaW(2!(+ ffZ~0m\x#+tEi+֭,{! ~#H2C*u6kSi Az!|x".T&g :ȇd~U)1=96Xg(ora G}f 6{V٨?Oq_6OYMF*N8kC0)' wv;ؖHEmGtM1{ju{ ltW^~AÎ8YjM+u1Zn-noӂ-_H͝:'6{bDZШ3Rw cU K -cMtQ{8 uV 8sέ*H;6w6of'~':\ amP5ϜOlmK -_9vzrMPDigb(T+0],D<%w dH̠e! & -cT[_jU&nٌ2=H -7 trqVLq93 -:_'rd fTޭk~ ࢪRF0"Rl :DOyvrLd0 OkJc$ -<>׻zCN(/r9 -Ek* %r?#tSt*c]Ry@fCU.t5)b`/ft?YrC䢮֌e ז&|~%X/m`XOs^H#eX51ti~'Tsl*;Z:(A -DzBEcs\L9LY3kZ+n9zfrl.#D5C0Q?!,FC0u Rϼ5s1yAK{l%|TzAPUg%tm}'tQt'< Q"9㸳4GE[teV|Gњ^_wyAJ*i@d>::%`$ٔ,d -)Ėޛ=?v 7gVƠǚm':(ZίEgyv "cd㳀ͽ$W&`M3_,ԄpݣwPKkLdP˾۾\{+~ -zjLc?.{KpH#MGaH]yѦjE@ّd{xl+Il"^1#u >;QVC LbLZ; -1:/Տ'C`~<03瀱ݿrA#SEB{?YkѩgLn}fDҚ,ymCƧal]k_吚\tU jV & BnDEq!$/.>$Ń=c+FA@T?+V,S #ѓvNa3ɶzo Eb1ʍxgX=;l)dTe4YIۘSzJi>Cb١K'glT)ͣu➲0(͐&ZgmLmp_crdȧhE+ijC9s/W*KVpyܫ;HZ~$<Ҧ?8m֍SS>$uLq"w )UMSe Y>e*"6eITy{}eV qv2 7,%*/X{$>%?&m3N /+s{HPӘ&|c|&]e.5w?z[0gWOE6'ŏYO#UޑmB'TĬ 9g𪋓3~j#huw*=[PYM"ק#1 <-gm]8ʏ^vfڂQM5E/*S r o"rNBklN7.V$Tf͆uO:"$-Vɭ!6y=zH$&>˯l&!UI(U7 U [ו -=CϏSs0Ԧ[&?@Zi|,h0*c혒<- 9AA9L=7  TϮ?f~$Ya@X4>3ͥE'cIV._0#j&tҮ譈12Yj\k |F|;E]\dF1Hn8_ )Unqx1iH!C1NȲ@>/<5~eDzmI>plvdZ)3)ܔm:%G_1/VS"PCbL6Ӱ!JH k+'/X'V*ES'ƹğDF4O3b!-mZ0|1&~NP_m &6)7卥Hq]̮IfFTj ^vR~mnu_tu?iw$ qd -B'XgUTY(4dUxWVgSIs'/W(K0A}zBmTDt?K,acilD1aA4z;vfiHǠ\qvhxCqn4j^-c0}fMH>0/ą.Qnńq.{Ӵ>hIIg>tKhF2\o`WTp`n0Zb<@ ̞ZgV_E\NC FTH `]rQef0, Ƥþz޶dW}JA!Pe 23qTaHӱ8Z?a0tF'-j#; > pD6}$ŭ $GΡy[cLfX1b$ֻrZ<߇guiu CR|͟[0-Ԣi:Et7T ]5E3,RnYsT3 3z]xxB7GNL=:~OЄ@m: škK^?b/fu,i۸G4YW)Q#)iYqO1eXǒڰnܭ*u7?s`l|u_YJVW -#z$%Գ֬qHشvoc 6Ǘ!Om7UW&Y¥` @<_E>V1fZ1T<&RP-q!'U󐈉dUŎ#UO$ÂdSPѭN:w}s|Jt}0.?eLr3UhX(S&j.BzM.d)L|p1tӃ7*?w@cgx(E&0tKik45qp+$A%SGbuf7:.3f׀s1}w9;i1| ݰqrx _R &6wiV 0:I[ŇYocQԛ$K=IB+_pcb:Aff5pPzH9"\k|4"h7fK_ -htbhkϟkЈ_,ױ JN ڱ}i֓v>hdTОV-F~t!#PWcwDZ -d$2*F|7܊m -Ss̊\?\^EE[ܕn>mfnZƐ UuҿXw%R\k:1Uy}PL9o_!lP|ޓ[ӝ]q#<@` UȜ|'c-7#̙-lu>*J *k^>g-6~P1^Jyrl.gq"ЬTu>QOT$ sV q7cpq$%/RpeeE=7 ַ/Z氂{-w^iy<o;eݳ3Q+`֘n"Ж] dYe5}_{L‘Z %1Z|*3ԴiFr -Xό HXͽCaݽ\ ֩͢qE6 nѩvFh':xozOps(AO|1Ng'xsњ+ƕh= A2L:,bYȮ!55P!Dkcm2B7NYd0c.88c>m(r ; QEF*GbI}F#ƶjxB{k hEd얇e\҂Fو*f@<`!Vs$s/'RVQtpL -ͪVgHUe* ȴ9V>kapJ*>gRFJse+]nmO}!HB'mbk#E~St4O&a5k h|HΕ!)7:Xֱv]M{Д߹m$(U۰^ hDwl a/彘]2E%-s>.Li˥CIhK]8ٚok*Cbb *o"Yn[ylxlؼ[ K4+ic wA633Aϲ$T(hw7yӱWۼ4PbN7MBX -Ieݺ WQ[0eP~pXs~ݜ&Sj]G xJVö~IfִUm u/Dzx$U6 MW֦ y-1XzDK&)2"ɚ |-Ʃ6zBW2qd#zsLFXi)rbat *] -.)~j25KdPУ>pT@g}lzs@k;v4AT@Բ{JcNj%ncGp9N5'MR*Dhj@A^{$?4_!PN]FƟfZfD-¯fyEb˝/oy1idn wRmfdh |ޝC<x4]^MьF!Qh3e^];CﲉnZrE| EXEbbc.5I)4)@pWؔ1(O*v`U5J~wD"Ջ?_v}GcE90ȩt(5nԪ˯{$]ye.A9$8`fR6 =cⶎZ4f҄%P ΐɴx08}"pHޣ_3>~^@cSA7g&1W)Nr/xsxDZr'y:̯-`lg -d|ˆh|R'A7hSޭM (b=>JTJ8Ž،k*l F:O?@MﶣK|3i;!L`( F?V6ѶQRk)nJӲO֨g]2]-4Lz▘Ag?WNÃ}-|$_6;l/ggδߚ_,ˮG%/IVBWݏ`Ɔ$-BP=hpEx ZvbL@{KTzG3d-B!eȬ< L**frg5|)=ڶK-:Vѳ.4r'tO<[*oQZ.]z`֥|h{t&۹G"z{,oyMmtQ|[[cB`u0"B!CO@qe;=m>2?f&2ߟ1;m+T˂c;~~3z9ܤ^Ȩ^ -GA4Y\7C>$ -k -\:Bvۅ-LT>FЌZnۅ& uBLJ^WP#B$PcW^Jz%L,m@R]lz=/D]LkArI=_rt i~>MA<Ԗ1kY?c+OiG+{i yuGEoIJ %GXfbu_*ΦiIl2PKTJw_ۮO^oPrș+; L!&,hļBƀ 9ҌꮊBgLI[ٔ=F`ahESfwaiFS`_R^6 K:#7A@0D") 뷑 -Zm 74H#{/*RD4vl sϾ;"h˅A{SYr w0%!pPu^&Yo6׻=$V@N -N3hOqI%{,oQH$]81SAp#4]?^ɪk6 $ OfZ^`g@fonYv@,pc>m+pr/`U`i[JNՃ -x# d졖j3&2yf%oꏑ#t8Ks }X$QgOJnDHBb> Ql+|9_xz%R~#[y_DDv?U>;jz" 8 -'׿>?RA@_16ړa{PRd-1Y -I}![!PI2*Ͷ bk By z9x&+gI4{ueՉy -u ԴɓV̄r.fZނdQ -B - aAG0\N[j4@ą8P0^?&nFzk B c@NUKL܍p -s!e1G1of[D&ǖ32츸;Lfkh^;U j{ Hf.g#1i,򐕀g?( / {Ӫ᱃gAQ_Itn$}bJYt>vTrym?/S)@#DM۵-RE+!\"D&i=E{' oAu$2[.uѰ2~zK,[8ׄ%o:Zg4#=E VV) ^50Bt70C57a=M]_ + XYfOtΖ'St^Sțs55?P qoù$(MTZÔqV5 "ϱC9_ 4N~Ʒڛ¦0ً?HQ\ C6 k]uҮC4cNr)C5fZYM̡OReR {BaKMlf=&?GjǵWMm5roG }p<%D%A!K͔qpƘbXV]P&t*7oEi')7c^ l|Bƪ @byG#w!~< X/ljrt \!RL2=B\Mepӗ,:NEU1gڰSe0^FW 3YO$ُLf܃ǣ j*M@c$SֶMQ^]ՇKb*,>Mn5U,Cy*~UD/nS޳3[#CwR1Ҝܼ*[_mIB^z?vOpʅwVĮn -cAHXY>>XLSv,Pk+ڍW@DU9q?tW M~Ɂm@?.S%?GnŐ`E8d;ՖRjώgK&ρ@ Q}QGe+Ԣ VY0 n -R3%m<~U<0/o/tF6_v9]K⼝YY4Ûzvjl3XRu߅{a>_1rsv^ex3e8So(zxT$e}(]!e= a~wq~6x]+#1+m {&=D gyLqEA4_U|^Xެyh3%ը)dӒg4U3r=ӢЅV/Ee%u%y:twM"_}Idrngu]n[堒O`w2꧒9QH8n|nz bRs-Oq2Ҷ5- %]٧m\[)G˜Kdb^^+b `p=//f Z8`$=bG#= >;AeASscqjUaO[/y;=HFX΂t-!퀴2c:'RK*wR^?!l i%͉8ٍ:{Y' -[&rC,yUqScyo@v$ŸcU A J  -8u~^sNvVQ ǔ͌j胒߳*VV/~Xقc9B{x9:(;}g)K$|>18-0"߿RHqS+>*b@.&]_/6ݭuhqJO*f^*7bi!nLqz)qXix;0P dTK v~=U;f2S:65H^szkbinrRBT̮B^?8i}ʵe~Kvz"\m!u!.|xޅ.[Un3ܭO؁zyi]+z t Q6N+_*wXjw]=Sd1 ?d'j ɞzƂSN{; YGz "*6nt?~;( ZAgP0<#ẌMm v_Ju=Gv=v6G A*(lE9c8όQpf I~t{Yb q^=Z#99m<8$sAViGrΓ.gjo{Y=)@xWY4;!yxv[{xytӫF:ͨ޽ZLZHXxu0p؁ʘtl ∉X@(.B$wzl}ބ+*dBn3'{`XYE;n^Jn4m  ISE(ljdp}:W pk|KW⼺_TtQF -|栞?BT!vݚXDGqץjO6F잿6jd:W[f -:$޲@T -ڕ5?-02ϴ`Cq>Iɼ}~8˱47#]st96j5}uVy R E%J[F˪F$r=YdƉ|ճMk%VL=[Zj0fqg.)8"~Fr>ҭ}Lk`lLX#pu*.5>Eg-GP|{ӆkG=EO=`cR)o'uc6Y{h;U!!H8>92֮"&6)},=Sw>ĤT} 'gێҐ1Z -WW3uɝGhd@S_ԠnF,$=4$͆{`7KV ;sĐE+33TYS"i 9ۮ|"U{Cr"d' .s=TO'>VjR0d^3ԭ -cDT -[mQlQfWaH02rNi=!ڷbuͲѝ6k .t7CMǜ~3I9!DzjJ-UHX<}۝;K'!T&z!l$H& m})u `!w/޼,[#zg.W>ўc0M)aȉ$fQj.H'xDV./ÏlF4sfv<#fvx"8&?<魺MkO>TM{EZ4Wh QaW掆nvvH˼[f@M@ #9MdzWoX:psةkYj"i)Æf$_(^Dh'.Zpa;}96zE28N\"$x&fv ib -# -O1Qzdfv(r -2/~@#+<4_ѡ\Lj>/O$FXPx*Œ?BW}v`!du\AB~]q4 =ý$B<0Ş]1Ti>t;b6^\a'n*0~1l AFůB![y Y9bf4(9^a>O][zYS]qK 7 Td52anRѮh+)+ $qedlj{KhR>2U7{-wxC2H4!79WYP4Ҕ$  -s,eWe$TRagbe9}R>C2(AF+Xe]H@YA>c Q~==Y @4RMɈ8Nq -aNw}|t($8[۬Dahw\Q\-<%-SEG4ƒ~'- - j25D^Whhes8  ӽ@Ke0Vw iBݲwȧƾ*E(2U= 0[mk]Ђy{{6umAM&  MNU:3|L8vIg4VUwJu01n{D~E| s v_b,"iSD>C{?kHBkIK=Cti&岩"/aTC AP!z =\ReBy|nn4uFh6o+ {Jq,7̆.o>̷zK -@=#ܤa힫gSCX$5ybPbC;_iXM -Jh#֨[\^}x3:@a.n搙ƻK"SmZ="rN3+}mqւsƛu .7=p48A^1`|Z1KP \ffx "jz]eh=\/W3Db6hf0jV -oѿTFv`&BV==__wBQ3eަ~6eM'UzAp]J%>|eGtoY -D[`XLs -wxWo%,5 -i oOr97~R ie O`o" hQWY??cfCgOZ|{;7&EBt;^V)íSn; 1GZ7%5-=ƉN[huȗ @RY" -z.\rIbkna7bƢl ~}Hf{OR)qZ"s " 0&nɼ*H Ӻfs%ZĜ5=iwXGz zf$#0un Gcpqͧ0&j**-g[8Z0Ii`q kW(I&oF -Zj/8|A2 !myh Icd>B-6yy?9Gt^0ķyB]]13 -7Hp&cީdTx.+Ɉ$k)_PdwLgZ(&6 +U vaDl~I*.MQkg`,dd|-?hhTybS3==zCҁNx`kwďhVʢ('4ZÃŗ}isXb/ -b7BNB.틡t X,Ab:&LL<`%Rena -!tRq)K1tMF8n(X 1^ q2+4},+)S37zO? =HuK?X(0w|a(vBsz-qn `kCF|]M $ |0YdY=m3'}Ӟ_c#/dK֫##49Vz'Ivt̾ڵl};Ծ 1p)40'ü/pݓTW#ow~S -`2!9M؏$'e9Bu1|%Q3WVCFDZC% -}Bh?yÈ1dPֿ4Sk;DPAC@T0V=< 7vD.9 3D[54]h^d_^`D2@<qdK&:jRMs1=n~ j9Kp?TFp=ڃ փaJoB? eRw@WR2wʝ]yBgyg)T% ?s8 1BRk;i +/8 Tؑ3|#!06fxC9_ @/.:F|pL@q^_(>)L !ao8s֊;z\x>?\D` ዌIK0#z&UƲCia(M.)sSpOkϥ\Y϶a -RelvidW[!.5]Ə7@nKwwhiz55&'l_rr.nP^9ӳݫ1eտ閼aڞ[S[; !nH-lFUZ1o]c$=xb'E}*a,rTZW`A36;go -HCu7O r3D5[A t55798h#eYAB֍t,R J(U߷A1g/L%)QxR" - a6ʣX6hO7.Hb!vUN2`|bD6x fG(XZ5"JDφFp-j׆RJ}>_;ٶhuTͻmj*\o~qE{_&f4S/p =A ;kݝC>qTV\˿+.XrőOB#kKyR&[ KT9'xEZ&"IUuOZUO_*-(=h{6Fq qܝMqQ~-{03g~Fp=z84Z!>#4kxfh%`Bu'dav}8H:j3Mwgkm1/!}էvhP$#YJKb@aiقjhDDې)'M8gϳۨ!QI\TpU@#H!̈|s##Hw)N x$9a:iػ)fH!]TRRMW `7 qn?2&vf^@ -9r/W_9JL=spOe:7sk,:H "i!nf@W4Wd" >=Mr67U:O;y.5ɯhac[T[bSh%m" -wM|.Vj kk= WZa@o)ֱ MZZers6ҲX&j![D;ըJP@6X, Fbiv=^5Ü)$&bE61huI`uXԢs -7"!{V -w-i$Y8oÌ5h-~rC`#'gޭ/wDNlB.ho4$8*&yܨZ!=>" 2~Jn1f=oy='éId +R5~Q;H'MeHI#[QIGXL[ROjdI"G۸(ݏc^eL$ȶ`͚Gh60ыHQ˼m?6iM[Es,b>p:f|&q~zW&?eRR^ UǑG1rX]y\dF( ,0l-;Y8xgæLRZ`Lce~{@ }3JQ9}0ϑeHqa;eIiV;'Ź~`EVʩ pnK8;N YeTAiyN>@R',3A~v*eFx}3$SD(P̪So ywsY:c~:qر3M %JHUCU+3qnr- .ڥM0=l 5~i՛%]d0xJ\[5ugsCT *D]PT*DAeg,},g9X/)8zn}M "_UOVJ+U7Zj -Kgꉨ`H l !j3ߥHDgfO$P>q"n[:zL6ǚF$RB6'+/-t쨽&hl.?d贮<;/qdrh3h@XV _jQlj%*#>}їmT[1'^VksUa*CF .̱+2ȶK^k1MsaΗ̴`^>\ϻ?K/J4Khw؊Em\ũ>چJC^ h jɧsxƂap%BD;펄l0:CI% -x.SELkH0z1+Qo'u+H;N1 R`eLgXf^zQ*Oz}Wd.: ܔON&]Icj_.>|vځx ~h0Q[' YOhglh=HylTM+)@!wFh5xNB*1+FUAj[ xXyOa=Wk{LذvتT  ,49{qJ}ۛb_f+QlATF6խc V]} 4@da ɍP@8Ü LOhBd̷2!_KWgRN͞nq%x"(Sfۖ;{`ڑlOL:o**ZD$G4J!!HGA.Rj2H3/yYR}@th[)΁|M̀܄Zڊ' ȗ+xr[ߎPߍn^S3O\jXEQxgufiM- C|=??tnt'-Bt}_'۱5.',qF/ |NNuԹ"}/|(AI]&k_xPdI8c`ATwhjR*St*P)|ܗu Cu7D yf枵E :b\]O7A l/V5/5מLxz1<7^yOnQ5oȴEP%"7UeS>@uk(;/HVNzp wBWL=+S3yz7 -U%fי -usQnJ"É Μ}f5]P9.`1zM)1zMys$]WwN0Pn鏧o%PDr0ݒt[ȼY)yʱjFd(VV0]PrP(vAeԝx lfTKX҆^1 ,6BؚQe5aD3A3F4JE=;$=LyM]+ز@B%9d"L];~DaNH{-5ZhU$zM4/tXM.%n8@gjys"ۗoXL(r!UzF󔧾UF>>੥yڊ~$~  ӹ/rF- TX."KhhSK.Xr$9]m̳r0A'x`ɗ*pĘf iW9QWN0/m8?c'[GU4%d[KN:^,=4}zUhw_hҚgMr1(<\ )<.HۧJI! ʊC}L=/x<%鍒jSzv$>-fGjfv [(?;7j! / -{a;܎ߕ `[VfS8 vVsG]dGZ<ފ5Opڶc0k -a .~n8 -ѾEyR~:)i_9幓!BGAy$e'_L?ǒ @\َ!2l_]hw)O= hrD` 顔Fr G%ѳC2ҿO)3h&rJ=Q0K8X7eH-ffUr댌G0.%j&vjHsޡ_u6r3@(rADu.>nK96`&P@r0!*#l*TH,Lğ3g"]=؃hom 0sWziO<H78,U *0P޲Hu0Oh!clC4k_#OvY -ǂzz $? FO#yB . -oE6Xk"[r Mgi>!ܓe`DP0.p+Aom@~UF*g)3Y%vQK^Z@1eM$ VJړ9i5$b,cXT+tΞeSޙ'C#Ϥ8&O\X6~P^aYD7nP;'hCQ"p]]~zD6йgA)h>ڎ4EF1U)^U&8M2qzzbuB<+)W >-'qia|P3 j?*O""b.9j0i/ -Ȟ%dY(˖9}o+\/f|wiOLx !_kDVoIJ% ū:dǧ=_ -E^~cof#v@߻$6GA:2\0wLJAx?)We l%HPJ -zRvط氫:fw?P{Г# [oDF}˖i8DwƸ-ttA$r F//hQ$vBKS:3o>$4'jo۷c ۊﳌI/'6rHZsr$Z$C9QQƬAldI;>}CoY|+HpBV͑ǡN얨 j:6C>r':[-g Ko`lK2T}/.igH:+b\`6⣕難j̙9x?LN_qzUQ]1L}J^ܒ¿hK쳢 _0˿27 '4-ylEYM. v^8f| RZ:}s]7SsѢ+RbpK#8~uXDA#%jc' -X#AP -j ϱ)hj Uk~0SA,8a6`; YDtp QՇsE/‰PR<{>!d,W?7,_H n ^K nHȷ#j◒y|Sl/{rkbc # јW䒷R̐G`#d(z lk;E*O`Q!*րra::?2B#%g' $'DNzDxh@^,Qgq}Խ'hEM^7rxfXUmjL.QU_+!ʡF|FTa"A] -Sר/=څ\o7Es Ϸn'Q3:@6w&"I=xX:N&{q/k(f]Bs.⬟0_ceqcTwZ7/@%]P5Ŷrnc -#%_UR-|ݴdOsеP,i~C,@MZ Jw)bǤ1u#]Ғ|X2,k^/:w !Mnȯ5j`L7^6ǚ!u=)Xl YApQ/m6P2>\ug -|?b"Ghw K --C*J%l|@w:nog13P$xPnug>u!4A[$lԸ DY͊2u:\6m]nÇT=DICU5k}_A끴:᥁9gUĚ 0R;rbvNM v!W.IIzb-fXǺ;[ʙ0 Hq7Ρ9R;tja* L6,< k" '*R+`P㓍4!k_kښnNyB "t,؇*8Nfk˔G*n‚XaSrˢ4:YP?@7BMrN#h^fK<ř跷^%Xf[|.upY bh2eQќ -Rf_d__Xyqcnqsul>uI(o(1\/9fR΅w,_:Kfnmg(^smXd MxaB$ETo(gǴ}6B9H](n)ׇ -n{9{|K@ɼ"X$pe8!JFU9uޯ06zmJ'dAj5r>;Z,_Ӻ/{f$c7E՚^qD4ms6G-wӳ<[{l`&1fXMds|ǫQ_c%>Cx]QM,8Oo;mozxw%p{Vy%@pKR7`8Ϫ}Wһ , - N oFB_T\)91Ap7Y(0Mtښ qZ燪q;<do&S4ٺ\;X;F"'2d.TOYns"LcddHwUrxy(9ZS܆ 5*Ǜ/a CtFcO V:k)t/2h#Cgςd?YQYQ -eT,fNbw1q0#a ;>Y&jV!Bo58Ip'4bbj>:;zNX> 3Ҡ@q󹸍ɕ\?H +D'[,3\tf{b ~.Ȓf-t| w.dn?$?]D0,hs>SfK'fI^x}ћf5Gp4ˤUj܄M+<'α"˾s({\W\}/.3O_&\F ݒPU1 ܾr!0OS}AqTۧJTBbb6"+/_:9+ڛ4e0snζl7ƿu1O] 72?O;Oֲ_}dLnT۾|— će=w?oi﹄!i8FNP|o6p6S'BGD)02ce,G~9+|#>Z(j_H"U U|>a.t(-3}!$xyWy$X#s[RKqnwU V^~aՌrQAXɚM}5i ҏ.gvSZ&O XN46tNT8!( UxnxvE&Q'4Y_=dxNDΰs\̘_ICho{!!goh{.hbFUr;L -꧵z#ڐ ;H/Q\w~:fzV3"DI -@\*6yER0@ĵr;7c/ ZV}- DW!/*!LuҎDʞ}gj6w QPΙJ͞~4*3L0?a-X\3Yv̫d[߱bY& M'&Y-HM8,`Boa=aq _LRYM~Њ nn}ΧΜ^[-5Ta!q߂;z\%邭o}pHrj5z*vD8a7:@E}]^1f͐r%M؏83qKo4p=Tc{.Z^~T$W? -,=h0E 44j$QۅPen lz uY~fSk}Ej|>_:`l]/\CM? p%_IJc[C_MdyFHzX%ubO"tC"Za}%`bښ݁#Xs<) '#'쎉jv+jlNA+nP) "Qz^Bp-(A Vmx}*U3^p[VTog'8=nLW/`r.n";Hם^쁦PN ZZ-:0ژY>ұ±$+~-Tt.bf 8%> U~aQg\n,a7e<nGWpfL%SXI<1tص\l7oZL|ᘙ0܆Tn41P@ؒJ?>.Fp -']ŏ -YOYm~ 5I -F^"$SD(V<7FG8 eynZ #3#  >ˢ8qASy녨S9EL۩3QZvg1sU#ֺDE _|y iV"+ݬqH '&/G m ;+l@u/~tan.HϛRq)z=-/]C:hG Fͽ^07&VOh5=A DYśё,ʰvE$L^&hQ.sZ,p!oj/ɐj&|x* !pŪʁmhP1 3 -ĨbB-JOuawÁT?ߙ%Àh#nGzp\퉳9!\A'/J7^py Wۭ%aML,I55Qh3Xe-Jo{HD7Th;AG͕M)9<{!:g~>Sh eС9le:* -A^ꗅ+7#rge$6f)=ʓ]dff;6&C05<$M0f7&*?r}~~n-48cg[P(U1`챈}yo̓B}qm]C~m*v|[JTU{ -!IbM.1[*Jl"dJAoAbV &GJkPP9jU$7fܦ^I&IRw!! -[áӌA6gy?(?֛G[͟ARpNk .kOljVO0mSqESjt(}SE - Zu~SU 8I\N(2?l*ݏ/;U?@ U.7HՕƷiLY7Y?BY,B&;a]"VMdoءZ'[u_’E E8,?Kk)ЮC.񛇬u 1m=;J?ӨvҐu ӕn^G9ztDmux)̾!gFLKFĊssfQ۲a6pɜb2ksn FIua5ͨ?S<pS#sZ;fN]hë${{ĶS,!-?TWzgyޢȢ jV dNj9~ 4쉞1)T2kL\#KOa˽HMKξ aШw@y/Y{Xub'1ߋ' 㗯ˏp~X.5QUZ-K5r i9o9ؖm[P59R V9>u]y/F\L[(Z̗Dr~TsXpO^lPL Ue -kJґDd ,c8? h_Y1y-Xy TV#Ņ텴ɒucG?`pR_*}FkyS+o"f~EkN-~m,1Ä4Z!x*XHY% Zɯ(%MTl2r}E 6ˊh+ApM:8f}>,n5A(i)@Kɬ"gC{քBl cpyԲ`6.3jjVloGIoW38NQՔ{O*mGoQ:{OmӞYz`v tyxU8^y =ɋ+s1z+lHv[ldpxg\:;4oK(^(W~؅Ɯ2=vk"p^4ÖeHa!eQ2TrU\ZzVJ" %#'h3̲ ,rg֚K-Bz8ӷ`%hNU]7;_A - Fח3g9`uI[?OgqqJΡ}X'貳<v.t6!1)<~0ڐf}uн,wo:ɻOٳ!TڇE[tY߈'kSއ#J gveV/v5;L1Z&E0"CZwnH o/? |r -!):$58c xߟ;Y:RT׹3PpiŸGqr6gֶ XS!AAVXxvM1=MQ9򶢆:*ƛlj>,d-p6)>oCk]!e%;VySz:I xs˴(Ǒ$jdj!La Y6r7ر/F×4[v^I\U㤛,?tY4ې`av|% %B뛅%ќʝ,bf8ϜNie_ưAK"Z*/)6t`@5g^ƃt(0ՇQ 2H,XM*75); FCًJL_!%1f"n2C-(@V)WpvG%dbZ]oŖ*И5+!c߀PNڈyF]K938-Ȍ7v6h6$c;=̒uFutNnX}s -͸kK%f0_r H2+@+YQd{Už0э`)g>Fdшp5H}qC#<{b;G;@91TWN|>Yԧg57N}} '$ -;8&OKVuKǃ%&N7!Vk\RĭfSX}Xpx*2\~PeD}E(Wg8kxǦk65Eƪ:5)sE16Giqy$ߞ!oe \f "q_ne״cB-hHD.ٓ^EuT|$0%?Y1;Naҧe֢ -*|mAHbg#ZUie;ս0n[Wb kXQ -VFѹ_pxv/e Cu~x`pמ=#]qiܔ(y=rutbz$$ -?\.ZRk)'&5xLb -(yf M,/?0X^&~\PdC68*1.R% CH/Ir5UG >dO?UΛ̰]G}'Oct>LmhcF>|Ej{|vK 7\4 XUaֱSP*a WaV:d -R`Yuhgl`i1q;a/LRX \ҵ -_,Tɒ m631E?x?ϳELYl7-ŬUMqŇw~ke ":iFA4xrځ2% ?'Y-ِIq/eic7pDEmW`WǂM~S6Z?Qaw\ AGYz}w-)7)-?@1a8H=Ke -51!s3g\XTJ\(հ)&6rC_iV~_'% _:`50Vo\s+񄀸u IAR&XTCz=7^Izu ʹa7bCdsT)`̳`2\ x2:X1ys=1lRkbphj&(#s;1sM!sC#6zgKv_^5w-lN{XmG0}& XlpEzhe%]Cɾ,u|/ +!^EEM^WhӠ˾ϫ4>c? Th"?}bA_d -Whޕac=qsUY]s ƅ󁃬srI$[i yycj -m'2©*nXm`G'W%TVS |XH (˭^DkG>_[G8Blv Edłk>f8%zDhs{ҭNSޥR/w,a"Q 92։iאE? -.GN֫1V5)r h&F:`_n*V_βZ CIe!!?%\'>P-;0 ߣ,ݿ_Nf`Zrk |>ZߴL{ Zڟƒ[Tv h1KPM](: -Љd򐫣mPf.)a}%fX@*0ͬc X +uZdL$Xp2yV/%8+L:HpψX`56A_7ЎĔyBdhpܙOp\T6m)a0ärA \=VcҲhA޴`g>Ռ),6,\Nlߚ8&[n  Բ ~Cot -B01a F'%IoŊOnbγ͍ԉdefPGWIDBL^"XAFلWcմ.uZ7lՕVKcfAu@<z-G&܇\ 56ΤTX6i}| (b6($āɡtޑ PrOFeS,ψ*tE bDiR$-TƬpѲ49䛀"o{p ug TзdgY/~5Pm8aa_a>@Qo@K毴I$϶Q>D Y|yV@(JG ͹͍ |.˝X U'{rnm)ǟØ-nR&Z.GBq*JXfD-īH -C: -;"mO7ik|=3S\q X-*RلuxU}Glߪ/88d8?+q VĻmeॡ`gXq3c}` qDe&WFnıi{ -:F.o7:X_pOY)2 B0^ID;K #ȓshXyPnBSc2GO(/ s"#JO -}bcWיy#rgTrÆ`,_{,) -}?]3a9"4#3@zI05XRAou!Tֺ-L{kD;IJ4UWO! tl;«11[~IWqX : * ^f;v}:0yt -ΧQJAӨ Vz7F~ *]Me=+gBs۷ -ŀ +tgx\\ZoOz7M!.STʲs$"8f]zA}{$0J85&-w ߵu֠E4 C=Kl_0VoӬHprOs(zMŸ\J')\?laOFm-ݽuE 픎a9 iİZ+ ;_*1Kgwr}[xIe"1gu/4ޒ܌#D1:w -d$E;E{u btʪ8v:YC,^5Z ^N= pCL`*>,%`Z - -ƉPf\}DqLC@ƈ@}zAYk\SW̚>y$ԅP:in U 0f"#}; /CTBܺqCWW!N)C[dO0_PHC(Ȩrq* ,~۳N %VF=^tn"'}H^n-~Hû@:P'h/Us&t<r1#P5W -x(Ӏ'LnPPT̄g` Wdc%F sזD$CYfF+eU!vׂSmR֑+o0k!u0{HrAB*n21^N7 h?2쀐cw`lDN ^ 7lcGy̕1^ht噼Wi%eyZb5!@WE*Gq98t t9E{A:APھf{wՔhrgl -Y~d^'ŌyLʤr֞ʛ݊Kk%||UKJ,5o"\F>/9mhp]́0_.{X;3W$e1^ :8A>gǥ#|F՜3|Adem&Dv -K@O$`@bc- kFKHCU6~$w HKڇ_jI'Œ"t ,qHs7EZkh4z`W -8XK|!Jemi;x5}3TiƄ6 K3˻Q1^|1cbvZ\FpJdwSfаB㏣3\ڕ'nqiê\֝[n%Is:mK`#; ϓȘb -XdqL)m"p'k2cN5ęv,I!r[-^tXW|:ы Ax0u],hrnyc?LGХ*L«+ y>T"Dr?jGY\c #d.0\꣸H@N *fHi͚ddh\Y? -ޕ/K0Dh}N<̉4i/M.W'=}52tP4AL/?kHڃkCnǪ!/>H|K x='ȆӛoOzЍn"gk׾ :RQ'ޔ$ű, 쮣%=ZR#I\۷}'\7yu5'Wa! -z4}V -5)IfL+H, ؚoaEtk0ZB *^LpIe¼ZtRۢf,!R#_kpp︮.h0;مu^Irop f o9t0*Gݷ̀@"@oԢoTف|nh0[v:z,qF}H_vkya5FzOQxNEEJ%hhɏ78Au)q҆j}}Lnq;mZm'tQ"8-D^e v9k"Hqǿܠ]CҶ9eXJQ$d]_!E; -elܗӡz[;li˹Km+i\ŪOJ-k8sɁþCqGXHo_@@c֦~!SWm}&_MæP4b"I -w0uGTLьmqdĞH03) --5_)rcIh FdxG;pkd<2* Ðqd &p{5"Yk]R}8#+als|$K|:c?w*fN ѵJۿLLs˺-f/NJ,2|ci |'gK`gֶ漥tFz:g3_X/¡46,8[/o2IkLztGR%ϧ?xgұ ]:Ǥ]l vyLT:Yti-.TXJ݃Е/lp"RTj?F7 hR+ymM;:\k;(eӴVlG|o^sC46!Wu/6qS!64.hd{c]R]S9\ 10}]'M ,S> N\P' aʒE(3K" ҚGZkHvf*)@|NV$E؃%#(HL5s17p^f4W^|q@~ṱ|iD@(YƜ9/re(rG*}r5+ӌ 0i)ܯ<;s?oR7UI;?o~F ZG檌D>Vs䀋؂?)[@g׋e$phĿnm7*ʶ]% >^%U-+?}ȯ\=:AU|Jzu4߰bƛTMB-gZ8GhP_Yj¹c+gz&LPFe4_^xԋw Ò;ճ?Gĵrjف&̺ʌcE5I#!ҋ3*XlĈZhԬ^st@>׹>:*P::)jHcfj.+6Z9f/iP0VJN԰ -fpr -\ҽ *t̛sq.b~K|t =">ڠO>}fn7rg*j ej!kP"wčӼP3TmhO;r e./p\,sOOUC2MvT:s}pS ^N)Cau!SoV/ A; 9zU Dn۠6 o_q ĆClXqru0hm]qG#rX[).\ .&v1e] D#CWbdd7 h]rA)a3fOǮ5et7?3?0V6FtYP-fe4#MPmk;zM(}un;/Ķ Jw =f1'HpƞD&X9;yLf^UDd ]{H!jEWl_pR7鏰ڿwwnʾP®#Q v_BV$$[ՋR05JiDS@FV6;X5~2jcxLzHͿ ٵCj2-.Ƀxoct71w6Mr_ުuaڏT6{AYͿϊb)_,g%b"`(IC`?,-=A^6uˈ_Qh ,F1NSV=O($~$NLr /Jw@]cTZ.E">c"]-J#Eȫum9( -׽kٰTM{nTpUљ0iN-߳?Ɯ1n W dr ћq(xTx'p= Pc q )!֥dβ[|dG#56+ﵥvPT£?d/%:.ɶ{ 噽XƭXIͤ\8OGU:Ti-P-j+& E+PVZDX[[>ZȹK'V%m|n2߰'z)ye~_!TLB\>h_829QjJ)v&쨶A!jCU%q8)fsj'\~5i$D][ | ~]+M(ɸ$ԮkCIQ7P sKˈ[T)Soq !F8rU@̊($wg,,TRzvS"b<*f>v 'qް -թdv.?+=YtL sc=OpɝL !Bͼ1,wdY̓ S -A1 -I ]Í^z*!NY.#$hX&h+jRGS*u$uk `a4 ᚔKxweyci'@\XADgtIw[̨ٳr'oqq0Ս4Q@ N)if|IfJZNGe?au>V.MHQV,i(b˻W)DS3L}eUS23+Yᘕ?q_rnx<+aZ00f(m}ȬZ!> - =-C54 -'.^W}4!e7ݙ닫m9NJ,UM㩉e@k|ތDiq@iBra4ӢP4m)/93y쒄h>z~/`xK22̂à~i[,~@_O/-(*c?A%eú7#S:Q4xo⓻!90.懍q=TU*1LmILP%.&Isah4@q`гOR0 FwD2!LMl4GXJY@|#W/=&Lm[^]?yuCs3~VA af7"6r~8giwVJ8X6G{Vv -XWhgNcrJ9j+CܵRA(jʩLJkocwdQu86Ayabz5/8@煙ܹ6u ٓPFhZTEǵap|()vq?̹D܇93 -ݖv0x6f+YXiL+7bT+>sk،&[>_ lŽ2g}ORUb S[ke!̊@>Zw2>Q75ŀ -0W#GZ^v%ҐKEjq}2 "dlSM9  gC"sC+>k֧(p2ZHA2ORm(eYhT륇bqg~gulFwK8j~&πoRG+Ȋ8c2m*(j%A/KiJ?V5Cy`K1Af -΢m1Bhto_ꐟOiitGfVH[ %O#dljyg0!6ζHcA|㖾ƦAV$W)8A#[;CT,? d>ܩ` -j]J{ =g+cY`l+l 7qsl%>mN5e1_zP:{WNVc¦˱Nاh)$dx#`i @{dkiբ]0>-) ^6 p&eS}xAz"kP] Ipx*xe@S! 3ͱR;4e; #x*3QuK~y]Hnjvx= -w&S1(FR1ܹ\`o7FjGZ|Qq^3+U[L#Q "J@a|C;nl ("9+ޥaIDNG,cf)4օ"kSBDWaB80/?m.nSt>m$(:DO[qB.I]i!(bƳ-1<H;<ũjT'MÐWK M -_xcs /NBjyFuTlɷ}-r}̌/l,Z<>bE7 ֈ#51箤>rM?`U& j (nSޕa(k:%hy*vY- ]qsRh / ml[߅]Nxl5>,x94~)qj`Z,^~^Ǖf)[`<uHVG)pnwm$J `9G^{ӎ5 K' W5Ev)> XgQjiy|*VpVP<6Z/ϐBqQqBÒo4Cv -4di=jߡ.o瓐-\DX|z&<)ڮ bEZrTnNWHE5,H{%&=D +*- w@F+L\KFeT%pkv%ZP-lM{ yAz(Z$35}oYuaϵ t{/ F8tK茿6Hx_zk[<=6'%ܖs \D}k'9BBBP->=+7XnYf-%c4ߨMnVpk;6;ȿ-8N leiޝ .ޕ -CXڬQ<_|7`zڿT~yMIV ّmY @&Gu:* $Cg@`AC8y:sCZY9H1˧>Jbzyb0(.J!1M0wC{LFuHPvEYrCV9L6_xjݬYe>-xgiMY -uuxY%>o !sU]X/@v̶uf","&%*hSa@׫Inno0>+&KѨ&1F+9'hڌV-e)&"QvfܥIEԭ5\].'TMmPTA|4{lY/8Tu]\0eXjKб֓e|e;(/?qTPV@ -ӳ3l<5o/hA)v_b Z|_x(=nwUD8qREȍai9UZ~k<  CpLFKN;Y$ s smʣFRS~J{ZnUV 4UECYN')Ȗ;Ăv2༟Ys{!!ӴOV8ɢHu{P<ҜhIO(p9KiCw٪ t/+aI2_t֮"0 S:'f˓CL{}N*a{\Q`yPe&ۺk Kmh ? j9EHf`R=j璼n2́Opb^*yϚ-VdYKJp8GV}5f/>'/QuL9*C"m&8gNLɿFKf!"9 ة+X=)'Ɖi'sĐBF.m)Ʉ.?FP -9A9XhKr>qsMy[_Ӓv3ecJk_Bpޫu"2p/SQR$츪F-@LMwo -ܾYssg:]g8q6,qU#I[؋!Ot˘ JgYj?LOH~L&6[ \G/( -@9U,W:*TgtD0z' | fL/!Re|P ?Ж0k(Eܭs퇄Jmj*Kc:> KHh>h޴*iDtYB\[ U[Q[|-ˑ 3/5|„zkuo T:{AEYuV\Nuķr` \E<6DzxV,?Ax=3uI.Nl>@~o\,'# @VFof;>One]IiqΧe79 Snױ6%SLq?n- 0^B G~Dci">)e1fb 'n]_ZΛ'#^r3 BI!:"zmn60"/{~ -_`_z -Lc2n&- ;2XWPTgƟNE -[OHcOyP`L_0"7(&U32x/q9'ݹM>ڲwIު > ,W}+Gu\8%;4!4'az+l єWˬ6<Ε=% srĝOSgQC$qmi#>EqX¼ҩ`'$[rWu/z_(yŪOmD0/,TP~: O҇w=*VѪ:>8͡Lo*Yj-ɖ;h 1JT+gN, -2b|3gAqy5f f -gSڃeRW*$󰞱9 Y#v2q -l ZK1vФzC_XpXO?QE1x1DXOVs|| -(lkPrdwx~ R 7ڠP#q6B-QD[a2[ ( Z2l5c5Y}KσxXeǺ'|(F6 -uT'i ۨtf]]r]N[ 6 -I' RW|[ɠ0jtͅD20;Dhhɳ.EJH9|W=ɳg d)O7qYMmqaBK -C\9<R/7NB`w:7V2M{ x`|>\$C ->Ek46{S"طCv|cǫ 8,jMlS fnBN_b[*GnB[O7!xi-o0wړH#vK\yB\.j-["e]訶"doeY"ϔIJ雟>{Peu..?]JwRRe -CJ{Mȃqɐ@GpkӬrMvWb!;'-UOχtzdH=֚D\T[Ʈ"2==̰X>qbI䉵w$E$݃5UwS(A 7K-%JxFfBx?Ǜބ?Z"Xw<=wNffՓ1`vҳF93lW (ԅg {06`~b>VuKYms_>ʩ ęX˔grV4ŵ.Z_ǟP&"}qç^0+Dl+uZN(kLh>e7h~FqDq{ԛ߫uF $7>ofSͿJh*Yj -?\pvt67Q$SYCK",rj+~EC+4AF>lA{.-:<\5\(3AY-g|' [q~f#ȵc`d -^z*1j-G`չN|i QWOOIy-Hod+W4;#ԤuQQquPc=0t6.THqO\>O`x8~1蝙jJ.C;+S5Ǒ+ӎ͓ |;&r:ٸ&YNqo{2ShLZ2Gpû&gzvljw#;Y9g0?'}cֵ+V1^h{Y*iʣ9(]n`(!A.FM48l棺[s(:mF#U]y 5/':6v\DDd!ϝM1URFC(ڮ0PQ;`/tD͙~>eU1P,~wѢE;Q{zWy@O96[1Rn?C'{=|\|&QDb_f2iԳ'%~* ?'6\X1V~ܱ\g՜atTuRn\D7VohAnHs~4xa*Ï=gSy5ļ@|f_Eգ6f9!ȌX[=of#gœb,G6eI9Bi Sp&k+Ie4 22 V(jj?("Wܒ&՚ -, l]VΠTE}4$*&yt< -|!5)`[ H9L~ȇ~AjG> f Uc~VF#)̣19ӷ0uی|$.wQ .='~V-t̍3o4.`w# a2 qBLY3;G@T3듫Cg9yE&u% mNApWqb(D1.? ->F֖00<>%T*ie2?}+AbWb$=mh:AYnPtA69Y6P(/<ؒUôYfw[`0͒ӓ -oSdvmlזFxM.:( u 2U4Iv%n:b*ˌ˧ѼRzJT`€`hӳNGnˋV/^Tc0g"Bb Z3TT>G%kV%Lb+{,7NuAς@ᾖfK Ć; GҌrcrd (Qkb+ "X 403*J\%9f*="0brׯ<g8' ]K -ǂt$N٬{Ju;ͤ,_YOu3Hv&*uPh=4J4}8O6bqNU7!PwK`Am -+3@<^y@gެSU:pepk1YertĦ]07Cin1[_`SͮMwvSp0;*aK*KF1g!=58Z,XgV/cK :5PFvtH+U0KY+?7³Y->43}Ӗ[tAC!Qf6o,A:F0LDضc/O]+7vΫr9/;~b -C4!ۀ[ͩ+U!eʫdNL] ,vwb.0#*Nv*]*6u"m1{I(}{C[m/B^Y\I -V"¬0Rgك")0ӹeh`Pm4w9чʈ*Ȟ?{L%h2]T~/14ZqcC$<n'70r&L$i pܘ9Q j: _v;}4(-2d[ 1bvUu ([BIU3P)&eRMDQMOL ݀"3k1(鮦5OqAmrK\j.` \ZmHfF Ic솯yo|Yj*g"3x-\/թـ:HmHk'BEuf[eFDW"x; -r]k=SH.Cui?r^nօ ci=p(9=IU8(z6RQzIӫ1ԖVGQ?aJ_x L*m6{{)wd\B[-6}hXO,l6U#"d{ҟ(&8%(sywPD9歏P*k8qGc0<H[׳>=.HU,rF -.{r'^} 6*(5GĖU -'_3ȘZ&pu0`'3"5Na" 9P:D\N0RoPbZm4+fu6ℇK?~+lah%J:3||g\V|)tI .R R3;k:>bV3JUyR޾L4TV;k[xVlXi}h hWjL{$Pxnd ݗ5rAvRN&ϯ4ђw50QeWu ͏XړPAHc˚!0<Z `.EQEzEϳZq23oBx:AcrE%> 0*|w41ٓ'c\CG'e=p~ĔtQj~BcQ0BȶF0xMS]g7ɵ$o刽-՞$hCVFtG-hHs aSvIfs_6$S'et뱧Ņ µD _blbw<ƃZ :)VX&&; ƣ-J)k_:toyqÃQ9Jrc0Oc0U~ImE?Ώ9G;%=ۚ`JD'TgzaԜ鏃*ttfY͜6~TtEiv:RBh>Ʌ%^*U5Hչ=0~o!Vn?pLh0ZV5[2E]~; ^ ~]l KtnmaS=xGٵ+318Psb %ָh6DImXxXk<-ZLw'.Al {2w܌|`d橠{J"UΫOCaޙee:D \Y_n#w`P@B>'f|%*m܌2(ApbK+ˏ5{q6aOynR=uZbk!"RSOr:זcѲTBeH.͝6E~Dԃo![>y$=|NWND/?_u'fzю,gsppL㰛l+^;#- -]JmtLtoڽ O"cvHkØL27K>@\Muk֛Ewq(63V[Xh6BuYt~sb{ژsLRRUj''C{$m{=`[}`KR#(@>Bú_-Cpr)=[lcAgԄaw)~2:?N2s.6x&wur9f0"RoDl%+0Me8 S"E5˒Y0&zͨR(24?cTU_75æ܊ bT);,!)9^):dKT?3]Bi̳M\O$L,S O}˓F3KhK I杨N8l`MS<(1Uٍ}8TN -Xft`k*=qgfhs7-ݽ*7[\sE5 ڶx悷=yJ_N:}e&CX6#.(u`[9)Oz>tZ W?XqFsZHUyeв@'+ܔMqiւ]ۦE[l^ }A8KS!%Jz~-L7\՗N p#P/|qUB o 3Z9lLbu\l{e4E5Myc+}C?61oxu7ŵͮ$o@#;T/F>Yғ$hе|zػ<6 ->r}3U|zM?Y`߬eTIu -{1rlY2z\i*QBQo;8d+6ΗjXj_aٱ+_Gu|>7Rł(TQU 0egdN(˵f xQ .kzSRp%rѐf\]_p>0{`.5_с`U63h%,[BD\͝s8iKA&If -dSW -]^G(<*,sOi -*)7wV@'9INO f@h~Q-d1<B^|c q"4:a;mU";;8gVxʰKZUp2^4h#n,HKx0%̈́wduߤwzߌn{lZ7<$X@PC2O*zSmtdCgwnB\kc1Z5\?mI4P$#ɵ2G@H OzY% -͒>bB3eIaF,LOD<^h4\Y/r -CS7y:9-7V)o=,U6L-cqMRJ Xi(,$T erw?rGo^ᨛrctKNA-3ըx!7E& z.$gCR唺t_ ڙ@eR= fO^[zws*`fIJÉGR)ɏ![v@7$+ӷElʼdDɲQ?Ã]%뉖F%q%6}hԸZ2kBYmP&'5aԂąN˙L$L$ky-BnŖ}MB$JE6Ć!|wB9sꗨB NASdmxbL -Q m= yV ,o SY)= -=1l`GʖZe*`nE~3V=J7<(tTI)4q׭4=%T =2C90$+3lu:V r%ꏾ$~]^]t ZS`^)*z܋i?|-LX @c%f`TUY\~}`v91. -@lds!NɩuP Z\J2SV":9&s{y;`xcI*gzx{FLwgw_l?+RjE.aep%5+$7.|T%7,o1GgpGl5֝2/'H]iۉ׾ 1PD0Sa-{+-1G:Ϫ,-]`F<;Y\ :ZC͢Ld]"Eڪ~qw%$ęά6{OMchmaPEq]$靟\-!?cĕ֡hzYyo@'P|0|N[N3=YEo _$:!tʳW?sGW} -OڞԔ֪ eCEOo$МW6M=MY#5E<x? p44|o+LD:]ˠ6_i; -2 H\){h5{DߢjH;MJBP&xmF5Yk]פEٺ -ٹBMlQ{zC`ؠd0.fXKmsfӯJq:aߘ$i(z_U354r:%4VIUP&cxM -(YV>WˇmBnH l5Z*J>h6[TZ8^[M²,.R0ql}8|q:m[)>ɷ)}XA~8ROl\j ^dZY=bn[*V* p48Yܓp0Y>Q^C;ΦhwfnvIN@QmN}7'{&%@hVVdv3N!mi-gy|^SZ.gi$ y jrU~&4f¶`(yoFHk) rezpZ83(RT?-=QJR|8թ:{)hy .5>f'a)l! -TF^Oa7)3@X} -~?7cjhN2bZ^:{GJH%Oia;otZo -cR"##op]L""b.3-5(;:J)$ghĂ8Uhb4]5 -rHe/UfQ,4o+'i8BtT)k4U.zB|a?`lp=GE iG- R͗CfmIv\$S=u4ؘ 8'6! [faTaU:P=,y4 <.PLr4sr<qxApdznWQ@8`|c\@)0up,v惭qe#<(uu#Vko"7C_ - *~&e6e3͵E5$e˃G5͑E+mNX9Tόvfp̑D{?&X,CɃAGhO/M9o~ېd7g4+ -%FNy\ $cJ^\oU6zϧb vg $ -/hPAn+{;w\%!0b5澊ܔO /!erTr[/g?'z0/S(k([k\#qX 2^~OHx"Wh}3l)NDwA+1}l3 8*f -/6tѓ Tc6:6;M+M OyH) -.+2Z -X2nb(6keY0is%:+AAzNZ0Y3WlqDAd^x: ?3d]jַ[QjP F-VB+@Ӵ j28<,al>XgH)Ԑn=n:(|`KhgӡO˩M/v1u423&lzJnPn, 6*0$n͉I6骺[Cꃁ قTPI)qڶ?!Y,L1B DU(=Z/`v5&Ev5Tf8YΣV -_ y0Dqk.)^* u- H"ea'C\ &ROLs0mۆA;rzCN}i{`^4 .,[)yU1LD -FZgDt~=:O2]: G:7dW^Gj hTYҶ6r.~nT' ->Ws=* %BG~uzm}v<?JWZt,!OD CglI n=!YŖ5Ex>hY/~C_Lf`ۥj: -:8*.w.e}ퟭNͽdμZEY Ŋ0^Zwwy+L_o*6ONHsffi?z5DҷY sΞaӀ[#(yߖA|lR %C 'Z bbKπB5ė ɹǎqP}JC/LsdxM5{S`т_5F$a*SVkmsa[ntV1pz+CMBj67ehf.v(-!9q&dJ+!g&eUvY"|fo븣[9&6.8ɚ9[.3^kNzʏ8W &x st w=ecQ%8I@rɆ%-2-I#)MPCslj΅55Jp?7( jdYn1K10*؋0o9U_\wQ>Nq5p@ ֳo^?X~$0_%s':'!;ojYǡ#})>Ni^ngi‰)~Ҫމ0^fg!u>ڧY2 懲o[aj9 -0wʪJ~ 4"wݧɪ_oJ&Ҙdxowq#uI _,NKqa#&coWq2+Ίn-f9$LUEX .Nv ^~?DyvNS[t!dADىZ'({Oxb}Z$aPD=YբD tQԢ)%wA- mH ̈|Orb%ik$'6-38JAmݳs0 {3gH|XFo9jԙyXY9V[I&C3o݂|Ap,giDCOC1z0F>=}\4o'ՒB%=2{KZ"p9=mw /QX@&d h_~I۽hZnGȌ6-.j񈚔Do4@!oPN R@<뛲$0o-p+a<2w e8 ET$)|2/A iu7jv1zb0gN.N86#Lngc;/7f>z&Y`F Z -bgt6*$;7'ӷ,LG_0@V.9B;mZN!>c 獽ZiVUx'SrX4ޑʃJV@-l>35R,Į EOm&-I'5߶[Ճx[gj_yX﫨8KZ{Th4>.KfE*5 ]rWU{r~t3qMjG<ɂ67b/d7^E Yo pPk͵csy) /\Ƴ)ˢhAnN5w/X-\p]:`gdQ\mXn"GnjhiƆr l_@y;z: tqxLyiϠq5Y+ gP[ْ -%PuS<dᅮ'4>+YH*@ŊܨQŹD<_hVTy#[gE^yዲ70=H'ٕ2DK [659 N.jT?D~cTm>{; -:Ģ0(G+Vs7WB*7r^`ʬY1u,:qj0*㝌D4;n#*:{مɓ`t`))+wk˛%p y3H$ZS<3+Ho #f0W5R5c/tU[NM5.-HA}q&J`Bvr{Ao\)Ÿ | ըwE4{~VT6e_Wj{|ԷIniֻX(9|RYpO EYQ` p ;h%)|ɷbmGK A\{j.$mxRpIl,*ɹYR v`EwѭSȣhk9g?Y|hRT0)V6kkRZVwa40ER8{bͯď(˞5ۖUp =4`Љ9Mq,CWV7Qv -fg. ļ ܘ7QL\ƳF#J^y4ݩLqcdn/V"XpyC82.bkQpVe"3̀Ym;ٱz5-[m{"#'j8;g"B)k\2󵝮6EVj+Blj"8‚>H`?1>LdإA6z?S] N3f:K5d>>_sZ^OMԫ,2x!ǐH rw8WyTX/}9p}wb-_Nz-$(j,5BII܍kFG+!CI>@GII -jf6Z֗{8T$\YR0U!m$c源4,kZ;ʤB^STXYK՜eiMƉ!vTSP3?%T5Ytۍ:ʙ0; -#m1f*}(1T"- -,##:dNTF&BM;P GsX"GΉ+ˇ"O,z8-pIBw-%~.hl3م)IV, 0E+ ]V3|ݜgTךY#\BR pe3`) u=w,3y<ܩDBD>PQlg}>֙rg5˞vhS!<>A -5ƨzsPDv $' - Rfau(G jME*xЦaաb^vivre,DT0P <$v`ܲGsAQ^whGm:cd4krwJyTQ՘g^\o =3M(Xp˥#x8~n9vXK줘_wҠ >~:uIHŖM a#Pe`?Ij#K%S+j1S6̷O)TwôWET\)73c-hTgC> + dPLCR*#;Χ@*{ aPɹf^V@1$g%ovxE@`HI{h{Ը0h(]gUI\x߼E -n8RB!;IqMJ z-kz-X"0/w.2M]$7D ~'Yk\q3ӵ9?Ȉw`h?3MtkK̼\U!)ʈ:$`pIݪf/yp+r۝GBKw*`Gs!v@ϓyzVgJ' 9W G{&2 $݋ = uӄX٦W}- Bq8+O}0"ŕ'Bu.HTw`oT{Jlg7< -TdP({*: `W\ ϙP>pFq-/K"3Tm;aPh, 7R^Q)ƽQZ~47hu<؅8G`o 9W ].ai}CL|4;*T"nJU#؄ۤҮAVUAS`ŗ<*#2 %%^.7CBHQ=J*YB -3$m:BYh@Aԝ9/͛1;:{zGk?ȵ ās-xcyQꜱd~ٍ\_"?Jv ǻ%@Irh?8H-YO9K1]ݠ7zSuVQIO@vlߤ$ntW/qK#롂[uvA!-(`i2֓U0e{ E6"RS>10O": ` #gz֏W]IDğ,CBCKVdcM/HW2HuO{($ -T蕤kސ@$Nސ` i3e78Dzv/Xwy -mr[f8z -23K x ZThWQ}}ӓ EGmYP~=r;=s76wCؼ>|Eo+ryf\Ǥ*_h'o*FI읒Ն?*9v@"> Ǥi!ðw:pР!@B6j(?=jnF?g -w\y"V'M6ib1S=NG'K)t _[4B_;zlU)| T)djǰ="pV5:imxyXd7|P&۽ -ù2R4ڧ5Չ|z[Lb:7ʧ2̐NlE* --o:Y J;4qEest5,^}a-P!9O΄GMLa8Aǝ'ݬrΩ쫾fL]RiE%S,C8EkUȺT !7qe-q&(.ĈK9k6fA5#!DPZ,y&Jxy%G A%C2QQ#=ZZF3ŞPl?B+c]fED+[` -v&a5kFe\,j_m@Pگ9U4eSnPKy3#,Fo3j+3<{HRg`V$L׼(cmP6D>;1C ?^|ɝu`89ɢ&v_U?Sjs(à,3Gf`hƫ>t3Aćga6mͻ -kL‰$18wȪg0dӪq<)`qIwcS~`1T],k7ŷbJo4XСbY[5pSϕ ңꩻ=9M6ڟ?Rng{M(c#}O+Y{eE4/Γ{Mp,.bEܶv㻑:1#6*&ЙK 58ϻ"+ӤKye &ic]s rW_ k~Q ._5ub:l{a~XoyXwXp-D;6Yo'yʓg!pӜ5?:q*Dy@' -!L5T$VאzzdNqh܅&8?<[L& - M7Î%zQ --͏pD"3YPN8 1 b97ƹRIZ97{uH Y>WJjsxBگ?IgeȲp|^ĦeUR1J/s"M_iԂF,(YXɜ})Ydm%h#;aծ,:|ZZ!r{hfXJpX7'<#XOh?61$V WtVZeh̓( ~ZA֧HnCUCd:KjƉX/t=7~ -l~%`p\J'li T[Jұ1#B6E:\1DGӵYjzoB8u-/r{@(yvzv:s}FqjVl~ifnm=x5.$NKk!RX -e;ئ -p7-@o[δMm_珹}Ox yźLۛʃZ,?U$9sfDsTQ-C\Wl#`lRdw -Ԇj6NLmݗ'"TOZ=09FXPކ6L;p@ljpQ FEw;fakn_i nV8inFO>`8ZD{պ"fsP3j!?\E։+G) 2~K٦ldoy\6D1G <<A`HSR0G:/k%C{ebys6)z)Xٴd[0)D>zQ@氩4shsOyƠ9XMGbpf!ɋFčH|Y0M͕XIrAjѺ$W(g_e2!7lp̼{VƚqyP9|Mq778 #?Rj;dWtMU*Ϗ'EQKk2´uj^>_z"-n8Շ<+QmyR/NUbTokJDu&a. KA.g4,7BN&a?xITB꽼k7mε)١ jΦ/\d -S F":o,Oܹ gye=vΖr#j/EzƉ@[L(H+ EZ.+x-5uqD7CADnUD/702+ͻ8!Yp>ߐErkMf6HwFע3ũh/>0j hUj%/S]<oa}o&S0+ƞa}S+ |5 Tm$˦-wXt_qsk[UR؂(}\e'wo^xľw}"0Mn;胿7A8wGcf[k?-7sx t1MY2O>*gE |㪵x9.Ph$;3bc2uB3 E[:K?W{*ݣ#Zc^~9H0"Jbxl(0.õsXO\pHv)9\ylbt)k.v j{Nu -6fo2/Z钩uK.VD.?4%9w8K28=6l?eγH+D,ŎC;"͆*<>0,D*kd_c"FҫC#g&,*+݇(QsKZ栞e9SF"dcy1#*gƭK @{n1!FZcfDҭm^*_I=EK kS.]?veIy]/s4`ļQ͜[o$G^hger-J}kd'_=8F#$7>٧1 z2a*O&9Y~ -M-I@uTLaԖFͼzc||rC TuŖj`i -5cUĜ -Jsoz4}{CMUoZW5 >O "@O u=s(r]M4ѶuCK1Z+IcL) _V}F -s1%p^$BqIn <1zN ٢جu(5 E޵ GS"FHne䀀\ZO";TB +F?;U5U@JuP%F_?l*G>50>fIbE(L58Rdը[I8CYL*?x_9VgkXJ,sRjk],tPSԱF%Uˎɇ% · @?oa2r ǏyYgO %}!ͧ`UOA)kěWE=jWғt u=v,$؂wjR&NU tsǭ̈́ffe-.4*w!P7 -/FfM_ȍ!;Y>(b奋44]0W6m6/'XzۭVf7h1~' GlSg6:){EPBmbH 9^52i%1R=a -|=WR!)_>u]| .?-n`SwdQd)ș2=1O\_ޯIDW(1TNQJ;2wJթ)0T 4j@<BB" 晋E栨V5౔g ECB-BZ>qnZ8 6Qv#CQ'8AeG+^~w}+cV_@2Glz9hQ;+&Cis4Dhϫ+IS asS 2v,ij9Nb\e@@->(|E ^$8&Mpo/ՂOE ?aZKZp^_"O,(0h tpy4$`eY9~lߢ+ِWhmE`I(ʌU)?x58{{} tYt`U"C<]C}u$xc_P,{ōAL%JK3m[_jg-ʵp)6A bvB,|hwa 15Nq Y0iBT)s`Zm{97_7m6U_V=Fk";+g x,%&}UQ $^{(cT IpSnyD-߾5Z.VB#;\POٓM"zuR>6$PW){!5=4TgP,\oxz FDu/e1cY7(|-7-YN7$S]/Jp }>S5?u\I(L} -LVԹi@oTbC{BqN'*3hZ,x=H׉?_C4~υq5<ƤOֺxjZ2W|Op!8/C\Q1UF*(Iߘ<@Q&+/ +d1{Y}~ӕ^Ұ7rE {OT*%a~f  (1d|]>kFZp݇-3 +m(N!" }A?w =澤`혹!vSWަG" i%C5Bh)ayh"W#WU`ԕ6v.kN(Z _UAa}I]uBϘ._jJn٣ v*3UL$rtXnitGOdbI?ӻ2&mŁ -=p=!UOgSl ~ [7cpKLf]_adnA!v%p|3R|2lR Հabl2jN3 >nLTՆ2~yau$ * 5͚)] m,D|2%61_ySW *Eh[SZ(R,5…:R*(Xi܅K봑;i@Ai`g,lCrA ^s,xcB <*b/Ӕ))SkءZn6?k%{%oaE '(sL7 uÓC-b7 /ʳ]WeX`eO9OZB!oUuCJw=Az׶uQN)y;kKv|b瀘C0ٍ{tg#7(& YEDk߂ )Y[%U"G?6Q5^/w2+ceV qH),:z5,%{~`Pb7[1 -b$qڡZ3|lr!a|gЙar<^Ov\ =}jٟG:q,~·b ?cF5z(Av`f /_nlh;0o͎K2;]zq'ZKQOuR Y鬬lҏYF3б?pxEs}4YƔw($֡ݔ -6h8yj1x~!=`ia7q-Wp[sPJw/a J+x'nRq%NS`ZGq -=(Ƈ |)5(Aҡnj4>8 0BCuJ`/h*&Q2NFi``,2Uok6( j an0'ʃs[0xaq; s,☥|Z;X}k.fqjdA"#fqN:7.綗\p?' rl=\@gC޿kej-Vg U:I!jl`0,lS֧[\oiru`Ldo=QgUp0.z++ctRTe䨢- b<؟QZ L,&o $K..ZĨ-xTc4p"ZNض`x$}Dkmiss>Tn/]xXVqowTSÓmX.>@dYr~z$/znj.N#nohvѬaawYS+!P}-Gv8Q54C+EfEk= 8xz ?b B֑]^QrIm>݌lq /6Br -0$_wQM/u Գ q^Q#T﫩X Ř|F1/wnq1oq>`C/Lb8݆r0DW;Ii)\fwP9-t-VrqiADYk2&h^+Ǐj@"0ddWQ8^_/ Dv^4ho`:](Kx {%˴ "a]0ϐKÎ݋&YB&Ikmc|#tCMگS*tYz@QY+mSkKݔW[ok>7Ӓ'ȦAߖ.wvA8CMg7 -y|" ͱEN|vqk|׮V>κanwӭko Shz'QF {̥C:ip2Evlp/fMk%%Ha:Dc/ذ &YвZ5H* ' vq1=kÔ7M|%YR75rƐ Nqdfq`ʖNA{ۖǛqYVItI 9.̈c1#U&Zs'<c*y,&KdҜF|$ʚu[ZnIe kBGIpNi3pMxF7Dާ[<1CɹLu"Dgs)qLAnӋJOKYB1 yR)HGHxX5ܗ;=ʸK.$i5 MCV5="MgD:-x [4'+`޸+umn ;"*^ 0{T[2̼Gh^X -My5F]ǙARS;8l=O^)QAXJ~qK\,Q%)OW*fiQ(N!cs8m[U$UգR7Gcz]}v)Kt\?1 WG4kRF<|~Sb锪D犊 d 2i\Ns>@8RRK`1RVY?*Cn7?3:X'L.v7f aK?/G˨_)qU0a#nz_Ä'ʉs7i2<ݬrH\ػg#u*a҈V.Kg0(?p+a۰E\c0$!<ќ>i,:bތU?}{7!ՍZ&ؒg0nq:]8RԵ?n:8f{ǵn .>MiZvu.\;6򴡭` -f}6]'$0/DSufbr4VW0]1sj{p0#OQL,Nk[t[d+p ^ x_9Cm|{Q@gKyfEkB MFG ϲgꖵ6aDs xd`5.29?d+grL8& 68xs|{M雥n&`^:iQj$:!#-DH,׎.#>EGQ*vZ\1bԦ4lv`TdtpԤYG,ϳ^:ˮ=Fsf*FUJ@?d<~,yb|%S OʸK_1#\Ew%tvk -c6GgkYݓnTKb)J#X 2s44 $5nB&h8WW,],Lx6ip39&Nc3#4_Y{}3,vQgI|u{XK4(,yj-*lbwJVd}_dPS&>\ti3e2 -nsPnϞkh\FT[8ծ4M4YZ*L@Oa1P.\q%b$tx>+$ypsX -ykerQ,iB`*wށyY!čBj7z;na s%dHjSBQO%CgC BFI2 L[8 -Vrʔj}f˥5T(N0 -gw osZV% -}BPr:MzR佌*Cdq\tM5J#:B[DMKwΣ|S>.*Sk>#JHa -˰ -!>=A:E/%fql;cյ% |I$s1J}fGKl kdw8Ϡ.fmnk?Hٜ28G -M\_ -{&3*#s̤6Ӭa;<;G{d -\ rXP*rIzkM'N9Lv^fp‘UJljF{B7/>~f,#b0<%![=(rzr*^GP@#qM}`lHi$wDi/ͦgLA*u"$V,ϐ7ͤId笱VfVO1)֢'i::^E BzŻe\ -[:.z1WVjnt -z`J?QDJqʒrxp:$Þ>7GM8f˃d֋-I E#C&)`6dwz#;tr b=Vpu46dCP&b B -*-ؑ:\H`yX7D+w")\%@sϡtZYYĀ\ -M2eXK`2OQCLηVT6eբEh[ -c\pY-G>ow%a%bwNr*K⨁;p~ ݯ6ct1A,l'=u }߼shB ͗Mֆk[:<4 -a}P94u,K,8=#P|:J[2js9V,^惙&L^yA‘. !QnG0.𯸻I7, 30;K~"43ӸDuf$ kY_Z4"f}cOdAeWm6Ɖ-pO =gf} + nL2h8}qHlDoi_5?gӌ{*p.Nϲr+VJ& |v~eJ55''&ATgј'NcUfA&o~ۗ/JYs&d;ߪ#'>sV{鹳.R̍P!Dq$n*Pi80ӡAY7]'ԑDؤr$1 7Bi߹~ QR҆جI;? Yaشp9w -ca/Hվ~)fec5`\y+84G?6-::;g|U)O^ y@)щt㈇uj>d7 SJ1WӉD3 y>pGOMxhvV{"׭52o"\y.]x,if5;??? _%)y -}K)7+.Q)de#Uq)A%Y-{ 8|[ jCp1uwq*5Ό=wJ4Oz vr)(齘^]kFad;Gljۗ떃mXrP!rsAp؆R,nYu9cSc.AYR6!n ?x:%Lw{u@.ʜ*▗F(%"qz3x TR@^ZȻBC-Eif7n,"p z}>NC8wnF^zkF,c樌ffg,MBXӮQt ΀kЧy@1s=X,4}j'4CV P i!%b=k˲ AwB%x}ȧIXճ0iᙺ9Ы˕Y FȊ[Z巍<9[m_;k}t]fs _f`H p RDO#"@)* UuDl3cEIQSq1b[%ZaqE2^ -3+ ^ +40~߉Qzz~nSDcQh灳n^FVDt $-hw 3&*9lWޟX U/܃oDxq1`C4 xҿ?nώiTb\5,/#An.;:W33,#B{3Vjpr*1 1!ЈMX.JRjZE{묩X-?il#`Ys ɗޔ-Y!{Q]w' `y7ȾHV$ۤAh6(qyo>Ǧq 8"m@=r63-Gt\òO7w_5M6~!&h|inEųM!ߩ@1qb>mB(;}p*Q2?v{BmLn`Bi度pb,L{z.'$ӳ2dQ$j~H%W-.VWΘ٦93:,)wRq%@K6ZBp[o$YGkf%֭++WDG$i#INT`y^3>+HE+GߛT$jj#!!]:3)EX^ aa6}hK-zSuҧrO9zQTu>\?[K FtOH5çvr<$a%cTٹgRSLJӜ+;ymo^_.`'#zXǞUJ DX͕IvRs C\?YaDELi -Z|J|Yc*cѵڐz^szaGuvDXcSR[␍NGi BWpyCL. |,٫(&f t5TKSLyR+j&D^$C:N-R]SAK о]T״1eo0 &v[octx{DҚ9bt^aS$wkzUA&R>bR<=/j2[ÁPo77;4cD}͊s/,f2+ -2:LYp2Qw\~N+EYZ z*8):)(baSp;N+ ߵ{$WgN> -stream -uNS|x??vAx12PLp/H.-0ڀ&1Z [%w,/ ϣP%÷5M!t~+5X>_W\gWQyHY!284RP#☸ u?+,xt_r#ASĂilnI{+ J5xv r} K +r23mD,KmIZ\* ؇mT}2Y.me]"VP/'?܊"areEGRvh?$IŗMN'cE@mbyBG{$(Z[LvMY9'X"㛷Wj r"P20mT{F>Pâ`R"QTHis%\ft -0u֫Tx6"uJh0zhUL,hIQ"H;būn`7=C vctMa)AEhHTv -xJ\ A-*`u~} B@[ݒw2'w˃(| vN'rlN1Kj:"J >+mîOKyLI8U\l4d/nLTSW^3Yx*[] 6\2nC"9_֗ؔ,5l𧘧&? 9 TPm|my?y}dſG ]y>{뀤R]SP2$'5k -Revoы#E= 8?߁T}ɨ0҆G\p1$%Cc1 ̄qu)DM׹eW4AA$FIS{@"̍e ZCXn*jaYQqĢRkϏ]e#bRQ +0E{mP}\GL-[3_鯱w;3+&+axOă'mՄͅ!wB+&LGǴ 5Q4=9r̤{g統XNh>rM߫GI ¨cI - ~YA|)^V9`u^(`@ -OvQ; -^"GNgID -#~=Ms&O0J U\jFd3{§WDamF&e8i:OKl.)Z]4̊1t*%ɶѹ2 C8vYwa,"ix$W\.,]*vodB[2A#L\J1fi'm/w" GPk'o9Rx`#_EݵPNj+-ʘ51`#/^rJn;w>u+{r ϕ(o\gw rN 8F)JWU9Z1rw֯=FN|elvgDapGݺ3hd?|`%DG+Do>4,M̬7o}ve_܍à fH?(3ˁjv?u52 m8JEB`'8PO`k&n9:~|0x? ^css1il\ӭ8 )+jD;_Exendstream -endobj -3612 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -[=avKoßzב\H W%f۾:o(r0'9:Ǖ[D -((MhtzoЙ$2xS) gw+g9d1Jg#R+f9 n Fl .o{BQW c3훓kOȝcz>~mc'm41)NSAyAB'y໚Vɺ:cBVُuOLHK>܉WxjЕ'7-$Wx(w$!DL6#+Cnh漕pqg8p-5Zzx5}wѕv}ʓ4`uq;B]^fMy,Ѣuvu=/-R6` N+v!{ZErx5kv):aIs AV\F,-xfLDf쓙+LKx. AԨe`ȸI`,8Qn pㄨZ> ĨmrglƓ+vYfSDIB(/22 [?{P>"y?ASFݕ11[pB-g*D^6RI//󄜍J& VXh!JL<ՈӠ*>AѨicL7(CLyd?vW]0e=o;ʄ`@4Ҩ^>w]%Iϧ-KHfZZ^_}?ͽ%F$C$c "d[ NOMo"@W5jKNPe(Fjqqi]@ozEMi2){Z< ҉-V@;8pY_oeOX ^s(NVpA?""%\8M(:/֦D۵TAW]\6V\%8q-GO *J$k-ܲ3,,-ª4 -oMbY'w6Xi?lB5Aa @AL}I7Ik,z$rM!5 -m/'tU9Y,kVsK\c88{bD۪!ZWԯ (mN`eva^ mjWTFhNݳ8)/Kz΀bԨBLsNK TaGĨ:SjZ3,/Hi`-Z$cQ8ξ=n9Zk\%F"/z:}%IJEޖL5"U#Kpd=P0|i(ySC#xYFM `#}8_ڛ/>I82@ͨzLCԅ,c `kMt> -stream -TaXIV.@1tkƤuC<[4Ө+.7 /i(9Q-1¾"GN/3*Թzo>Hj{[Fb}c2|V5,u nF@p_ўxRYD)DO-t"D;H)K|wZcYONRs~:,jPhǷ,ĸ/40HreLT$6%f.)iϵ -lէX,r]2p*J)l|/H!fJ]/H=<|6Cb?'XI )MlX1*G>QIѰ􀓯)%c_v ٲWB]s͖L8 3.lFdM=}(C(E(ŞT"wjzg'_l\te8:~wf фwg*+rQ)3m:q3"rTb=JY'mW*B9HW15T< -\۾#H/(r0햑fU! 3]ܸ3{ F`ȿ׀y#?go FI˫兦 n@q3Q2RݹxkJgʁ18 ZWۍV#6y7@5Ky _MjԌ(+p(cP> 9'LaQ8d™Njz;ʬtL=;oSh;h{,BG\oO>ƇjPva.F=pDf*7c DRD[|U=)c=dE(ƝQIXo~ lCǹв%2d˲((' Pa?cp)4U@`,}'Tp;fļ-+MNg)@9늛Ҩz3WKRx%l/WtU[;8@\]4/xk[{32b_n(x?0LD$WFh(ݩc{ $5 Z'q5 #j\}ڭDFAM#vsU si&x+ȪYeN>;Go+%Hi3 W  I D|f:eroOK޷( b7}x epowǒb? ?oư\-$*U˝wI|`~ mBHrbd{jEOwB:0fLڦ.YnTףQĂ}FR5+h`U}ńY2gR<Bz5T%F3nuo oQT딋AFendstream -endobj -3614 0 obj -<< /Filter /FlateDecode /Length 2288 >> -stream -CuˍJ@BuV("$l#&;Q[POșbTt4: -h? c @mh04 Zy7`YPtqw(I -kfh/iB=йIXt_BvmϘ 6!oɳߐj;Sc (Ꮁ>yFF-\"Ce5c@WK.=eV}Ny`HOŢ_ -x@ 5ϲ( {ahQ0^i7ٳ) /:ԉSf ۫Տs2qllárR#=uyRjֽ6_lOLTӣdI VWfxTu͚I~ޗ# }Q Qm?(۳i7r{7\{ھ)Cңw#^X_,%3\A[A5<]C_2s29Of>L*y\„A"B5G]8o#O2-aE6U]΍x;Gԁy\w"N\Ӝ/@%)`g4KQ(Es\| sUI$":J|hy?. YfQdjlɈ)'P]|&+7۰4ZgB%=-2E!|2F7Aen<*ۿbnNejpav*xf Qy"~a/3uԕwO)[.mcBB*۸C5NΈ(;4CIaeUH:X@[צ:Kll\.aKf0ڒF *=>P\[q29^{ksL+dmVuFk̠J9zZ+6~| II U:*~/ʪ\y34o-h\C>i=(t3Ox>P ̚]](n=Hc5cS|L41_LM"꟰>'mҸ-*Ԥvsv;W^)y_]\4*) c^ֶQ6kAHpK4f4#ƒ<?Da-כAT8y?N$5 1]W -P ]&J0=P-8pB%m|Onm ueyk:A&Xfp*"J@!wi teos {'ZN rbE'[ssujbspVs4ðd̒m6yFBL{X/yN}F=Z^ip|+ -I9mdFcPnl'߲S{,Gqɝ]I/QclD@a TQ9#}iTau87Tl&eRe1+G^P[e:jd5)Q Nx ,Syh3wfL( w2nv:AFj8"C5ͼNooug3>Iv)4/hIL>l? f~ Ck{2V:ˋ bY]mVx~ŕ0 -vNI| QƼL+e -u;"5 u:m !˸ y'5۰"x)jSktu&m(}dMPUҢ4V>xCm8QEhbdO; |)h ]~Zo d2g8Kp1o$ݦќ/endstream -endobj -3615 0 obj -<< /Filter /FlateDecode /Length 2544 >> -stream -WUNɵ^"*Qda%tjYDBرׁ齷VԹT -? >+N-G%F{y5dvo${#Ťo!/>>F\j; Ҕ` r1x~^GJ@}ȹ5*fQ$z3$q|'lG| ISx1Nc35}Kpm-͂İ}`T˅(tt8 xf%kx^kE&ft)m8Qc2YBr2G ~+,@B/o&DH1lƛ L~2j6=l|':-ϓE^\Y$Zo:ZXWx;ކPh6%5KK«tT- z@6M0a Ki~_s)n:j fLXaX9>`rm׾XL5".֕&DD<9GwgKt6V4ZVS]"S5j$1j\8z$1STQ_\V{|:ݫ&'5 Ġ}I#38ңDa/2T \dMqAtJ8]y9y NNd{giE/AGX#+wӹn6XU=1tf ;A23ቋ}4sK&7Q arC5u. -ԴFX.} դykͤE~:y8YՓǚO4x ^"pV3w n#wÎ3')Ֆ9U9/,AkP t]Zy }Z%:vu:0ʠz#σqI YG5{\O"FXB_*[M6 R?n>0L -u菪w|"Xrpn - =YVNI۠ a F()Yǽc[,u>7z=jzx\os$#We7|)EIhx s:;ǺNܿj+oG5=/&dIyiTL{K:W5DF%wڟ%'ACjf'gRuX<( 3wVDРr5BV_\ !Vע5 ҮS^zg10d:q!|: yl~+Ki{0p ;ڣQ|lqma(S[+iIX\kՋ6w7RK0*T:jc=ן=}@+qwɃ ftAOXq/Qח(osvNWo>N]ٶ2_/t/, ZG~o(dF-eGkӱ:&@pMu! $uYs](v72LĠVSW 'vvO!q725OPm:m-[wjX^_o GHN""!!j 1Ƨԣ Wͬȟ+"x԰G~SrZEw:nN BLikS}m`)EU|PR_iVAmvڳǵ/ЎZr:\2]cJYuE4}ClEJKcmv̷JZ(ǤlU/42O)Y@lD81LAI+UOWO +DF 6N?OO"qŖh5^~M[iV}wƂJeFầN>%ĝqgGɣ"Vu P\I7Fwd<`{U(z׹<F T6"~ֻ<7ynL%hm?5#,H=JPtzɍkK\%8 o1Xiv?R9$@P%sTyv#g}z նw?j_Rsw:V>`|9qjkEvEݛYǜZendstream -endobj -3616 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -" 4/לw$?ynAs+˘)eP(:\͓ 4nPy]x((ȧj4襙6.BJrVU`60:@Q2>W5WyPoxbŒ /=iO i$9 0LZn?פ&swD ~VxHgWsSpwNIbص.Pҋ)懯=/&}6sP NլFQJ%ygno{H-y-ҺUM}*6ScKC⤤X|2(B1V֝  "G-+;S\ ϻؓA UVYYGɯ`lE)zYJ֙L̎]m=Q/hBeš=-r&7|` vyGDAeZL6r. yi֖獧|J: -#LH/R0 eucr]%!=D2WL51/T`[NPݳl l'L2pd<r1W%A.7 m'&(X%U=}F&O)/M[ ‡85ka|݂ࢀسUI䤢hɂ$dM>~yD`Ӓa8,(+[6,[]N[WO"tJ_i-)CZK+.KT/VnFr79.P~ ym?ݗ~ -h s &>Z( -\- 5gY,&-u|~|P f"^uwwX,-p)#3m/)[ &hs]mZ Mt̎ ~泙 a8PuX]W.u"ٕ/qIHI*45ެ;T6l@w/}?:>>jo/%YG r蕤~%G\ D:]Q?D("H -#JkEtdXT* S$_K5^L}Iꚜfz E>򋰤[0~iwhvRlaLՙ#CX2GXX22F'9$+8ah\%AȐ<FríAz)tacCŪO(g畝jKNJn~@Β䞇UxxgJHj[ Yt3~%f[.FR`C {25~{5]R)nvkw>ұ¼qM[d7Q.MGpwgÔ 7=+y`uRh9b9Lk53,rW"z ,wA!Xšc|MDT3Uځ U'3\iڒSf̏Uq.e1f]>(e3Vn2yT+fٴ1mSu@sR:T]b 4킄!x_<@Gݙlp=7 9}4g xQkABV&k*0W'P?/6|ӠWO_Eendstream -endobj -3617 0 obj -<< /Filter /FlateDecode /Length 2240 >> -stream -WBrdYnXQU<{3Ro&͋ĕZ<ZBs u!FZc[os7l1(NYc! B9plܕW2w!Yt*# ,z;+LSi^2ojg[҅ljmS;/_)BK9~l:CaBlT(m&EyfUj+KneB^ !HJ-M|eD4$:DuFeԿye8crCL]J`щڦI^p i!+m˗*nN-x ˒TxqH3N.wʮv9 -ObJ\o )ٺ-R,ZxliX4E'm 0t-:;VKHT%)׌gك~I&Z_9:tZK@e2šygr/Dɔ&ib} Y4+2%L20Kt9B/ -C=rCxh ;ed~"*Z(sb"HU P9cXgÒěEk1S2;D cV~D2VXo%Gg% =fMSAdf?7[gCk3i l9g39ҿaJ\z~^42Mrr Z --̰yi*Ո%vqyFأTХ-qdhB0O -5*euX,cvܼ~R#S.\_LTߛaa!;XuƕWfjIRq!@Yd]}s,?\Vvt'@NDGVf* WyiO"Y: AQ~>h|e*mBz}Q#_jQĺOh 4@=R> w7(`?#3q> ={ؔy S[Uܦ9sBosړ{ެI©vt}IqH3yJ!=yJG6FB-mNZځg>7xѥ/7ڹpO#XS_"FDPAL9Bڍ3N][g%#lxn.DA!8V12RlCҶ׳.hjz ͔DrSX -s'49J]ReV\ZM -{"c${N?I# -*nL:ѷ,p=72" w|| -rlU9MOG. ->p+2^:'] -K)>M0a&}Uϖ3> 3힠g\J`>Oe+GʝU{>еZ.D+Lh-={6"B3X*.Fh٤%^u)G9y#q*g1< uºq>>x6{N+by2]t +  Q|+h&/Bendstream -endobj -3618 0 obj -<< /Filter /FlateDecode /Length 2160 >> -stream -cT59Tܕ*FD\&4`t!l)nG)k%j>?VkYU2@!DHwsY4e_oۗES?:ĵ859\gr+2VhyoÆ_^ˍGdQ[nrOʇO ȵSCTzzN4-Ba}is^t)_R֠ ˡN\I,ZI0\,A~A{ c ,F{84N|_F Y[ At W/Ei\r9כ3mNiir{Av}Z[†i5}/aTg$궑u$œĽ̨JPt]H SWܑ EUR5`+~.;1.ʬ^_"ǭ#>whh\yL}sV,dPzU{OK LN]H-qXwuאX"h5T%y !LOܴHT22y nМ^-@1"KCsJ\i#PJC'Y -yQhL)!yb{ܳҬ7\2M-{waqghYfSgD{0 -틖5p{qŒd$MB?6|B,t63C D/jY`aƛ f 2D"or"tH]ԛQs֒V{BjPS7eaw(q*|%bd3QeUXI;{U֟_Ͱ7;vt4ZՊёlKB NAit>NހhK %}LMY3űC)05UlA(4EY$-Dw{a4j ;0&kq Z)׊7 }x*I)ElRV >v4qWaLퟤId$[XҔ(+"]j/Gg95zQemDVd5$\>q$Pj!S1by^x;&LQv^k7pou0׸^TrФ+=y^(s&C#.` ˞4x53Q+sfh̩[tC-os6_Rb<Ȝ>'Eobn5_T*ulI>l!UUCuyxPW3-kI>o -E5c+2u5[Cl>Pf٢HA s'Ii@ھ.VԷSBmyh1,$pY o0e;Y^iCf?KԠnԿ;𬖂r#ۥ^}_n"N -*Ռo?΋c^柬[C>l;v#[96蕱(#aM;,7s{,wHAɣ=mڀbsz[e+H:FEos⋚dN,lc/s ~#&ހkmr׊ Fwb[2?8QBf*emxv]~,?wa6=:$AUJnxQ;!ف:rrendstream -endobj -3619 0 obj -<< /Filter /FlateDecode /Length 2800 >> -stream -?ejrF6 ܘA&M|x,?Iq\V X< iTZ\XٶeK\F$WI9q}Mf|Q[\Hz#d3꜄jjI*&]8w`7%w 2OTkf<]M"s`y4^nj…3L݌^N}~_$<ߚd{{g -Hd$V@J >oX!CF6g%ųзؠ_)a)z HKz~./ȵbBME[ڹܫU7~^ڋb@cuhuҍ¨jl0h{8>xW;?[<<L,u'۫LVY# ЮAvE}KaHeNq/!բ;.47K(h+r3&3ԏ4jA#CdpB>ċܘ8D?#_#;]mZoB*~X=E`[1NM|+3g=T~ K.buV D@TT9U.Rư,yه(P7/qMK W ' iOnu~1rJ+9M/\1F j]~ǫ߽g4n<>lG - :GW@ -j@ -|Ŏabj--@1-}_JX"IV9)EȺcYAab/y'PUNJ^4D04OFse<'KZ<[sg)yR%0 +8qdd4hsIҙl9ř3X[nQn%$+>CqD㾧 - sn"oݢe| -h)nhXecC{e.kÜ:q RI - x4vj}\a(&}Ʊ^ \vʬ=)dGqsVzvIG;ѵj,ҙ,m!ebjα5 ->1E=wmq׾ƨpx8f5sbx[Qa|CdƅE.9-uΣj :$_Azmd8^M<Kf -ڰw@/ȎEM-<+6}\?h[?T9XЯ+s eR*{ت _|>7IM]:STmeAӕ%Pїb"XAk9q -j!Q6=Faqw?ԂP D -@w !7yªv]#Fc˘i 6E1R%7^{@#F?j.v%rߩ^\+SW -qH~)b2n`J@f{\a'ٗ#TA,\=HkyDm(ң F ]Xt1pq?!wtsUDc>PLMp~T+m#3NNL92-> -stream -gNR[ <Ų(^WL?-==0ی7M|<׌}aWJPq g #F!~۝BҲD[0,ɮ7H9+? A_NIh^?e>% jljRXp-;{~ƆD.{+I1fE3i L,x6;˟{[ soZS )KߐA5,UxH_8D[/C}]]hx&{C uM -bRA -QJqiWnҡzL|vS;P^D l*xFҸ4|#*4t8=%U#vrZekU!ajK3X7[vLi7!)N1UUzAZ;` rWOo5*VM1>:?eҒR3G.×;i}:#͐nFۺX=Є2t4f[Eb@;Qg0CкN{O_ֆCBrPK0[BN1sSܶFX9mJ ȟX^'b#VU-VDNC;0ӟ(36^5J~iĘ;/rþbS]vs vH"L}kޡKDKB@6)6.tқ A}&6';j?IͲ4\tp:M:N0k|A_&IJ(A4/iZ=rDa9"KY)c|߳>-VSrO%qŪ=_*hl60!@+|I6 -!cy#_-oĩBA;=@n^1.H |E~XɟgMŀA8TG5X>8QMMۨ!f)|vZLWmtL?v6߾(=hxuBzl@ϼStd;otչ$#d*$cSxXu@k™pWiʷt|zQ 9A6 F,%hҧw8\jzLuM|. ybz@^Էt+ ?X3PY ()YYꛋC3>e:M`.Pс(5`R>fS 56tĹvp9&52P΄~F@ht쎄VT[=9;,\i=, ɠ4\H3ԍ<7%u @!1`p{WvBW(o?UwQPNWdpg<'/ff%G~J!O.4t.ۄ0p~e" F4*D$d:;ӈmR_ sY@yI5Y -sW/Md_QG9_X{Í:du+ ʌl TrQF(@rxjX .RǘT;4o<} WMeqF{_Э"Y|̴Nܙ g^tL8XA7Rnd%.SLQUO}6^Ux]USmѿtn@@݊[Sb] j]k.5 |-@nK'gDL9m$HYg8V/e Wm2&<$nqOh2>N}e^<.i2e -ߦK#ۊ/AKNG-(R4RŁ ^xE>#_;4b,,} y.Pgd›`38Խ2J{V9 #o2/=&DŽ TlMx봗 Y9kR{PZE,ϯɈ(T M0#qMFRr>J3Pidd*AYTz \SF]Mi"ڥ -LܪQJ7$K\ 2Jf- 0NsJ܂Iz(R# m*՗,I}VX>=|EI hj^ G"$>V`(VMIĀ#R ,i=k,[S 3աC ϣ^C6W;Y0̪l)b(iYu -TohGXbpɬZhEM_ZhIalA4]s0)^J .j?D3 -ig+ӰfPSͻ|ŝ6Fdnjby*UKCGyoa%gsG`%Cm6 -bj ,O`` -3PvP[Q޿la')+KR"؈1ίendstream -endobj -3621 0 obj -<< /Filter /FlateDecode /Length 2464 >> -stream -1Y( e0RAj76D)4C@w@d qoms<ȩ}Uk,kwu 7з,#K_nqA_Tgw -%[h(/FT+u2"XѝIv;`}$?0ih]-v6fM6PuPDYlxXˆ،g` ȫؾggSv{= [ިu<2 B*sF^}6>9>8K`%'&rYl_}/ߗ̹ε%:~81:Yl Glܓq!s ' fTOiMcfǡԩq6/\'s54m*9lg [k>ry<rl:kcT56|"{XS@w $^G?R=XBH~'np:bhK1 <'X^߲)|W^0.tZ:9SB'dI !2vq@DI:-6m ->_u(~#XXjET9>RɧG^`(ɻ/lY).fǰ 205`y}R.gˊڹ5ؘLDKKD_抦Dӿ͘Fo]u8+Qـ!Lw -M ^sQ v ݂y]{ 2['=38M|YXT[:{FlSWȺ/K\aHNJߌa݁LkSYt/3/?9?EO(4YJ^S$rKn<.GOrNXZʃtQ'we,*kXMݿx8ђdAXȻQn 9]!h$4pـEAv Nč.P{kڱHr_8MhѻVKI'IW>1P - :J}#KK#ʢ<^0CY2(eR`}V1'oRs5X5tʔbwl*|DT5`y:˹yS_܇.I44 -9.ċb#ixt)y> -stream -Dl#+ f}+x'?Dy̓` ~U3,lbGTc9J"υx;ٹ§v) -?N^"ե.RoI4P~wrZp؛&zWfJЦk "> {B]\Ec.<frDem]Г]tbj:^}dU6l'–G뾑.TUj?suXhg:&ƉݗviLpWr/igĩ4ެ@ -^ǥ{$=>ˤBSzѳaRZc=|?)kFTc(XW sgq-|&suH~a<ԷeLNok^m9]$3fXQ]Y(N' - Lj&8%!S2At| `$|?/ˏaE6XdKaChz<ն9QLO*sii[U Ƃ dPq{o&ح$zG --$O -D׬aG!8Z -?$ -4Jл|> -stream -#S{L״Cd9s::qMSʥ]pW<;n41#Ž mv\?8}ԛK:a|؇CPY0 qv&tyLMx@V\?wf0n2.ŸjAJU5"? ݌+iϰ>wok%4ĝ'䗜 $~kˠ/"@BsE[kP%ӕ:$̈WPA=,3r!>%FNYC:訃!@7)pa7~Lkø D[-KFڦ덅?JtQ?kc?Y=X\CW0} 2yuIO ɡDyנN!!W!ݘ -4 '՚Kk)TS: u+~U 0POCdv\CKs<7th̘wõ%/~ ]. P)WV+ќ"4k/%Rx:IrN_j#ΖatgYg<8"hݬQFGa. |B@*Zǐ{|?.q,c^YAqYG -Vn&)'='Bx ݖpY!bS(-Z^c KE"z)V-#SX ogc#X-cQuAf&sr'{XHdmR|bt?<ļ65A^X` &؞#'ެGA"OI9"_b97j4'16Ja"u.e ;V (ݓCSmrnyꈅ͒7ZB)/g*|ɼKcjFSE?[p5;[(OԯK*4H -%W?RQp;AZ?+6~|/rgs5lƲ1fLqk!*/,MPNJTp&oᢺ|h@HLǯZi قgiZBWcPɪd02DI5WHl#d!/r$VE'"{,>bїy̓eΧF1d]B/5LMԦM¿,G/Gc/J>>:x@6pAztVSa'Skʡ-:* (Ҿ\QMtDs)1fO>e`Pm1 ZlBD2zZs_'ntuhG br!sD -ʙb*ݹAC)UQ( 8#RnNhNS5iLjq]\5T 泋H-^eH dmj~)o&T]#woYJ5 -'!:m4翘 -Nu3i 9Ӹ7V%xI3,"ApO)1J~kf{jGQc.$-ĵlY+0sz J8gX=@[۽4rWg[v;jØ3v_'qn\JC'yn8hh+8Q{5GN-+: ; &jטMhͪ *GKu5su`$Ƈ~+~Ez^A)!k[(_fpbA 'Si\" MZB5WEթ% #W*ci3nMwSlWwznzj˧{ &HNߧmPB>C:)$q2l=u߄T'"~(O@l!i> -stream -0a!X$ߗYItIlQFk4~BInhq' Uˤnvk=Dگ#,9=wmdy]\",cɚ*I-xTSԃrHP9`#Ƣ)aEkBFOu*㕷_!1gpLᱜ|KW51SNNh/OXsh2fTkB'Dr: ,m}[qFժҪ#f`.F0k_ʬ[@-dT%b -("s)f8XbYQ4d9."͐+f)ⲭ7n83GC߄A(3&~!A]=Wֽs+Zew]t.'I  = * `F=vLpKd, ڇJB¬d=NBԲojf[@0h /X wD' eF73W6C -E={+ 9Zz9lI"%R/.i9%*-A.f+,tvO! *l9 v$f 8q݌{.eK'o=CNFze&~b SGъĜh/M7p6^(;eQ&Uև৫ Eq[PX,'l74JY Ҋ?0]yOd*]p ܹ5k(JpB:H2e"J#>+9\? z_/_te_,㝏m%M䂣y&|5j Gro5ю?# D9hԂA24I~f/%TMd s%Z -s -E1}K~TӁŢ/Fa.]BE"3M4,Kc{W:LD^F_  A6VqY/p>v U^PĹ4$: 2*m|V9kbZ՚Fg A<̅v«"9C#5|H#YN>MUnq̀mǺAZYַi@5g{|3#haGb m6H>auQkQbEym7/|?䨉 (~.w}Fh$0Eo0e}֡*$WB+^wve%`}h刖qOTJ8Z6 hæ/k)fVY }<9nšjYWﻤSP3unW\%OXiL$ Zp.hZn71{ +HV8;&I7$!(gFs[]SV)B/pY-%w9#C>td> #'1˫gAݦ@Kg_"J,d>%"|^84k3Bxxg\SHȿH,' l'ձ4v+͙B&#NEٲT€._gق(J pH'R!W-.'r~r9f\URSoqDW -owhAu%/~㴨0|C#iu6Ao!r/9N.54vs⩑.~[|* e> 2Gk<RNdwbbcU]hRԤ֞I]rŠZLð+uf2& h`pynCK'nk=5V?6%,(sT 0 'k0XYg=DžNtGA휊!HF[9IʽCN\f _'Մ&`~٢y!"0ߢY@"%o#g<cpX^p24MM]TW2.^/T3pȒK~{mRvwDf>#&%!H#S6N~(nZ51~֧CqܣZv Dzt WӔ]yxY=W$d -R2&Q@Btr6<8rg2)84д/ڪ^Aͨ,ULesz5*;?R-m`57A`&endstream -endobj -3625 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -)&ѝE '8B1ؼqbFUaR-qUFЀW]K1b=QFa^#;J@U NQV[`W՜DMM*kr; {+'No0dîp@M6Bx1e$<3{SC| [ka{0SFd2"\8M{؀;e_yQ9 u0J58'1D-]1V*vExZY@㔗SM3"f8>V)9|k`CdvwNU*dV3"jd ګ9]5j6D4[W R`ڥ-.;\)Uw=Ӓ"ck 1ޝΚ6Yw+3p 8Mg)V*zN1)Uɢ/adԪY4YU94XsK6D -4_*A{b& ޴4'J J)4~wu0y"垽k7"!}[δ0Ļ$+5ɣoSuY$g8r[Q͏雄h;c!9!趢W9#%nMB/5-lO֩M.{ă㩬gmЖCh+ڟb8cxH,PRxa?q|N KzQufaU -裎v0NO!i'XsMbŀD }sTKݶ̶Y f5ۮ̯V.ƅTA^ь7lVKqcրHBLWG=,U#h/V=Le)=,IYR>HpͼMb~.;ؼՀ-]_ E};i~gC3ktn Ԉ㩉̷rHExn5CdMb`wce:zׄ[Z+zŚTk"lsch'U91aGFM`ӭ؅{۶xtSKeH/pCgáhpN"쬣6>ybZ@CnUHZ%M¥h퓧a;3 q:+/âv ӎ ", \~q#@9xxخB;`6)$ ѤmMcuwXBzv(hͬŦe;Tv6͡G0Gg>3Aޯ0H?qE( ^7[K=\*-Nvz|3pvYƒ5̐['^ 0"5 -b6Bɻл80qg\MܳrgiM)#^3rpfU nn_SjƐ|י46oUG / .[שn1п^Y/T,WYo,!hCg90 -@q㠾z%ۘs3&il}ׂڧo#sM7!ks -&[6K޸ӕ쫃>gSkьCUw~\W -MZi̝!% XڢZ)Y - iU"A%6 X #h->-[',G(Xֹ3b +P +(w ]2eڳ_y63<6'n7/9Flw8h~$^wYE[t LZendstream -endobj -3626 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -VA_WC^И,XwB({4@Zꕄ)0K!q]$cل)9uIKXM'&ft%^-ڋ м^OOg]2$ItB"թ,*}Ӑ[ ZHV??obz+~~ˎNkq,h|LXNm-ӱ:-]EKyGiM] PձP 8xzBI@q͗^}\xƒBl Ǹ\;fP |/dx" h FO<g-:-y -F6 ѿ Zy%&QmVaxBˈ5.^O>Ȫq,2QR(5FIJp&A7[0ְDy&'Bǚ(FTg ceb>Q%0/|d{.A̭]յn{B>M,xgn9.*.RpwkaiI^ O":G七 \Hа,7'uq7\b1[#99Y*Msg_Y֩Jnq+@)7fFI1ߣe.2gtM#=3Lv\.)Im_:c"?!aMΟOsUþS'G -}6ZaO.P2/eTiOKE/m:5K\jR/yPʸtKOh6PuU]P}1Iq6(5e[ͻ> tS}3+T4z5Q}DQ V L? "duj,BiBx k< -yR\#JT1A|Yڑ{ ɥW1M"98,MG,fN&ոnpѕ} 2E*ELbMyZUߜ&Xiv"2& ŀvp,[iyv@i(P 8wpM;\.ek>6"<YiTO.*lZ3dTt4&..d;]VGoܝT~ M: jxQQ5Wrˋa4mUGeaP K  #oƨjgn چG8uעu?c1dS0Prb 4S?aH'cDUi{xv)?U< Q\*f&1\~edr| -ฮAM( --U` -/ - l-+ɛ0QnJxEM*o0xe.%|ꊔmN-/B*0p6AmnJN1$.- FMw(]%&}U|fqyYzkUBZ b&WxVR$,y-H5յ3bFƒ0?x :#HeQ(BuWrvvߍɜ&;u*3VLifxiݧsK?^쾶n0$c/K{b̗sl痠 fq234*sPSm5* jHNԵ,..t^ڲ'"lvR(K80v휖E{YlVXȢ d67ɀ -t6eendstream -endobj -3627 0 obj -<< /Filter /FlateDecode /Length 2880 >> -stream -LR]j^䑼?`*B>(-i<7EEw5x1Y Ӥ3X{Gm '>I֯m?b~G"u? 2{r-MtGjU]))[5FA&BddWM@ntM;@iL LޝO.&ۇѓag*Vܛ*7` ^%u^ -A)dٽֆA&'a4iCFߗI͎6k0AN4'ֲƭf6 1k&"&(2$K2M_vM8xM]w2*vh(w hZG||R`&֎j@64asow#'1hK>- ]0:;me@`̐*܏_S~X@@Q7l IX E۴Ei6D"p]&4*yQ5\[n)Ȏ J2 %%.&' <2O}ٳt0 eHqd#xpu$I`aqO1k!S ued4lDЍ*j,Nd*]RBpnjNȡTTg_~FN01 q -Ib%.G[儨_beDF2l{+qZ_!hL(c|D ?x 7*'ʢOD^ ;`pq)-+AV7ɛQpL"%՟|F4|I:Bcxj^(ft(~\GpetY*&ҹ4*Auzm:LF݆qHaGV r1HE^H@VJĜh=D#Q9~(=.!E/ױ0Q.[{~YZWa3EŞCC#p"LxaS7Nj^ttIǏts%_ߏeZ <^TʌPF poQDZ ++K6[:k<7KSO',%!ՋMƟb#hTK> 'e:wJ{tHt"zsMvp&DLMU/z"rn*1PgW9|C]~@RqTخwmŷ-e"J{`(kkLft_8NUk}d1]3{AȣLx}LYXe)BYV#تO] w%0FEg9؎J)e2 뒌cߍ8Ay?"nZendstream -endobj -3628 0 obj -<< /Filter /FlateDecode /Length 768 >> -stream -YSHRq~̻s$ɓkˎh):BÔL,"t/zKy^'! w"0D0*N 8b!7@ncG'H /@d+spc;y)vTReL-EP&ۼ#N.X 1a]| `.KCuyƿn586s,&(8o8c", =](CEH~>k%mDe.XG }'keߢR_NL#/_.}`|OB`٧Ơ6T2m~hmMO]y\pfz` >/ -EK5ݭ!G8"ųD`r.?|xutʅ}P|ꐄuO19cPrAZr`kd`/ˬT&qO=ٺdpxDv.®hHfyHzNW8D$ ta7g!^(աnL| -ql!f%`(,κ7كF Ӫ#l{LwxKI5QUe8IŻ{=Civ;Y }> -stream -Uh r*=9Jۓ۽2V\X=Bh֭Բ=" )dj+r}q_B)5̾ OxҬt&dI+oN[啀n?2- %y:'[J @I$r)~]D>V+AE~pRa,Q,lU@7)eAg>p7wjH e1W+axYHgiza??,1T&qZ8P|U`/K&&)kNqPLyq}Dח͢j$E̵Ǝ_vh>vDZݦ0H#*e~#5xёM>lL!Z -ȅ.-V8Np8nX&TÑ & b}ͧVl C^,s/Z !%_#ag)2׆9M&wd~Rcހlv3jnӄ>jLOm~]up='Ń$N7x)^ڔa?v?0lJz|F^ޣ~$˩FA -anbqȞgS`Fhu DAaÅ+9kHz:[{VO6v7l۱ 7(v1H_?4u[tU`ɐ8hGۛ5]} 㗁.4T.6SР0_F -a|MQ:#:G)T*0-|aD~ pv4t{6]Nok[Gx21H 6P#oΠj].V1%(8g`QE}^ -!/si#5ꖅ@[!B9bݨCTAb1Tx%YN6wgT@LsAnribhE%'#_ԜV*1B +$gODXsoG1 -:.vUV^z[0ǒ3%ώ#V -#̣eĥċ+2^ -β7L{nvЪ4RȊҿ)KZRg^./fi4i~,d^.raxS2~ r2Ҭm~W9`B>⠝4b}uaه7sX7"Uo4(3P;C7gA|J|9u=PݰZjg '/VC諰U3yVhwʵ+(OEd#FM 7Ó{Lï%p~F`Am(8Lm׸Cm\|, dzZVM;X~hEIo"d̀0g.{@>׎ݕ25:[nchS@⦻px?kPRkj-CY2 -tCYiial'w9խky\y%$_Y#"nXJyL%'qDIQ͏=[ʴOBkhT ѽ;%79ZC\$sqmK`xUC>3N3Պ)D~&忍fxccte6Ucǹڕҡ_!`w#& ~,窈+`q/),o@OWlQ#yqpRԣ yݩ8嵿0Eendstream -endobj -3630 0 obj -<< /Filter /FlateDecode /Length 336 >> -stream -] -WNYST9uQ%QץdsQN'oX{yRc $M Q'o\pDR6 CEdEЇL(je)dQZk$- Ft0țmLQHdeUOfQ\ H-b`|`Cg6V$ j5nendstream -endobj -3631 0 obj -<< /Filter /FlateDecode /Length 2528 >> -stream -əHf `g :{xIV9]OavY3`԰c@E`\yZI UXU`;C87?ɂ{1cyq1CVe6<6.:y%7V/BNǺe[{¥q;qod-tGfzA-9P57 ҌI K/<8z_:ªgEdMLLldW tGm*6wZi!H Oo:jL;Vt?Z0m&\Ć~9ad]R3@"U,0p!T8hSⱬ G%AI┖@,L>>EzwzA;VIkcs{Yy><)1JF]/| ,0 b+oC -?ͤ* KZSd=4)ڰiYֈb~; U,;xA^v\Or]9Wz)[,G$1!hALs#S衱v B"m B0ޒJƚ"9eL6?!U!5y.Ӈl>93apa4"!Ƨ}3cOڢ6lKGCS[3wi`nBfK vN{:"D`;O`cJm]ʬphm_w&0#iGpoj!ȏޗT>7WQ{'E W?#4.`: [;N -miTH0?v(B"rh%σ_hHn;Af~/*2|:!=-ɳvd |'|Jw> {W -^!gmG@IܞVF~ 0Ϳυk4`sBr*̜\77rtʝ6Փ*JANN ~8kЍ}37>h(y _+z.3ĒLbbfm+eN<*Cv8t:Fࢢqo:j* D˥}#PIh4?LW[[SZvW4]x*P#-kb"[? d¬Pg$Jz5wA5x\$$/3$ MRsdfBQזMߥZהG#>k{/~鸝U3Lsk{IӝhB>˕Y*Zoj҅.CLi:9ІA, }I=09-HM] "@hZ [7.axivw<@|#2䫷0̈́{ǮSW +J$t&@1c:+T.ـ`,ૃnjKgi롡Mgŝz8ŝW!#DH2tײC R[Κj4'EYnON(P>3;U^qVn;$[\} ᣱfzSQ~:2\F.D}5HЙHD'LNQ]UШэg yDVZ(d29^r{Lq[❐7vTpaZcyl]j]ѵ( -+`v>_6nTBm _W`HӱrxBP~<7`撍ϩN1|Hqm]^чH$ "yU.}=SU@FhCț?$DNaCFp}[+e] Gԣe|| -@-\}$fM8QOf%oI:(qdrG#Hsc:[h>GKhʯʍ(iNPuzPұ^_9}oXi/ZUoȟP}!߆ՓiqyIfX:=q6۾OH_veզ3 -fendstream -endobj -3632 0 obj -<< /Filter /FlateDecode /Length 848 >> -stream -H1RZQ>np(%^= hXnG:m&:Q)AT8f`X|Q[1&LE2Y&!|yq)7Sur/>WlS{ܥ)Ru\mKݰr~w0ƎvyxAMm#+ Tz&q uT4) A=֐ȩ2CQH_+c} |ijD}E-H9o_wjԟdUsH1;ç"u_d* N /Wavܠ89kfCSqٛPبx[NcǢ>ٮ -(Hg{ v4Kc8,3X#X`ޗoaz9|8*IfxI$ݖ&M X6LZM'^o`G @2=z}+x~G'ٰY,eB&Qi^"sau;Ywmb#j=QƩ.06o -s @_#9Eq;tbnC!O,֨=*6QYJƮi*2%/-xD"r#4~e2ǥbJ3$Dv`|w^l/dC;O)6NGˮ;7z蚼LI*p$\ -3MZ6 uj&O> ͅ{@XB;pCEH:* -Lw3EhEK@`с5-r8@endstream -endobj -3633 0 obj -<< /Filter /FlateDecode /Length 2480 >> -stream -FRV`x?M`X` }REnlE +:JhJiH@`x=عpR2a>]JəҺ) MbvDHz3Q̣RoDB<{( 4]`L$3Ҝ匦z71SO:;C-!_ą{cGaG}W)i<~Ph-Hl]#¦]Ĝu|-wef/pk=xpNOf -qU~yh%Ck(yN +<hKđ m:Ȃs'Ȥa_kqMdu7ɦocH;HzJXk솴"@Onۘ*؛'CK&XuP#*,6 SvRR?ؚ9եhwu8|,lMpQ]!+[nY,vYa-KvN!CHCj7@)!>7q؋Zvߴ vCq1{r;<"ö H'{V!@}Κ1sOgJk0*_S`\bɌ_i#$RFϭ=\0oćƫIUh^jjK)PRt(*Ozv|`wMz #FXږ<:R;_J@Oͫ0Y4bC:N#uVu_X0N:{~Al4?ԞUeJF0P#F4~QWV? 8X⹔h՚6n;zʦug潪Rg -e[>X b:kf胣DM_ɋ]C=%o$Kq*- RWvձr(VΎtK|q;rt`Cc3=WgSܡ%ep``)v _ -7~ΰRޘDPXz}rQ"Ww5U[h |W5C5뺉5K0-,IXȚCY&mvhؖL U.`w`oCRIŚAk,=r77%=9L.kptB#'H(kf;y/F*B4QgV0(_-fVI,+O&ד fG J2't0\8wƮhHo—Z}r>^|,.v@`WQ~!/J{a&})^ ٬ns3&_R5xSCxv|G%wEa:}PerrEAŠݴ[9mWCC/p;㞃wwvendstream -endobj -3634 0 obj -<< /Filter /FlateDecode /Length 944 >> -stream - d&[s.NoOPz ݃Ѿw˸pEOHuf.3 *dxdU H4dh϶V܍@[Q$Ne_9<{08MQ -,穱 -cUp4V9k0v"|֜$vtYג|`sf!-<ٰI%i)"~mIx0]./00Z <x[^0p]"w:n|_Ҍy҂|0Y ]Ac荺 %Koz"q|iJpF&ċ X,T-k;'B!.oJa,@b5gc_s}D?+JLmdmcAFz E1uLWCD7+rHJE/ $犠a8\0] 1龘 J6wu2W2Dʄj^=,5)/VقCYuҹy^{_Bfv Ҡ[k@s:ϧCRř/ \oT)?h~^CgHO{+^*SLD>C{@(J/Gk*zZsMT!`DQv2B8GpP_]։& U!`h |$84]\/c*7*1HPeW2&[WxlY:BPN8iQkEQ[S~C(Vp 4bBB0EN1Ͼ 8[ $Q!]l1xIݟ=Ualf(g6.*NҬ}z ҂ܑSmSB2fɀR Y6! - RPDHj-L`{ӲOj`5Puendstream -endobj -3635 0 obj -<< /Filter /FlateDecode /Length 2608 >> -stream -= C[!}J$GknZ,%$[&- 4J&lv/[&?ν0Q d,_{IVb5ekEƮ|O¢G@85R_n7i!w1L' g\+qn8J^h2Zk2έ$b90U,LGe mfҞϤGUgJ6W -e15T> W;kO"o$dR]RN.w{ʥhb# <xcGZ&$a^W]!@̅K-[Ԙm;39(& C -˅91S';sy=E&Uj8vj)o]EȒvHz7%_oH"Rs\/Mחބq?!nJe%R&* PBo[e]/ ZaPVY.3|u"дr㻕,zr%SFِ: "\:M%kmҿK/5(pJaD(/QGr ZI,dqˆm_KFfPeґ r73zϫ]P%| `dyg0Y.0D1d͜p:(/""O|抑$?w_C!$%St1|u#NZ80: AJ[*`q$L5E\ }M3. -BJY؂+ʧlӚ2zݤ23U+^mYBvHx"[FZ6&EZIVK왷lǑ>/McfE3iispON -OuY+ϻJuB Wf)|y - t=\o^n&+vJp=5[죵u+D:!'zr#%ZSGa49Dgrl `[HĢCS[rŘll>RBNxuې쾪oA:bO߲ZRӷno=ZPЊp7mlWL:4y*Aӏ.vʩr;Eg#,%Uea"8hԞ=0 Ҽ\FfMer&AqmD`UwfF(0NlĆQ"J_ERRa@')֕)|Q+c `99bgT`BFbY G. 6ƚd R\]LKKWɋȃ3%NJmR9KF!/ߜ -79uBm3qp5H=zc`"*)U| I}{2 -x˹ub+lHysV,drl-0_gf (X.AkFnEAw%qC-|<69%9 l5kx'}- ;z{/!N ViPU74p,Ixݩ'FZ9W6&ٷ 7SUZ'*9:8(IGp1OȦ`(7i .Sz>OjOάj%G4w[l2v-M!Tc^;䖿f\I$3$$1QX%w Vrչ'@P4!y (Agt=5ߔPxendstream -endobj -3636 0 obj -<< /Filter /FlateDecode /Length 1120 >> -stream -+UޖtY ;xB،aC-?u}GY6ʱCTyQɀ0i]~ -ov:͋!F4Lb-b3Nhz "¾GFIjK{*AəNzS~W|Qlm+sU/OU7!^Wu(*˂[g#ۤa"^V_aAoUff[ =+2[=U&ɮ减{y`(>$ ?Y\3}TcoV,po`,él;oe/ia㖺%06/Mmuw#i|q/?W`p_Wf#:.,n߫ A=p>ѓ>kgN-&&$~a -W2mRU oV뵷Fm> -stream -"kmh -I[]اvE q{MQ#^C=2 s]\JXط;LҢo&ᄪC yAQ>s[+yH bەW-^bi}י F -isT=NGU1OWC_Ĵۢy瓊(ޘ0P&K 7.q"Ӹ| [̻1l+RFV @>ELd)f ḿ™ A"D - {ZEhcZ$oS -'Iw*#PR,`Yy=|xbD2dz [/!t!`q{[67}eFY@pTJ>; XȖ7cݯS,vjSz: lF6Ubm2RbS:AϛDlRN;$@?|:G%ݙudС3[W<YU^OB<Ӷg9,zOإU"r]MV&SD>9$1n3oQuh|U38Z_6qX9^ke[JT2M6Š -G$4$__/GAنEbӬ!YN-/tkdhI`S*<&Kz|&lefE#s2.]H۝VedUfpU|/,a&Knw?ͩJZ5 p0UNxviafuĊWk"hAlxWP77Zd3Rd˯hL]X.!O;zYJ9iCiv[ ԍ6Ve?*1 I]#h=&(+YMk}n_F詰чI0ecuR[8vKsa:@=*DCd(PFWeg,5N}ЌڷhS,eu`JQ u&?KlQc -tΥAU'CP! O~ۈ+ycI,'!RH|޼TQ6 iAWt.4Ȗ5]@#{w*wa SÎȏř$bp`Atk*M{vC;\s]`oYW>y.L! Ĩe H9L"h? 엔u6h*9 u)?CAŤkF#+z(jCl,m{>њKw]HCΞ;3d JfxQ% X_Ԍle#9}ݯ܎xaOM9Pg_Y87D!C=[qPɸ;uhS1y7lеVim -,fOwvD .YkC`GkgJZP^f?İ . 䪛vur.4闌cbQ'CCt$&3Wt>L_ɤ5Pa+<&ΧS9:WQ\oM\_J@hBf -DPB˽w=S{ËJraË:8FS{^.c!E E=ԏ7 E(й3ܧʢq+P54eC: -=PR0. -C5̕zTncg*2Ŷ7,1T eoWk)za݄Lw>nv-֘ph-:=L3(Hpi&sg -g{/ Ժ'2h^p|gxoExoV'ў4T͏M?'Ub)SH @fތ4ޗ{# -#: D rEZendstream -endobj -3638 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -]eC醳Tʗ|NoM=+er8̞:;'5\7K o'h4N;#=eFۛ`:ȗeDi;^˸mݘFmb?pXӬہ)ͫ1 (l$M -34c_כHZ߬K 9qV/( - 1(5H Ƚ39O,_|nHNj;C$LtgȻW-_PTE2{:ػxB5=Wh+}Tl|^ZQ P ojSq4ߪ o -Ǽ~I#rcO[ v]@X;b۰ T FUa5ME#t@vn.^PX]4 Jt&w;> ş` cnv&f$(#/~u5SWnщyJEVgMjd0'/Itcv;Ok7 1+I -b$]_0:[@:,UU rEf>go飋)j gWP3?L@f 18U )S&-_/yA2QSSF_8-ܹ wLq)c}_ׅTE琳M 0w0fs#{c#7H!,4|P7N@+iuFb'RrV~wSS7z2qljs&-_44I0szfݚ9U #=a1 ͚PݡٚGX0aVƫ(@B gN]̼+uцf(!ShywQXޫ\%컽%a6Xak&9]%YnM&B'6]8o@rTg;]'4k9D?jJh$^LH ƺ y_u R/#R\3\+q¢bw!|TB>- .%v-VkC\~΂v 17H: 3g_ #BfĊt*ũ <sWg󔽜vs 8rnעxG,-IZM; zΔھFh@"'2t#kE6@]y3oLz\ - -U^UBv ^1Z+!@NI%YZ}@꿹7b>jLJ\$uuV[GS5$wxNsLmVnTv,!gasoEyk燲սc !9°{E[3)IR*YwXi@!qv n\Ewx֯߬ʥ7fOU-7'A誒AT V 2ηfԞǸ hs;)5endstream -endobj -3639 0 obj -<< /Filter /FlateDecode /Length 2192 >> -stream -$.+ р<@|")Ag%z5PͩWa]{Z97ek/qE{aVY; 8)Ҩ -sDg^b-p}q@#izKD9gM0.}J1b#̻(FFk)tsu^d|-hx+"J*'mCԶ5HiUfB#+u<%F)\Pz9nAh!\険.YKNrAJ# j4B _&(<,~AR?*5G^p@; .)O^4*$*t}2G߱fTs!-[WvzhPԳ~^R@ԭ٭&"f”Pa:ćZ:z0$cD -BڀQϜCVb(kJ.JcM;k2 ֏\D7ڑx7r񫥫j֌/ ؑ~nL6}w%@LRljYmױm|/5SqdA6`׺0p?=X1P^ū,dĉjDډy[&6+̋f#EJw-ӃLe, -kY D[fZd '% -q˗klU1>s+jB;o-wZlO8+~9/SV9ckPœYn!R=ĤlnxX(!gvV&FEehR.cgM7sO[h]I%D։:lѴ+`RW1.,X6Z u -k.yR'q'<551?AwzYpaR }C0E7DJSA =9Ӽ82M*awSp9^Ds]g @,Yד\i'qū}9S˹x,$(SSxb,݄^qCr4qܴNC^ӣ(Jt˯wp[ƣLs]j_t&˜ʉpPEYt!%}+tQH-3}!@k5Xۊ +]1煵IW1CBWxGNpr-EKY`%QV~W!;yi\l?-˝1څT@䆄&xUkOiHl ?AR_]P;TEŀ$t#M95,ڳ&T)x.챫 7L%1>TKPӛ>@!+o{ c+~N,:L݋V_;<ϖ~vlhljB 7w,F]_*=1!RϏDQx 9=6j!My%`#T0=BKKZ{t-81y[qMd }0<9:0^Qcɹa:0/5jLY^cUYSȷi*T:HH$P!X  q䲶R8_qXH >R}cmթds -1<.jOk`*O -"?hendstream -endobj -3640 0 obj -<< /Filter /FlateDecode /Length 848 >> -stream -yiaKYIvGH74gyyd]U HY.Kn;vT7;-b|9oRqdU{cB¬ÔjD%-s=AȵncJv`TS+ȇ5 $vE)ӓTq$}TBR#(ç<'E"B+w 瞣"6oj;r,'Q./шF>غ'\ɝ^T4?&Ck&O:⒨v^ F`ڎu]TMckOmA;.Nx -3t$&,T%-#K^+'- ނ7686j^H*T*Y -!r Y]ВVxD>NuPo3\X)rENCfNIԵ7+Å~_UvWMm<XG$}os'-lց^gx\T~Syn[ֵ@bbgF?2]*$nax'{WAy!_R5cfkpMۅyN9dNؙ[gk=r -&l 6Ã/]1% Y :e@A j>σwh# "!6,ЫZlRtXC"endstream -endobj -3641 0 obj -<< /Filter /FlateDecode /Length 2624 >> -stream -dHTۓ63%JV/_Z|5{d}S - ) -&i6xz3 DQ2e2_ȨS#Y - RT"qVI v5Ww6l\L|z$7xh),d%i]1ͿTiN H2ED9LAm` ' cT}6` ]!v7_CD>聿(?BV -kCR@& ~f A/c*@i(1k`5R&p߀empe(k!Nl3apsL&+oGz0)v8 I(%Ƚl"ڑoWTzhpk췾OpxMR>r`,G=\(2?#ط],Xھs؀4`OpĒ"=ھ6 a\Ji:Wo1?4ٔ;.618:!`TXߪVI̍ZIUq3ZOwMCsuBs|gNG[>7W7(5g7^8t3J\jGB -{Q⌅/.Nm*%۾X{ӊE @_,%%bpOJpF̝g/ Rv,w%Ĩ#Y ^>*W~|j$юڥk0ѵm,)1Tu; \7Iضێz\ [he= ɮX :rL/:Eֈj- -VdO0-!Uc1ʞW&{v%51(2V,=$(ȣ.G᧭Om `V}H=Xf!yǭ\>wE+XW- -CI=2.2x^A3 Te#u:lQY bNë^n!h ); -X$_㉕Xײc/VTtOJ!_m?ܻ|1' -4~[ M "g6!+ڻ: ;-Luve_靻~nRvɔ'[w-%Fc:t܍0 -q4X_yӗ 8)RB9^ŸTq&t:Go!A2\Fb]6)A>U4lW$QM[ -WB] C;"& ʢlj/f=1~ڝf~~;y#ۍNG"߅լ&tyBJU 8 ;TNK]Rcˣ!2d)(6SA˒ k;DR-g=T~ckO<ߐ"+fmdZAI8*@'u֯qI<kiSw]lwސdD;i1U]9YDډ/5xs>*3c?%+U/^s#uo z[侫]3h|cs%neKGBI%46{l f2hZ'ñ222LxL#6;7`CV Hith{v 6L; }tZ -nKݫߌj0 -|_(q -.峼NcrV kj{_-2 Lgh)~yhHƖ*֦*h8 nKy -s~G‘ϭ&E5MM=V$s?Os'8{5R,иJE:A@3GuIP0K#!1<ׇfj*~ Ht&PE$r}&sIo^wX~$LfӴms4x$iXѸR.ޯ) }hF=@$6}.j?rx1CG#d%W׳ }OE ,kMQ5,aG`z'Az\3(u[PLήY{yyiy3 u+k^31x:};z\RtCq읽%bJxz4zjR}*#E;@π OT -@eRrCʢa?! `"}{ĬkX6/iLQyVQPxpXx@XsHiHjGh⎞TfERpxa.3ldr$fn6@, ]5ϼzokkúMěKgf Ζ )]ļ TX˂Ou CϏaT\ ¥Q7}؎WLέ`MDZ#,%T46!mX6v@hnD e[/QaY^ܷp{ YĒ32P2ڜ+oj`Vuwz͝0Ng_NVr_~Ob9zDzcCѴ``N[ETi6P"c=~N -.3hJN4v鱫\X E -D n?QQ5y@^|}14#heOm |0}=em-p#.Fp.MS0]"*BD~UJpL -%?:3a׻3o -&g6 vR7u,w1û4=qظ2 - LGuAG>cʾQTzsLO"y{:S`*S_Fz`@T2|7OmXyWSi< endstream -endobj -3643 0 obj -<< /Filter /FlateDecode /Length 2768 >> -stream -N /3A2,Gn[ә QyVU@ x0 e=-[H0Ń"۸x;)`Ag포Ȥ(HF0[QdKYH~O`(FBSSe+{gNok&LM,s1ksYO?s^#Pd!ONox*}NUJiz\ -lR7a.ZxAkX[a[$b#:^֠pKH̦t~gCwk -_֍lf_:h`y݅$c6-}(*ꭘ9ѽ T<1kD5V"Z*@gá?qm:{ۻ ϞI_ Ų}(J/9-a:f -[ކR6uU4KvDBFHGI7Y?] ;,S}t4^"}tY1Gnp]mRY@!-Šs ,k32cR"o͟ڈE'KZ g27JB:QhpOOQB-cjVsBf~a!يxF&<[@?G:G28ʳ,FGv-eߨs(!FE!Nh'r@/c@&'?D|#d4TiRkܪE>P4~GWFJp*B$4k9Ck^Z\A&, wțkG&7耳J:OvCj.oنizh-dIMa&9QbD7ԧ-q fhۂAO;dC&˶ˮY*s_)8O2U@Ba[Z#1$q/z4DMF%jXLzG0܌Ջ>9XH&I<0tɊ?^>efw-06-XmȞȓ`;jm:zɤ$hL۽64f _e| TӣjiwWV*EQMaϺ;e$ɏ@i8aݜyRZVT^Q2APԛU2/q&[PI`[Nc,ܣbcd@unDE!{F[w; `埩CuP#oR {Zq3 &n1?%*=%h?}WYK)QWBx}_Ʉ1 *"<RR@ܽInd1|2CÝO!(eGf]*xzQ;{&@W;`WO2b3^X/S7R_ֶ`clbt}֜&0fo{4oJ4"H[6%8!b^}jI KPaANHi0P6Gtӽ.t?\u{a7zh~ѦVgӮ@ {w;.!]p\UsHֹ6m@E><2HˡA k÷ -ݞ0c)F?fϑ`gA RxO)+e/)Ūiky.fB2n k}2 )|up 9,җt$y4斌 ƥxh/A9#{V}14$>M/L9szxrjxV0#Q/4W)~tk+h ZǤDiN -,gF^-@Q\^b;ՙ -m_s>iA,n !Fonۼ{wd"ɠ|t|r> -stream -*DbYc(NgG6wF/G -C\ dڂAwm8ޒ*~]9 -2<1B:XP) KZB'a[)8V-A8 ` 蔝ḭuh&Uh)~f -iMWX&(8+-5(xpd.CbM:+?}@󆿹V(WH5UbJy.lBAH wc_@oǩrhIȈ-m`&XTZ^cSAY[agbWXOڟ%'bg(s:JR*]QO~>htrۙ*[p74@~؉p.nukfu3 7Ksj3&OvB`͟e%ohhSlFJ\;J@ yYni0:yu M@ ){z UQ'foB x .]hª0M@smvCwTifxC0w?W0Cћjr(L<$W m'\VZ,sjaNk IFuM=̺M:"@z+ЋwK߻5t=AʥaKR,EK"chl1dW,2dAK 80endstream -endobj -3645 0 obj -<< /Filter /FlateDecode /Length 2624 >> -stream -38`-l9EK e:F -ψ,I9dhCW7oYFk@DHOR1)5`q0kw=ӘHN&tO+8.mtpl-@C ΜhbgNN҄ -~X, DڍFwԒuvh0IWqM:aǫ/,>YH^oݲݯnO -}2sBM8gUWu -K,+aU׸y avJǪ}dyheм+.0cj|x:O# ɺ%UŸ2300QF!Rłަja7jaaaNc,g^W;y TSa(l$r~yߧsvR`5G<(ge42a)x/7_ Up#25 -ץrL@|fE!ɍ-2v#?x(z[FǩQ*EtiH:IIGF)L:}Ũ>FS ˲ x1_+2ݓϿ+KYWLi3g`ΖdEU|Isv@qջs!u`WS =7ʣü'+Gp8 JT}-%+]egוv " Q {axO#(F| js.ߛ4yxQ6؏ %oUAR3ϖkIypFR?y%6ZLOxjZcG(y^(|Ϝl*4FGNA4BK e\ -l;o5]EctFk8$>GWNaiBIcAl^D tlXSy?rGH,,,UO5+kaYM5K}3T9ル/lKAS%NRDᚠw_DW)h3N$;ZoEF~zbhD"X*,3zXy|fok#^?ȅbz}65md;L)ܥTloVa(bxh\1v'0q"+}Zv.}d`bPP6fy,r4 Ci:Z"X8]1" -؜s-vLN(F'm}#_3Df*cp`N1 yE.*}`yۙ* rwP -9ۮ)+ά/r`eԄ=h` 07?5"`Xзe -`i{tFLgN$ QT99ޡ</'56E[1 :m/2E~!#ִ;&F"#+k0NԲuendstream -endobj -3646 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -iXe T ]c96CUY[Y3SbWye))9r -.%R=IG'BPW;@XF%-Z؎;`T=C'߻5П 9)fPG8U!!yG򶭲\bqJF+_l.|lWU59}hISNOp :cU "rQ ;GzBw+]Eop.S>+W;4o"{X.yhyQ-$dvǗ,FN>X[qڼ'UX0c.)Q}[pf8Hʅqzb**OoZo2~ YW]?F%>`|ŋ5Pa EcQ`R>70r}rI.+\ >U>e`t}GW_(+$zbz-/}ovk@`* y]^c' ?Z-֭L((T(\ %dQ:BϦ=]ʊQHNoЋ 4fïUO7U3&iҁXS>>9]cϓwXb#%f_߱Q /`z"UYo;B߳H~^Q:0,u;e;sTkjxnL]p#Wv pm/pp 3 ږ`ʗԎrA= =8pJ"I*\ZapCc&b[k8C0 zFp2!p&M# h "0&ON$X,K7#MxҎKHq֧\nL^U -cjuxA7*1f<h$΢x 8+ c6[u=sO&]V?A@UxTm̋h01a(?ZM{kjIjijD1$g'𶌐("GgMn%H@q NLTw5-Cz%kXnҖAc֥P I|o{r*ĵ+[ׁ !\5+1rCD$QwhAq%-YY=fxs.dügyL6[@aM{#h`FV±Th>\jF2<1@Ҿ]byDaA('vVFE,HF@a}p,ւ&jη~np3[Ӱp1Ǭ0$X@NSnGdET 5R_7oP5q)MY@t4^;W6m]n^޵bJ8* 6};Wmmdy7)ⴈ.mBendstream -endobj -3647 0 obj -<< /Filter /FlateDecode /Length 992 >> -stream -8(my3(`zp)~Ueԝy@u))u>C{YྚY1} ķgw,esEK`1 cM"Pi?% {=vΌ!" 傿Ѿ 8(HbșBl٨Xu?K.z~yMCMvf:An/sSd}4Ƨ6K->Iq9`V?e`Ck}G5v= 7ݒH,H꒿Hv9smwWL^;+ -ќ']E-+e_VFq7IOVԖ={;&)8B,Kh |3]xt߅5›t˅S!.IǘxIXmEE&ӱf8Dx| - pD7??,K\*;+ԯ KiZ7MW`*&aƠח,2WFYQT]ַG7uϰc,Ö^ pv+T/Dj.ﳡJǜ",|};p+kW 73zZ{`c픎CL@@R\޵wvmBQ3|l^(&>Mg -M{ Cxc8Z:jj \) F^ f"snUQ,\>C ->TS V xL‡}we,ܢ1O6| @ocxt\?p0P-1{=t= }.dar+[C&㌪i'!=G5xbЪqXS[Pd(qdZ - بG2 OUu͒P 57Rw,4ɭN4{RkP%^`71$b)"n9Q -1MQ'{.6ϿF8(97+" 68 Xë x> -stream -s< ͨΗpeOc iBOt -*V`gB޵0i?#`a\.`gaoqYh~X o~MRl6zy~Elٴi|'}ܼlyw,r1=h3e&5Zs4b g FF@u\F/*;G#K ti&Ht8ٙz~dLOu5?D=+[\6IJYadݛO|RXqnN3-HӔϧ|=ZYZ;%%fTo;./Sl³x^ 2^UVv'pD&0|s{(,ck}&`|xY*͇t;#IÍMV$7a+]WL,ٝ)P8i2dڥFy}O5!ٝԆN2 9afB't= L<~Z~y320 Wʄt6簝OrV`al"DK}B-Y $&Ͳ&3{s[A^R^J9su#9g~CMu\_eKZC -> -\%R=<:*z ke3k)e}i-2Kc »볲A'N}Ҡ~GrR1ӏT,^Uڋ&MoEj n4},Q 0.u]GD+Xp2b6rkz -UxmG~ ^z֭lPԞԻd8:xlH+|To}>'ꩬ\X +\!]=a!5zE[VDN 6J=̛RJ"7 ŋ .QEr; tKV? ^#gI,.<3B(ꟁҩ~ Uȁ 59?a{Sg@-^#MͲ|u&sSHwi$pS#3#L1M{?[GJ%>ņ̓2R6eKN2ii۝!xb]Zs6?47V_&~RA3d'8FU#y,gȍs-ABd-$k i"pwC0 OBƬ0Nw/ˁ`tزָ9~֪xo,6uQH:C~8"=OU? ^cŋ\b3T;5:/}5p'Zq bͫ0zQ-N>/M9SyzbU9rW x5T) Ţ[:X[]KȐ@?iY|qkN` f -cЯسqc t8[FFwJC=S -r\FGQ`;w )(-fye$4iIdBy~(Чn"h5Y;bW^e -ה뀙gz*4p;}f0*X^XR{}An@h!34 -endstream -endobj -3649 0 obj -<< /Filter /FlateDecode /Length 1456 >> -stream -1 bQ烲CS\kt u:^'74t 3.%֔>P6:b9~@EL0gkUM h0ڙVX>$%Z)SoiT9S/p-(LjS_/JAmIhfWgw@./8I,cs?MNad"T.ԅhOqV$jلg![UWTK+S7עlLWx a3KMzˢapue1b7/ؖ[)48WX A#&;e7])|H ܊98ӹy\m@d)!:|C)H)8Fnhdh - lJAc/^M^ɏ6KSB8gV)(*(<'4]i|l+a~@5f@ab=])(`Sn%HIC,%Wsl yP?Kv@iNxn.I,jDڈv'/pL\8s*3t<c+Ԓ*: 8+0r` aTԧ>(`.,#LuCq7dOh 6QFpAJe5.흙^uLV$ܑfd7Xco nճb 7^Ӵ:$J^_ -DJ7?!NLNM `:sg"C ||p+D@[ִk†ŮBTwUIT4༁6nxwS n>9|hxV;Y:NWEqxE=YG)k4JgV \Uk.ww2 `iTmm&c'C}Jq| }Go?,<]6laEYQ?tF<%pǩ6bq q4ckY$IVStO8Ee bT0endstream -endobj -3650 0 obj -<< /Filter /FlateDecode /Length 2240 >> -stream -AgŐYJBq\Odn80>Pގ _O=Xnà=cK@t5Xs/6UER檾ewb[PGIH1H.yp"gzhnnEQU*~Ud8>ڷ|D]rNFCHi&Y@B1Yl!GDݓTgRP -ln/K؅ԏ3o4"̻j?`5 J.(,nTZ4XFjE#H -^`5寮Q!} -\FƗp~ToSSe2k=6Q? z{eu^dWFiu6A)%tL޺ˈW3" VgLMYNC2nliz+eiAͫ8_.f8E-]c[˾$ %۶ݛ- Kxe- @P W]muf^ķDFKR ѱ}F= 2(r+[kd ƊD~1ESkݞlTZ,!,U#H}_iA54V.UΒd"9<#58/H?,xEکMJjr裇_@ڦ@M*ё`ɘKuHU*)G5.ٻt++Og׬7_6H:"82 #erĭ׍͘8~,}!PZze%: -Gڻ̨{xnҡL9@ؠ Y0QUן lrf)WC_ GOjFh FM(;?TTV؎%c *(JԼ}l5*K7\%X SS< /ì|-^@ĮB3?\yPwQWVnY,Wd%pBqY'-BtR Y6Q>3ψ7s2o cm?c2\n7wҩLY$ַRHNC{_5*{q>*6F% ?i5r:zW^ p> -stream -D3~@UHrI8y1q&hyǪff~ISe! -~\g& EV05ϊdaΡC?%hroULU_4ЕpDl<1'I!߇x>y!R&ewֆL{$/qk=/ #={J09A#SFm vr[&5x`i>}-[Ȑ'UC.?wXc/~opM'AJp4<7CN.mm/Ug)lJ !?mb*~]Q!U 58d$4rlt>>a\Wk> F=cE?ҳL 9p#AO1,6!DkGU8lkI;LkJfA%@0+5Ǵ.>)™^=lxGMI] ӥK=FXӓm4Y/Y쪧֎Ɂ?.+Pq(;\hQo$ŮrKՀ֜0۫~c_T;ۃFğ OFI19Op@dЬ -GfvnxnȊpj>Lx#Xn׉/:zh0/>YάY6w:$nzԈkx׏3W뫸חD bxP"͏wxlz3 "&vbqek(DFc HX$ R}Ozz ZD-lc ҈5 -&B?|80im'xG%4%(^3H`d 4љF3.I&{72Qt c-Ԡ3x2[kYa COp䎎΃xQz"; 0-oFG`׊~]p}!T vO; j0Jzd^e|SRgfUb1(I?9(?@>Y7#H֞,I{l5~FvzÛLA -oɦF;hX -,mByi&"T|<[x{A>ax 6Yqޟ[lavLJ_K Di!G8|d}@@{$9^;/v[f][JFy!6hR k1= /Ho5+9od`h~_2@VH -Y$ӣaȲ&?zBQ"cru)>$7F h0%"vyW)oS8y7hڀ)| bгZD?5٣B<7tǃ ߏnqV#g}u܎ZH^%ɰ1~w+kQz%sv>3a;d+'(:xή%ʖwԁq#Y.n,p1 94 ]I -CwLy(NR) (Zmݔؕ(Tg%Y PttA6apC@ |Q%@8gB·|2fHݲ'bL2RmMުP=tKR -4~vDq{dW6Uwj4~a5K*"D) rvj|Ȫ&kg!'%!a]q6E44iX\YǙ/X_kqQ7FŹN-Ȗ>gꋐ4&ݼ6%+ m).h ֍ХqJa 3Et$Ðh<{xP~Tj 1=)9S_JA~^D!]~T U ̬pyjN2xzHX:%Y6e. G-З["1BQ0$>=qlreQʼ߃f!8EJ4?B`=Jrqd ŅdHcUR B0y&*08qM?``;$fG -a=<*(a7M!D7VNw$#$}nDA\_ėb/zǨ^h(0۾%#(,h@9ݮx:1 Vb۞> -stream -5KaX+(3wzz*wrzcB׹H97 /nJ~滧Tr+OM<,^bM-yzAeKђٌFǰ_AGJwGa~qxx0 :ox=ݽ^('n[GDܹ'ȫ+[#{'$6J`إ}fV~cʆ2 kw墯@L=caF$| hD|nsŎ'C{`^dFIʷ4Yb9=h op-5 Ҽa&יBӾc0]k<t4/w "6Sn1i d|vi' ,\Z#_8Nr #|1&HY5k#a3dNx9K,dJm"$5YHa}2!9L"r2N`[$x7=I%]}8cVŹPep -v2&MX),|L#1nEVxQ=]$a3~%Y?|pIf3OpmQS -֦ ,'C4LN5Rϭm<"1i,HkpyXnn G_W:DCs| R0c$35: r)ea#E 9$&8^# ڑegъ.k"FFaK[J&#~ʯxL#^فQ~]3\>q۱dsAx#W{?}5+ -YtL&t!E[*G9Mo3)U:@қZ 9K!_'>;oAtOfIW='nٟ-~QbJw[ŐC>qoLD!UlR=/$gYw{τ>ΐ;#B;d(}v̾|լwtH轁Ğ Sx?ܟ`2yDC:iL>KKm7vtDO>g >y7{%hZXφ[!lެXezLz4L#Qp Aώı˂ȍ&SJ<g3:5-{㺷NF-OL"kY6?=}3c;qmJ(ϧt7N+iU<1(6EYVgJFM\LzHrxI,тW3IW{)l/hd\2㖚G }Jw㞔Ȓhh4~V -wښ 9ϻ\2FuI USAډ$oڜN#Q -| 1oi hH`3T+l艬s4:@K J5 8;/i$._^Ѱb#Z'mgDH)nlУ^hdB3eq>~pqƝ-1̰sƆLU)o/b!{e [PQą b1 +íh|ӵpþ,c'0ǭ͖9J+)xGck(FV"iWG jݙkkDI`dV}AS03VH5ZciwsH[5Y!2]V[??"q4!%[q:hwU,{^1k:?UV} AEEz3. I{r[aDn;7^OK(2ŀ@u( 1>-`F4f6Wa -u"p7A8݄XpHV. 㸻9QadsU_/ropEI S4\ Q!+LVQX= GhѦ(hƌ|wd2]+endstream -endobj -3653 0 obj -<< /Filter /FlateDecode /Length 2480 >> -stream ->[0ՖU=eX>/swg-glg[k( -sHO1!$\"}'Ɗ0vMW('Q,p0SLxq=bv6͉ud3'U~/ 5N4AJy+5T^2%);;~tFW1LR{^AH]dMI"} ~ 5b}nt@$JD5(ދl4a5)L1Vfc Xa z鵢]1D v/ >n - *0jIeGZQdt]H$^_1'A!2N_Rl_h%t뫺Zm,v&ld{n`*&/5j],@$5%b$ۑ?!P,"w3J|>? cvlzU9]-i1p)-?!Q`\81D)AƧ%#䮳 fT\բ.26 ˰l~x@ZyTGSV ) -(uc6+dbyKWs`I8H\7 "qpzwSBWN΍Dxv+NBm}/Cy"F:r./{#u#l.bcc52jRPpÕ %wSH9kWekogeOPU! o5*%r׽,]WV3ZqFkzVzXTt^齜ZBfUx<}} Qm̫#_q'_9ϔm.sxp _F qVZr2?l0diy+zP@6ȇ]!)+Ojt~@$VOx.Ρ$漢 X(TOT^&4>V E4iZl 6q L6'g"`Jۭi-z:dR[>M: rƎBD@{^b#C}# gw9Lܗ+H 7{wZq.\cOۢNe'cWAm_'452꙯\},q &wgp-yIɂ_=lwb:̂N[7xU?@P W],wFs LP穦z=x*Ʃ뿭~vIV8v>3VɂKri9Y̜T)_2U;\gshs!l.Jo jҐD_K >\ЍdX'Z܊jS_82 x9N?M*Q "`QgˊsCfk42-dFI(I_| 7,AFJ,m`|,s\{$dvπ瓒-v\ؔk ucG֡߻2.^rR *ϳpJvJ7Oml\z5uG8ZVW~,T2cA?S)XNEoFCuDxVjZHlL EҎ^xI]}X=z|x0FFH&:_ VHAR8g>s,Өu7N"Rhƺ-bDSh򘄣Le}@3d_Bn$av'5zAPݟ~_h;4 }gLeQݏ#:=yDEz2O[>bwl^Rޭw'4p.52:GxqJ*U{> -stream -J`Ѕt݆cԋAbnEA~1rLq5;5/2C;$99Sh Hus.kx]ɁILۺUh[PyTC}N=e.1S*˚i~U k2}'YގZƤ_Pɼ{(v|h+N-N: -y6b]vqoEm kaӮ>@Pk>Nyv94W;I -HWWR%?Dmd)]eC2{VV.xA 0ͅ2;/>XG[j,\Bح#8ݴ\@CSpjL/.7#0Xcp!id~jxI<,lc-a9wo^z~B6FֆFr'Y6Dw@PyYEJߊRd~=!.n[m_wxIK6{^BxsTRjg'~&; 8: ŴBnjPN9j:߮Y~NÞY`?|z 5/ ?u HrK<t69%8ڪ-$ -%7E&-͝  jwnrC7ta,ղ[5F`h,G7pm.a0%b+*f ItKmȘ |uM-s̑Q=153nӮ-ZKg{{2qGE s`d0> ]JA@Y|֯6UyIyLoq2z׵? ߼vm2 #o[006?HrHND3֚9`z\,u=5ok@IANZ_}ދleۜsOaEצG&}҈7J6L - cSeԾbYFQ-w}^3/+AvOB|D'j -WF,Q&iZTO}[[hr0ilb:`/^51Y _ZP,.\qǷJCĒ# Ó+],r[w-fe -ɔ]6Zep -r_f6h:~X˘q6W\ݲ8DKHBfu \ feӓB㛬x"y6@4¨–hj[y"C嬉=8EڝLhAҒ? OEQ,b(Y9(HƝl%f˝3o.3ݍ_e%p9j0\Z e'' 0xǶc QR=MlE `uh*ӊ0 [oGZ9CwsRXN58cZύ#qMaCsUpuJWawM xCtC^cHrȅ8T {@D Yjd>C{eSnq\̘ "IM0aFbRmjjהQx~-4/eN:ǔ(ӌp q^I{.AO3Ppv,+xzSpyf/N} x3Q۫>|.u9xן+ė6/-=_o^ݑn?D߻tNx{Oiy$d|}2@ 8D#J3fBV@tGYB˜x4C/4zW(SDPL$vZ*\\哞Oyj„ˋWҘBP~{{jU&BH}JUz)~b3ܙ7E?uQ^AÅ㫿@-؛fRkhRKU>I!AD4O&A)\4zoO&y$͈endstream -endobj -3655 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -)#Z -G -K sڠ r"})|p@hFiLsҫ9\J`/)(0_6m߂^>7wܕ#zг1v*6sQͶ)TVpP A{+lk@F6oly!fSق=,jt9/:P+FGɳ<Ms)14.62]P"T&nlhe^M>VwJ ]!30Man1k쨿G/N! ;vHkJ'SJʓA*I@Y%fCNruqX:'l:>"/I|K8 -7 @0; hԬu@K|w>Z|gp0ˎ=w`QF^ . 6j -I6}YVJ$$X'xy,pqel[;v/ @Qg>4Kɛn{Y/=AUUQL?_pS3%cuvPkzVƶ-Nz%g-Yb̭5NA};2e}hPkIV"WC oev+``6FG^ٚ-W$ykg97Uh+ 7z;ٴ!@ 0&?$B!ͦz!j!0,h?\}dR HtEoUqB1۔xVXq}dk]/+57eZ5;*PBgq$N{Jmendstream -endobj -3656 0 obj -<< /Filter /FlateDecode /Length 2752 >> -stream -F? - G)W%)ݳC8fvho4ltȃZ_Ć\b#N' }"&DŢrjWVnDo؛LjvLE"5-Dg^=H .ЈXkibSR95Out-(EQtE)bkdžV:~߭5Lf%{ou0QuנѭŸ/M~ ؊uLkf߈m.dVX;Ilp!l3U*hx X {u: >d@7skM@Q9?)nOlT ʖԹ|.=;'tt Hq@\;Ԏw[!ιy$Et5mҭ`їO.kh;ߨ 9)5c ;1}v-pk쫞}<:Ȗ)NR ~3pJ=]E fڬ&Q뢧$t3\.n` qE,1oWjۖ_^ՈiY?|YƓ"- k2Y!;ƅ'%hwoFZdVpp}Qg%ҫ+k\3YB3W It/~wM4$EU -0[;84B2L_T HQq|I<15 ri9k^-K~ۻ:"묨~!>'ngo\ʊRHj}\7hR`|,zƛtem|?"ܖ_~#H_PIh]B - CtEg)xP(V[iG`38&1&C|MKV,^y"jsEMt۞<$w/ʆkv~:w3^9OqD='3M =?&_&B3"KA~Ҋǜ2ϖ}d^V  ~}Bl۩ -v++{U!FwFGKڦ^סel;nɮ6J@RleVotU~A^^0 lҙHa_u'$M(bbtSeT@ҵ&C!Dݝ88%lbpE*Kf#b^]ޞk/ _-xm([1I)~P'|.jFrߔ$Yfu*]K`# a" QՉ d-Oߥ|)CɔpvgH@(HYfWK{Y O5сz0DpqVKm7bɵ\"GTI1^"9G'`# -,O[3.[$@CTg2Un;e/oi]-6:fng"h@B2A_r!(n > RZ"A#8,AM괔!*MayŊגI{`U`Җj@Oi[~l~ȊWsiݩOݰީN4O-÷ZG0.K}xâN|3{lP(@*QR>qSZĴ!㽌8&xjSQ17=@nq -( -|5Z"\ ek͓[-ʺCxOͬ9 *XDv3#@3ئӓF!@!0ԙԑЇ*'X̫DE - i+#uϚ: 3U8v<|N`>7Z'ZD7:[$"! {~xpT0'jAugx5^|Ȅyvj~R'5% Lڐ -T=&Ԧ>UC7t):JG; y}| -׶)/' ;1{.&"uYxU2Ő fGxm.kx sRW+S4C'[Is#$mn墡5ܜ~nSCzY#|`er!R5u+nNt߷';tHZsxyr{e76.:I0r\ h> -stream -;ի_ی[~M;N?4aGTG^ʧ-Iyg@CB\B9I Wʭ[7=LFi\G{ՐvVrRl:GgdQ$#nbI.wqSa#X2;DHw&x9^gn$$y<ҖUZ7a>li.%F{>أg_;AI9AR:_?OfKfs KyZ.4=`:~ r*~NOd&\ynpi(!*ۦ¯{^Y;#uإo?p' -o{MNtCqAbC ?4?e٬yOK!>!b+Wq_<;4 +1" S`?ƅQGGzQ4,ӻ 9Bz:u9/P{Q@r4s~ef(Rۼh|'ﵤbx&.&H2C]t,$;p(oYNhixDE/OL ,O}{L"&=w I^ -XϷSPٶ9 񓲯(SOC;Y:e*lCjcv!NzW%bk#:+xxk`}KȇpL1;9fNLɦh+R/V5W)2}mӛJ?; $|:o~Bv (9z?=~5(v$,=FH[ (Q%ш3vs\ەIRjr݊@y}J}/쾍 l,<]63[q{1 - ƌft~ }^dhr| Gb?B^K C#ϛDzAFCgָĻRjw63h*t%2o C+9¼1X2lI\ Y5o0b!r{]wx) TTendstream -endobj -3658 0 obj -<< /Filter /FlateDecode /Length 2704 >> -stream -ȳu QRYKesf2j ES[),&gWF<$u^Oc\!P@*D K^5tZ8#Y)|']̾p DHQ(Ѥ4f?P_$[ K\Zsh3fPTQT7d 5 TY i8c?+Fv]r)}Jvg)u-3|Oxo#Rߟv9Aڶ= 7R(/Raslf4IݰI ~w0K'*ZsD%.ᄯiD6(?\z/7,b~ ŨPaIG[ Y~oe7rgD$n&--D9hf/R'l?Hko`~`;%^ڇ3.<激xx55v1;1)$}.ջgБ\_sijħWt{V -l۱I|u-Q3E'ytFKߪ˧-vz ԫ2d%mٹDQg c{_i]aU9>\vWz nfE/J1"İD61m,h"zF> sN| Sc֗A" Tmjmh -(d2 b' ]1^!h1kf#ng5OHߡ9ĬBd՚3c$Oi`W|cpWbZI?(ՆhY:-GN_35IܝJ=ŧ?x6ڮygza.Ao2@窺 [zncvt@=9ʠQ(tgz%t?R絪_OVGNmnf4s>9ML!?|}ـה1}5׫/;eSRŸ NS0X6.ijKϵ"TC{C -2ZN5C:g-júKSk}<kej8{dKC/u9tlg=*}oM{ F'ļMo>‚5D"ZyJ7lC uUp:nG^R䷦2yAaUF˜~??]d%7@P˚R+ @:8+}E~v#3&;.M?8>$dϚda+7HaBuP|$!%uP?D^B h`>44ڕ,_5%5~ x: BRxbސRzǁ&`pWm*~r']T[~ hڷ8uxl++L[<MAI"AFzc)ɳP-|ų"uG:2LfٚV*Y8O.J4b]ŝ*BckEbrv|D;w]m{yax -%Z~A?v8kZhnC.U;~gk8"νg¤cRܣs9x*l|5ne-Α bpZ#`0rN? -CZC'#w43|/@ֶE;$@x* p؉&r\J~ݠ -\g :FیWm0K7DfO- _KDhjD>gr`Iś Wlt2Yx.!^‚;AVQ - jsE$[9íJEX*B~H@^| -Јayt7ؘȿbn(̉1<4k+ڦM bIf?L܄iqGz} -ŭc_Ί0zȵ֏)+@endstream -endobj -3659 0 obj -<< /Filter /FlateDecode /Length 2784 >> -stream -e:,sϩJs8/A7 4%G*\[u|PqT/47E󜑃AstllZ?DV{'F边!)94LLc>o|gAI k؇)^EBHaВ?!j9u ZB*T".Zi7 qA -SφHf~jH{*dP5yE(>xi @=tZV"X}_;~פ·|A&,N!#1iՇ?Z &4!ʆzz| A&;l0ahU0(7tYtvԃ/E/M|\TDuRYɫȲ 8bWZʠ,6.=H3P kI[479G%8*ށCF'7M139~݄QYAl 42t%adm `[m5_$8^4W8PL{GvPQD v2Gي~RCC.CkR+ߩ]Dܻ}Q >'Jq81 -S/#k d!&c૖P뤶W5`+boy'E(mI -S)uyagDʹ[t`dJVl)NGS }SiOx܍tUxGCa+]Ѿsg|PzwGeuVbx Ⱦ&jwk4w+r`Xl]~ -&\G* ˾f_]}+<Ss/wĉV}iPI!祺CǠ* xCŌq?Ԭ,Mh&;iN/=*BnkKo\jo7p8q [ q}B eFf<2RvulP!n'hG-rpڐ>( @^&p*aXO54}tkypE!XB );jE~oRn8[gTII NRȅM xh&Um^= lT!_DoQA0CB=0F;' MJX2%706$ԕTqhE=50@IB?nT^ 1xV5̭$S/ϾC2`"8qc$;ď cLY6oh(u⤝FaiɣYȬ->㓌*NyO7=U*$&ë]Xkݪ>F$`6og9|5 0R +2kl}zRHUZ͖6 tذomvdG?Jif$ot|4+#YCK[kV~v,jk6Ky>u+liҙz<b2XV0MYYA}4KtX'SJ#ԗ6FFΡ|or n2RfeL{ Z:od ,~Xz\̩$Mܐ l/nRb ¦~ |=f 8Chֳxҕ.$ 4svvs;6?N*DM:71ϲlN!>TnZKZendstream -endobj -3660 0 obj -<< /Filter /FlateDecode /Length 1168 >> -stream -5 \NUF%yC3į/ GDy0f -*rxn}n^JrGy?X-AAA}3Wma`ֲK`‹p$Umg^I AP*aHUd@{q0UZ%2Wi)[l1Cq]mtu_mraQ+ksCDk(^1nfSbÅMWb*Ʊʀ -ĢK_TY5=J_8|&GhJQuR/?ԏ ܩ3W("!_ԡܑI@ D@f+OVI1#яeM8~OdWrgb=RH=kJS$I`AJFg)Quv- uJIVBfX ךd̀Ѫ;2u>QnDeQrs1?,O&D@d Ș}݂B&׺r\'bۀj2jrdZ]<,Ǫ0<gfza,q]ABȣQISYUszC4`wF[>`u)u3Y!VWA7$JRJ&tYBdCdT]\OA}rgv(\:r;|\ żOk Hh -0W4 *-/A7|qx1 "j\L㏷ #O;0s>6V34dJ-6_طXa_bb9dX"/ԞX҅ W\Me|8עW(|ZEbˤ:+ -B(|Vq!ߗ*5L.MgFDSCsU |eH -*v2,)PCijhUf3F놐:Y{V%zqp\f4&݀ M|' o_{37ݙ3pϑb6=uv6mYn)Ma~1(=0. nDxL?Ln}}oke2@[եRRQNX}u}Q ި l'%2oYq)Oendstream -endobj -3661 0 obj -<< /Filter /FlateDecode /Length 2592 >> -stream -b"4='83w9F 8}^ {-czzaY lhoz'H))∶vY!d+*kj*@:F '} ^ Zʳ3-W2a)!VPYw9`Ĉ&WBm pa^y%&kt+##A4DT"f;z~ep:M$Sbu@py^%|2\rNzBN-%k`YXD`:0a9P.?a* -ͥɕ#{sK:SL!&)Ūq -hZdM'K8 rh`Db%jԴޝ`4 Bd퓎]R(^maziuvQ?xZA畣O\봯\-(*_ G\RL 3lT (vezWt%yϺE]D4WS,B] ?#/|{RBͰcW~a S$ @* 㶔VKYLd2,=O5&1ݯ<"c6*zOmّ=<5` RNz=C: gfF.\{FS\\| >hRϯ̬z̔0+uw>'7KG~7 [v餂)[+OP=p4CV +1 iKIT,9?570_'1;Tv|Wo"$ڎV `a\m%e)۳Vi^V\v~U}٥Ain5fbcmZg TPDPa9D -F6=ԕ/k3?Nj[QÙSsvZ_VCxEvS&FoTT\gRHslmN(|P)N´E>i:76nNDyZ/'8_ іn,PlHEqā -DzEAPvNī.,ojckjCr4zb 7Ӳy:!!|O#etO6c#/)'J:X+o?5Do~ 2&lv'iQvd(4/ Ez,\B?R,Ej'(JPBQ̢3s l*0Mb?GB=5q7v|ɼU08bxGtJ$WN27I$u%#wƧܾMfEDom:|5|s/nv!jBx27HߩFQes?N -W,ݸk5?lROrtƑVs7 tATZ?!K.7qWe/Q n,<8f ,f6bhb!&n^#-L'G8|?m+cqg͜7lk<%"=\&be<+T01lc17 W|mt;R -{1a|G > -stream -Yy,`* Ʈ̧nKBDvyԑ7adg M_jAs:e`6FMdc ;9>g>b[-q3e/%03^3[%Ԓ0bW E!"ǛOq_Ɵ[7{f^ -߮.۳:p1Y3V&wWsY? "lf䍥,  $|˰͘Az2#:(/y& pdZc` |ˋ+ š=05E,Fwi-ƻfg`zuf@)꧇.D6qYS)'׏ H1Өږn< ߺz!̞X׀rfd.Ag ؟/p ۽6(IYrQ Z96ΐ%92=f;CI`U'3}\CHt_P^K =N_XT$<\'Wgnv M)|(} 0ZYY-:%z͟`EaT؅yhr:S"X]IZ%=a^xJY(hi[5@eM#aiGkCգBA4{eisT8<Õ`  -j$SXГ(Uq-:)vk!'1ӜoocdkJ@CO <3c{{7G6iכ `)/t*.F bm -7 cU^zbW`yШS9AROardo#! -^G`48И!'iu|b:85Eohfd+gt*ɬ[`rtC*X.aV)@sQSNS,[X/#yU2m@֣\ͪMGR>=-J-iU](5Ncg㟸δh6 cbYA -GMM#v [J=EO3 > -stream -̶9 abh"زeYx/"&y^Ѵ_J-8$ҠZzo)m?@`Pv@p9Tp!(bJU<s֛6r\=|A ^3-' -u~CR-c0bp/mɡw  -E0Zrr"p(sF59W"Qh -p G:`D밀p3/gI7eR -P,,Kymu#R -x~ŃH!hVSJAz6'݊r;`fU{fJ'_-7w5˕ hYĂ_Y>{V}z6U͇ |LwtxW1Eꀪ!B}U3")߄%Zyr=<ThjMAc=c:"vR8q R(.P_f!dE9 9^ۛSR5/BEj Pǫw (_26n}ߌbcN]XXnUNrǝyF&ߛꏋ+-8oA[pjm_UyYVz[܋dPGP.)8z'Sg{GUWXlQ* qX[ÁLo;Hy%ZO((ΐzAӊF8A\AUi낃Tc7HFCM -&dN@Yڧw" -g{eq9w?"sFrE] >fSWUM!=sȝhG4lBtqn̟cFtlZ~w\j -lEkkW0Ce {nH{-J|Aq:rDȧKĴqÓ=C!8s nDIep-/EHjgC $E ޵cܕ,:g DIꙭ܉p$pՓT5]ZbIshDYO݉>"Idy!@G53 }s̄K( nARCB&KC -Ж'ǘgGDn\SQNP>KZD1Z|÷C -:# -Rm=#Q{ִ$l=&L%g_c - )bPQ>cwZfTbhif]+ "梓-톪sGM#[b;f9G+QdTey 7n *wj$+ ̳03+oWx6^><$}5S6f+iB6^Hvў0*m-Ntꪦ]O.G$Ol{ذmk=$tvلq\V?`8c^f8mKBD4rՌ'5KP` ҈X f |ʖm5sAU i | e֗ggjP',|oI K|fxɑ/,at=&% endstream -endobj -3664 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -[5Wjߧa@ښӜ?GwgmO!w3UN -owXsM]Ŀ\6c[S>@OvӮuzMagT\y j ?tm,g9pZa Oz6 -U lel@I.xXTMgDs}dw>]/߈'J|a C%+nnyaѱ=K8`Q݁ŔCm.TRflPs #0R} BNG#"{Oa ,-b%sn _*h_S q8U QB5~HXI{iqw) r<܅; Mz^z0JgJ"] ?-RӶxӥ, ?|Q'L^xcuS,؃n<ijΝ7d؛ƺLގHreKwvx 4"n@QX>X s -EP]s-M\oe(N7ϓ$n~`]+ÂUl`^h؈r-4t4nQsB @d䅪dμ%Ul!pbʑ`.[T[iT Lj|;[hO|fGnr2lD[ -h,,tB&O 꺖P2mJG/9ZR%f)}`4"QW-'p̏~3cg1@Sn66 mpfŒC6J#y3wWKO+r--}ɭs sTw~D@縎a&kءn{=ep8:CW3t |J&5(TUg^~\:? dIGQUiw08*O$Nb -$۔[l=M0T)ڙ\`7A L@w7w: բ^2N.`?3 {&SvEe,MMXR`,rӓpPq(&Q"Jږ 6ɈAVfnszf9CuLn3y@1C*st @()%VĢݑ=uLSuC&b-w2ѽ -i6qR/z m?endstream -endobj -3665 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -.oE{CTd+ :;LiDey;kGG;  &68@7aD"B|uH=1"̛4Q)8vƘVUtnoHs}|#Ye3˭Π ͷfT2i]M8>`fG0]3|F-1 lUZW ̣f6NzD D0މۣuC-I'C;h6::J͌ݼ;isi׌tt솆ןPh0]K:]ɊցAcjAJ+638xY5^( - H\Y7Ρ[_qh4ɕ0<ŶĮuힽǘV,J6s) -kyzn. (\yy:/tY9 ڙ":qjX{fQ٭ -$L0*A=b0T-j^/( YȊh1&lY=-F| -p:zF2J(]KN·P$OH(+-90R -7nvAG5àW-]> -stream -eiyR';Fk^jɗ;"QSJR -V~p3K}RC F1tnn1ҪOQfZ|@DaNM$G8Lw/~ -ּ]9 R]§5,v쾹 }&{)N$,!YΓMSwu?BoMG37P~#F5/nl…`,)*WU4}vvtxKoESW>hSNԑP?<^:e&Ѵ[ S1ʳO_Ot161'xȇnU]v4m]V.,'/l5vҮ͝c˒qG #L56,EWJN("ɿEte ^Kh.0[JX-Qf:0s*/%(y5y  jaR3(Oe F3͏w}hv#{CXc@NSm <(噋8B֧ǘ0Ⱥb[m,C/t<դY$V.N{#J-LYVȮ>n\AiL8kQέ`WTqg>O|ui'-#`dZʡBN¸Z6We/b,_t?:PLGݩ9E@BKa] d;+93(݂ l@(ieb2B{cJZďdu*-@C%ql*[z`dD װSOF{JgE, 7ڵw30|F s{pPs_I$R"n n}IHxoj ]Vshڹn <#/$Lv51m,jV1L[ְ7@^?M܈ -w{–Lm'h-N_2sQqA8F؀am43*f| sm{QfjJ>T{΢ ob]҆# h -h]RaDŽT^0̻ajLlپp^?8,lͥ\/aFٲ^jie־kI \/!B۴E(`qYPH&mpDXK{b <,ZL;׊>6,#ǯM@ nzv?h*k۪*a~N RLV乜:rm,}n="~_bVVU5( IC(sI31(k2BE3$Uk?,ib&M1? lïd7߹Si.pDvҖDZ 헔@,uUJͽˊR*d& ~X3nݛ6v Wr/I'vuǂ& (Q'I<>墓M!XsFL31jW3抂s4jELֱ? rZ!Kػ!lj%f\#84_J.w"B|mhVfQ[!A p8sXv8CÅ4a1ubEjR5Ѱ]7*"|(w[C[.[%r^Օ+ֿg;6K/K%{}bH 4V:V7ӥܺ]%?a8Lendstream -endobj -3667 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -xz`. ^j(’6ojrsҡ,p3f^&6zMZ}, aݝ P$z~P[J#6"{bW i>\ޢ?Z;JHŅzu2w]C ;UR5o`1zȯ~nH&ր(v>^.^ #?A[ɛ|1O<=4kOI-:p8[qbeXПMQJ -{"1-1]9eGu¥'-n -dpMqŵvmQ;ԏy3d~9V%2fvۛ+C̖"4F_CoIu ->a Iߏ@,XrzEsTt'mSTarc?k@ cRG֗;.D"D`'^ͥ/ϦߟvVT;[7:è+c-i -oV_6~#K䖯[W# a,8 ϰFx*%)@.wx*TH;&L-Ys9uظ5Cwݏ -ŸD7t&8rs5N[ .u]5&д*7|MErJ5ed&pAUٓR7`r@_iw L'pY2f(R_K:B:bL -0vCm:\TI5mL_.0|JI]1^kD -m䐷!a>S4Z|chR8ue1ϻh)K3MQy[<,`6*%ܘBF2|sBk:/dT -W?~! N*:ʰм\5P=#l4r˿firrxۿ]Z-cSw+qDv;°C’*ja3eD3vۖBhф޲h~ .ltp4za''\cIZ*vpʃIBYvY?z"մC#ˆ؆<ž^ׂw:8A3)2#'۳XHAG dRǨ1& [bмlz -Sn- -2 -cjp zv!a*z,xx;&hF Un_IY2|9Н7_E - -_K5W~)q8Ӕuή0O?_qmcoSsӎ`NUr$䙔mQjH#)۪uqypo77,w\<yT!K~ -RNen W71֙x%wLfć'\aJ=#q@n >oR̬6˽K鐁!{2{wf%_<<lܱemcLNZ&\0=U%AG vbUK^C>;^13|ֻ*s!ol1jtcd˃[g9 -Az,L:Rqxt]W)w1 6cq257 ojAI-#yw%}:E9 -RJ4,hhkVp`fF@ʌ5Y=v BRf`:W>lJi,Q $GB7逾{گs0>o #]]s84~N|9p+qF\D Ceъ/`vcA \entR8p)5 ou*_H?'%0!,Pendstream -endobj -3668 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -b7yw - & Mc%œSC?>:]^Ȳܦg,'ooQu4rj0֬h?M~&ài ]r Q+KXh@Chn#q*[y"Aʹۙ<=OXkn!@^ty{&Ch3* >7]e -qhVxq&xl s1=d 2/GYu2txJw.R ;Y?@S` *GSfw{8`QO`J=}x|=ip=eBvN_nE.{P Zŭ0fY 4VU(kwk[g5`atUG2䣘*O 1R@7"՝мcǒP $F"=`Wg>\7g|+Uݭ.Q~܈z:`&0Aks{^֐_G, M]U"H _ eK¬Hxnm|`m%K;bH}FN15ڻ_ްάf^ʡDdb/zC"nDrAIrE/$.ֵ/HLaFT.=7FeX+8㾭d@MI{ -Ԍ'[*l|3m͵WI,ً;<ǔ$r gIgvCӉyDBb7Ogp$X0z.P_pA"8K}![DMTI DY4{8ā,Ly@eDk$Ufr$/_?%ؖM2GJȢ8b{|F˥Tҿ(7$s 963CO' ]F/Dl랖' -DnOe4hM}F3ӯԁz>Y%wZ^7ɽp8Mn])ZϞ5Ts8_HVCXl#1ۚFz˚S`vy8ݏ@cRsY.z2 -ޗ1N0=FM(E5~Y]ѽPq~ >37 *.WA)0мLePd[BeN%مd#8F+@ί9zGv _^6g [НႢ].5*6HXDQZ'(.Xͅ5awSBrwʮmw1Q!`5*X8!vAl%Wj!׊&"̘DnfM> -stream -ċa pi3W+I"dHK6m&N!S^4cW ^=1Z q~. - ~jqIyJFF:'L1Ww8t #}C?b|>ěׯ] XPg+qd0\q 4c ߜ+{fGӉT!yM/8 ͧPWjj#u3?J掟'>,qi~nv׹ |צÊ&F(!d4v90PTJ6-Bzhc ȳKuexk~pķ~M>YeEEjQٛhx3wRu}zEtn[CaN*h@}P:}:pS!frABR:_rRD,p TsC*1„RI,ȯPkϴ%~U e2iP~$Osy8C{ -6u1a"W|4!ם|w󑞺E^mn1d;#JhE2,S'^Z ;Ođ6H5`I=VË1μT4"Zn{kEY݆R7ԟ=}!)sR"7Af[vN(#ujkN>+R+WNQe>ت9dw V=IINzm Cgcv jE!ʡNAfTZgvXi}te4p[qwkޏJ*0&ogM=6ʼcFZ$TN:Ҫ2 u=ʀn\`>B i -nMniOvL%_'mFВKӴ& 7gng"M/0Ahcv)8C^+0c\J4-3״5cdlURYթG&T-,LdB w=xIۿ3@m0mӣæn1U}!~e(-$F/D^D>9D9e|ˊk5k3ohkyGwDď Wq(Loz1qt;ڏV?`өgV_5-TIF]h !fYQz5ĐcY/^Ka -F[b&{܎1ƝسE $ij6|Q1OIj ~cC`8Bgv$ST~Kj(XmktIa֣!.hvSZ tU99GFb0NcJY6ˤWWkZ?a[~UU5Ǯ08XXC)aB naEB,zFH"w{i -q:C/fY'ޜy<^{nЮ7a + ƾmls0)ݶJ*n^H RlR;.#ش`h1"ȓ1rQHkGD TybqkmW7Rڙ~qS^:'@)Na$%pbguJB7*LSܘڀүQ4O[Z2F;Omp-``N#]QKnZҳ?9蜻[Ave>+' ]C)֏q&F/v Z=ҝ*fJ%3~ll/%f6ZTv Y9oD~dϰwrYP-Qt+4]XvcKD0e_j b5/lLV |R%z^ڷ  ?SA-Ygfl]>~qAU 1"_?IaiЫzLL'^ k@ [yhA U]65/Nip%`Uk %*$ܣ=ERkAND 4RbzVVƟ?DKIwauĝeVG1ASt yG!#d437ֱ{s} -.MR.ˎUO&j=E2endstream -endobj -3670 0 obj -<< /Filter /FlateDecode /Length 2416 >> -stream -qZS6sʁI&Bj;Q"N"7ѭԿDkqZ[=v}( O@VtC8Ig|=oxk;o`ދH5Kvr :2 RղD{f+zoڳXJp!V7b}%#$*lAPز/L"RģQxBNFZ_'^,B̬}lQAS6Pllj1r@R6Q}=yLvz7ɁƊC_x\4}W:V,Cpf7 P>Ԥ^$Ǹ (~e 36"fX:ŧX&G.і:0KYN!dZq -sߞ7W"e6B72ǀKD7Dx`exNJ?ETRQEA60ȅʮ*׳Y*2X:/̷גIY&`#-) -'ScLmrlI@.goK#TUm~ulКrB0WHи:P<ٗ^.<[6]+RֈQB:k J -9^Y{d/U܀r 0WL v6z]׻ V+Ӷ1PHZ:?)h^ W*aKB.'Z[eu?EM -3g $RVvvZZ7tJoۗ2&}BD#x%rO0 RKUD00b+-)xrxPNϥ }o`cA KU5?칊=NHHɶiq}E=KK7KՇ ce8(uaY񡫜w)SW5 -֖Z)S~e[Z0oJ$\ +ϓr=N ~>x:7zڋP0bnל==v!&bQ%5HzvM,-[cwRZ[>h'~!)1<*Z[Twz46$a(',pDzq -vJָGlv)~BȲr+z#5'$Fz2ZJ&3:PI'VdKȝQuH#OBCي3Nx^*Ha1umyVll pgʑ/]i*Y"lk/%5Ԗ78 Q8L[iLaE_#|~. 1U<(],|t`:\%34>U ǞO2uSo9KO O9>wV]ZK b\5md)݉RD|Yj$A= \zC"SfH5˻H|w2{WF3^f^h݇ B{&5HB}uezǤ?i[Q mެ.#?w@oỉYR=]euRJؚh|ؼj_ߞVBRzNr+'Dd]6 W1dh&gd -`˼ccY_51@zܿ[@sO(̵A1(l>ZxqƲ/ 1j+q>M6endstream -endobj -3671 0 obj -<< /Filter /FlateDecode /Length 864 >> -stream -ZNݿ9tb2aSۣ`b膇Z\^hh uLGa{SjF!'1S0潨 gI_(di߻E5sPPܝrVdB_a{7`krM&s !(kvтɍ$Z:fyG##~M]'@a)Y5 CTc M_B MoRÜA;y_䡵Ƹ7݃yq9 -dFAq7Kl.wFIƜ}eë.DejΣLUPέُuL7%C J:K8Idgz'Ю?]?P~lj3nӬ>-`_qQЎodtV,s¦֜Qg *TF]VvFկj-N+Լ&ׅW%ue .s)g}l 8Z-_Eh1*#vÕ+ߏ[/d cQEMendstream -endobj -3672 0 obj -<< /Filter /FlateDecode /Length 2128 >> -stream ->Tw#eg4CXRڪiF])?կ=kS֏X3k]ƪ "n&iYU{onrLIWMj,=L .Vh̉p.t5c*e^ZRJAK9a.W\ >%g#<d$njߐ(h}֥!F)wk@*۸9c2sGBrN=`!Qx3P_{^С/`K#80ʾ^2}ӎ^ZگdI+\d\$¯Śd!G/ƊRdVSȒ蠿"9Y𓚤c,[7UUC8u& - -xn ;4VGe{Ah`-F) \r>`*(9=Ƕo×WBv - ⠄.\ZE3BBT|&y)(.vj"J2sKKo>Rŵ '$q_&*.A HũaMuĮ6O8¹D |[+fEC+dߟrQN5`w:dH∞U5?݇ o=_s<B!ʶQM3"5(|%xTM -Ӏd~ v?P7]UMg+Dh頎hYXvueb6GvᵂU'^Awa}**T= yYF9Zi\IABu~::PE5#AǎM8u1ΆuɱPdk0`pn$ |RTKMM~퇵vMAmaz ^Ry f"'N6J67°!B)D~ -7(E=8`֩{"gj2uM~_{jHp) ɶ>!u9L^*839g%3KAf(Wb HXlGgKRT^XGqoDwXu 2:Qg_Gd~q^Bx6w;>(|0Z_\~!IӢs6ȕ(yxglibRqM6'0vrI4ebcҭ|.@C~r\ȷ)à/#2ֹL.`t]C4Iæct#+ -\8̐# j$,ŒFVG)d~60 ;/5f%{(`Tw~)h n)",#7>:`= <(߿p^f|YZ$1P?w1V4m/*_2=z-w+}bO0t7ahN3;kM&B r=sň5 &Y^̖F ZsC`?HO^xNJKKF=@=J+~֞@Bs!:b>)̚⏈AYA|'&(A IB4șk cWw1I0{!>Y!as>`aZyTohV*fʣJ8}FCG;X5OƴTop,mX~qn=NW ~ޕ 4+ 43y%ixKOZ(6eX/|K[&endstream -endobj -3673 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -ܼJ˜!(t^̌ɖ=AE9[:b;Xt3d+zQ҇E< aېH/Pɡґu1/>Ylk7h#H'CXQCGtS -_u5[g' u։'$mtAN FgoGwMX5pc/ Gh` ~{ "umB GKڕ@hET|A?wyޱ0(2Tm4\jS‚yPQ`xa%=kVbݦVw6jBYes⠫x串j|OEҲ'FNjO.pU-?Oq,lL|evSo+E6CU!ӟcdW-Wkcܣ fYO d{y@VjsfFg N -2Id -wąec-O5V@E?sN\|ŋgΒf; |4ەP)ѻVn##K25k#s/v'HnSX2_L JG_|V)>[#.a,mRo^Y#!T -a`L,p9fcqȉC7mr`_|s矑Eُ+@< X(23{x互vrg\[u :3^$m* !A@MjFK|q"X}a?]Iwlaj3^B k/YO\e#yL~{ܱ/-~LAg3}@ - -jփYl -G*eBu'vLc7 " -^_C;(q8W_W,DӐTgާI*ԇ~%-Vѹ"X.A} {dNNX'!#W|sޒ/hK}+vM71}HQ(fǧBzZgyLw~=]=I2P#FwHxfYȤr> R$-j/:Gܥ"%6QTQBtwwVyPܧ(%¸QB A~1uNV ;;?L^ԙf]t~%:nv!BJ)`Nendstream -endobj -3674 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -jc\I29X"V[l `0C=Y/Vw?&*ذj{K9dLPpy 8h?f ji?~Sʴe{/QsMmzό1TKƦn 1\K\>s#BC1P{4 "*6O?iqg:8]2ynt$4%ּ"? 5BN)ps.&LcB)҅ y@OZyixW赵|v0}L`X 0Y`PpX6Ⱪ1_޶B{> ~8c)LɁ?)/D6h,!߉/ax#eRIWS2(N\M˯>ozvP{,z8`sjٰ%a"`qoڬ-m楯6ZDS!J';nd`"FϚ' 9ZA&*lb=|\?GPpxlC7CST>>Hطf6#а$$P>w&v4y1Q0 -vXHP6a<,'H< ja; ĶՊH2X*aA-Sgg]\YH.zt4/qPx;Ź )r!aJ^I2 -)'9P3I<&#Y[WqPNCNLJRV^@yz~*`n= |+:ٳchfG~˪VA)8rO z۳ -Gm "P(0mWڵX'x ZW~B&ӹ8̘$XVեڽUޘw Pka!Ӎ[B{nq`< 8N^Sxܣ(>΄W+zؘRXɲ)m&u=io2ӓ!m+BD8aTU6;b;\x -,KWꛟAX6?Bq1͇f1gARC^lΞvG#I]y\iΔqh3tOTP& d!_k wwcHn>SD"~KlgPU}e&urVT<(S\y%q/u,MN [fUD4&\'[/tf^J;SY)e!t*.ݢdL1XIINBV8;W˼ -B~LPa)' -8ٰRc%2y~lMd{IE rJɢ-h_o+ 3G012{KiU -B'lGt|u2-7)z-COXbPuJ 2, 2(wp[}LfeAH~"Akendstream -endobj -3675 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -{od /2YO Kr]D"' bHxbQMXHڳ'dኀGpNX\+-bmz8#<.IUY楢YEĤ&v`/Z^k ^4-+H488!p|b ->zɻ S<}&,gbO(q/?պje}?";'@r *˔K=ebmJRfN##^-3RoE`j//&+4 +ɧ<"PV:N^a-͙eζ~ݗzKm Ɋxl(o[xn&z[dOON[AF,us޳~FzG5 U ?з{:Ђ 0'{OLwQQ.?͑ }:)mr;D8ː|AV]S RRsLKQ rk.1i8^ӥ0hv$8wVRr\M%j ;;1zZʶ3R?,$%V!|9@4T76LjfXXZNQu‡uQE-&&15?'×*]r4 -z TgfzN@< E7>imO3z!s=`b=t O.(Z? /+0:Q_w:4OA+3QG8DT -`1X3$O[gd1 -6Tޅ}TE'5KM %)˟kуX|WY`(kIwǼn_{PWen ,l_JM#g -I/;fn)}Y.WEv{վs})Gp _J0)VMp0;08D|> 5wK>@v7 R 㩃be*H4y!UsP N# 0cH ? 1ccD];o՝:5jlGK -2[dC0|mtQ]cFG{hJiՆ :ȃ!OPpH:b0f_rZb?M_'FBnz]a@U|0TU J>')RɿkSA옝|"^eDlkm?U(7D>DJ KVCe =틶Ays9BsS i)˦:u4wӃŭ1w:8~fYx2~3h-['L`)ԨZr-lY%D!FTrbsj]#~ 5 uCՂǨ[A 3dxRunSrTT%.O@Jb"}h"r'$z@7;U:58øtS28׫~BuZ7=[sc2*.5"B6%dYG+(fs$:-zcݧvh`Pv#ެ*:וCVgJ-M -+ۭ򭳥IYJjet$ (ɔϯW%)\iXS;Dܛ~ă'Bz&oL\^(Hs3;Mǭuޙ7h0N{լendstream -endobj -3676 0 obj -<< /Filter /FlateDecode /Length 352 >> -stream -cmٞ;OL5KHpK3$Y1:?#`Txv[, TR"'ܦQnzDKPGU:_^黸|VWl"c2Eܿ$OtV|W2W\_dPy'c9%z,1vAb٬@3[Y۶A܅bpnk˦RZ7zY_ \m!"FSIN݋ǔ6Km;ԯҲci3I/TorS3CL̂^%Cd.o5MoL@ O6ѻǴV45eendstream -endobj -3677 0 obj -<< /Filter /FlateDecode /Length 2432 >> -stream -@ ߗhyz]YlD2^\65*?#}^5p" LV ÿ?^R့%@@axwE*Hn)q[oo N{w#YHު 8ɩLs$܅{0/RHOGqARAq(~7~}0/ -OA5jhC]R>T`@EFX t6דcicEmy:AՙZFܺ;ϪpP B<J>_ހz`Pc!Ө]i,p,6wISpyVc>u}_!~&*H2,NkyHnqosĚH4JiHivDqۭ`?;6HLDzT | PʰT֎D!OG&V$Ñ79(&q2|&_IШ8]5$"/ABZlvG7 ko g i/fVp џ6Q> -)-) lAm&D QpAW{8Gdxyy9_>jU.eU:⨾PA+=_( |V璫)qh0:8$ [5J_2[.~T6u+he;cq(&&R?[gjBѩiOE+, U~M =\d'Jt2AG $~!@`a,y֒]XFny?w13WTL -ǝW;VK_rC)?QrY^)%Dh0SS^f@.'q#?axaWވ(%Lc( r@b[ W3j'!B{i;aC~4Gaz -<Ԣ}swqb;*6`Z24 y^ -c#?qo=|S3u!8$ &2|i`_Ntj ew> -stream -C(ӚMRԄw NFeEEJ}P  5G)*~8;\SlAX?q!0frU~b>fxkEw%ڞ 2Z݄Cـ Oh#~J;" EP#<9Gr>0v:&<&;wbOFOϕeFF4sƛ[Ux3PQiRӶC-B9ǞKt Q?Wla+PG [h[,4>nA[u.gzA;p+"t[ұגx=d2{p׺O94r4n sYvd@fWAz"~YAFc͉#M$3w3TJ\$**[87\bE(>ԘЂT U۠,<WqA# }ζQTe[^RwiAsXSG9YEd* &_MxF0.hS4zrm.`&X]-7bErKJܵm\0qRr.0O vIʊ9ED5H+}vd676es#ӸY eic`u; -@E0u66ߖp^ꇡJ3YCIQNOX F]YDtDN}(}v eIh}F" -g#!UnÌ;?B Vj Oqh@`J!LDuì}d*-0aԼԷé+V=2!HL^gp9Ccj=7s=Sm붞L`z@AvO&W9Nh| ?g4jIcg JojĒJVdW<߄-/7;0<[q&cD#wέ+Kw99O/ӪY'0i֍%񋪕ip*O1u fQ5)10@Ĥ>[z?(}*:7J2Sef6iA3C9'dfYe05 wǷNI#=`Ft ퟕX 8V)l-$(X\ -]_Zd$]xCH5jpUJ̼lTYW -iĢ緎2j;VX(JT@U8mKq"Ѐo -4x]`[vI ?u (Ƒ֦gM N)'Wx,ί\`(>yupek%4e%>Rrd7FA^haO_"Jm//!ϔ_Q\756*`s,1A^Cw>;qBHvlNQY};LKmQ轭zg U7Z5.SǍu:.,4ƴ52U!L]!m*TP*AҺA^H.F蜃njyft>3йK>.sV^ '1YݭGt(鴅KLHH&̹5b+Gq\-Zfק6ۂE i1SwpH$}HZן& vrJ2"ѕr[nċ,+A^T,=XZ7ֿнH*iy ~Ϊ֠`߭/)"Q.]*Z1t[[Gs&T>{LI6y6=]Λg|diH/64kj\-&mLn3%p|-}ZQ 9#BiV΍M!# ĹvHa\h%n䰧صb*zכis0f]=yzp+F*$Kړ<RZ泣Di -+2lr/$"Jpe*&@O7nre"li下lᢐIT)r?~N͠,{*Pendstream -endobj -3679 0 obj -<< /Filter /FlateDecode /Length 2416 >> -stream -dGH2OzIE$1BL( ؗy>HQ8Xԓ`JJj, dK YgJl Qq'|8C I`6> | --F^UNUFf3^1KsFJw&*p[Jh52o۸~8$YJёfj 2rte^;Flr0@¡U蟉cpTl^v3Y5ϳV6=055\wFpH쾱s7 4}|s)z#HꗵBY\BʖphX VJTW6kl~:[8 N ̶pMKv17.i͋_ Ъ; LKVץDu`Do Qn ֯oetxH4#IN &+[1> =/&&U*ڹqg-':VU^ZNrz ֶL˕ä4v!$8\Sfǣ-)^ -2-SP 쀪aei,4*V8;]6< --tfBǃ QtANf.S"^ѱ`[ jU(2!mZ<+$$2-;hEh~3b.]D^?(|sk'?X11-d/Q-m8?PoOfUA},?v&X%s!}ߺV5Xb4z -P_D7c*1obPH9V`GΔb?IpRuxuuHnĪ -JHTMdN);3b[Hqf*D54ϻ - " zbk&"TtRk>/!MԸk3hrΞYŷU,kcp8Qҭ0mue^P׏1s˛NWI|Ȣ%p$v 1^JU0րZ> 7\9U8cz).F)y|SeR}X{#*`{:f{NyZgS=Ï.JTwYtK:Cw9uN_s*.ۡ&EUcI71,@SI#c*qc^sm|Gz^*HCSNdvbHٻ}ʉI`A?(u_]Wxuo5plIEfZ3sK6 ecW}(ʄ0IXzf`T̽5$?z/"eμFR `~u(e"CnR擽":jJXD B`@ -p `Uݠ3D6{hsY'K>}eYsy o5C=uk}&lɾ`}d/!|T\مm)#Li6E4z]׻J>s@ *[g+;S`Y_8G Վ -3V@VtI9@ Vc&#;V4w,<ˈw"|!sn40晘C)&rkmxB9endstream -endobj -3680 0 obj -<< /Filter /FlateDecode /Length 2720 >> -stream -tzX$cW}U'}f>>ker=sF̶i`xΐOx]%Cpru;t#AV7G/M"C|z;b+0 㞆]4bLmf6|}. vϟ̫k %YGS/ 0 -|1QQ87M.`sn-ě&ԗCtOYE~{cλ;?"d_[o_ ~A -ދZa(ьWvJ{Wխߛ !:ȉnVd0錉GKKnb%+;e ! ďHmg~y43kEa5#[.ަ )`P^\Ĥ<ΧZVb߱0ז~@aHb %_(7u*%£,3;3ַm|0U'zX1FI#G-ųlȋm1,x~/kf>*|5I3ő\*NTDRbDiߕЄT_[1,l5X=P-]kK ¨KED![]syrWv??:kDNa=Nw_.k ?qo_նz5N/X$Jot*3Hx -[/D&ˌ Q z5ުbn=i]1.,Պe*S[ -Z!3ȭRVs^`dٵ>WE/W?bʑuWHuTei&ļ!Gs]g5ټN%  M </[UyP$OGb&JTv ױm5:{0syFUH39$zw{H/:lE)ǟ2"x\َFpcgJ2tZ+tBi?Q-{񊼮aF/{>vWo;3=a6e͎Xb]J -@W~vQ$ YFqiqEGN̰cP(d$TyKu^WP r8TvU%-W\Qƪ&CNy #!o vpގ:>iWv+vhRJp|x8qP`tY0ao:qxe"#̈"ʼnSʜ<^&ACŞ$ES $LFxg<?G1y'2yAWTou'MRz&|`W!(Zi՟8ɝ:pPN3R_Gc{er ('=jkr=,la|FJc.Yh`J#c,ܡ+?}FvP${;dҖ};Q N#?%/ . -}{02j;tjdk $"uA-Kؾl#ĨKx2K |wCSW/XC8{jsyGMkU -nwnS2Dz?2|y؏eLk6hlt?Z=m"r>@_ bn[ЮL8pbp'UeϢcdAwF1[ܢ9.5<r~V9Nkֱp;<6 -c|aĺ#\{",,"O\Іћz4}%>OD ZΥy#nf.|>]ג)AC5rT?Vw``>4\e/{8l*ȋI( <~ҎΜ k[*> -stream -OFaL}8 k.B<;,ن O!)e^k<RU/891=1&;ͤ!XJ>BfcI7Of%3{h_ S2p*Г7"ϓ0p%ڤͱՍȁݐ),$'S-^dIio0at/V:Xj9зkQB><'^_HE+>rY։ZSLMKU$zDo.3$~r&lu"Yp"oSwVa6E78;xsmⰞ M#ޝ(0]?^jԹOgi4fiͫV`|`kG?G!u4o-2w\qۉƜc͸oV;[b܀5gτ G\fm8N}E8tg*@ 1ћjh󀌖!֕oLw.ء< @Jz4]>\~llgĘ_Z+"jf彑S6J(F!=|+p1]2_(hR8ykz]nQsX#'u~zpƒP RFĢ^X33C%2Apj14Q/1yVcQb.IA.V0㱑:a{SBinzp/z?}ylwg_8ΐ\"(AЍls\ڑlXV^i6!tR~P,<"Sgq]󳒚{H;:h/. JrPcs bYx#Tcu6n`VK@N#"Z_ ov msJ7Fws]vudfj {NRaiR}_oN, -%w~:lpgCHI^ty1Nk6@%sQԪv4+-{~@iIIߝw -3R812"BF& dpZhs-#0b$~>Wc$ %I7{bxzC"TjL][&o׍gsru[7i$y)?'f"ɬ Ujňw2DoH -EDm[Q2_;3&ˌcuV)*֊+zJ1uhzzuzwXYX~g|T}]`wO0d;\4ot[(ǖ} ϺA?W;^E[> _ڗ|ʹm<< e-Vl[=,d:3\3%q@ śq޶ܭ.*X5$)1تXo),GmWhÈf,kZ[ѩF>x@(,QúXt kH 6u֜Ҩ5S"d8t!a,glz; p?ޝ cxA߮ƹܡ5QW%Q1t2mE"ZߣSKh#o}~[fQl=O{Xo⭐hK(wǪy`}F\6AS|В3jri^8JLf\,8R`X!> -stream -׼P̴|NI23.-rBUUQNkXPC0Vx!x=C1.z,B^cN7˱ ->YЂWxk -f;]YRBY9+t 3AWrib5f89!Ō\ތ.5.~^KM|.`N+웳-rF~VU؍NtcQAE0$48S2[GdR8K{M{c9,/z_c9m["i;e睭7nRnCȺ{ͮ<'4]@l+y$*חCnd/?9&{4"@U!a&Zkc \P 6&L ~ײWT;M $3+툑y ZKF4_-}/-&AUD hA?vc\ y߸[`+INPDnjDĢ7 -H+Ķ ur?q<&֘N?xahO@=I?aWX42zS?u 0yzynsr2XK{͏UDy_cb2*ֹl19zq= kg!uMl^͉束,Aeu4ذ $[R9XYsOrFgs lKL=Thp-b@d_٘ũ -U֚18|(B঺S#F58х>\O-Wjzf12ᎁ@j5+ 50S:賬aak+go:_^K1"OxX uϒX/i_2Ds;%$y,qR̶+zggѵPܳ]<;Mq_ܿ6eWQ"l ˴:z-jkt,fI1CM f'+f+3'o/ P+zڷ~h/q/0 |#Gi[Ƹ߱TLըY#լX:`JI\ b*jciCڋ#m&o@dg}Ouxe|kaSҴ9͛35ǡp06ϜH3H;azܕt1g%T{ǙVi0yСF\T}be,?x{:79] -)X7\1c^c#Mhxg XHhl<{un -eeIgs,xIpT:+,r\$/àzv@:˛p;&&^ -o?*}9^-du=X3If }͞^k :}u =Pú3, yb5>GN4*MMqwYRJV6lpm-JMݗafP%' -.1Ʈ&wjҠb壬kBVW/;P1[kԃ"D;1` -W94Iw3AqAzDF3`qT{0[ pmbʣ|ΟT{9Ғ~r;&~Mk 2( p']ZI "y1X|"Sp*Kc4bZ |&wk~:ltU4:3#N1oUcs?>jSjXj#:f -^vo"lE6)evވk) iW>%h5նZs [g|\gqvkS$]ZTo}Wݺ+㟐WoԞ c -)s˯/2|T)Jf7 -N]{wĴNK~TkDphXLҹaW5ǜQ#ʜdf4]Jf*/ w ee 6űѭ+I-ةUC 'Ayͫ0E5_% _ Cay <'!|ZB#Ӑ^.~l9w 00YS *^e4dkReIށ3\2_gM]4wH8::o %lޓ)'Yݑv:}̨|l(F*@^"7 -^cJL[h_pboS,]6A8ۡ0EU2P4F"]`ܗA㹶<@O:FPٱvyléi-屐IowY$9.bS{rǼ[O_ ZF#Ęw/! ъ߾i%pAg{Ud+%m,1^B:þr/03`׬j"20endstream -endobj -3683 0 obj -<< /Filter /FlateDecode /Length 2176 >> -stream -ӋF4ToG2O=QE`x#.G03&3)Ja~{İ2<%j֘ۓw8^FS\%&&}ɰ$a."b- `j9DOL-:181bY UY&|Ũ%^qL{thgSADimҧhC8y4nTu Q2HuiG .)*ہvs twYda{D)^wv֨(W,!$Ns7ZdzT Q3vBX(D`$+!AMZ9̢%PXr+7%O-w|EN *GmG3p - 1tJגZ'7o?~= #n_Ȃ!3Jdm,rccr4^bAX~/' ~BLA1MOi_P\mCѤ԰03,-haF!c|amo&㨯GUvݍ+$A=pO4h#ghkzPħH$JVIhm:Eg%&c)dqaFC;: 5 AL(Q{b.c%{wHuyjOh3|Ҟ4E1\45@&i<= *=(\׮ng6u>Ttjtl5xAxA'4{1eRnjĠQN4"$㞬u+)v<>lȠbcۅ<#kwX@^yihR`i#U@t{ſb7;!QgpmNpʑjzjy˚ -a?-97҄ءYf;TFқǜ `H+OFEk1 -j]Oendstream -endobj -3684 0 obj -<< /Filter /FlateDecode /Length 2480 >> -stream -e6GKIbζgCԨH}/N?  w`Zص7XA|fɌ I9%q|Aj9|ޮW,n:_` yQGbZD6wpqGcnuef:8cxA֒>ʓ"Xޔɢt;yI\͍!V2tL6&h5Kg92`G-ʦJԄQmBW(" lE 7-9X^xKSѦVXe)ry.ܽ11l?h!֠gJoqۭ/"o?+S5/:K_2U;E2<LJ5XctxLJ-™eV .mϋ+|9T -~|ݎGpmkdrj&ѬPiFp>Hq>KۮD`j= ctj7 g!֌܄#`GuS]γk1|Ygjsn7ڈC5`RcolvRX5=_W\rTgia 4w>+- 1z{{$;ikgw:{-_nbSLgFShh<BB< -m%½;Rܲn zFi eiخ+d'n ## 7EC E†oxtL-f_&V8o?`RN _huvDG;)`1sKt4;h$CjP2-홛e3#-̏gbWҜ Wj4;vp ;,++;'R#fIRIּ󂈠߮ifYC}_$Sj})Rnf澊'/`)faK?ɛa):t,a)q`kűo -)+*g?쳄2A_(!گ\OɏlOQG9 pO#.D[rW| PYO2.9X'C.^!@ -!>I >.ajꢊWESG 3 dR|̷rT=^D91GYXn?mGHI.`"sWT($]BHaEkƷO hpqY!RA4A<\wk?V鞪`̡ Q*,*TWX">~QBF"lZ#AkK-v> -r -iǝc=2|X8ϋ"twdVߡb{73_WqJ\l54Zp"#(y*x!6'0PȒӖjui/GGic=nB0=wEB٨}4"dp3Pܠ S`*req5= z1ΆZjMsb]>ތ0a-LS0?P7|^Ϡ^xn @Z_bw}!'b~Fm5 /%X,c]z`Y#kr$ffrLpGTGsY_qשon0t'7~> -stream -xE7 pLoT<̝*5&v|ԕI9.bA\A &2az]>1 -3* U>x -vX",9/[4-wA]#} hݣ۶)2p*kpNtHf?i[~lHiM]WJ"n)ޜ@׿AH;z-7_/AF -tJaBs8. ;v)rZy ҈_iM6O2za Dqa -`)Ӱ&iUt 9"B:ӑI-}~EQo! `dSɦ9[Zp :%9oK4y_/<1If 5rеbP17B?M@㎵}uoʛ5M"8^@;Dzbɡ}ȍ((2,VNLHvkzBpyԋ! CEX4HF -aWgbv/rögEL:t!Ds`8fYuù^OҴBgI'G1t"Nqp쨲,Mi+3B0SyG΢g@4:Nҭ$2N?-ol,4>D $̢w`J\Pi Y uPK3q&|D֖^]r$4ouɣ^}Ls٣S;O4y _TIĸzWtqLm?q -[-9=߸IN JL!:tbA/- C)FA% -oӴe5 /K*42nLygHnb`gRklEŁ@25a/$}u+N k:7WJ&?bZn:\&vm[~<-4exB3 --Z(IX&eK]K:0)xfV~MNJ1`2⩽/ܲ>e_~m.8^5 .9Lׁj/EaΣ/8yW:X1ImSs6@J#  e쵲c|J_aaVq6Q3htD /DK˂ 通,34o5r!Cw>fmĂ{HXF* 㦶5ѝQu8 -ec4ad Π&gX k%g8k@ɖs߯e;.+ľv求'b{*ze)u+dԭ@sЂfJV;ݟhyL>ZX8Mp .=NصYL]z]@}u(hnhۤ}uq !˒ce:C74r=H@dQ.g4KhGMi;^L =b5|.> D-TTI,l -149K@gt#ԗ4 17.qsQ - Wue0-Qgyt8C" $fu{1pը 0cm$-6;̖LTZZKGثn΃@Fۅ=Kƞ{zF'lGN/ |iZQ2(!δWcVpxW43C\|DR7Jidciq' õD=^ A+w #aܐZl/Vc"s#B&bU"Fߑ'z7R1+\1q=X% V% TcFʫ bc40maSMN!^Cw6=zmJy@G=~EѧzAq->&rHendstream -endobj -3686 0 obj -<< /Filter /FlateDecode /Length 2320 >> -stream -sG,DKN#-vs3;RGEU9陮a5iA ![~'POtA[-6/&9De33m~51~L'^0|v!D_w'uf7m瑫vZ{DUhqߚ\R*1&xC^K+9H`'vDx(|[in(FW3]dk 2hABdI*gCf65bDª, ~ɤbX#UiSiXah єVwo‡ѾD@>= \Z/m.,ޝXC ե iL)EX͍%qU,`{5OH=ߌ /L}. -r1]ڒS Ԕc2jڸUܢ)991[.vfI("J[=I{/4DQF~xU1%L`{;Q}UTlEsD[l̈.>oɑdHԉfsFrY*f3#Oռk z՚EPQ* Q7LyjEF>_c f޾䅏i?W}ǵJ׭"{%R!= YidфÓ#Հ̕|h6]"oO~^ oQuʏs~7zH.ScP:ٺK 0:CY ! _)gaWh\+* 7/u:u K(p@[mi0 ܊`'_J:hYF*xHw^lZgf_z?Џޒ_T,C:,f1^KOĆg] u3Lt JO{@\a ]wI3l"|lx~ųK:*mpߵW[4D{΍VʊhyA̅eiVVjKդ6q}99s&1{z uKo- i#qi(oJrp7Ώ%vg ׽<"Cj> (,8x0Fd޿i:K,Mr:9{TO4%ţVWҤ6.FQwRcFr= ;>-)>1wLfߨOv|/4YmSX'WgW`P!wn]gA E\;hA3tw'19 QĥA)D(T0"gy$V4wA)MR,f2ykF(FO bMS<԰ -EC: xKx#4;O0i^5xh`߷Fw6{B/Gf 1Ȃ]6m ./9^bۑ~D-ۿE5 9LՠS׹S.фPXid9~$ >puѩ5-^ -Hfb{BEwa"gT=`k4s[gk\%ZȾQvaa E(vQ_P$m@{Z~ -Oo{I@'&U -}q3%|#B3quo?$ oۚ3;^5xP4j&K&, gV6gPB-8:L@y렟;HTAPID0@#<:)}Cc6B>ʞ -X0M*J?D`7gmzBn\4-׎ߞ1dy>Fh*w西{x(ߐz*(Y8-#"&;[,U 7m5X=-.&‚%Oɇ(]p:c]]Nz}&˵@?[GtIe}\`kidNe]#O7IZQ-<36kGcPtڵ Txt%e,~"eNF\zg&ɽ^au |xߢ"1endstream -endobj -3687 0 obj -<< /Filter /FlateDecode /Length 3120 >> -stream -I>2uԃ?U_(h/2-_izi/:XSO -ΫFpP&(/MPP+c6?Wb%+) ٌB'o[IQcȡv5e e'mfo?oOm7΍k1BfB"d;کu9.7aYs]Jcs_iTM*Y9u7n}˄eU  `j3e$;ډ.T+аWp3gDҟRDx&WVNۋPᾀa$nm5Dav\]Ŗz%<|7DZ|W.%ΘsiLsgZ# J# :8,Nj h1dDH+1= 7"o1 WT?m˖ 8s9[xsyQ5h]<ۈ2O*Z&-R]{Kr4q Q(fb]A$z-ClOrV+ 6,-YFR |)$2(=56o m2LB /遭6<'n 'n @Xsx'sf$6k40yw_+<NѬ A*(r.U=q82on5 F{ajkbpCsRT)]s0?5*Ќj-e3L8L^װ4f27Uʊ<7-?G_L{jH~]Fİdp⭇mr y;z1ooEg J]г ͦ,n #Kg<_b]25$C$lS3h*6$79\3!4rF̪eAS5NN+:%ZT ܞNHL[Gx>Q+Di[<'Ut5\TPPWQFȳo& -Qh쥡9v\I9 21S:9vy'M_ -`R^Fϲ9աj2xؠaB-$gNEO2"[5 maN40jvmP=a};o7C~ -ߓPS\Q bCo#rȼQl('śxG@Jv nƚ<v-Xc!Z!>@n/"Ri8&$*s"RZFZǔMM'?4VN2*rda)(! -LiG ڂ{d| yT Z##NRa7 1t <~UPNӑ^C1OfEhc ZW؝1*,ILj;ٞ$yBVS"|=:C♳l{vBb(S -ؤ6R_ApK@;ZM\(oJ*I}-FfrDűE^pfQ6dxbo'0<㐾x<@>E';/bD`=߇̦MOv wө)0U:Mg{M*bkc_2_B _<Ε>:[ћg(%`6vԻiem!R1}ڏ-h%7.&IKۛh>Iktr!1<# m)avESP:IؿI/*j%_jf)4SZMȪj=J9FT}4%`ivu ɿ -XPK -Q?KW"ѧП~d%z,ϑ)7)/qD -E 4-v;nEF8}g!:P͒K^ -0v秿ɔ+Nϡ߈q ˿|%NY ,9£T> q1|A F6w[o(A6rQƤuQ/{2t՟$zI$Ϝ!h{$Ig/[fsJ;&ZAUiSn|˚,M;};`'ܲ2`)5w1[N2xY$ -zŃeM%r5VyWaay[#գQ]ny*8e:;AMнޘ+#')gm/4 -h_a]ohD~WoDHm%]/5h`ā,c?Mb~΍.\4Tr5FQ7TRmB&:+}ԆD␇|* l<l*9o|19 t mbX=Џ^<x,^xl4^RK7s7ʃ*ߓs4OK?GҒg9# ~&ZbxhuRTåJο^ uVk|9Qci|y˩<|uXҫ&לV1IT"zlv>4y)ТpQү}dzE1̫di> -stream -yd\b? sFUMè22\MڿQ[tV(jN{0|h>Ig?1ʿqBl -1Ğo}n룎Ô~%OU6k0%Z XB8AI S)ҟ%c(\ O7*\'3XQ$G31RGoKE]8xbP3[$SHUq|2yϏSm6gEGhj!%ј;˗ERo#MIL>i*Ȇ X=+b&;cde^5~ENζٛ Lh LnGsݜ~1(TY?5Lb|4Ogӂ.Mt2|Q9g%l~jl1! e3L13#pbȻ> DB=Յ_fXwQ5: Y{p`MK}YogjKpBq5JODkZgȉlOeŁ@o:E\]Fr.xr]*p sʦ= -*ٹ[^WCe9L{ǙS`DcTfL2:výP?'P ->٭C]-4s(&\Q}pS5#lXH0AcY -(ftq4ix~H釰(Y^3~*fvt^XOiH}S/M GW*3Q -vIœpܞn -ّM2{JD&k+)B!-2 Уx3~ X&pЊ4'b@rTD``Al&z6{baNfXlxĻ_~^A^rj;+×Z>RӘ)a8&|<#TʤϺ+Vnht&xvdT.UX>ؐ(L 5ӷ b% !_>YS8H#1"p%OQ a Td7 nuZW.opt<@c PX:3dk2Uߗ&f'UdK~pL+ڷ&WLD{_,`e -۩~bME\':F nvCl%(@5FIpxx~h-2egį `8&&L ڛLxECFMUv3]Q_[2Dԃ_@;jGk:Xј t%pbU5&TtV#u\> x!]{'qq1U#K]fKܫ\=D$5mwը3,ޚC'1h0u~KlXcXP0ֹ&oITOⷋ/5"6з spgt3$?ܙŢvr85Z&^nDȐP=75Rn#*ϴc&QbIM*˕%s91³ )k03֒dN *$r9^ UAGxgբ).. т̟w:16b^(MYG&H%c>F"41QXZ9wDxQzCgo8%?_E_w'5OTa!PZk2!3TPBA)Ϡ%a^23 enk6JbRްĶZ#5`/R(KIE+Zק?Tn=3A5sH&QCf "q4kRl5y2#_?j<|tBD4\\g01Bmd {Tc.G{r6|gLo<@rsT W@:9|UЖsNt|e̻uPQHlaیK>sNGb=endstream -endobj -3689 0 obj -<< /Filter /FlateDecode /Length 2368 >> -stream -m Ko n`ΜZaq.DVanftj os?iu M^30 q; z6#(A~bWu%M/"C8`.ClE;='`!:<^cQR3C{zA=+Br1s_F+V?d'ӌ3ҁ׷`m yz|\Rj1}11"zzERk>i*̏Kb8C%>hF31b+1JuH6W`b .;Rgi\3\xGSړ;3՟40a*c B[C{zN')sgKa<.ǦzzfzV~ܹ^Sԛ̦Yuj=;9ol*T;?>K1ͷlP^p ݕ P!l(gO4rzmH\X):hD A䐿݂Fl-qns|s0>od4HiARJ{ϪVMC GRrE_6wn}֡E3MT,LIMjɯOil5:d.|i|ʨ>͍e1HwY-m4NQ]}$A=ʘDۤ) Sm -6kh I^2P:qz&/b$~o38Ek5,)H*.wP&S%x xA7$Ny䥒j́}[4LWOε5b1^y,U{}hr ll`|XOr,R|p+K_-riK+%w'=\x]O318s.N]WFa=nTs/NOdb!#`b<ƍ_ _f' t2G.?ua4'&ȻQd;) }K[qb6PSbFo'āR=tNEjC[]{N3tpEB*M9$~ bnfXT$0˙򂵯 *Oͦ0tvw?7[1ք'dtv@sZCb:}ͣ*RKrDZ_)CMПNRX7Klb+ZlҜ$cy <{>[mTۅZbEpl6GJ:D9:@ՄDw^7ujN]1%^w\1=,TibY<%'[oZ72\_|!(@""JAomJsm,5<@Pt7qBN>u!nRcAHRus~ qdk.; -qRy.{K~ ;:%Fs[zǵ|XR*zWTW -\rC@ Dk<A~w2hk>@}bڶS)o? ou#5%[V6O{rM?-T>>R`͝@J!oD%$ $OF?+ itU>4($SW8բz٩F-v5|F*㾔0*VJ6ԓD٩&!* $7[L̹z` 0wkGU.kr{P.]ŕ]ϭQyn -?IΡ溄ЍXDb'؆/ ,69jxSN:/j|+E6MŢm$4OȊ _d9E>\pfR3a |R52W$]}J-ncL` _wC05HuK&M8eWגyT&w! ''ɗODPܔO ta`k󄊡pw)0պp2dIGRJ*",/2.=Cm|0! J9:-&p 4L6e?]KBc-PB|`i(@OˎL>cv{זj9JCyIHpjNi?֔sHT;|s{VTu4Bk[ y!3+0 ծM#< -WpQ2GG}Ad3AFMqZ| $S+"(;v~%}sdn ,Ifd=~=tȖ`+pm$i7z٦{endstream -endobj -3690 0 obj -<< /Filter /FlateDecode /Length 2624 >> -stream -#V;IUw'Vgy J) -s&*WZg s"^q{c 1MM#1mYc+1%PW.X1'߮G7a-`6Ha?Qf\v6:LP!1[.`ܣѬE3\ :iQfh*N:SuHnbK/?~lqH) 1 wuD`!*2EΪU*&gG,P + Ĉ qHдlgi^Rf~uE@͘sn{})T}dr8Hp^3Zj#,0d G=$f;7ѪC&|"զi$$|gqu}b3!*Xt!i0Z\ 1*Ul -~=|cfSO"t?JMh0R,1eճa98?)l@ږF~xy,*(㉼ .,U|p~! X;ڡdCѾ B>h(#T`/v}!!PşIwjSJ 4ܧZCJYCu1O:6tFB-b2=u/1q,S?XVE;#Mo|%C e+v*b 7&sʰP~pFj1cO>V({uJ+iSxm /Ǫw)Iù#pO>נҲz<eUNl pbNOCe8 %v'v5h^:wath+ x~n Ls;H K:O.ïU" LJw'mBji3/V{6T?C{gQ@/J햌1#!8Mu^g)<)X +Cl=ft5=ϦB}[i%<8xwy*c[RQξ&A}Wd3=S*=8j?A#,3uȩ5ZBc> ;5:\+8sڦe Q(VjHFL-H\qU^y3(c\*%GdG\bOIxZ,YpR=C!ZB-.+4*EOh+SmWFĨpYIe9$6\T ׉ U¶F6za:),9MvEʼnX4*v.ຕ"+E8Щ梅C+o'%vH+s!7%_yMp=uo˚HthMCggTXdԬ|o6Ď"o5ӫRr( g*'";7S#}'ȇ_wtGR= Iz;c2[2u$6dC"ް^3Gqg;.,"9m39dHѪhui6!Cg#L(a6sr$: % -Ih1.& O:-c ucE05z<7a@L8l~ޕY*Ge0dD93/@5w•OiB"Xzӳ ䷙9S!KrrNR,S a39eCL%0s KU{<lf(hJ> -stream -[Jh/[7AV6MQR^oy4KC bNA_rI.[9*/WIQwS7Gvz}/€%lBFcGI[Cza;0b4~xz^u-"~4 -'8ٍpfͨA*pi3. |v: &;c?2 %?g>? EB)5}v|Sc)-n` \^;`Bt-˜ ?!WاR41j",grK -g0}sfmDXܥwrFJDHtxCe*-0se|*7L^봚}gO}k|j>zNsAhx@6&1tgƩ{!Rc x` cpD'/`as7Jl3-(rݨ\֜_T _3(K7a'pK7}ո[$$yY(_bfvL[FgfNv.CE8Fh ,Kޡ -;ZZm{$DzDn؟[:QeӞɢR)?ME,z㧦) !Mz #_QRA'LD4b@DpEZW >Ҵ<}s+ -uRW#ip|Q2wl*v;8R&_!BzХGDUB +hy_ށ)ВZ&KYb@MgK1BIL8wog<$Y5&j~@E7&-[s>?.bG@USTRtz1(+>)Қ mh+VPP]֎W9fGKQۄî_da`o35C@\oرQ\ |tjE7 /LZEKXP\lf]P(?&K$+i̓UɤeoCW^BaNV !8S2{l{;U#}:䙙4rյb0R-g> %5EBw<Ы1,4)=\Wgm툄T$ZmHbP;)+qx9 -el#Nerdkx]P/5PSM\)Hgݛ[wЎ?^LaK4;g@m -b{_($ Nܴj}01-ΡَoV[%Llǜ`ih -f!m}jX%5mq 5nMV(yn$"L_S[Ej5neǽ? -EFaqlBt|&Ǥ{TU:l. S gi{19eVR@!P`|}6ʜx\{6]rf$=h .:~E婩:͐͝8#T{a)W7c%Jh{@k# ;1[ ܹƌ]]0ڻ*+G`?#B\,u ?/}ubPG ndV؁cO{Axn3!A%`-@Y|r/2']DoW!<끙]!WohIgߖEu.EOB䁧d% y^> -stream -ֳ#E# ߶%{S'uE`V#|;0o[mD+Nm&zg!I# ݈A ȎM{'Q.rֱh(mHz0`IAcm]?.axR%Uy2Eɳ>4p> H+22?Z })Vzlƨ|6U!y;20c -~82C$XyFkBISfkNjZKu2'}7^){JbMHHmx6tGB[s]@rAxv'&ю%QS7#0qOB,os!^r<~ oUU j&i,#xX9yEd֙F EZFIAJIlL[~, QVwcDߟPg Ӗqwr)b?ԕZ>b;]g}x(ك,{ǥ޺2EΦ#4Daze؎Cn`_iїwŝ\fXF_%fgb -)pj_-rCuLhMoA35 t}h" M~~nSo'~@:dI}2ƙ6Yg=m`VGt^3=F%Vq[}[ ħXx:`26i$. %Y҅*㌫Ҕkc b;$7 Do@rve5!d4y(r0J?4raH Q?GD(]_EWŇ-#F?͖ O`ο9R4ZNG@4}&`~7OM=XOO%Oq$LɑG'W֫&SExVb<*\4se4'sxW\ -̖:qTP6Ԕe@LA~>׏*g{wa -)7kH+Y QS)MPQJtQu#f~@e i"-QmACE鯒HV>Ch7rPВu1Muh-gp`@ߏ!?V]cV%# qy/f?H %;:s>iEx~=v%~)"/krr7}-#w~L^PrR 0nT5!W̢w[u+QyGlM7p> -stream -r^W@^U#>5OTHEeFYѦwt'ҍ4V΁҃S eb&ŌdjhI/|)TG]mv -պ3b4{ub:5W S[=<-BJ1 -8 -ebJ-;No Ni(]6ӡ7xoU?hXΝ:V9*Jגz`U.UռPڌD'zxFD!՛*`'>xQX'>ZY}sC5'զɢJjʾB0SJ`evι#1%MCE`)V5lQβ3nhM6;ms Y:c8_LR;? 'dSTpD)~O㏙Hc,mmO ->>u)`2]xΚi QYC盦n,zzp7(Zyǽ/ 73w v^+qfYgf -MD1XWītoil^hZ1 O>a~9Q78>Bl<~x~uQ:i ύi௼*.MA9=5&MMrU)8@[wjuCc)΃,-2YZJa 4,䲵qFȶbgJUH^hKfs!CR38\DUualUޕ40?Vα -p[磻O!p 9U6ՁBlM wfP0e %,J4aK -p擖\ ,-)GSbSpkcc؈wvi}kJɐ@ڙUeƽ0u9Y]|h&&,T;.f!QD8wڠ5T[dWbhCB|[B< ڪ"oM}a("kC@:]`צfYT(diKm --s\ - CZ8 oNk왖J#Nۛ"if9kRLS%plfin}4ۖcbS  JgZ8#[%rѢU.V^ c: ` -el.( -mL02?lV(  -mbrr/2tlIY2.?zmƧ0=#~'FIhh5H˽~4a -gpHNN() js8n(9&FKffl8#JQ{i3iU^%ѹw{Z$CYmDQYUH[}s$!c)0]mCKV35ˆ%5 u3Ėo5*gvw>"¼`=gQ-5'7D_=+`?Pnʌ6%R'kku#n䦆ŧ2ȴv tʖhX -]n;n) -{%Ѧ2}UYd8PAymOQnlhCYK/Gg2c&'4KYa&' Vaսy̴XZ`t{> -stream -~U0QAVR'}ӊo?f(GGqCN|CSXVJa>f|+9Q0hr8D7TV"0Nnllt)"MHQɗyOPᜈKg$ wSpQiqpfI'ԍsK+G|r4Ӕ-r07NEkfa11jL4=VbM?CP| e.ٛ Yswς -5ZpGI"MSgvnKEV#J#2 T)lr.z5 ]dE -x>"#{F!0I҇.B"pm"Z /r$*傕֩ ِeoEuF' g6Ap4yǢrMP2l':36+@:y1brg$uI{Ɖܛɚ\-za,ĦXfx<ɏ`TCz'|As4^T-Rcubl,0,nbhMp7znKY52zd̞pͻT?Ǖ۝I7>B'JxF 6@y]y=HΦ*M\Om>W=2]a -ݞvv(M.$ ;U#yWZBx%0٧fҌbאkhѯVΡߔ{<_Cn=+髖2u#/5!(^@-IPBԫflrWR;=\)eUYq@&1698Aor#"_/|)}}8ٹd%NAF%̫=26 G}+V.77Ĕ#΄:z  ~7ڈkiiaK C'v-"(-SJ=_Q4B>P^ųlI1'6+&Bŗۨߣ")' fOwg7Ƨ޼95zLBXj)1dU`|rk*u6:z谯[v`gd-̢28n:uq}ۃ,_wÈ_-wS$;L"K [wq!XrVI"w\ncK1WĘZŽ(noK`=iB1P`p%7 ѷ£pM3P4+D.@yTBUX GKs̄p硿QŭX$Tv ?\n-VU9*!63( -gM>z C%M^e?(vF'ĩt^6^CpGx#2ZQ6&sKdI ? .)߱s fJ )HWGC”?Q78oCY]tj?Ck("W`EϹZKkTv< ~[PL,]|E\7NkcEDuvM#D$endstream -endobj -3695 0 obj -<< /Filter /FlateDecode /Length 2544 >> -stream - f0'~'zn~ѮDv( }pa(^1VOD1o ig'[ ZlR]D0^F̶ʵB=c\cpp7,܇'C \8)Q>mdƮbGaȬGjX#-\a=pRGN{+Qckw%صѐ) -Yb秖Z\\ }ߪ8Bg_R9vr+vyI]K#rم7!jN({wrJy&s=N0`D{T NwwoA3Ny r\?dE=:?v3rI2ӧnk bCn1 IЄ5 kaZHLP/m@ 0tٛY8`UBx]'"AG)G'̒4oSyG<,UWUu.PiY ^kZylg ܫ1 `ۛ*CY{ZY1SF_Bd|愯%.u_A?Y9?J9w6ԖHsЗ֠/لwB tylʉn4Z7𘧕6bqУ׳w~8gsAV A|vrGvb4͂mc9 >s:ޛEpggIJ0#ӷw@AELhsr-(vpK`H Hn͸ I}F{wd~ G =M @FwyIbc[7etgy EK% F^VjB kF~X6-wܾkQ32(:2c+ZKIp;Zq3&z`P9y ByfR]8=1ʥ;tvK"55\ռLyg``_KGb% !U` 8%N&T:Qx% Ob*" 01 ( @_L4<`iй U.ԏufREZ~{U۽WiVwӰ&e'lm~=O5R;I6BS @kmj+DH'$i҂+NE'8 0$,Q>g?lS+(DEG+QBĶT\lB5PsYv7ݐפRzZ qu4 k,qR[vϳ= MUq2ە'z3lL/.VwX;Ӥ>NO*R2{rgz -'C k߶X,Dm섃Vﺨ1'IyeL"φ,iac'Pt '|H3J9o',/2(vV7M('_r"9zAa/2DY (?ͽt0mo$Р!aʭ`\z9M q uJBKƫ}oǑ |l;nZE>,&p**;o rǮwI5Cޯ+-Q!U,pIRNQn jqU BR>.iL{=ll|yX$ -}2JYH\!񟳖a:Erct; ܮˌ Nl%IFkUQ}n>} c"5{A{nj=fB~1L51:5t 4wJ5qڠԔJ .?TڏU4$2(A?bf& a(pjSHo/L̉$`>yU8xʅ"ҴY|+i^4ϑuȖ Vc(g -?MoDZwXoL&rNUcnZ0.9B!Ig!/;GS-S -SMJ\b>-gؐc:ӿ2" he"=\!q/~3LY9C|hAYa]TJWUm'+8i-?gB=$tٰ؆C[pb렌hлSM(G*6TL4(a,pL0U!|Fdj$v}aI2 -lȵʼnb徢..[ Ӱp_=\Yv p+>P$Eޙ,\[5h6Q "'~=[g"~QςB -; -3\w2(:\è6J?b䐈`\":~./ -endstream -endobj -3696 0 obj -<< /Filter /FlateDecode /Length 2432 >> -stream -Y8T\9Z( -egu 1RVξ|?*^/]W:]=eRJw!M&ܓ詃؜RlJ}ZIrA].N4Xz|sCY}?A1`ҠJ׋fI8yK:̃hWA?;j20"܌PO'׾WpLydģf)h$+ÏvI]šD H:)/\SI+@X0rZxCet=;da^Sף.KJ  ͇"QTh^<2hRY1p34Z Ы]'Ik2=M!MŴWANmZ"5J,qE @X#rKUZj}D~>:Պ]XjtvEc9Dk*}7RQéDu9"-'m(`|)ݻ5Z(d -Q$0u٣ P(gM0*gFLQ ޟQ0&R YT_L'kHhnq i7.ݔ'!0*G9%$5}v#pFr!FFbY EG%~@{b>OY ՑlarrޞZiсm;kܸ^9cӬ9D4-ipRD -}K,Eeu}l-F^yQSȍ=ksg -J yc_4#`j͹ (ɠ @I9S 0o׷xWc3D'uEGReW¶zcrMuRTܸR:$ֻ w4T3u&mg/͵ ,bcF$8>|gq.y#k x%y1H0kȿ[%$86b=?azl >TnbE#3_-er(E53e abwil˄$FMvyyk>Je ھ -L6ռtmwt!_xY^}GoPty'$o[³!&v0Q$4z -=HiX2?rdjQپnIU3<75H*ş卶ؔ7ZZĎKZij(5v=迿^dS~{"062\@z64OVKz-Ω˔Ni}˝Apsv+yK1A7ad褭Z͸)J8Z"bi[wFljX wFAw*z2jci5ֿP]b4Mm_9oRr -CFq׎.&GSrD4cre %G0b zJ}DXԃ0iQB{*!JApaql;½Ikrd&TgaJBcZ.~h1N~fR@mt5Jp~W'Eb<k%wINZ{ٞb*c]'= qܭrZq݇HjFwelU /nxDK6+:_ ا1a{QRq'91!uk 6%GD_gl#:5PaRF ;3IvL=FxC0 -TA#ĹCжgT,Ʊ"Wӊoi p3N=+?E\! S"RXCZ30aLG.;N2 -z>mLO<bo򳍱NK`˜᷸`C0*/&5a()&FD<Ѡ7*(nu?^iLOfn: -?͸7nV &~cg<vzfA*M<}㎬BxB"Ti$k50gY/iT{* > -stream -ؾDS3v6aJ!on2 Dqkr:x Ljݤ:~IV#?֬kxiF}?- X7 < h9N]=r0*TE3X4t w{Eqmy*EGD)z 8{̀GYpo}ȗtI~`mbbHcBc=\<\К -WKU¿>cu bĦ, S+Q¨csA8|nɱ9HN|k͑!pצ.ϮDjd@[ jz`Wtȇcp\c?0I{~M 6nt䜿.A#y :z] -l4~2Ξ_9U*J}b/H`,xpCz28]2đNI73 ƝhBGw¦mZ&(g %m9'^Ki֐0$L5Vܠ6Z[$~ʤFs -AZzۙGӆO  0N5Q<7hR!wmJ#9%Ҳ/arl5Ũ>^viDHSJ -2; -zWc*Xd{⪴/k?*7Qiz9gSC+j-?"/7Pa8v3pH;̤ 8w鑌uՀ qf oɃ̨=~1=6rZ}ϳdKn\L&'b[ 7_)Lz:y' gBCu7ueBG|.3סy -S(!m.R\b{T 6пe9]+iʔ]+U [aKaEm2v|9ݤ~/H{rF ,ҚgYC3v^%i>31Fppjd0v * 93*0φm˂'O -I~|Nӂ98!^8g[|#S"@WG|[wӣzut&1mx]vk:C#P''{rS_+xoWQۯ:Q xE!.2t1{T薿 %9߉ßGlnl_Py?hJ7Sx錐6EL/81 ?«L5C7O'ӋFB[O(c{G$/{)  ˜z[^1 %Pj#]g_MDšWO[Ya~&+ES8Ф5~ 'O]m: *V"62NJ3幕87% 3.#lS!ZTFg,_*V'eOG3ކy> fZSC J -ضsCg*iYwTt֜ ))BGd)r4"> -i5#D͒¼~Jƕͼ\Bh1`hA?K$U0MG$Nv -Ƕe/bR("6rgb2M]GN]';TTQL.wGd%mXx -O'f@1J ⨖2T!;if$5ޫg? -o {b{kN]^joZxkhb HՇJ*k?``g W鴀}+0AhV o vv~{$kf%64 [@uZ$<(_qj>< j-?3/vŀ,4MHKSܲsøK}H桱 ?sendstream -endobj -3698 0 obj -<< /Filter /FlateDecode /Length 2144 >> -stream -R-TaO!VI%壗 rRFz[5ڷ ¤~+J\*ּ4 j6QqI u.XkM4jӅ"iA;mD=lQoXLq -_D_ u7,f- ;\^ -AheF B(05 LHeUˋ7W\[-,׏Gv"6\k`ܬ7!.I_Ij-QʨN|1ydywg^XWיO{7!|8 RS!  .M^F!Q˗tK&`V$$I+;iucyy{)Hwg -.^WT_,V5&L29sUJj~30ѵ"2r[ "ֿߩ7Q'nJ FKQc@hJ^YZPGa_̖^œMA߂>I1IU! ?=3SگmN4qCzDIK3ZeuyCo%ɂ;p+a}=o>x!+#Brk*!&?$qdO/85|̝P\p Dgp< K@nkVw;C:eR{-KṂznܒKp$ -utnh yqy3G(DXaq%LĞ^YǙ7nKHW+a8Y}V/8p;;0%1!zܖD4ېԝU[ػ -Lo8}No|qŧnUKׂB*֬vE:euI5/R0$n)POf]''h0qʄlS7mT*/P*L~5}RU 7]O4S<5v*XV31.񢇉7p-mH W@vpTD_^Қ -nEu,;h_N>g]<=+AV&Le|y<#I 8BY?n}uDDy^t_5!WA$Qlm.Ǒ_֙w N_LK^ 9b]-aO޶fɗ(R{QG؋Vdbɋy?M6 öt4wwOƊ5vTEd -}SD9~mGH!(t3sG^OjmoKv./n 8m$:,KĚȪ׽ -lN/mp/VQWS^63X\\NY(e(7_2-*)Ve(Sˁ}vÆ1VzA)%=0Q:OKImZ7$XqP x2l!5U nu -u s5HF>bƵ܃rUިjħ -"_Vv qCj[~t416w#+lkٴ_$RF\8c8zdC"M11GTπ:((e"8$4D|'?=P-1Y?9A!yJN~pet$Q1o˛<^g?}l9icϭ9hP[^7U].9_ml \gg=ᖳ?U{ۑVl6ꥮKe^nPgúΫg 35endstream -endobj -3699 0 obj -<< /Filter /FlateDecode /Length 2912 >> -stream -xh{uí4Mk4GxJmv !ujv$XV@d%D@~r'-W*FФ*ȿML 懲pZ 'Pn$q}ݳLe3l/pGԛoKk}SQ>Q\t"}=8o "g\Fud Tp` -rdհ.=}>f̏~pS}˥|ƩmtB^!HW*Ïz{Ez iؼ-&Ҋƿ㝒)^61Ʀۃ2EGW9!e&ةթFR5EiD2(fG/!~ww.dg cI\rz5Z5JUi싪&GqMv>o[X\t1-@KQ),9,(n*B,-E:<::Ԏ H5#XV7bXBG& M:9٤ RR6sx.YO7d]>:L3cggR(t uxės߫:*ztAyN=q&ON:icÝkNI״.obq<<{R%:[&"ݞw'ņQe2"81xu5HR]}lֹ $ -ܜfaT\ЫYǙL7<ŢKW7"n9:iij"7b)!f^ 3li)7# -luׯ k^ت$Ԫ>Dd:kHp'h%.Is=h=>òcw޾{4|`xޝ4 5k݊Kxj@xt%Pyێ"}(>*}xJ;eil&n_Qq ێU=9.ibƕ,$uIY-(}ɱh#+iOtB0r6{U:vPWlg?$aNI̎&OO+oGʨb cҁɭ|_v=bcγmg>d܏uK'ZoDC[xjtg&{wf7aP7 -@Dc3tWVai^UX%$'bp}2 ;גFM_sB #f2 *[I4,-Ƽo6-iW=v$(rgq v K}g"%EQJk*S k*Z7C$/!$8!t0 -H(};iHq -0UK)g ybHcd&6M 2;8n!l!gJCw^B2x_EcVfk %H6$ z1I s:ӱP2LWd?4qMx',D}> ݢzik[Ș}ތJ.o{;;TJfTO/R?5U)0zNF0lhؐAR?eLm2mgLΪ|Ɂ3C#_DВCWvM('t,aY7j* -_tBr$=^e2ӥ{KӜCI`fT-U^Ea%nobҁ:dbu<ȉ:xh;'.8}͗d2ѣ>鯨/z/ % -{mOs/df |y`"-I!S=El}WD.6ۿ㇘xZJY1,NӑI A:uؘ>jXc!g K4({_lT}6 -Aa(f5WI*Ɛ]y5B'QF٬%C!6 ϙ#romqϞO0xvؘ(-:*31iY{fh\K[~iܣ[<(Z@釞r&S0?ѩnfCÄGDO%O& -!,vR)m+&ˮRUN @O:O"&q=7Bx@Y#G-j~(hpw1rAYk -KQAWNGCa/gm۱.[K!:QQu/{X:>AOwD괚řc̹GǘzK ,CNX<[?,n֯|;#zθpz0 -9ft!u #B,Skg)U Y;Q_ -ڢt^# ǿSB0;xJ hIjr?9{֥l?ՙ+y-endstream -endobj -3700 0 obj -<< /Filter /FlateDecode /Length 2448 >> -stream -S=D1v(IA A)-=C EV1ԉBQ?֚v@DiXFw:ity+u=5^Aw07"a8Dك!1OF</;U!PB廡p_C8 r l/kpE?4jvU(xdiđ0=$9Gf3FՁqfF j$-YM:zs'(ȼsB:!PJ ůSzQڻ1^&Z.mǾV_}B8w&wb++>;eUok#dKE Iaә s'7QAZ=H ܧMIacIVl:_MWgz뗸KN\[s4txUt."rae9 !QP>'TPhvqLjs߅Xu$f2+㉥ٚ- m?,y]DZEFgPfc<-* wmyrh'YTYp s󜎸~u!ڔK)glt!a?O<~9&jpVRz? gĩ@;j~Np͜`);nqDz@C]7_b\>` rg,F k62_ b^DѥaꙙFKoeM)P%fS+3*]JB5?CE` Qb3.ݏ+;@c;{< O\0W72w Nqq}[z?u5uP MQpr(ֱD3DʞmŨ†,OBtJRnF=T!'wE&)X“v/T32y2g u-YPTr^ڃaqSgώr䩤w: -"ۘL@ -1WUu4W6xhwB~hxQE /l ,TbEFXYt#UHzDˮ+Siqb W[$OU&`\uR]h`@J ,Ѣ<*($EYMP*UiT8gT!i>JůS.b˞dLL)x$ޤz! o -cVYaoX߰+1 pߔ XB阈7G{-x)ª]W? j-sNzi(&p8hHN AT 6"cxlN.,N2/pZCj6ٌ>x#ͯnBap1_ʷS\gL~9㷇|g"ݪmWx@8nUJ'^XYKnvꂻ4TSYi)* .0η`HNԚnY<7!0 J{|%K-j)'3^x5lC𨰎AyՍӅܧ‚ө. wld) DhLeYi_Ev(-!fX  YehYE*?>s-)!=K;sjwl,[n}܌kq^½>흡B&K(uqƦ6AK -Usn›ڛK҂Ьíp8huym azf3(΀;QR"]Qm`gfa\ˬAk"uisuM=l/2koy2&ߪ>/LAsn.77mG6$H]^aqut2N:N<-x1[iٹ?pYx"x@+z/endstream -endobj -3701 0 obj -<< /Filter /FlateDecode /Length 1520 >> -stream -]rksk/S1+(ۣ r|.X8!)r=[́ )ӒVrumsoa1 ,sm )Y0~R!08D~;ɉsƑE+"fSj'SBЇx' mkI)aj9]+9&|k8/|$f<]b>=@f/bk;mѫ!3d&I,Z$G0xևd\6" -?Tk:t/<"p?ğw,w,S9{m< V&yT򼽹B?MnSi86 {z8#}ݩUZslf\~'^ub-P}D$> wA {A,AjtLux? g]qǹKB*[TAWr<1Qi~f:]KTyf~^X+e;z?- -Ywn$GY%ܛ@t4 q՟OhEϲ3isKXNx!1~`VQK3火4keLC6H9SGS&Tµt%7\}@r)ES_d{9n@k-/=gendstream -endobj -3702 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -xV@"2焑f \+g* "!B AE?]Lc[E`riq)vRκ/q>|UE`èĬJ/hʲ-MD]!0SCj6rhÛ4żi IdžpBmZ|]>VlR U9{s5RZ1RIuՀofX0FX9S93k>&!?2v) -,> #ׅE8o㬫a z7۷ZnIMǪbjyܘDwEc]r6yKCC)]Wg~8E& 2<hwUb._/oKĞ}HI0g(]*L':m=NX,]욈w'H>9'K*K$l3Vf?Ux-Sl`2,۳ Q/Z$.Krxfo3Rg\^CMːFZ?g0Ƃ<)1'u?lTbz.I -#7*fV4fò)MgW |E@*6}[g < ``aWϋԊdm"X'0*S}[DCĀ%pzzLC]"/`ԍ/iY}"dzWs[:+9wH!-[gy. rc'a;mqM֪u尨E`"m9UcC'×1;J6fCgġӏFzawt/|»b:鐫"vˁL.E ON;TigW:YjaESP|kúEOa [6ncY"09Qkpo3@D]rA`.6j8#N U- w+Ó,YvRhpgHvF@09%,TB $#%eϓ99cj<RK^~̣Њ9lu i`]xs<^##4 jgYm 0PZQ;|<pEhomFmanGwr@÷Σ"{~Xǧ(){1tܔoR0:9,DŽ5 0zAys*~(j[ 8ZIUMFo6'u9;?%% ^?U -Th _ͦ> -stream -S`e-4+`q pP0un)̻>) -%vKV*V)XTbo,sX&/i^5K9h!~4JU˄^~C"v-xS!)z՞R_EjšVAߜ< -݃?! gW kP.;C+ʶp1b|(udK%irItҔ&j!L^{z#ލZeلZ-Tr$b_|yx+D2X[0Ab߅@ف܂ys4KQ_kp%5u5NM,} -e ~RSΫ%0+>Vlfj♒J.ݗ?!L1=*<5L!ӝ1(uC.5G5#)Za|ߴ/qt\"gƝc -9])JD0'WZA}tDd˧ïDj=هblSf.5 YIu'ɽ{(>55 D>l k@',泲 9\ ٮ#GmI,[gAKB6ڰ&O* -j*7G6h,^-1Í}V%+ -K߀qk%pꜹY3cC3VR9P},eS9nK뷳y -#9S/~%:o%e&Wlƕ/"6R lL9JG(i}&]֎Ӟ7~,jN p8Xm|@$Vr9Bp4RpE BUEIYec?:Jq? #ꏆm`qb߱#֜YP.fM{~Th6u4_(]^ =9 בYPȊ,$Ay&V_&BKX6p&`!r0r L_o:jL2Ktr֓'< 1H,q˕Iӿ.BgW'-^^bY=ܪ:Գ ,XV=H{S._PoBLTJPed -~+Kb1jX`+ -vO%*SO[むH6N39 9j}%ªFAWSx8,+_X0,5D/iѮ D9=*>b{|Y(JId]T = .R\(+({̢,s("ș1*)\QߐMXB\SNr-+^hW -nUhA -/2bS^gU^R >=]`S$ۅ5Se#^"\,ucnQQ0(A7cIDW6_xX+n&nf?kn+zfh2P]Tg>93NK [a풴BY|O:U"$l]Օ}x g IQU -A 1G+A L4kq#-Il!ԶJOR2Rsh 5R2I|*kS>+0e\X{ʜ{9 -S&TME(/0v,_pXSDHW5ł !'}e4g{ -%DBs(Tg2ub({ZJ좊/ՙ&ﻁ1%ҨCݔX0w5M 5a^e z~h, 9pkNDQ?MQScԐ? yX)e -M \*>Yg%AWzBaeH7*Y\ /8lqe]/kši'b9iՏrM ) VmƒLG3j9yv Xͽ q9,Cyf7[=;L^WfDQ+k;c8* ~/RVd9DQsZ3@#[f%{΀cs%3u-߮tx} }FWjʰ+R.B̸4(C'%վr;jaX\coF XMOxqi%z;|k~2A';tBVݵk=зi&mL>/8ū;aۛXl )@-s?kg'|HX|ӑY|EJͻ"uHH{ep=+6|ECf65aXQҖj?5 ~5?endstream -endobj -3704 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -O;P)a%83go]+1] ꤱLi@ (8X;p2!3N RL֏|.ޗ@zڋ7g=@7dMo)M[*!LYuU01o{3SغsA7&'v]HeGRC.V95xG,-tF -j\{!^A'0^o`M SX T5HMiC'Mb 6bI(,DLJ.1l",ǜQ0a^ig{bb-VH Fɀ&n&yHR|;R9#̝s\ʣ WDz zm9aǝoѐYBS7zӂyEղo F -{zlE^hIpÊ/J`(zCr翟~1 Q⍘o[eA66FJ۪0ޢ2蟨igJӃO*DlgBn8'B /a17!%QEufg}p<]&=Qs$y9/$)HpLjR,&VAKOCD4m 4~ >6 %ھ!X`Ɉ\u o>l(D*[kdَ\MB/"TjB̀ҏ5gj'BO$v3(Е}vbSiWW땻 t\T;q -7dq=j!~ώALRDכ567ܮ OqƴK/ i}(D:Bệ^+b&i5ƃiWTUmr(WRө.J:'wNUtA=CİԽD4"% y sy".}܎X zrt8c3$2 = Tв#|2)I3 `vó,2uŠ8 Vi6d~6+>gr{1ͺX~$ü -nX)ZݥiVԁ޲ gg'nD9"srh!O:0?&2$iq}2;nVZV ( -Z~{xD ݝ8~+_ENQ4lqgR7]?ӌ)m5@&Mn@< -ʛwVc}DY&d7::vqKIe{qfpR_K2IDQ7(D(.i34Եr$}~d70yw}0ku6nTcG0noV:5n9x*Ty1IIǟbu6P؏}ULn-(/BdB {~97P1p[̓ faQ<'QyW4IBs -% )endstream -endobj -3705 0 obj -<< /Filter /FlateDecode /Length 2608 >> -stream -)b [&Š(40ƐQfNqJFںR5+([1c A8c#}+]4EHM)a-uDi|t 67-2W[ %`KtSjۈwf~%*g }.=(˻u+χxo|3oXdSl+-y0v~t s̍4`_ -*4`St_1zjS&i;r|<턂ᤑ0kOlzWYCFpJ]L@tNFR"+߇؅)i%|fxy߱Qp.^}^(EGڧڼ DSaiGAR9)\*SwjRx|Rsqh^0²W([s '-Gn 7AFOc]aS@`@~,*9n[H .` v3s 2">| `(_7_kff[+:tfĶ귃NN$.T:C7Y# x&j ?;xEpF"qM|Y}, VLm |DJ8DļK+묐w,>ɰ7GQ3QiELt,KZ}fx]|Oh';maKG% ?*~ #hA%f9hB>dr6jR~fE5 -Ղ "53,Q&sBLe#~ dqey: Jӝ :P1'^aFnF L wFK) tJc3u"CG\7O u/&ս$Á4TywCV1Na#r^ -4H-Tf@p!x`K.0ɜZd)-W_@H8Y%^v1ېWכ27:4O3g"QXL{Q_l:k;6#P=/p?Z`-+}~V * ~S෕.NP5}H(3M]P\wTPU*s*SJfL|陠8?׸ h^gWN^cfbOJuCIGJ;v[ -!fɅg^$PSǡwid2L.Y"m{#Cĕ:pܠ"OFyό/UqAs]j㭦L~s"ЭV}E_ -L"3v j5R/{&4[*Aγ|ڦb0\H(c٣e-H`KgJ;$ǀnYp,ļlNY/}ڼ|7;> -stream -7D+ -W .edTo -Wx3wBk3Z*A5컵Zp81AKC~yHŠ+êPiKB>-4s|#QHGtAʶhEi,)?t OMEkκFvYz OKm 7ŮW=:   kTT:A{Az v+&0]ͫ0& B]YES.vjb>#'_I<$Isݹr6#ok,&("s1i+Jrd6bFb,&^Ku~q7ArVOrm%1⽰4ʾwKmu`O? ]ّEvBBl\ST'37"rCI;ru Sy{e +Bzh:Zҭu^ۻT{mkT EHnnY֗pmieל)ذHt)Az[ͯ穌ӳa˄]=h[̄D_g|nIƒB(eezyZQ[25tq_*w-9=NS<]t w4 !&lB}QY4v'M Z_6>Ad㨖Q#+EXOâ+;qgiA&S@٨UuM{d̕n87XVs3`I4 *2c yXկIvw(@SMc _Qqr"[6 -,RcͻLX1TxyωW:h{X*Yoi|x9iz[L^#!·SW_ݐjq}_<`pg2kaʩE -gM|XL! Y̵8vfoXqz~9; ^~5G0&H( Y?χ?P-$itbܟߑYHt0u,XmU܅Sp0큘EPt[NdԁUvBU -O D@kʂ9_p/7E6[=k|Jb!Ofݛ> -9ͅLa(bXIn`viy!)m*A}~VȞ(=%9/&;ˢ$iiYX5WuZ$R5LVX}kj\ai -,g/bIÁ/-z2=B{f!056r6~hxj FK"  w5I6 - MhE.JzNrEf=mzx(K)9i =TkXp6q,]O@T={h YMގnz9orm= ɍPav (Pam@tb}}7ޙ}I7bC$dFy-F8Ow-\jˢCjz߼j٥H:[6#<ݙoHNiH K<)ն9ٴ9.nosO6%H -]܍ǚ`Q||$ Lw^ Qcto7hMo@t9եq"dh z+U+5:1ꨤlg(#HC+B 1j AkJ Ɏ GP7st? q;'e/y ۥ0!Yi,cБY%I˽} dic? lJHo!"P-o B'g=,7Ye2L09\aJ/ B3w1*Qt ^z$"^K O-25.r'IPtoxGUix]~5YV|*H&L9Ci & K22ʹX~:t0%r[%YQ Y>V;xLx2#ெcd7my@] yۧ`9韉{qYj ܈ONQUEAFI.Hֿ f@c?>JޯUVeWt8{'7,9J8h{jvpf8Y1V=΁֡&E;_`ڪ7 Hvg" ( gc*؈;z!!?W.&BPu1qM[!VD{D -FptXee _0&`)Uǫ?g`7@V5CFc<E\BKj3!Cw&VjsdS3&@uT91_كdK>k*0F$ɨOPuVN6Pi8kX/&3(u$r@(r8Kh7iiQvڼ){;)nJ="lzmpit67,kt@Ү݄MC3ϣˢ\3^7>~}Ixw{Ƃn+`N& 풼՛,~@>t0k?#pS+?aPN 7A3uKX<=2cq!("'@Qu}Z mש"endstream -endobj -3707 0 obj -<< /Filter /FlateDecode /Length 2384 >> -stream -U䁍7#a?A՝J=06GQ ig_w"J\̹بUy5ޡ.ħRƐo!ֳfeG| 4F <. B̶SRC-0ȇn-wIWq"?AU]\s gV:yFb alOf5.$VwrRANƇenL}en%3PFV6٨ `Z B7ܙdɀ^5ޢpֺWC>?C%;.Xzv!WdC][}+=P`:7 -46 ''R.iq Y&;}+ hUf#hG]~[,B<ݐ'$0@Ӽd|sgZWXRA,ve[/5#PZsO2UɚMMkk_ݙMvgmn9 y5 -q 0$h.^OSձƴƛWy5fcXn t[bװᆦ-$d/QLXSN?t_} -:%`]Ϙo PVzp7456&[<^ H/;7FSޔt_A= ]o6 -v0o=F~kDb':?n șl@Đs|kP&WDYV#}j -#V - 5ċču6 -PW$vSSn@y1~3߳}CS^ TԫNZ6>Q@vhh #ʍp.ot2C_tf3_0Z}Yɞ ض9rO.%u;@e -Yj.tDR@-1HYWVR ӋP*f iயpN"twlT͈euCY0|o3Oa'Qq{\;=~CNJtc(m>ynVTqݤd&Ϝ;3n?r5R+> -N^L=@ GE+5u@8qq_fB Z ˺=8oèMjRXGQPiRZ/NŽ-D%C]z?oZ]H }=GĈBn6w>0)cQ7d͙hզ5 e 6c^F?k wF54߁(XNTq]7#,8x06Q\p۵Ӹ=t=5Ka^P"NrE< ncJ֝b0"|z 65Ef|9WnS;YH~׍o4z@W29vGTq+@| ?F.MRn^$ -X2 q~#fDy\_9;SZs:a?G%?G[.ga ghc<+GJ -ָn_Gu7%3JG;RgADhm[AL]]no -kN@#'/v;(kXZJ?'g)t9#7nzOI[D}繂瓴@>|HuNsHx];TG[Lȅ<뼆e[mLbƔhgpJle96oLBc HDL@1 XhE]YM͈R>)tH!dkL9Dˬz1pt@d:yo0I8vi"-d`j,,d7LXVke?T%QŞ3iı)BZdM ~G& ) fPK }U`(֠E%Gy]ޥ$:o Lx"_L{eI3yEꐗ\ 0oќ>endstream -endobj -3708 0 obj -<< /Filter /FlateDecode /Length 1360 >> -stream -8!=3{J ˞+&fS,2bCe#cέjD7pM+>Ic&`@3T1ڠxm1Ҕq~+&FL@#Z w̯8W4v6 JͦRf! b\wlM`+1BBTVn)w󡁗[u\1jF4 -W,Xeb.m/Jn0KM7[E2=3wDT'Ji%Q0JKQ` rEƢsfxkl0}67xi0T+q ͠|7 Qnp3e^z ؐ/i]Uwi9B;*`7O>KK6Bx^9[JM7rF^}<5Ckm+Xmk,V⌨>\\ ~OVA.4ZռR#b\<Jdz&Abag .<jcm;(fc}M|iLS\o&[r[!p[KD/ MQWI_ TRw%+HwAW{C;bxd+ȴI+%[y9ߌbiFZ)i5*tFѯŝW]: -ksǾɴBq :N -mDk1bx.nPp8OjiT*h(mi^篸U!vi]|=9} -b;^?]Ubi,ԼwaDU[]?S3S!)yV-WE=%L$Gm+UL=bF^A/{~'^#J}ct3y&<\dt[Km &اt+B#_A`R[fRaGء%vs݃.!o*բendstream -endobj -3709 0 obj -<< /Filter /FlateDecode /Length 2704 >> -stream -LƷr~X6W' $ꬂ~vZ~l#xnAIKյbN':-oq#Gѭ`(,`Sz'"wmpT 3NB&x?x(F' ,6Zɸ>W(lRS.<& zQoatKiKwl!_wH(\#1IbYTVG*@+VcH~:)8}1l4UЄv:+~lx &گu4D0Vwɒm1L 50*9{ƇAc1~#{V$xpk^޶囂GFfn,_ -Y~t\SYclbLot¤ˁf!Y\m6E!2"Zirg#W3aJThjA>Y}͠ɗNj6wy MMDxvs?gՂ]/h pG{-b+ħf S]pV,Gn6+֗30wɢͣw C$[.}y&e?[J۳ -۰T픋T{.rrLuPA`Ng-ux>^U5vU|+Z9Kޱ? :ۮL!1gwN}Uc#p,8XȔ6k NZPɶG.Ek]b?89&64kml^> VuB2zX#̔|\f99,o]R }%Z<蛁=3 (iuC G&^NRa^K^ ,M3-lU5I3ˇBw%J#)bVq4I7.E'afy'xi#E𚣻8,Ꮽ/}!\Frr+%TI e^ -ʎ@?7iDja~`1=n@AZ,avs1uKϳF'Li[Z%r!bp2 ¾TÁDpl_ࡧن\O䕐T ]HԾdE1XU#[@IsŮ>㐮BN9 ]['WZy1Abb]MՃs+iUzfcIsn'-0}6)/reĈ#On-XR灇/_>,<yָ5=Ç/r?SyOlL̔ڝEԷ]!hǾ\VN2uG||X6:ho e\}j==o+0fB_6'0LmL'GvؓЙL~_q{EtkEVA'8at3(}Qǖ3C 8akw?jU&|T&Rpy LzW6 -@X`{wnY?M -Ydy8nnȪ@Cs@aR'5:އ }~:yAN$e hY%[}u+̭>)>ZZ3G<'zkY߻纰xa0 .BƗǗכw#c-[@MNuX7m'NR5d ;~ ,tӖI:D]fzY "}W=JA8$YD;Xhx>6O5e HCt[糇aAҷX/|;э>L%.~#v8]w׷7.>EdYrvvUCdē?ưIV:q̝M 83T,nn-p,q#ml,!1]hGR tz{IJVQPۺf:Zг}UHggf$t`Mɾj_h kJulM78CV(UB K]m/47o0oOq7WĔI $;IY*ӹ*S7QF!6Ca:?ݰzÆVnԪzvy@/Ahl^uF͝=)" -0>]_O=Kb,,7ujv)ӱrKӱ@X|QH:n}4Ъ+˘ߢ9EUvHR -)# -&Q; ۪> -stream -l1tK;UiéN j+-UBX ld `=e0ӋՔ40Ҡ`Jc=>H?\P -Ty -*ung -n+ݛCOF2)ƣ]ƌM\!ad~oP7Qv/vq:I͂fIc'RlSg>kJzډ0a -גac4AyX727y&d8q "lp| Aٿԝ^/9zk"pCSѨGˀ r _fMgPg `CZJ - of,=Re'etJg^ ֭9hD%N~8E!ѣqgMRӨw~scn6y1|zZ bʧ O\ZCgM 'RK[Hq6<&ߘEc>b[- x3.Mưcǧuck96ɯme/:~3ΐ7W:E>vR<^W, (Cp)1HGM909>v]సl(5(QWMiʱzbZHoJm)XQ[*EHo#)i؂QɄ|2k8ʮ-^ժ ؉N#A`BMb :Y>l޴3BI&BZDzī @ /= -CgD pq”&{\D]a4\Yr7tCv lptQgޅ݅a俓=`ѬOp+ 3fXܑ ,yV89Rž8~mta(Џ)}>Od^w5gͲnaJ +[ 3W{yY.3hX c_OB+hq=pHqUD&yMk~dK>g ;>, KL(jq߽݀J#5/>p䉘\5dVx)Ž.AuLL) fQo}QSwK< ޥ9Bp|lXĒGi7)h<&9KDzx@-EٞXj2Im.ɒy*mBlM̳klƴI^ -}ͫmA'+csw АaMy:njf3a$Dӕz\ T.VfDoI>!8m ^J2q -/֐M"WbT$o_ rx:7b -XO%r!4̭`˄{rfbĢmԳ^ϵsn7cc<5e -TvKՇvNɂBJ1βX"(:MbU} R}_w惉˵?\M-$ &bQv~r%.x6p{>Il) 3~pf-VdGo ^-N\,Ȧh#wHƁur|mQ"Uжw:\b HmWSu\Ɯf,C2Cb?cj{7ex<)3M}mŴ0(lEn)H!Vd{V;g{i9>M[5Ad +gC dNTvL2$cb_nV(++J5Ȉ~5;FyjRijtf!JicMW|ʁu SGYmUX_Dq'+D0UCkΨgb*wUu)l:VWdr4qngdV23Bm|vm,{}>u ̄ɼ1I~Xepo[W 'ˎ)(r_,]&$endstream -endobj -3711 0 obj -<< /Filter /FlateDecode /Length 2752 >> -stream -Ff:ODBn gb``?մ@]B6x)q%-m4~."e 'W?wd6"%GFm{6kv3/.XN9UAO>nˋ@KaCxQ'u/B/eF4YjV6Սhhin}1Tcpahgk>"Ff{BĕvB}%kקIJq*rO!'o_,aΗ&% y*?$^PKDnSCqwٝ!(^~z˖mމ'.lۉO[!;gyx:6YߵvL5Dj2ӳb%Chǎhᐉo,H eMmKG֋^%Z!7!9:fapϬw:΃֍G5]Phm$;CW$".]#cu&DBXh]@Nc k?qĕ mMMbuq(yBR\|tZL:kECXbׯ|-URw4jNX>5ܠV$-^ѵ8Oޤ;L -m:L Wzڰಸvxf s6ŷ&NRգrL< iuL+s}*lʛqya=+)*w jxvcw䐧N<&YPRLT^ ~DꨂmzI^[$z &0Mjž?C)E|Ulcre_ZugrvnLEyѹNf ܞـH?WE>&0!wzZG{8xM ҨƇJ6 -J@@cvd}l˅ڛG{x~nI$8{;`fX25-LΥ'RBo 7XU? D 3Lt~{(p%7HT-=lS없DxR]dH(^zy0UYTHj|E]5ZI:JlC$շZ g&"bP/["uE (?epj}Rt_li괛td'ٕal8$Ɛ15.I<(&_<8ڔ`s6zXHT jSMcAp@pO>ݽD5q0EPG4Aw J to* ^Ll2O+"E}?zJi;=BSJt>$낝]lt -O7ҋzix9e3:4^5Y*%6n`C^Db -UDz1=W\ !endstream -endobj -3712 0 obj -<< /Filter /FlateDecode /Length 2880 >> -stream -)rA)6FŒq(m(xɩ94dȥQ˴ M ->mxƀs=xMʐP ~& UA.76bhI%ԯ$, ϡ,􇩎#f -rޡ-"'}. 8 Hv] c[-tqĩ],w= -6Hp&P!/MdŎ!# >3 v/> #ZUBH;{[~>52,C9aҺ@ɰ \T*GߥoJ7O(vG{󔐜2MXO(I!:܂&J> 7F~B^dP4y:|R44R./MFGn% -\ 2xNs_=0xtf>;~>> 5fGI+ocjR3a 2|; ]vdW5|@Ejո5v} SQ - -]kko s5ÞrR+5:ɗ0]ɞъN3m8;UwWZ<`!P*7xw17jT^0N!/&dc֎VF})%ϦX*j)tqQ_nVUye/jzDNHNZޯ{U/jsBBg$:%\[[/f7~&ytev=?q WI1GS##wDU,29w:CǫW27^ -DY_) {cI 7^5YڀU!qAfofx` `%sѳMfsnTK?Mc5d[3XlF- Ĩ@SV׵O%Qrœ؈'~AUؗ,+k#OevވN,BZqeJ^R>G/|l?XnlXb\֥`#mV-bB /,"\OkɗU -Tb}le¦EXVqN|`_@-\τiXv[`ܵL* L/κ=g+ndg*2|<:|O|! uE#\ԩPF@0A&kwVs<Rnkh c~^[_VQk& _fbȗ{҅,eHiZaU2#=0ua4;U;4:PݠGۊ/ot/@:]תVNE 3-\^',nII+HHD<7*>PZ>XRr\{n0^S( -55Ґrr9L}W 9K-{n v{"-9 -R 5òE} /,X22w>kmk}Sb ˭)X = :u1# p@jHOԜ_(&Ƥ,9ຒ#Bꯐb^{~hU"Δ8.%4 2l?a0m[wAe!I`@ҘӰH}<]v3(= s z -27\]\ Ib|bHٱ"OGIW)ALZ:8_ -x)kɱpj33)8tP߫1Č,1xί ( U..2t!A-ZSD&LpXW>m!xvf.D{y`m fп ?@/`;L⎄wؽwrzr؇gD vٙv4ԎҬh\$eAqB'i )LJ -q̋2s$f=d G&,O܏=kUZĄ(Ye\"BiȽ -29F24x_#m`^x"tObu?1ޤE|ò(aS"ˊj -)Z}0;ɴJI Cf`F(Y5t9:2Yzli|sY\|u0gv$g]0S \Kۗ4Πs!9ԸOxzǠ Ҥ'Ť5w`pR7ց*qX-x;&R8CR_φػrJiPôQ!^m(?b?kr? >6 10#倪3%~$#ڷLN'as ]Vd^}8[o>2/n1X^4ZcbDTR ;「+fO5R;g< km6V|'c28|/֛"H\)ZqXga(eI -_01U:X8-agPWr6 ݈p1:fendstream -endobj -3713 0 obj -<< /Filter /FlateDecode /Length 3104 >> -stream -1 :;╰ $ΌX}ٙ/؄Z{*ᘕJSJC`%g\i54 [/h85N#Z𱏳Qwm/)9HKۭQȗ;3s )4+v);E=fL|5 S5tV -aRӌ)`DntrH29Tu&drYkG, IP)؞mS,}ט&nU1=+Jn'T'$(l؏JP$D.ԩWElOVDbMr3[6,h :b>)嗊8T/40?2ͤe(E2FS'd:^T)EZFQU{JYjԐO7XgW\ZNӂoY5^5(GHͫW~x7=nDS"^j:҅OX%}+id q,ߓf%2l;8W2*윖|8jEDR|S+,M+ č[/O͹٧]j㸭c顗²Ŝd84b&Lbp@S0{ 6-[yޭl)tbhkf;vß9+ " z!v>K:rUbi,X|vXA7* ӯ]P68[qF7s~ht09Ig; tIzdoCX˾>'0x6#[e3룵w28(AڼLe\V)FoPN[npxnoy?ʑ47zۗ'ޑ^维77NmN ;2:5IțTĠٲN8zT"t - n;B'B᎑vz1l"P\9'Ze3N9{> -b)Vw'BQʗ\3:ԝm(Ѳ\tpʀOԞ_OЄc^ + ޡr|Ř4!dsuJJ<1h+UAڐٓh"BVnb$vwϧRp[OA:UO2̑;LVqZ@ޢ$Q,]}1:X9^ -^9:(0Ddj7E Ѫ0Zb4$'rSf3V'lNY @XN>]JE^{ @@o1}&$]u3t Q|J@zZIU>},ӫb6(f_"?ćUnd[,^p$5TV |{f7J Hlw tby4bX/$4C=GlC| ucS:=g?\?[ɕHŻGnG@Z%DfLt5{,ݽMdT[-wLRpendstream -endobj -3714 0 obj -<< /Filter /FlateDecode /Length 2832 >> -stream -ԁ ܟO+6HG%}L3ϣjrv9Y -;.iKWU'W%3]mTEnQ'mƲlya}.6V rPѓ%<ѹ*Ѽ"qߧ -IEΌ?D@Ww G jCoޢw]B4xJ:48L`3Qo:qi5?t&лTFpU 9-0HC4I"gsZ -^Sdz/30~UjeezPFNR}f0I}ղ.>3E_/S)6L]9Ty_0JVQWF -{;WC"vE0IUA~Ym"vRS3kd{8NI=i]šQ(nST~^nbX(xEN!AR?'҉ap9L՟ -.ֲ#Ŀo&V >Lbf!B4_PX`g PSh<Ѝ̵̨ۦ:N yPS??oGW3%B/`0}M3ȵ/iǎP%f'L| bVhΈ^̹QvAG.H0mC-W͞7{U[Mo663 .FEm1gBI6W[Zh/g[9iR+R"3./MUw'aK-xsЌv@u -򠿜RW3mDg ?*6S&.N>=y8oY61`6v/nj3Z*ZECP4X\0ypszCd6:,WΣWM3MtםpBIβȉ,JŞ8cqr6T PDVlEExkI@oprL',y -@ 7*!n]pD?N**5 Svgl ˛Ry) x_ΝWPAmc3lxvΌ2܊7'd#$7 -k!sTCcp$䜽"ToAiy:d+%% 1{$rTv7qZ::H|^qH Sc/W \ugiT]{r>p./mٻ<>W ^" `ɯ2_Vha4,hC+urYKkv} z> );3z @oK})ہbJ - !)ɛ Dۛ $q\vt0ЂdɯG 5.U2uhLՑ$F4\ mGOt`iWa)PMw nPc/F adyxjso:dP%~1SAgJð.}g;rR1~[EnGucײyu"I+PVnb?p`&MP!U8DÛLKf%)$0dbsesJrP]VN #N3fAf p{>K`6C$cp fT>~s)#`g@0ą-|$yqjkS.5ƩZAfQ{8m -EUdJ_yg('gfb1C 1[{fF96?Xĕ & =l >CO?λd5s5sg٬KM.'?i+[O+雃sz bE,:2|nvS0U7m2n46x:5putd){sd/ecjzۻS/2b yk`LV$&P<]D ؼ3E3[|.l -Y65l!{7#F6@8R~׃4(W\~:V3QmN?,%_tʏ-,K6.oJI8aW IPӴ wʼ>)e_ OvY*j%ۦpȲ:b.*LE0\\]1s4h0RYo#Ċb{WAP+} o!mmOX9} -F**GCYJjE ȷF(ݳ²/-fh7;mCx]Lz|~3;E2z""JU3'$~vF|ir -fXɎxbPNyxmPh$1P] vz$ϩ$ it˅wt6&LtCo7 ١,u5! w.3R).:Z&4s-P[W"#A:Zi{0):ߑ&"|B'58)Fz*N?1)lu@42^Rc^$ŐHoӮ@ Cendstream -endobj -3715 0 obj -<< /Filter /FlateDecode /Length 640 >> -stream -2Ξxk#Nar :_r&*UGΎa,>w4xaـ<N&|cA("[:xxm>1XcϺ en- 7k -tU"aNAᑖu'\8?C)oФDn=T8L6Λ Sxv;(H"wt7%l)YM$!ѫʿ\ѧBs*0B+kLDݺL^×pb۪Ӿ&:M%+n^6x*N v|MޭEN>lz"]Hd~R(_;/8{=2,>=Ne3Oy*bXx~g\4lbM F2?Ń7L~/s%Bpp E[כldVnC -C?<] y{ ;@?aN1c0l3y2_,:d\>&|+ݹK㨤2l (=YďKv`3.N;5К> -stream -/ 7 T }+Հ+^o^AcyP=$hO}kfT?sJª']!h|A):b&o:yyS*ߣh(wYSKcnqFTr˟r:EC}r!poWe$-EP5n"pSHkGP!@M!D5qYD|@RY#`i}顦ͻ"F{DmIkT:ǶIuw7JÝM/1",c -p(?|{9e .az JZBn+Ah_KDhCF̆8fn&pA3 wP6O]ep۸w% -_IMN/NHU -:զ X…GgVD 1+5Bv-BVE`=Wfd"FqAw2v0 NLޞYj'rn6g*B%I|b Ȧ/iW[%vДQc0P>uQxox’V޵o^]ζf.!G$(yû9YJGR*;% X x x\c.(#UȂQ6+&vuc+ضkX. &j,KhO(]hToF_dN];JXgU#F' /\RdRN[%3pQrI&ZwL<8\kNVe)N:lL!R0.ll(n"5@\}::nN``?! F!5 /}} RsM]b$9[* ͔!r_>fwYFJ9=Wfhgqd#|"i<85Ӯ0?,Dȷ;j!"ʆ+ ?fcl˜euwMCRakljY6g37:r\K}W| Us6sv"Pf3Y#夑ͨ6rfٰor"}J`PA8+ЂA}!laiSX=P #^ [ -Ĥ/=({tFFOպ@RWz x=ÜCBԤ57h cT[+oʥ#{eIM߁VQA.[>YjTQPECj(6\0y)o2S޿^h!599#Mp"4|A8KY7tR ׹unY/QZohaD5(ӵnv)slI=zc R!Q8_}N^`݌ -7xFE29VHk;AT.R`|t[]SnKݛʘ g*Pޗmo!lWe)݇3Fg -.$ULQ3PWxu])iy~(8/.EW:>^2lWd@Vf%< -9f%KɄ -ys(m>g*.3O';dBQwb ^f&%MlNX9/v Oѽ -J ofޞk~ -J(RN6~@o |?d]I ..cڸY&\LX4Zs :riנʈ]1O0N(Nqkjq#MW)> -stream - -|+qJ!B̯k ҢE.0kT?Em: -]Vgp3ҭ 9 %"*G6b!ߥwguGCbJW T.X*JInQ"L%m!VL!X`8 K|Ra;UBYzne2ZCjLz (ܳUeCג3¹Y}/ea `F1=af1j~HI-9e -OVG %hq`[Ѣ&`\+Т=a53-͆,Yα.PR;+WX2~YJb:aĢI^ gz 9JA(Љfg(o ໬L1+U *5,(p=l7Zq<[XFɜy󜬀-Yr(XySu ([o1\w쩑Uw=mji -)Uf[lB!7ᜠL1]55Sd0;񣭵s{ ;iս*̦``j - -࿡=tJ(m|86܈tJ:؝H\ GI/m Z>4e@'ϐh0 (Y:9=H50C'~_T0wC4Q(㌠#lxauά]9B7{G*Y -٘y^"Yֿ.ݽqCil]z$gԙ,ܟmL?nqc xfN8 5 E=y8B`Vu~zAzf׀AڮhqBZVq5Tpu@`oCnڐl;VAf[Q6x%[͟e/PJX/?/; -6f1%6Lh]ۦ 9#Ā/랺fFɴ~Z:,\N-ZFGZ嵶kbNw{&mZNoѹZٶ32~]&A=vLԴF- KtV.K-gZy5n&@6A oLF8ߑ s&f+:tآҮ*$SAkJ. m&F^mQణ{<ƞ%@~BhBx^A-IL,\sj2 ˣȝ%tLn!;,js@?et@1VCoq0|! -^_b1E * -;`MQhdA^JF@*cьKSnN6TH&qA.[N;A@5vn6=, #I=0`Y~".U}\a{wsdHAxz0ц|PAhFث/t-MvR ]( &s5p,Cі {%;:'_\! ɸzCjxm<$4pEL!6A!lzG/,cu+! 65F[-P&*ȋ! l g 8gL@`m(In\}``E%UG}ļNLfY+)ErAR]46B7 [(h37.8(jKIHpe>޿? BiSC_Ji|KʴS{?Y\D~[Sni \0 5yjCk,XCZNh7㾕ܴBFz5abni@.hēWC`FI193ȶl,]?tE A+#XS'JB^`gHobQ)z> -stream -4[f ~x cΦ)WnUGm;Lw?knK >bDW!߶O}#h~A`O]>ٗA]-[4*OLy -T܁@*ۧ%1-2lTLdԙ!;{iҽ -X[≞U85ڴo>;^m`!%:eT}w -*: E*28yh.6jbZЮIN8# zIֆ.m53ؖ}7/*="nxǏ-Kd{P!L7RG^>#XBf?'Y/d1zUE;Otk'ٽw')MP_4IFd᭑ԐeNNJL&Ӝ^24Պ`DXXE4Ib\]?{p#(Il=&j2"-3fKMTZ 웲x^hq{hzU*vLRņg ;uUDW( L!+7ٸ dpL)3ft,ŬUl7yl.nsc 5RaUm'YiXsƗSTiBp[F_gu g+IhH!,Qb^{ݛ5 I3.Swd;1 C9鱢GդeuD"P2xrG^^q2؝_hv[?=& RL!~ʬSF1s0 QuucD -C]BVDc'U>Q|'H~*cv,{ez }=@eޖčV1 x@~& j Z6|vX!u󚣃z/^|BXtK7Yv3?i:L^MΫ#G%_Q}[K+).spT-\,vv(Ͻ_wh҄CCwe]eH9vg)/c i -"iG kLb~/ܼh'QTa=B,Y@P, >%Ar<Fh}J{It(awQ9(ዄZPBGDe`k&CX>]"a[,r")A؞GjH=v=K.*/!Ґ1iK;[ IX(nLK/?]nloŸ_È<;qR+D{0ueI*pǴT hSP5dTp!KVS -xnLйA>Y&lEtv`NTf<7j$ -l_hl}e?{Q1Z\*;=tbqA@fN 2,iĬ &-B)PH R`9{Ѱ`}vu?{5\FfNִ2wfUVU` Jʕ td7rzÁg/>Am륾IED!a?O߳ EϾBIiI* 鹺ڃ ̯<]BzL@>b\ W#%Lf%*Oe]ۧIho|* -ʸܤɟYr-⤭YxQ]=)򱸤sʤn ՖdVA-nϢs/s%q?wDtvs=⣨~Q{A. I\G:tle/i`-9@l-ᰴzCTeKUdaMMil%T` ->y*usxʩbR* &Мgvy"` 8!M ë^l"%Zc<)qvGEbLXK\ܥsXDJ rـzk g8b,\^2aetr72/Ł%o H$=> -stream - ?C -ہk&ѳbihT6e-X{ažֶV^vI͛P35HXf>qbP]|wJRT1#5{}51gz=.UtjVNLFLa? &_@޻봄"Uq)'GvtTi_LY7[Z#<60dd:2z>_`@y"+?XM.m eH -UD|*e܄А= (tLûd{_m6t3J^V QVdG'Bls]W^A>VDS&ߚi*(6&TpF8YnW/eح/. MF?Ō';eWvbƔ @z9Ig+J0' -{Ya?[$Fm>A2fzydZU3 VKaQt6i$/"rY6FR.d)GG_9v}@tm8̀Sf*=$"|r0 |b>p+ -ECO4kJ|Cq1wb7G T8 nXL(MwX: _E{Ҁ <t#d*ʹ8rͰ2\ns+N ]Vmwr7 w(r -7Ii+OY}Kr5ޑe93ebn3RCRUo4$--u "VŜNg*-ny\#w k[b -c>51JaR`Cs%:zm$DcWx.Wj)Ì -H*L=I"eUXcXGFigBNx9tr߹h]OX1L7' ~I.b{+aKa<%? n!00qS fxBM=\bסAK#"~ucUYI[?I(}BnƎk&WS |řk2箯!g5 Jz,:xJ 2ׇNSˏIp -й9t"_19>ؾڐ?cĥ@R.n:z2vuHU3OlBOHdYk3 ED7 A 1.0tpI~}0ioj V\^1/,΀v~?RLJ'W(w>gb厍塖a/uv–}mopp -MCDX$_)߈o Q3jީ.BX晑ύdx_b#L{?T@)S#D=̺r[r^$܈F*j4؛+cc#׌vV"mǏgyD2\Rۗ]o Ž2a'x+E/&zjۘnnn@q!H/zs^^rd14(xzF}{G -n Ig(rR^JmOjƄq笾1 *#rl+^yXPUr&y*"M+پW̵Ȍ1`K(۞xhTk l)!BRU 0H֮G,s S%^콗UiRpUӈxD`| Seh(T?X[MV؋ .X9n0r0ɢV90Ԓ?^!J_ԗ>ugԔ~Sr3qC3ѷ.ZkrBðu<|.>EU( 7W:K>(xƪK>( ;FOEVNW-Kr7luTDE/C׿6y Bs,vtl W ȸuv6j+` $"܋ZJR1gOaZr Zƾ*oVj}"c]DP5frL#IrP*E!͘1WH)d'}X}gWo ޡjT߼Q1" vuNvgݵ1hf7-5x*> -stream -g|N@x  -c>9])s'7Lw*1oAT>mv 5VB La pO3Ew%jx<#<<|/(VE.@ q@iE-| 22UHl ꐎg%^YdO΂ݗmJ(-EGkN1}ނ*/AcluVԊ*UUp@|k "sa0ZA'55.>^EHP2  "rdX_on +`襓ҳ~AV$Ϡ c7ՕdBBFH'si1.ݤnrS"pR_@Ֆ*|2 aIAy\ᙊ\ hM/2NМdxeҪ_xH#J;@'}8U?u⬥7gc?A̵ 6 vDKC&il'@aaJC),ϰdPsnP57JbW40_W<_}o1W&glcGv+'azfA)qqO8 L:j6iWԞYvoHʔ4>"nQ]`41uuCͪfU:e?:;ЅkV2(%J3}z恱@ÓB^9%}1Xe&OAr+ǧC=ABwdf^`6XZoz$(Hȓ@A_5S_#-} -)zkC"65%n~fH5P]Th RYBeAʯ0ώUP!%" zx2N10ri+ jȱSC<'!sWq9b+~Cn=JD@O]{V ճ@I,1Vwy^c Y6KMwnΥէd2%_E)$:.OuBKb`,lڝ0kTO9!yٖD 7PrOAX͎`%V$f+;7XƉ#i@77CC,6n1*Z -ү{BF60[>a#Pim]b#I3ȴ#` MnHa`.J ߻d Sɺ+v-|Roik h{E42]NF1]mwdth؎m0sOC~WxtD,p?eG3yGg`Č"On~TH0Ȁّ!C<. c[bᦆ'9@rZ)6/5JNyP:Xf( s[L|9;vh(RDg2:n _.R󓒊ηj{t {VmGwI܍VO(t -zTMtרP_qH&zspRSF//tr98jixg~ѤifiʆU|k v-=S7kezx&ʗG;Ul=mZn8'~J)?6endstream -endobj -3721 0 obj -<< /Filter /FlateDecode /Length 2368 >> -stream -Y`<eOK!!PMΙ\2Q*xzݟLj蟅7֐>W5fA{Z -B*$>{`FuS=J "يT4Q DQx g5瘋c-m۩u4naU#?6ރo'JFZ(Zz^%GLvPE{݊9 \{EgˍkHgjwwt =/ux1BĢX`.ؚe;oG>XNEjPv]AMu9&(UL|qB1FX caktۻ_dXojN ,3j/:p ҿZо.z4?^L ?jqGZ<9F൬OM61?$񼇋*jyǠ/EhN%/{ +$8(ǁW[rFh545KR.V?:p{t,}!2J9ӀIpEv64L6 -kO+Rե?Ǽ/+X9gvˆ-MT27;a{ ,ն{Tgd ~۾.HYN܊/%~y-5桏=yaSYi}Yq߲cM6c v pe6_ Xh1h -3~,3%8(7@Z<OXP3hv,įͨ*!WcӺ^%9ö|tbyWT;rGfAVʶ@<ɿLyu~˯ `bI'is$PgSKW!/aX35W=3)0Ƀݭ {&]7bR^EZz͛jpS 5NBuqc2n?1g^DxjB,Z }tS"&Kѹʥw6ja)cG7EtQҫ,,jH1P$ZHCv%% %$JJ.#*r#;Ym -Oį+3kuOgq[ |ƚѿ)J},z;n'2}N2ߒLUZd\oqkh %kk-8 -jIY+]ZQgCL WpqXلC9,wS!5: -͏&#fa$$PnppʃzMciݯ$eGĵ9$?UBO0W =(9wrC{k=hV+> -stream -Nv -t}o{DK -˾Mbq-d8ټ rjip"9"$T.󓓫L?-O?@&v`ذeC+7u/զ}n]*|?4g}IŸvW6َYOӹ~Ivs 0-^EdzI?+;ત3>>`rZ suX.7U@H*tHOĈ^tMjV_ߢLJ+Ļ) ox@TRϛ@,cp;#_-B̅O3R,D< -]|~eB握mANW4iۛN PiVx&ɮ4뺦ㇳɺۀο󊊊.ȘcH6m1l1}xB.jފ*:4!d:TVCB(jYuR^s}k[|FJyæ2~R8‡2.FaFpGxE0403dĕ 7`%,Q -k7yM>3 -u=LY#,sw_J>S]ϕ@E5e#/I0e?4E )WV5㐧x -7Ae4yb4u!81mܐ7Lx P%ie"^b'*\j*?S|Ň<> -stream -tZMx| [p)QO^>u"T)+{4m>t\o=sOk˷"&j):ɳN#pkODB1UdxfVvNS hw{y_* D7K3M32D}`l8qljQ@T2tB F PtC+%|i {TxT:lU!:6sO˙kI _b|oQ2(,=|"Pl܎7CА_Ϛijgh:w/[e_n%v,bNA H}ƞJF 0PG$Zռy"b dxֶF{dF턨gA0  \*) wfHcTd ~N)Ki#lv5p& +$1{ff݃/΄mPɑU5̿' BF b9TnA2H$P[P/VpX{s͓L'CE3yi3WڳexPVdz`>cӗi#9-rvts8b[,Gx~㕰V|"*nZE=>n\ -3U - fn㱏Ψ2ۅ 9*UKk$GŜFÀ,v3Yp"*?'wY1t(W9pśd[-OԨP\npt <>Hі.t-؜9B;SBfɑZ0ۜ -* -I0m*Xj0u2"ҙ9YfjDHFt؉J" O hoR0P@o9f^bswLV]ec~測C6Eap#H&+w k}cN~ GD91ܠpϴ߿HeH겙VY@G ߷Y,TI}IhC !~ɛޜoV3W;uX?BY<<]Y 5x%?;.'QWU. Իptdܛ|CTj1X&mD\4Ӗ'-<;~KV@r[:rDkv}g6W$Vധ<ɢh}Vt(MH7b(֖"Tk=7ZX=̎"C4̐1@U)"`} &m0b` <߲9 q(RqJug涂} 7rUOshʋ}<"ajRt -r2kq7]}Cfq0*J~?($óU -ԥgendstream -endobj -3724 0 obj -<< /Filter /FlateDecode /Length 2528 >> -stream -ߌH:\r%K"bg +y 4&n":'NߒZ/P]SrzY@ߍdUu1B0W~˗ B_',#YYgZ\9O@]vyY@Eluj"17eZJ{t;6&"iDʺyaȼķELMQYkh;i+u'R:{>%.v΀0&\.uwahOP8.|WPŭZа<ҐA0넆TIRB!Z<3{;`SF -xP1*e_VR#h)ܯIސ-S+/o*9+DVӸP0x+o }]LTr oϥ V3v+j&ع O<1ԩ; ub1 0ԉ"<<@޾&ga~FOy=4R=~qe)٫zͪ&!@tvt oRHiOP0xJfI,/] TIp}ƱyhqJ?X;1S9`#f~c+ߛrٜ K ĴCdf_`Y3 ujVt1~aT}qdvth7F:8r;Q@J1;/h.OiN^<ʡ>^ W_i;3c6A7oH8>EB}h5MkY c2ȴ_x -"æg1d=d4}+0A,nd@pOQ*MBFQN1 Dxx]g.g]fOkZFɌGl@Q5g Z &g1q۴|:@-i,tJׁA{ Xa=::_}$OMK~B: -aKؐo׳m=cZ8ˎbzeyɸ&v8;S PcbKVzz)Օ*ILWI׭Lu @K͏P`$GklKޥl&%Yeּ2/ae7߿HqC߷6V -WKxOV&\kz_, p}CMM^ϕO5TD\z!MHi؍\e4v|x]0`RK +P7b츦 ZbQ~aǸtZ2& endstream -endobj -3725 0 obj -<< /Filter /FlateDecode /Length 3120 >> -stream -]f(ߟ` -ob;ǿe :"L!zȯUz yس@ %aj=F7gAe8't4L <_fW ^0B@{B?)`WuqJ%HV#uQ'l7*Zv@8_{4W @3b׻u)3Eq$!IV]X0vҭyaJt(L2ԛiL)׀,I$JY W A20ʙo4U!!%!50BByXK zXq/}Emݏȟ?o_'y;?f!:QmˏdW{ ݰ-Lze2v@TN;!CWW׍DX.f#Z|\'7}0ff%@!Jу&w̧U=EJ*+-TA)ω C]-h x"Sob<6O!g-q=WsEWoSD΅"w^fۛK}XAʷ1Ck@z Do݆.PKE!guӄ߫suB;HxP$瀥+jqb=t9w0oGȃx y.,2i1bcTm̾aQ~.Moa5!wځhPX!rbDkub^⠏߇ۛ6&?V H嘺^.Iݡ1jp p׳zO,PV=Z>A >n$rnA#n+ϧtܶ {S~e+{`{OYeʆ<(e34Ru;gQBTȽb Ǵ'8(Z>̧BFZ23jQ]dp\~; GG͆5ڙEf#I$aG8Oɔ7t ^ VS{̛k|P3*A~3MJV"p yu3kx=ј\ (_\ȪX`{Eo:Ja;WUP2+u$3n1KJ8+Æs_etB.z-_m+t7t.$';Mj漖ī5)Ⱥ6s7Vdݗ6V&zHb1˧3꽮!Y!g\nweWU<5YPA7{6oVƜ\A#=WE&K6Rmg -SzO%79!;v$VǗ2ih"TG;vaז %FLv\Άʾ|<}Vcmg " IQ͍6[oګ$FAw0zs>\yrY=!Ii}UT !`]hPw~qmcB Pa<va-sY$ZK| {Ŀ#+9< ̛nb0C!?Ylol$,Vh!;k '8>=GFk{L} -0O7qaOU ve($HJo >䆞TvNԌqjۣ@ -$za>:5GFh%ekwf;{_:`v -;UKYM;%Ғ7H$x9HJA>+̈`OphpG % 'KKV)zq7Lz#_[!+Jg3 4EfM1"0>!2!/6.9- -D'3WNTnTq֍@J"YFo/"bRb8oF:3sNL+='oƭm^#;М$K2*9 4bq&UxNG^NW{$f$WjcFLD]u:`\&&E8'XXݿ{dbk9$urt[4s^ߴեtQ+CW)ONBO^4:.ڜ2Q=dX -[\a5ғ_3Lx7 x$o5v37LUgQSw쿠˱=~*@jŌr=84Ge~E傍8NxM.=wlܔO޿[ H ?V[L\–5іxT(˙kn r8K`~`YVۅŊ 939~m>m}@8g[t~{ʤ v7.c@XB9\ZmcND9{R@.]9n*,Z_ro1W_g=GM!ZkYR 0GirLt-`]ߐGQ2J;ּvk>r8nX18TM-w$'B+=>) c-Q(Nlo_hq( -Z*cTVSjО L ~rAshЌ@]N[Dκo}SדJŘu{ں\\U8,YawD-cA˾=j+K8#_|Qt:tbDS3'Y`ק弐+)̳(&6J)BԷ;?sh|P]65 ƅ.#Yp2LViUʸG^g).SY k|a^|- ){bˋ#㡘/.rs{VW"[Jy||5 (endstream -endobj -3726 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -$_9rK F?ڙ[ 5ıqL$4Dk'%D儀ߢ,4 6rwlkZ'm6ЅDZ8ؚk=w/lRap+Nt9͸! -%Cxw}fhIXD8 ux|ְl׷vƤ ZSMJJf%lz9G",$n@lU#jAR?7x7hiAaE1uYe?obt-o(:q4Tb}HfGGQU2~K.m[";)irZtۧ&I<4'UfSQ螂XkT[.ze̞uȒeS2rې:JZ{09E )6 .p?y!Va>n1Ռe\!LJ;3Sggj%g&+D֚c_+UֵKB?KZRc\zm؊4Jy.Xh{l. Haw'V>0KCl Uvg Ok _k%~^g=&0#R<ѐG'z*Qq-,]3,Bv?c nbaK.Ih+iT" p+8IZq4+ի篶'&|<~_2˳##4EodυD\&V fJH(*d`1${Nmrw7b_*.-k0抎]7D5ѿ":E ot\s†Amփ8i:1qGpg}eFV'}/|^5\1Ӳ\W&Qr-N["Mۗ r/˗B?k3؃AOb/>S)*3lv Mw)\uءVF1^FK׀:/endstream -endobj -3727 0 obj -<< /Filter /FlateDecode /Length 2528 >> -stream -gUt垇=5"rqI| -~OPM0Du;{c]3yMT9zↁ:}MGI3'L>a#0l U^  Ufy'v8%rNjd\P::3z0{&QsDo,v3"7%*LF\Q%]VL8L,}xGRkܜ9c}:zƝV"Ǻ} -+hKaJ@X+«dXTOgOeyzÂ8CAqg6]$:7\ Cv J,VrEjDղxй&|$r. a`w=a hb297X\DC{@–k,$W:.[.ZT !;ɋ!Ha—K=0[4жoO3m ۰&?ExSfDU:iŌKC5~lOveh }} )Jwcc/A3ԏ 씫GR}wU:ϓ^cG>a8L2H[ oܖSag,ݲ?S-Px -}E+j+)I xy j憆&) $PmR˗z$vslyn?-qDH x'e,}ngv4T!;2AaĬde}dDz;{Vp OtzU>n Z ]Id hsq/vS 9@1&TD/:@7㬜䫑"\!H|R7 nߧ," -RK -JNQM7汯T>}9 i:@jۂ\|^0=ZL C>_GD{Hۧf0E440;좙Y8JA8 l"ppxP(%Y#L7`y\?/A%K):r<_o`Dϑ*,u:x؂X{97ĘNC,4u[ on(oپ%=3=w3¯h'az-X[^2;6xqK/ښȣ!122glCAKХM0I -k`HZ#a ON uɾw, iwVd|:=@EdxH7)Nx䄐7pcPA妝t[Dwynjt/8-kzVPWʲ}gzy9 fLܚ}ؗW Nվ[R{@[l - h2oTie7T- i^j&bD>?T8\NX>V8H\\I5W!|.V*L(mCݤM: o;Wj=srp=L5{L\o+ad>K;[l"f&\GO8[P~K.i=N"ǘurywegY'ݸõ|̕5&C̦4Õ8${)zQ~ endstream -endobj -3728 0 obj -<< /Filter /FlateDecode /Length 2864 >> -stream -(A04{iM7k?*RXˌ%ѤYbt&F".ĵgg.*m -p=Ez)4I`asp=Loc '5o}YV.J:YL\1DtE+H[P^0GS'k_,$\<:I8N q7nuV%9ڰM[_Ǡ3ۉyb]Eu,}:/hP^#0DX}Ă˱E`[dkU^B咄N\Z@&3fNMi>u vO(\\V#uxeKPu,uz&60CԻ/IWUs#%o9))̘|ץj &V-qy;1|H&!{񏘁kY0D[(KNNAsL訉-Ee,~>b +c KEt;MfŻi- -)td+G䆶e?)c=0[DunqD$n.(~ag$Y6ó5iy4!.޷x&E&\zdYovWLt[ n#(LKs0:ԥ ^jBt F{0D5ҳ;GlmDžZ,@SH)X; #qבr@#ץuɞ -YI;MQ60תދ|_ G,u,߫|'i -F!j( E)9H ?TTߛ4{G8>èB 9hrM&\-OM^Q0:؜Ϸ WOF.G) 뵧-~tv-Tˡ:xpCA?`M}m<}eRj .l:EbG>q,V5ِPjI_Ŵ8v<8ժp@6[D(S?\ 8%Pn?0 Ji^{5)4{\?f Sք,@Qe,<;Nx=0Q ^lG^8Rf~ևpXBV3p#Skm:>sa/YBȆɥ']ke,:ޙ5^Ӎ]þµ8$LD Wƕpya u 5ҸL~ZAlgD味1K9{`E#"֗Ӡf$(H -Ε7v~^vFwh=ZW{OY 7!^@ӿ׌ADz"~W~-A~L-[ tU޲X&,EVӛg}89,=\9)YE~xUk1smZ2{o -y#:ߏy5}}GfنU`iֵS~rEyI6!qP'B;tlIfj%-gi5c T=~X\^%]pW? v}ek]r+h"A3A}'`Т鞕ޤI{<u ii wf̀}r,ƀ7D4ʤ-88q[:3̰"!AfK56ip$L`*J|P#S3-Gd{ڌ+n>FN[f|d%壙M!F\+>jҷI|QOah,^rU]<nl ^CȥB{< ]7dls6p[Zџa2صi8shT^`V^G<oj*{.yWWℹ+WF(haoo8DIQw]c=͒38}W,7ݢ\um02g̣4btkqQ2oȳuT?X6ڪ-`+N$3Y dX؎endstream -endobj -3729 0 obj -<< /Filter /FlateDecode /Length 3280 >> -stream -kH=B6PC-$>~%3oR FGL<?@w:GRMd;ȓkt ~HzLc]vсpi+ga5fjO/)8V` -$WYw2 -!SIlMCZ{Ch6eGPz(\jD(n kKl>,>]ͤI9{Th _=n8/U_ǁBRDqw I帔U 0M[ɝސ:92(6~tcamnc6n^Px!PWtQaGĔEy 0-5zI[Od ٞ% -FJNQ 8l2 n*0S2l]6mԮ$ϧ%rs[流\ -(lLwFWzlzJ ey2֯k5O!C/ɱ0XpDاHOS73Q͓|m[L2R4ޑR7?ۗ:T5k1T[ ЭӢ2blgwK\9,OQ2Ѻf?-PhtIpX1 ZF 0KR~ىŞVϙh~U(/Yں0J.,@ -3g:t)i xfKLaP3kC `޺pE8bS*HBOuV-ߥxI%>iV*9;ђm^ u_>9d)_*/1, أZW+n1$\}$ .^tGEG;Y\xBen0osY3"rn]7We`JwL8͟q'\!Ȋ,6Ҫ]4s5:'sGM"- en8kBTZmѶ;e d8F_١ֻn.|:bqokbaWe&g`Sq|W!ȿ&@; c\ ?o#Ad ['psaΜ$S3]j/n:SL0REYP}A+N b7 盿-t![M/aj@o^wMr1XB9ʽp^+emm;CC=NobhYT .`suנ4U\hk#Z@Į>g"p4vmҝu^ -. ]Qw4/)ÌT4Q5\",/Eb}aF[Xyz@IܦH$9!%PGØ:Ьرx0:OȝB8:H EEj%愮7f_vW$[JpgEΫJ&sgRLBm$tΌlyH/xs0dV _;ihcד#u֞],i+i.0e.nj ^v -qI'RADiלU w,6}7p_MtD:hn]NFf"!W..V[z3& -@eoIO*J;i=l -EYV-c%/ɥ~'TLN˰S󠞩:$)ݢsuhpNӈiy;}rnOV3:Bf7.dN&լs2bw}\Ux注p@Hz׻{!A6>=p<`d"ؕ]AB2].zʜ M%E@G(P77G} &c9UE1jĠwEWۍJ5FZ^M4R#tXcnVu}ïa-λ@]s :xjS9痴MY*nT`SNz{/\Q,E}͊ c' VA`1-4uv,! | - ;ZIz6;NB,ȇVO'WQ+JJs10.Zao o;EJ֭^_x36=e:()l1p\bU@KϊTb!d6W94qcwHW%m]SYCOk#~q` rPѼYxO0n)8 -u㒄S!ǟUկ>{y`e'H[|{)mӖQ22].#<ǞWɫ찉W&GJ2@~T]^@^{&e0gh~а"UR?i#;PMofGK h`vDt_<ԟ\Ugb:d0Jj]X@tXY]V$I'5!)7)?U]0uӱ,*#l>BF@'a!fւ_}$Q <&ip&j9J+ (cѷ(g -E ZS.N _'!呻 A# u9'-te:!Xz%DBkJ6Ms3kEh^ b 0)g;+P_@ob@|>q>gFnyo,\LΙ**-CGUP.q'\Pe]r}$QCl\hrbnl) - :E_#YŘ~ \_D3va+;s(^yn - ȑ{E -3X7{|/t %)؆+zi~endstream -endobj -3730 0 obj -<< /Filter /FlateDecode /Length 2592 >> -stream -n:.ܞ$ -:`Ūq#|(W6dd<0<Շ[!`!Y2 pQ %DxsZ}<?2Z]/4"Ψ_to>Bbӄ%P(]؍ : %RqdhlW砻Sɇraf&9R/gU˾#}Soda8D|pBȮa2Ci '`Vz7~~ S2!!)4E_bX*?as3maƜyo 'D㢎>2d5GPxOdxj^H03+}.}ZA,#I o\B'%=R-.P -Ԥ/;[ۉo{,?Å+}0rrWy?˥lM1)IhTD z1шRޞZWX`oY410Ž}We3wV7I*"`6;vN.&N7_ZBW"My7܅0܈PlU"l ;%p;1CN -~l n_#̏ȅO -e5zKs o*ӜH_ӗ -bn͊Zm,*np$OaELLרm0\V2e0sv5[: -<6kVyWp׹rZn*>qiNMk,. a;όm" }f_{,a=wI5Cs.Ccui%x[H]/z8lV\ޕM; ~<~,ZΚn겤UVZhnlL'm+7&IiBZ>>f&kWiYE_;I6rI2#_p -~nԼr(k@!2_E k@),^1&۱q.~8ų b i/{T6IOxO\,11bTUR6GO3G}G4R6kOL[ -P~!< rd|l&tcf"]K6G.bLKT٠+WFS.F7(rP`H /&Q,IkDYBC2> -stream -%,ȫ#ˡɨ67RJ* BU1IHǦ|ooGZӛE/ڳ`@N7EE 9,g-QN[c$ޞ -l 3ӠiWr+ƺ4!+,g-QAiK@tZr4,BN%s Avu Ȓ`>Lt -|}V/AؿjO֎kK+i*#l[Ձ]֞L.J m -t[5Ga/T$8ݠ'{[A(#3x&[<{K*@I!OG?ڕXnt%! @ibQhkܸ;.϶3É rֺ]@򸂖ω 8/2;8d;V/>֕Uv[GN9'tJ*^QY0ްe ~k60NnRNΩ!cS |\ ĐH`I5"HB0\Sv?=7j< 6Gj`≷;D`P`4yřFVrF(H]6+=LYhkɚh@. -T*t XT$ '8evv#u<nߞf5;G. +"@[/oC ΄La(Ic_1 AX賲 Qr3mɑ{&N5E)7/`: -965ܪPE _-$ٗܵXII =ԊArkpVƚ ӠI~NIF`=R,s3ďxC(G-Q(Ҽ1cKɭDo AHb&Cٞ !+^lOr -mw`1Ԛ(#3L2K)ۍ<0op #f UְQ[8[ .ɷXݻkz<1i {G?D^} f=֝UШa}7O%UCf ៟N@ͽ̙Zo&|ͧMe(l4&WxĹD"HOYP=drN`]Ch.i? 0)z=ZCƟf#y·JkFRL@hfX2NqDk}՗ w}NVP)µ*~rYbd 굆#vEUNKXuYbuy0R, /[*:`Rٓ!sJ"*:bkhPne} -C<Ã=L/4w7s̎gV^ -Dݭ& ۮ/Obu5]z>L}R_͘*ޮx±R,%˪+$\ Dh4EAL&&r(glN~;:DDY^1^*~{F +:*tZEtM*qC1NFVq! ],A|0) e--J1 (KwANDYBfrt$w[%**9#@ :  BB?i-@5 YP?<>.i|\rNpO$Hxiơ%u<Hmy}r>H+D T=ނeDL= MV?Xi&u70\yrc4j^ :cPch Xmb(}Rb|NI<SLL3̞C6ZÆS9>u@ar6aT2 FJ]澨p2}PfOZc3$0ή#?00#1eVL,$';8vɺa7/ˊ"N~N+x) b^pi;d[i{j@ l.`(ʣFͨ:*whOjVrbT!b:"_[_^c~0yաQ:lfn(e`D\DixXe9VٻPe>kgU'F%'FO4M& %h6=Y'frI]wۊ{mUYB-`!Ɔ¶U\KҪM3Fl'w-|pjŠhthH6 _?ƃ,[n/Tk ~ץn tb%]a( ͶIkc3?v#<7.PgVFMk$O2SʧV|g(%v~Uq6jq=ܴ|oIgUI#Bbw1R/G9t b> -stream - -؄>d6$]2]ށ_([P}v݋!\ SnCݢIdL4?rnPgNضAE@Tmlg&RRlɡDϷ+^ZvL.v ef5v$ AdJApD#^%XƸ$yl-Vݏ6rQf!L$P&ovC0zRKq"E5{b*f$ W,TKڥ=g) K=;wEӹݷ^.Y۝.@Üc[2ÛwtoB &%[[r##*Z'[& 9Ե!3%J^/PVqzMad^6T2"6 _At#w>[tC.qtp!B69h ?aAXզ9>9\$9ǎe +DϯQSah%IInT&#bc濰'K/`U9c?W*C` bVE#I6E例SslSZyn>G6@spPR,bKrN2ɂNY>>v#v)ԭrE0R!WR!W -zl3;39דSE죢/4ʍMڹM޴I -k: C4<H~J͘hI@WP|Ү@oy,<^εrDkľϚTrbYMOC7dL0^T)M쇫)YTKzes:B;aS6Bl6A4֊d >{ce,GWKBj<$a:^/ᩅ,2S!_=ʒ^zM𑀖&ԝˏ] 7¬i+VizBL w:513Z__1+In` JJ䣼kb_ dN[h/ +E\=rx²{2.Ȼ"hՈp_3S,7 l)E.%pbopX5/ė(R -ހ0>=͔P#v (J:zZ\M)2D[+3ܮCy~Vk1]OQ{\Fӏ eL -N#Wne.b&_%iَUEڎPE^@g\<͆ `( . 6 Y+0q59h_@bMj#mt 8 XMuH:ET[-OqX$۰>{TIC@"*I%u|c} &|?tPtV9SlcB7\L1$Cf^ ujMsΛMnΥ?J;Q.8w' 3-))(H ,yͮh?ƻόqJ -CR Z y0ް[T,f y*'/\nI -6yn+k/%<'>3:$v0+ x$>I!nVtJQ,8mډӓ`O:\p[kP[`Śea϶8J~MωEJuVNh8,}uQP;A=Rt&p74xsPFQm:A1v祭Noc0|(D1PQN{x[Uy12f(Rj"ȷ6H*Z{ZFf@gzQDl k`Wޟ^p{{c%i;JY͸dKI/H*n5?)qGD=AɔXw`'e="ф!.Ɂ|Cȭ\*9 - hT]" ~]bq/kBrj&&دdt69'urP8ˠ{/8s6!.W۩癰ސT!7Q12a2UH{BSO -)T -hn@`;/1jUr{x٤`>p|?&U*sZ#IqhJeAG;iu=%j:1ɵMT\endstream -endobj -3733 0 obj -<< /Filter /FlateDecode /Length 3280 >> -stream ->J/uӉu^yoBO ,ǥδB1OT8ֳ+ǸXj%>3זbq/ DZ(mGk־::~gqi@[~gca<.Bigf@ ^ (Ƌjrsp7 s`0C tBl?ּ4r `5 -yA@_gjL;7X5'6DKґ{qŦAYU+A7O@B*yFSlH%\qhAkᝁ_'fx48pn0b x3FirBz12sw(epn7B[9C:9ͰYɌC ) 4/%Q։"xFt(`/s[=d٘ZUa)XO6^OFDC_b3uFXX+ C᫿&LMRq -S0tf&!6:g޹O׺^_}}+|w)d;Dv|O"Agޙh wW)J,0D`O|:X%$R-;>DdЉ,EawVK1tZ'nfy7 3:.u1(YyRJ7MW`s3ل|OAtTe@'1193t~`[\v^ Y+G,H M2-۰aE#&4>KpյW|>|Vʡ7;Gf# +(A0ѣ2xF-w9og Jqws-m̲@: NwUW7a - |A0pZYOvS{BNֳr~UҁԉM4J6 f@s{%H $ MH cjǒը|B^.[ۄZ!D2-rڷa_?|=tpz !zҼS[:)x0{|;W|dq *`EToX$ *uGbxe D'@f|ORr4'T[\>DA+0M[TV@׃'HtelL^ܩ YxxfrڅBavkPs.6:M:!+WQ<2'E{Ӻ wl9Eh.YRųjZat9l]#hgf/ D^o^}I6-B𸉋E3A -U$S\quX <P3D hbI^ࣔI,܅UoH ]zCI//1oPWy̍*1b,Е})V?儼e -rr (<;2 -+yb DI$o=GMVh ;bd gk0 Ӧ,I!ˑ 9W 熃,5 -.aLtoENbkDmf83Eok~ZbZ&t[JQ[EPM>=XĭDB=`7zϠzj,C@{>S%yG9e|Qg ߐFw⽷ғ_."j"1?i(N ɶⲨ}ZQJ׌\ Yg _R<9ҏCAu0L00O6%$&d2i< -P3cnjh%5wJ [c"CXC R9H43 тW.dC?;BG>k)Z 8]<7AJ7] ۷}hGޔʓWH&/TY[?}* -$/Q0 @Y8.+>Cf}8}y^; 1CR1Y|?8IM5+*s7{p[JqrPSU} ^Ec֊즰kML& CO {w)%eY~. VwS2, ד΃~7W7xe $vڟ9/=%nTh/% 6 HO]@!k_b`SguXB -gi*CKz jKl|_8yHJ!WkFŶ5dy>fnVr4l.aTίH[5sgMqc # -n~)$4)n|"lɷ 60Vk= e4&/@^l.@ 6n57Vm]G0ddmVA#(MDIf!] n9vV*vMYuCnz YПIe*1^)bGJסt&ogC3 -HcͲʛ"vfT~(3PXUfXf{1K+'1q@Cn9A1ZuI] 5g-2^ڣZU9nH26.׹I/FOȉ®Qd`dBфт=%e#īR-aSIn#z? 0RlO$ Ck]4qB`/= Ejvnz }c?8]oV_r6Ś%Ņf7sH]pNnendstream -endobj -3734 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -VdUG7j^SBHk^d„,,?m^b- -7R5;\3W mµ묕٩/9qTSIi׃=/>F#,f$-)z ivJ&fI_\L?Sj6,dfpg.˧2TcG,2뫈W*vSC\uj30wtfcRg9aDz#u6?YYӤqwRS[Ƅ̦H=8'3vϔ?rᆭx^Csnp`|w"g~+DuVF%c%E MH)4X@!ZEa&27ǁȥ‹mD XiO2?SR.%\>w\B8z -6{'NG:#TL:6,vqևh4"D,"N+w-75>teE;,MgY}sr¥Pk]?8-Ʃ, lKej M~F$L`$TA`0.G)CJVQwgi<:`ώ P1ሸወ)*~ &t75Z'' -mH_W)3c E,QJc3B~96mk'JM{LV́ZOd BOw̉$Q%6iՖ -נjr>`xY|6Ì@[ +uy~0=E1ߵc'47QHq-/UOu.۴76[}Ua[E߹x~N<~~qb@{{LQWN65UTjV&e Daޢnoߎ31l׸I KڧLD5EĚ@w\E28*5O(z-J%*+`٣4Cyz|BT~*XI(Ϩ'ZO<ھmIgE¾/گ3`;m>@;&hQ+^䈵jF lendstream -endobj -3735 0 obj -<< /Filter /FlateDecode /Length 2832 >> -stream -D,c)k]])(=Rz2_~ci4 唾7FlFWelN_tؤÒ;g!Ng͞XKKт?HFA譣e-\۩YXͨ E,^97W]m'!ǟP_u<{$вL)M #ImX"h¢f[:rǔS]?\TR[ x+Xupm}z -9Aʆa4Ȼ =~@#`4c:3n-ᅈWdqәrM#]OUDZRs6/{XL)l!^oZP -^nr{E]nq6L2g&rpI^(.IoV2o[&n4Lt%q)MgcCu8;(6.Ht{\ց!;jIAkl|͕O=V 0aEQG.wGlrFDz;Wos 3K/Q_[ -:h+Ux(}eu+O!~K_u>\1 mhL[R;Ȕs1|'a]h"! u.f % ٜT!^sҠ*)o4z *[9$7[T-0;8cThobY$Rqam z8߈$*M_]eYU;oήN1בwEOɆsgU,ÍinE,T$`jn(>‰. / cr B$")EHvЧ9=}}֥ka˓5$V9gĚ 4eu9H{}6Z5h2ѵQmg{L+έ$S{v|Mv˗>l$Up!-V1qI(cZpC- -'AGz.@fFo4lY3(ъN N#3AfѩE.8]2"I"/r-wNdm(Iu0wb.Mcر_ڎ^wU.2PͽpSǔe*_-xR>3o{[zenoy'p{;]Z]ϧ"Rk'z{o!I>hntruh!YL[_O%cթY6#:y`O<{a <7OEC.K+i(])ov;H1yR [ J3 ?=nZ[cwu:$^y?@2$ 3SSۏr9Y44Yc\ 5fY+p|`4ɆW[M;WJI9tV{(.Y4ǑC|jI(@hͯ?eRgUEK!ǩC@pu)1}~l&m5ʻ([ZT qXWH۟q t^79ltv`+[3j^NJdv }Ǐ{I"OtW.J$U-T{Ca J|w}{|o7<`AdG`> -htr1w9qN*s=?&ჶ;ݵOqfK?\5N8S{Qa>=7F<(BJN 8Wq%%Ѳ1ЕWj\11G_mvd(V uJE=%JfOzs6af{C~2)Y_gd[qJS#RMOيVS "̏ Chδ2B]jEW\-vOŋR/JM5[혣"D}-?P3;iSWUv:էWGQVO|SД> $Sα} Q$>@e0]|d{Bẅ́c GT=M$PͿ/1k'$lQLAѺY1W<euϘpu}@\ !pֺ*oǟRNX]zpkw o׍SZHoXp5Pv5X c J@dsq-%R #S}׎ͲҖT [g<'>a؁Yć!E*D1ܟ~<tsI4yZZؙ^/C08A]dendstream -endobj -3736 0 obj -<< /Filter /FlateDecode /Length 2592 >> -stream -z -dlS_ngVYߘXx()) XZX ` BpRo'+4z9X3W`Y?|WU!*ɽ2$DCs#Vz,ve'¸,F Oq;6rP*aww] ngfb .Gv-'-yT_4d?;az}vl^}[nTőȺpG2_j’) -b |Fz6w)7mcS^_{%<+R/g4eX7X^ HWC5^EQW,R/7=!x.0@%3`|ɪbp3:w8o]reb9ԯtAocoƅub7iRdˆkrXdTFx4*0,C4}}MG\]1蛐;` snTdb1["YϣIl ʶ*:KՒ8S. b1Moc0;BGkT|b[؋/$R ^o 7aد~Fd4+AzU{If[˫<z{#4 ovƼ_`=m~~g#2j`8qd IA`ƃGyjOsA{zUx?=rP{2`FR_#FG;pC26]6O(M~rY^UJqne- l,& 6 -#o"JdjeJ!+sS޾k? 0~ظt -bYW*5IxcBAT]૗% x)\7ʂ)rm$_dD 4+yu@cI#N!N+Iyb}Ϯ 7.]k.̍WweXT)[OrZF]_¤,ǫb_In  -F&>cFTƶˋq%I%"P.unֻv.u' C׊EkMB[$j^(fL> C Ԫ%vva!ƙkMnSfiّ=t(BL@t{0_G7sP6qM|dTGDA{ʺD(k(:#B}*ɢ#s b,' -qzeaG21^`Nc1L-|gP/_d' -Q-Pn 2@7ʠU˞uv-340 - -IP 2ʣysBa悺r *Éghm'4m!'&Iݓ91SX~h㊟ *tľ}q,yo5 5/ґrQŬdE= iaΛ_rnI&;UVB2o qUcQq -98M Ny0Rzx"?F QC#-K|E-ܠTbr3õXn<[!ѶF_p6zˉlvsF)p5&ܔ彿6XUE6 >9q;tifešU~xcfN9ͽ>Qbe IYk8qJ/Į^6 4:n\X@ͻ%pOlHU-9$ v K\쬥SO eԪ98aumiWfV2eovGv%}ăTҵPMWʃ] -Cnu*е/eO]'1Y諦 &9 -NCZ0fz.Ry]#jE%nc5F<-uC]UH;X;QGާ1Z7 J(9#n:> -4:|Jx Od"MXfQmy_)j/,W9$h \nDA2F q, 쯿a^I߆D1WCRG>w4u> L`"K6)AgqrvgD.xag[FG[e-8ח1jh|.<$NԽEG)OE6ї yCKr `+[ARπlo~~r!yC -jVc@7F:b(}z;u_k].+)Xlw4o1'Խ`Yb>tP|C;&2{yfY4> YP0W\LFyED~a mݸRA9MWꗘA1 ëVu`mEAadpUr[>D̎+)$MwUh7Ʊg_\X)~#OJa:OAiJ=bWxI6|u)IB|endstream -endobj -3737 0 obj -<< /Filter /FlateDecode /Length 3024 >> -stream -ԛW{j' P]"~/JʝzPe.2Yn"0"2T?>$k#Lit$$ܫ{W SZѷ'/ 1lҊ>u07%b́Ȳhΐr?uA0ݰM8~NmaJzWP\k-':è˄O ?-rmv ^G3Нf p>0Lxr! [KoG%,a>HmR+>RPꊛ -IG۶᝜BN9T*Mzx=ҷLܳzs0Y&V*%wpꮘ : '-ƷwR~:eMDvgf0C@C:`݊ff:Iz^PLpDͣշw-rя;'{ q6$kqzGg讶uRho8OFB73!b?S}S !z"}\3 GoYew!Y &56Ձ+G's5-gY"n JjhhࢤTL٩^pjNkv'oHcl#SE]l V+[|eAA0B!>7i#BFyTPl`*#* St:r6۵ %X,i40(+0SZb8 -_xs|`*2!ə$4؀IK 9__4?LRl\loΦ,6ŷ1u*öc?Po욽2.r5x/l F&tCBZ{1lNACWAL % -~C[BjW¾?wk)!I_]xzBI[s"e;igUYyQ:٘@xt.9YVOp/AwCStG"뜕c e@d}DKR<~ +]ܪSvWa>5[ckh,$vU!m^1c.`耿dZΝt!)#c7>+0\.(THJxr4ԡw|.耞G|lau@~!YġrZ0pd*TYk1EGfU,(h}8r]\'{|G+⾟BĽԈĈ(ԑ":cM!~+ǁkz -BWM["HY(dzgP2,5J2 2RY; 9ߍJmr ;hoלkG?i ;Qi?^7Q . PhQ#/ڔ>ZkmhRBٻ<6C-z`̖Qۼ yF'Y'U;nۻ3UdHGXb25RVI7X4(N%Ϩf-5H!lCOl![;$X{4 -ECԎ KN6ßz+{,B۩W6u'/ln|-n>,R&#Oapj%Py3ZX2C - Lr&BV;8iDp(} 90sgH)"  WZj[Ka'RgfIGݾ?-Uky? ҂Xz~5hULB9@o(w$T (ÁE>{rJf8|] -s7DF?Mp2o Ta7jLGc? tjIKhe;7/7Ƈ=OeϽ|آPѨ0Ok])5ҦLs6yk=QϙRt<=6uέir_08vi@z}tG_pg#Ɔw%:0*y@%H?iFF+-7^aOl^qtcԴE1`یmGj yPC[LHaiBkhe&E;azخ <Әc<6?[q'߱2?:~^[f!, +rI˃=OوyZzzT766ke@kj?gFx~yv Mgސ_c.R;Oܞknp }+ - -fؒ~;N"IYzxʩir~LWr~Edzr_\q45j\4PFES; &Kÿ1 !8MsjeCxY rE8\/EL\KZam%%if(hwP~cB&`<Ŗ5v 0 cV -.zCz#p9ӳDe..q+^|$H(ioY鯺gri>=Y -W9{+k="tS7Sn;99$1,~_r -7mޮ݉T7]'BuNN,pior$e (mGݮf| <fac!3?<{X:N@WB,_ӱ2LҾ7 8鸎cR4^D:8"fn> -stream -~%ǘtz&y NfFXkJcmq_]EMitQTiOzvBrb2=e;i{= z3OߚUV J_h۫Y!,N9W@H4z{1s;ay]s`FdD4G\3#@[?eGLr#1KesK|ib3HqEaeeFR]!/2cӸj͜Kd<0cGUy(}4ܤ=Ÿfà(#v}8~caGTF6=NMY/kXyZf -6?DD[~zs84krxՊ+5Ǭp#+=QoS5TT)onDhU죋#G]*o>SO7B  -W๑oQO -|v{% o{;Qu?ks4eI߬Mp\*[՜ jCS> Aq+Q0:fe?>V[j$)BKiCA7< ,0>)#,jmAg[ek_`#|\eEUD -} kl7CQöj)-MPI.9Ene  g-[G5vPVe4$ɴ&a kAvZ@.cq+JF.*v+V{Jǘ*nd#@=?ZIȯtPUr[oG3\ۮWǩ6RXbTzܜ+-8 O#B 5ϼ<Vq?(;'Au;.AٷׇΏ0i '³1A[#mG(s:IpWQز q.sS"9^vwT00"I&l<ʘ]F" KRv_^{q)B7͵<nv`_W ԙL_Znw1m8R>!zw{"1s <_49Y s+VMS*g!2ŭzZэx`be~)ghF9DI,qyؾN(Q[:;{> -stream -kVp' 7O&M\,heV)KUPzoẼ n=g@J)P7̟yd,#O.tw҆j/:jSDYSn?UMPr#o>Lna ݐ Z€7̩ު^^StDp]\1M%pi48s-+Um@?438`.%r܌E-=-y֥zZ3.8'ya|@CM!JՇ\^7ʧCo5׸/9l!> d6M}Ԙa2t->InfG,SKfPtǩ=DH:ܨ3Ws -]Lbz97lEVD_74^3nÖ@Rzډ[#-V-KZ_;ƻqoJM }ٴxr`ed)Og7#GRQ}nmE& ӎzyc^Lai\t~@{2$U3]vIsl촿aCi, /N%`mrK13$8(a7)شFiQ:TC@=(HrIkĻSf{nYQ0H*C~i`f#L<=7LEtC nTzVJ]`ڶ z# F?w. - IwV92,`}}lA@!oԒ@q#.'*iB""n|@'C:%|a B&h.u/;_˿G1 -Bc̸r%.jF!`95$>Ϋ(ŭ֢H9~{aZ9l']}ޗo5X@?kaD5c9sK##Hgšd_hO8TcF eDrfNS܎mD8`QFUe*.ߊ@YynƳ3|RJlЌ c]ESR(f]`,+UkxG`jq3g.O|둽]]Lgf$LejƢmbȕ卧5dgeg"[aJsTX]T el/)P"ǰ3\\S$1c\p:)63C15Q]J_4 X6e 4DG!C55Jy=YUcaQoMۗGXH8c5>T\*EL_똛n_N.JW{+$endstream -endobj -3740 0 obj -<< /Filter /FlateDecode /Length 2688 >> -stream -KlX'b}BI -VX@zP`U~ IG. !o6x`”nJkI^ҴvuH9wm'M"] -1>;p{Y$EPybIN_Ptuŷ_ xL;G[nSn-Nd% C=}|x5-1@Ȑ&!*M1 8tkSAU@B&1FsH)<k`M6Wu8q+[Q]rq -JѦw9a8F `!n^rTklY?8Wv"yt88\Z~tyk"Bg;gB6dy5h -aJ5B^)!Wz-aBoS>;lq&_UAdͦa $eJc*zn\zAa0+))ـ? `b/թQzXvz zmPς:>nC-.`Ul~`g憷~RV;&zaҿyE /UA<) nqVyS9Lr锩p:/=a.bPEG۪Tub&Aל`I* cFF"?Y%$!r~]uoAQs;19d/ L2??,c7 A y q} 0N-6 -; AHD1H.K6/Ca4Jx7U~ @y,!6GirWZ͉Etd95v=6'0)P ϒ9IwT-@'T:R =$C֊S\&j6^rOz.)aP0I"bnra^|xMST9^HJlcL!,EPcޤg+mC?طtM7ũYդYM['N"'[;}kp+^fQcli> -6Qsۺf㨋>NtZAyW XLqG9[Ly}ɯDrb!0[%qI[N#4 \(3'sZԳkDspۭM60ɪ3#;p rq, +V7Y_ [a:`7yQReoju^A*ag.&֭TaXlT:ceFQflME5P n -XZL%Hq ({9/0E#j]$ Rٲu.k .I`vkX nzpd^5Zd֙3@ҁ@Y=?xt C~~t>ƄEPY +Uδh{e=IW؎Son#+[IDv#Y\ؼ2j4m뚤 iA e =:c"bק -`/|e{,$huAhз'ƻ7!?UЅ?sFX8uW&_u٩JGAz(KC)A3 rlg[r)3%=\~afD՚wyU<]´jp.Z'$zD!1aNyMS #{H -ϛ:{eP+2[[8Pͩ#?k>kbUHM_^gBZCL]G] Mf)wi]->}^fY}k8qUҵycܴgv(ZB}Do@>:s -I8kMA$wᾇ)[a9¥ '^FI6@)w`jK + Bi}27z,i&{ںy<|*`;6r3y5aUxeP:u3WҠ`Uӯhh]qo(am2 aEI`C[Q{IR̫;|yi>CRzz,I5eAhalEݨ!ae ;t#sTCdQ!SlJ -t;..?+Бu}'endstream -endobj -3741 0 obj -<< /Filter /FlateDecode /Length 2480 >> -stream -&f\Ol -O 0Q3g{7 +6DyDβ~7jQN/@+QÙ¢y'7I$Uj^o1(ݻSG_R{ZH@kyDBu0Xp\o*ѳӱ(fۗK">%v `F !`Gd [+;)حay!=r -V{C"(Z~FȴYzŚͽ{XnQntVUX{#`_klS_X$:Oni!5!`ZuZ_ MC뀮>(~‹.B϶{& -a%S J@8+ՇR P+/b+tQ+wwAD#Cߜdf4{)+e[2@&P|d⹏YNwz:֫_WaFk x/zN&wA~DR9V#&;' -:ޟ2?z{yRap݋h8ȘS`ZG\Vq.O?kZ:^/mP6o}DQq^3ǿ C!rˡyRu *gچs/xVvuF|Ra+#hoנ泎`A:HWH|?触dN/}AS:],qƅ>5 ;`aKL>Ҭ&#K4!hs Xhw~4Qʵ5hy&eyU6iZ!ބ-dAw2 "|qUù˼H* -NYGu l! A)oVI;z%3f៨gXWꁡʋSfD4OO&IIdH/UeRO;vvX@;)t HptEl-uL|jH/_gvـ?5 rr"E߿G[v `Hї^$k /L2: l}lNd w8̭2ajK3%eei&'ZHm2畞GKvo≊g/dzXH0t9F9 ]{ O6V^;Pb5 nd-CC"C2[BqlTK7~H:s\vz(Iq㑚S65 Jw#s=?2PW)Ck#E(4C0*G Y&*yMP!#G51?mYw"޳di'YˋTMJ[<٨.0Rpj8ݒ0Pѡ mPcH <3{d>fjnB􋴟hcҊܷېċ:]bČh P$ #䜑'T|'Aɜ^M1iMب cU<}tJQ1 jY7Mb8g=ʋiM}z-)phfeJ[R="nAD?ϓ dMխ:s9X2BSL%\l OS ?> -"MZsӣ^Ϧ -﹵i}o2;(,tDi؞:݋Jvw> -stream -݁MH|- eō ţ(M -3+%rJ*CB\ֺF&d6 f̟R=8;R^^i"<`)0cv ljnS*a??Mu}QydU$O'?)iLњ\,tpͳ}< -%Q ,s_"O_CWV#$} i y_! `3`̪^-"V -{s Fl>R,'6ĞĴ8,וo;KT#|'Q尼MY~ ;UKڃ{4˫vO>0Pta Z/ԏE{xAڌ؏IhIfL憒S!^[#O64 Y?Ѻf~f/|lR:GhFc?ҼXG MlCkZ&4 64=z' '^FeQBjKԨISIRKyp,!*|]TDNBt0toj |_ -XJS.<߼1VT _OD1f5q[iwc7+#2ų ̤)4?8S)jV?mR +z_Wo,uOI,rJƢA)bzU^+bI*&;s4! |Ȅۦ^/Ŗ4sx#>.ǻTؑa2b/c)wg.ȴ|4:h|O'I4sG{ :h5SfD +-#d7Ag*/=Qv©MJš-P)m}d9=$2Fͣru' cƻm śs֛=:nQ_n -YB?qiQg@#(WXMd> -stream -e "W[ j6*w#Y9]^VCJ);뿙jʑtZr yfN*u pEk0:yj ( yqW704϶X E6YA{I; -.ӤrASZ%`˷#EAr=9wl3)BI=+*&lՠ#e6k>ܻK3R.vS@0 x+{IX4.'=So_P%vG`/ - k;*ƍ/"\ -ɯ}0q0OYۦgmOӦ'é}]:0Z\+!"JX?L(8Qz$i)@R@:6{8!I:VkkÿReLIlD#7ϻT'vZeIy_2PH͆_ [6Z>dCm(۲uXvehAA_6o׭8h*dK%&)Rʟ'yrbAKӺK#!cuh uZqGOs3>b j?DA -V( H7n#:Ѡ>Le4>]jyN:dΧL'6i3^MOd@140b<n|%;Qk房-BAte?o~SrAPxLmLa'Jc8 <@!]Rj0ѹV&;RtH)cgL_Ԭ!ScyMlC,9OB!k^O $ʮZ \Pehe &1\a,R]ճ]Ň<)J=잳.pl6\e)$u5.Cp=&H`Vg_ 1팓hDyX/z;=J_'tW:*Y Xr<^G7x}Z6Cx#(a%G18a7BsH-4-46x# ::G`*?/inہ/3 !~63Ruy)#-LN 4lS0>_jۓW)|&EnbK >K>nBbqі@YݘAX XE8 xb"Uky뎋u qeZr3s~n?Gnf NnEU Q +g:S,8WV<K "AmۃIV-]9l+.Ы $l{װLe:T W.gؔ8[%'uv=JkDa\!O-&<ѣUP $)<% 3\2`1]dxT>mZOHH (y Oە78 U=`g 6&@H-V+OQ=FN51]RɗW{xqh /9=?vl[cpqGa1-~^OܪlO}Qy)J֊~dpb(1L@/b"&[Tp銨Ne{{Pn(scs fG.G_ّ[՞sZ[Βֽ8~we~CAEz=wb䆌tQg76MuP'lg+mM$ -/eP^j'{j:_,4)ڒ~.I~r(cM -!6﷟2qDpP4*"Ew -{遃Q*f[P Wr5cG4s B]endstream -endobj -3744 0 obj -<< /Filter /FlateDecode /Length 2560 >> -stream -%ʪ#[=>K0KT ]!@5-d -V3:eG}\b5-x3$>fN3L+kFueWK}jrUN ;CZfপ9W-*LpgoxTӳ_X }| 3W!~Ҳ)uP TRUM dVq]M(:e*.ŪLSvYMx6HZ]fm^H m: [ -9AK̑DvRkQ= 3JΣXC9`D7_R ֘, :5·, ^b-xh[mH6o#|K_JGV0#K4 __ tV "-ݓmq ' ^XS{6꜉LRoXҵw`F4C~LބH<.\ue5iPŰB%Ƃa>&44Ol\} -;qwkZ g!ISP{UC.$q$ORӷQC_HlgXߟ<{9Y/e/HY^+SE2)4% -2]~{:We2\ 5[(n,+[v0?AE< M0Oi[IblȪJ@u${8"!u1=2hAfgCJrDǿVs6F;Q#u`LOvM,<#q(c 8VQuRJeux'G{t_T4H+ 0BowQ]VY+?4y MxS*!=?(C1pqi:PkdqHg;GbY$dbZىb^3}QL=Z,ī Dj7߾Lopn?ThdC+| )}:D(/ %An1mRo:u.>"n$=`|FkIڴ? -M%Y|2x8jc661[CwDH4X)>xϡ^"tyq{ʎԥ7{&/H`ȁS3y1?+D("KvW?HE-/Sp+\o.;Ʒ}&CȎC + \$Qч-LZO1#.ICلXO:-)2Y_>Ve_ZIe}'g=Đ@oC"qͰk -XKlF8FyLĥO鶎nA6u|MD5Cܮ.34.bͮJ"0'$v9^:QZB`eel )I]J|6+]Qzp~BHSϴp K-/hX/AcUm>2JanQ=b_'e`!UNxb4[)SYIx:28e.}5.;"@ &I)G*MT1s)S:zwMBF~Hj -X5졠򼖝/Q>Xszb8S;ve i'Shqq{] -? ~_t yM&(ͪAhf\~ݮ wFaAg6aXO^;aaYtkJ^`7:4GKPڳkh׆l`hNp: A\طeud*i -sT_T´d|wuDX}k*e!k7f3:MuУ`ҩ׹U{ل{h)Ukj lȐN= ݽ, r %OD'Aptt&O:sQ=# XLIwD@N]A%z -bBǕ;; 1<+C.Ck>&pup qendstream -endobj -3745 0 obj -<< /Filter /FlateDecode /Length 3360 >> -stream -YomOv]?ڡN~ׅrm41NĠIYah@Y)"n_Ig2H >ͅ|}d{hTxӴmk9|s 7Z9ԵlV(szW:@3i;;!!xYW}h>Jg[k^}DS/brɱJz -Wf0M(Tkj݇c-X8]A3[ejt_[T@8NզzUf bN- gȥw)qo6g4A6t,DG=0SaI&uvnslE3M(ڄLamR;cڇ'?x8l'm_⺶sIhZR]><|:(۲B0Hb @^QXS|r -u4Gwq.1ao3 ٳx,sJ;{q,Q -_D\na"߿jaG򾯲hJژKFB^DŖ^鉐Bϐ(q l`u8# @aĞ<(stDwH~dt-$%9tٛ$7L5* _ڿI#Ѳʹh7D"F Cf&#+D}Yyu?l-N8T]_Agg14y 8mRiv"qm. JIPmdjZ99끧ETc>يf&ȩ~,x֝/i6/1BZsÝh B΅)fnE'+3H pfsz2#@&N=njv}{qZ d9HKR~p -AL7Oeǔն1k&pmًpyfcHi?H# ༘֘ӴJ!o|'CϷa; -O")ޭD-*yl[e"Lz\ pGa9j_:YAk8UA<򥕮#+'m3[eWFT9Vl&V.rkI{k?'x7J3I8!Dfԟup}iudGa: -,6v^_&;D>4l.n2VZ`ajx{/fku[7ף%?&*Rw|KDVU97nze1%iCBIq:hыqĪh{R!j]ys$X i -m>22Vx͛YjsgV`EG0p@t~^zա/E)zBZ 6&(eѦ-4`fǧϲ`0}'nFM*~x7M]1ܪ] [?[XWϥ3XgxpG}ꪂ.Fö-9\tE`?3Q̓Ze:{ð~¥ǶtVn] 0Nґ+Q\( -PPB"XEѨRU[n7DOIx%) qUݒ-eZt? 88^Yԕߢ Y/#*[?֋u͌'Cs^~ -+,L(5Ζas^3"m&8%TμCu\ 00t7dJQ%_[*k lyNH++Է+^ 9bVN)Iqnf; -`=[h_~ʡ;Wr -})\EeOLkaή8y*z9jTe%jO' -sǗC.ϣV|=%WQ^cRTlVRT -pendstream -endobj -3746 0 obj -<< /Filter /FlateDecode /Length 2768 >> -stream -#7Җ'Ա'{U({ oih b 0BpDspGgP/5G~ >)?PeM0:bfGPdM -ѮTε5kUqip^ B S* K&)aZ.z%Zu ;/&v3Fo+NçM) F Ԛ$poن[ rK'(.S934 BNŹnYKng+;G!n+"s{߶/~AAm㒂'KocoFSu:rP/ߔQnsiy>eKRjR^ׁyθB.eڟzNA4(}-9_=*i?09}f#4yg-Z;<"pz?e-@[w{4HIā_V>7G 4QQ/H8ÝL)(Q=툖9ͼgUxeb)\8Z R{;|᪵^Z-j=v/ʔj 0集sJZNN] o8 D,|W.u岢HŸ^)]Q$\z1S?8 y'Q~HK3aWaGXM(ֽׄqϳ8$" -+c -6YPK p^x?%@D@B9-{ \׿pkL+c1FbfH 2G{c iM TE_=|VAb#LJP&R^yAO>Mr6eDszݿ| -6rpB/ik_wm:ZRdZ!吚/DSJ;lQ3 9ӳ.8]/?S+bgµk9mˆ7LoWAZJlHFqvMPMxZ;0 -P剾0STD]0Ǟ[mh3[WY/&ٝ)kRmKgT]^9$p8""Bqʮ $yB̆ZE?QRW2և`V\,:D<(pYx.sA&\Zv9WP}u٬NR{:PDLŻeB 78pud(d{!ڕ-BA(F+Z^{ ^ele] ".F}&$L B$‘sv!q`}PVK!C1cgE7KZ:"磧64t~ -rݘ`ls%=a>:>VU;ЪB8~ S|o€h^սz(_L"&[)#Q7L%>c%tn`^/-sbyҸ_' gEg܁1y@?m99Y*!^] -2q٫:]gp/ў4r;Ex݇+)f0Yendstream -endobj -3747 0 obj -<< /Filter /FlateDecode /Length 2544 >> -stream -Rwl$Iȋt;Pu tlטEcm xxgj Lj+IwWLiҦYV(g}/2UZ}>pry8υ;‰Z}fo{AɌh>أ rk^347|;.%jlX o,r"49P:a I5_%)Fkv7~m芾 1bZg8!vE sl9GxC fZu%87~h>cˎBMsDow/+$^Dj+.aL1_.{``3< rWN싰͹?f峊/Me+~WsUHoIHCS4}OIX#uY8o*Zy٠nJ =gfжL-s2[nFmT`<pi Q3m[ -W’/x+Xst@$1t_LAHY eOցRiSCc-_j{QTBTHoO*67Әc:q(0+H֐V'{Y;-v]OJHû[frobcY2 |zMa- `jz*h\(mgSѣbE"EHy•q*8̮3і1Հ~/nݦJr0´ nQd-%!PCp˩Lϻ9$T^]Mr?mϯz:FD05lr Ny{]phe`/ZBo^I Lst@ d^W#ߌ<5VNIۗ t|*W}B'Fld 듘2 籁]ZQnHp4c^I:2WKvWZv+7FDӬ?[FTa- ॶ'>Hg#9Қ-FVlz&E\ȗ2U27-6c>{T=PCHa@Ry̌XB2_0I2Ͽdnǝ)f}T (V1r> -stream -,൬8H"x~›?"(/x ՛ -Ѥthiq~wKYɆmؗJ TڋyEEةK?"Æ*(Slp`"Was2H J)sˋk2>΀{?IU7mӭ)P964SL?1[obg#Ksx!W4A8׫THNm6Z]|#1i2Bz75֎ΐI)[0jB>k~jUbKsVw:c+IpC  -X멆C@߇p* xs"6W)u$5wF u4p:*:@GHĿyS\}6}i-SO)1KqtErCt::=Rձ=V`c:N)sy$ms<\[ayX#%="ۯp&=ZK<3_HEjucK i*uvQBo7jϬ^nf㿴M.mm6-;=; 2xsWY2X y~NTep?Pgr-f ;k@ܟ2(n\-mI&J\OqugWUCȨHi:')3LTu?-Ygn3i 9U7z 6V! -&VB G` ++=G>nyape~p1II7)>WהGWmr"gd('3?(7~XM|D ]+[ѼmNGwöpp,EvwpJ ;0eaľ14:0AӒѾ'wdl!e-5_])Dհx\Ke(VjT[7&ðߙOTP8h;wI&#>0#N@٠]9̡:tDaR+ZеUpڊAk6Ԇsٯ?Ux҉f{3}'fz_]2.+oa4 ei&R͘VGn&:tL^.=_)o(na/>.|:NPd:@ H:H q%6Rj䎍R'S').I4ˈ_Ly쌀ˇ3L0ZynMc rW}s!|/ -,/)vQ yf4%MZ=eNV=Jp,[M8# X 1 $cOj&fl\LPƒ Crܕݑ'kޮcF&g|#aɛWX -} ԹfRa' -2c465 86:ĕSU Vy*V:9 T0h -A IcגD~V7J[a`8J }Bendstream -endobj -3749 0 obj -<< /Filter /FlateDecode /Length 2320 >> -stream -i6W "_϶t @Gm&ɡF;sa!He&;{3g"'LBG2-ҰlQטêEl}/`Ka>gX:.4"7Yetݸ!q^' U5`vA鮡Akzr~ϫkp*tP -%epi8\_6@$lL)h(^ 0TpaQ:kkx]sooQqQ#IvRH?)"א$v 5ג+fLT*fJ<"_3U-$ - 2|K;lfBFl>\ 2F>R!_R* ;rDn\BK5h<$&,4ӫ*ܻQ5l@"4Hzjd\=l[(Wy8M8P,P K%q )zQp=&0%!/:z2nTӻ } -_(آmSv@,D|%C]pEIVwQ1'h-܊8_')}lYvd*OO]j|z#:F7Akg1q6؎#q8^*BZNjdƱ;_f0y|YYID=USO X' - -k -YWYJYRg!/5V D& ibH$O8l! '!^!YW qc̻.'ccBU<4r!2uQ5|2EoLJfyN>Тf7:Tnϭ} #4hJl[ -7SEWM+\0roN -'L]VdYgSZUQt#y;$ -a;"7dvڑ 2L(` Y!vUg֔TgsKbji- F*͙䆿뵍9h8B My_dn2d(! T|h[ЧV2wBc;л 1o? n)Yf)>=<_Kl3m]G?\S F/*z$\t1 Y|Ac$a1յy+1•4aѝຯ("|D5p*.$삭 ,/Btԧu,:"w%bQ2Zs}^Qۥ7 uE`P-ngǾƽs252' -9rX)"C>$fúsfO?8 Fq"$CY*ts2FIK6hGł{ёiXz{F*jL78)d#nHLץjM̩/2gv8/ jE%O484W04:5 -é/2ǜf#+E}*9))?CC]n%~nthPwOI~nc)0OΜP(_L'XٰI _V\7_kP 7QN\ ՇqJhާY9]VkX9.Ҫ9LJ:<-єXs\"\X*;OWiY+endstream -endobj -3750 0 obj -<< /Filter /FlateDecode /Length 2560 >> -stream -$giQAȗv(8n2ۆ,^8,L6l#Wbp.SEDy'QfgcwdAƯpsHb^L ʇ>:wU,}ZDTW d -|bdc'Zxp2[0Oe^PL{) [y%.U kA+$ONw`r96ģ>|sxXsM:g 4?lir u ˫@tduAN]c  -j {gaxL͢-IW|x̚ jr~5 -%nKQn@돣rd[ʪyn?1۫{,D_dC{kr>#-tIHtډL"ߺn /'4k ]Q -*#"3BZب?O4JŪˤ -rRTŖ|SɏnC' ykr-D:ʎªMҘ:#gU>eHQO =>bf Է5Ḡ(<-BwEvkJǯi*%pZAxA -92ㆸ RcKFS+a;ҍqcoYk؍ʯF,gMHaYT[g HXDUc_PTt -  - WZP1jqڼ8EX& y=}[ -N/Ćqb!ڰ-gdb*T&6X .cqC{fw,cJ7x$E/7YP1`uU u:Of2T`ץm5!?ư5&`^>>pU?0wUK.ȗPޓ_Т-iW>4%;>7`xzVFio_oxkJb)Q=zkؓ3'̦#+akFwFV3oֲJJ*m_`|>yɀ=}">98zYk" cxpSH>kð9!~,F) 6|0ŔCN H(rwW1viB]J%:PO&V暐FVX oJ_WKh 2z׍/ CH/cbiQl΂vE7F\äg?`A!dL3qLVVS53Va:Y3[y֡0-AEwHE'!LXq:6ܭ -!%E©g 䶵_S%!8ڲ 3:%;{uA{sq> -stream -djI瘯~mz}kFng23A~BǸ5N4|ADwYlpjWƇQT566X R_EXnYef5Ny``CzWaE \IRaUOhBlxCl(}6eq"dWAӠ1Cζm{ƂF$\&`:=`vxqAvvbH3y=h{\8nK ƕ#rۜE>Yhz,[Bł뽹Hy͚bRO##[AWEDvms?Cӑf&ʌC6V:O8D7Hg5.1)zm -Uw j^Nuw 1r> B -J`k y9aZIҊw`Odc1/XxZw@D//et(+eq~s!3W|8Gդ$V#`OOXG'‹Y94hisހLTB=[S |ܦfM@}g08vR&%  }bQ`ʰՄJ7~qZBf;]>1QLv P*t{z Y+vJ}-|g;6G=8JH'aXҘ%OG0+1`ר 4jx d=(L -Sy~4]4aZPbO92?얃)3R6Vӹi9yiwkg_ri迌| 9{7Vˎ!I"O>hSRA/7 :sAC P m(2sLp?<@hUD$7$Wg fb -ץo0%Mn *ԉTJ='7ozAYv]*WXCbU' Bʘ>T5L)_tpϠ'ͱ[{[Z(VHػZ xrvr4o$fKO=,ZNoO>bg&2Jpu˂fI -̿TC\,-Z/.  bTlET3l+Ύ:޹JxKk@PߖܻҒz!{4p6~sOSee$G'?#%=C~'П|fH7cw@mm:W|8kr &^_$NN*CT9wn]2LvJ+ H`=xK -(ޕk>'ߍ^ൻ2cڈ }Zq{s~XCj`:ڗˋS>Ei,Iu5#ݤҩ{̚&û>$!t.6:4k{9Ji|)5O8re(}{a:>tF H%cm[h l{} 9HSkJp= -P-eBUu%}"bөPwL -K)_M!פ &*%qu!H_1(Ea[+I9: :3BQG) =ֳIsHFcU;.ʃ)d/渦DpQj-yNcDf|+ Zc}nƿns"3*SkCckqy̔<;燩Ѩ&/8:,14kKR4g [es+GAjF!,H>@Sdwdi܇!j)S}ʢ*RHhTeL\;A :H%Kˠ,/q[ZWȾ;FOCvm@_5Pl+YOyI [lὤ𕢙wVudv/ 9c&dH߱pD{f]5&2fʀ:IYendstream -endobj -3752 0 obj -<< /Filter /FlateDecode /Length 2336 >> -stream - Ϥty"yG܎~ު\WP,Jg_,Da8 jx6q1ryGUl:B sRR -$?%d0=R@2bJ=Cɿ͍Xŧ$:8@>{Oa\ly|&>]ZXmT H0`UAQK(EJj|_ZFM߸2³7q3Pf}~hƷE2D9t&0/@ ?l5W\^waHB̺`Gon/7 w*,Ӗݙ- a$)FRp]!S^ uo1ɱk r¡W}<5PbS/Jr W SgR&XsN՜%Cd'(dYO%ٓhVJ*DLP\,9cĞIyAR;br@G&⌱?bl=&9aARS{o9,f6rWm`7IimaUoAߴ "YY0N(WBAdeR”B0c4PDz굨@+: (+G@k3'`NsgnFP$|TR 'D҂5% D6xhf_G,D]NG>ׁLlG~ RWi).[F$A4]=Q[\?mB ĴB,W>9յrk3JkJv"."q2@ I2f1A*\ijs/%&Nl*W eu~Yz&b\&e(;;E+)}Xjރv˹Z~4#Iox}$ -ȟq5̦)9e.@WSpU0SQљ\UC~ʒ5"ܴFSK5 ڞڗ_ wFu/޳3џsIp'xH*:A9er[N1SV[ - 3ju'ogb'dضoS>a.-S>kuBt w-ė;{K&G# >JH*dO.zWL8r7LEf6ݾ Űktd#O1Qnz|Rp%srk=E:ʡ;zݨm=oօ[HckJ^A$HxB!ۃ2[ EHc&MgY ,e ;ȣAJk4*pH밼FR0Rs(w2`\Yo2DI?Z$MP] u<m`^orc[g7>=WՎ(;_Лoߋ&$X+pἔ:Cy{/z3TL)EkFw$mw yIOvי' tҨNa.-RUD6v(x_ۀ -I -YUKpR.>Ɯ 1Sr=Ht>`)-WEomq矻9`y_{nB6/j+u= !H:pQ՚XW4B˽V2IY$p_ٷk_٠ر=)8 su.M-g~m65R*#|IVUҴe3nd&QIX;05 -oyy VqVdJq=%dqj!(* {t4qj L0{'H筪hZ8czUdjf49_2ڑYMbd;J6t#n\ -/³nz٬zAh~G$Qq@u[;F)1ŏZ+uZE {Pyzg钎%4q:%ɞ$W0lB@s%B\w}$M24[]}W$ -2cޯS=  %%|o__Qm9HƟcAܡ<.jSoDendstream -endobj -3753 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -݋brR>%[(KIp8bY|gAit7YJ2Q+#*LZr7ppw`\DyP\!w};M:wKb -ݡoyN휂l袤۾30y{yEH}ؚD"_ -XvJr={>\3:FRM#!8Ө#ϓ _8+O:;Fzd~4wK_pl{ -XrN* u4H KMEZVg.,k6r#gd&)WE9+|~zYmȱ8D3b)-EVa`t˘Vfka4ʊ;UYgu4:hOnwG җ˙OJ2W'B,d ) N.ekJw_<=Uzg%8CYפ5JdMo^v= ^mė^1s=rmEgUASzC'xՙ/VWw!RU&O;Y]}!;sKiH0p!wme9#bN;!ۢO(M/g 0T*KbZRו)3(W{S3;z56'{.)֗e"Gn&j2OxeC{ |$]% 긼0ِV\:־0&tVwL"glM0#vԣl}=޼YtKsdw iWiggƤ^v * f;^SLe=QR˼%*Rq.CKXñ&4ev6~X1o\XJWM휇y[sǫV.+贿8+HR[@WowrM:5(ȿ|} fdI2OIEN/6Y dZOI$Rz'E-*݆`*EAq{\g -$ 7t9R(19v2u&9FV1ue8liQ-! QB>aqV_%CniztaK Ay'&a'r<YL dWh(^~h0$(~|;C͛YR") VkV'f]WU. ݅$q,]eۦ5€Sjendstream -endobj -3754 0 obj -<< /Filter /FlateDecode /Length 2768 >> -stream -( ?r E.jj fL`I2A:^gW1i3]8} -1`YkS -i2I9~dqV -^CC5g_!AtuQYR8S'B'_DoBNM~>æ2oޞIs|}97Ry OFՇXQv&֛;C4 GǶ wr] RZtd ~ V<UQȹ?1e5 cg#x v'lR73 <a̷O"Ka[v!Lyne j?52G鴻N}Z~SG!-BL00흺0O0 8MOf]kJJhSLf8 ^6^ZwQA-8X2dWþTS35ŭvIӛ?Ӹo&`Dud4_$W/X-:ڄku8Y]gSA~6\" Aj#\mkOB_85[(-r#h}{;QAIA^ӵL/NJV5W~0 θ-Oz@>n1 BlUҫ%,_!6WЫ~QPu}olV7R/?*d7#7׮Bڎ${yky?l8mltUhcg {QIcw^o*bKk K7!#=ctQ >kvL tVv|5M{+yu{{ɔ-)+"[k82p?_|4cA3xӤ;V!LW\H89e+c(o nS=U嗯XX[J8KrTz*e!k lm*/ϔ>:X_<ݧd_PW$>eWpՏheB<@hFt2Y2iu}kG N|nZCb};{|Z٪U+|I\ZyRѢmTXO^d?a Ą5~EHA)M=alK Z4icW%鮶jq;T kK۹6Ej[uR珗jB CwAP(5|R4@L!_[4 DV֮r5ɭ"5U3zS -ܦ84J 8˭niE%%skBayb q8vGsπBό7j<9J߬Mn9rY'WBܜQrLr[z^f f_<>^207,+o`gF ӡg#j#jbH9B)p> -stream -4;C(}gS;+3OrUG])FWվ_eD1YG&~wނ9(11X|pK0olrkZk1$|%7S9̑s+NZckl)o?cɖvM3yWK5`1ő+4;_i#$@dP*̞ Aq-tdϮ;,V{ +0SZJ(VogQ/K Is'" -?Z2(2,K 3i0 v]H"2j@s -`I*<$W'J+QB}-,8>^$'&\ "a68 Ou"-Ġ# G)UN2 k~9]PqmwLw4]EG;f@+HڧI7 -^X"Er|8v霡X o% Z6 ,J\d_k]IM#nz_E{L+iK#!ͪFSOH\tp:kaziLۑ >Bg4alK*ZNԤG,)MSVqT`S0%ɤ - f`LP'xՀHl ]1#0Vz2,b@V9g,݉A}"G/@ղM>=*o-X|r @7vˊ,fq @x~[+pO䡿Nc[+Vbҋ"7ӭS%sn=8k*ڻ%Jy%^~.~@t꿿 ˇmB<šo(g}?MV5 HQ@X5w۾=.;T{2!dL^69`^2 m$TE:Gp,"D(ˇP$ef}bH`EĞ,5t0f" _郋A&*\<|{9 -= H ^!DO<JVZАZ -j$\G" -GyP3wĵq`fASu'a=||s{fX81@ Fhl-DB=zx2{3\^>j[;{Lj=H1jĘtڔaS o#dKyo97EkYN1VXF2.:԰S5͏*Nv)k.Q7O |5Բ -C '}ZndMBhծף !LJHaDN`V436C.InəDoEN#QX[l+.SfVmA2\^]aȗ`1sqͭśL1wr7DH7Tp͝\|hPLˢ29˖ Tjhu^ ARR6p͞Yx~0:b8(33be C-M5zsR)32 ҅ol^*6NZm+[զUHJDU6GNk,%GHi!>w\HezBVfw [JIy_%ݭBgiǽqjGUT8ţC$;`g>ٶy "T&]: $RVLڼ^!5!^9b'Ԛ v:jם0, (s7۪O#g e3r iHdC>IܺԂ(8r‡ zv+GZC^Qfa dZ@F'qnL1"w -C}zNVo/2!q6@If񱐀| _Lk. >lm;qBw+n#oL$xq@9eݿB#шb`GP9pK|Q!*㇭JLjWO5JpJ+ɚ6]ٙIg#_S2z6>MÐ+WU$ikb=ȼ@hL/-:LLZq?c#74&~ :' ($+j>ΊكCPGGAk?eN} JYUl>'`Ӽl͗fJujዸ -3y5zP\booX|QLX PM=̯G454iHItBh)(s_$U zrԶE}2u)9T?ٻΘh-o՞endstream -endobj -3756 0 obj -<< /Filter /FlateDecode /Length 2800 >> -stream -3d -6N!*M)(@E)[A饪SLDcdϊ${K5*tҊ -Q, O!RqIErώ)T"杈h5*Ϫys,"5&:Zgqp.!8Oyt$0w4=c u[Zfڻ2ס1F -F~A/KDtj<_=P]Bwa=G\=k6>PJAT!xV,ͯ[p?UXI4;,V!P0ڞba5zag2JH֖ -7HDk0\.F[ p"M${O?ʛx -2ȩ/ &_NU`t/4Cz6j`2bA̻ŪK%c?n[ɞjUOc -WaԬ.lpc?œwfea6$a:Vn[2/b(2P]BFsRѣTp@ݛyo9P6ĭ㞋;-m7h2ȪImf/% {ݡ"9P"*{,{YTpT%ߒץ(ˡ/H&ie"wF*C{IR޻_TI -=pO3A\g1w fK7:6P wKy -{.V j՗({:!~v-Uw)ə\JSZT⣈ ֲ+?CQ.t2+?>/})2֪[ E56YoT*˨g xjdkuFF vh%-E}U[rlu{) -,ă^5]t ]~(j~i)091S8jTYA5){rH2R]HPpwMfǵ(XԽbB(NaN_;i# 6VwbsˢiR&Hn:uERp-!sYӻF҉,!c<+.=uIs[oH'>ok|!ٜ*7r57RN}&<@GU*AX }"MUq>) mM!GYMkrv^@v1rxM(#C%BАqoNf I@́9V.h׷EEUcsxr VyîN%ʄ.Z`ꦽerAxX%7*F3<^a0.Wr)S<)\=xr0kƤ:.8v9 יդd\tǖyƐT3Ai$$g+[>.,ompz#&%}%q/|+|~[HLA)խW,dZ1#:@9p-RJ?K C̾5&nEԻ9LKqܛh;@@-SsH 3ub> -stream -x"n z`m-+eOr&ONuA -l+^#7(@'?GvdL;, OlTߵO$N)m_/bYxB1){wY`g?AMjs0|j!G0\L1f1[3KqgjօqmQ^{I˼s9kNI2E?8?;ֳa]nSkN&K9i)\ZX><@MN޲|FaCĜMRnB\ ('v/{tWxG_Wul>u~bW0esNeZ﷯I ޳/]䷲luZı4+Q/aGR޿'q]d櫁},J1'Ĵ-+ZJMS W&AЗ<+'=9zˆ2]hvC0gڇz:vxIi×$o7tsC5-rEkGGjJˎPYq(CTqlƸ&XC`Z bdSrm#!lC SPQnX@AD+]g$^'cTה)x7N8Sc)S.@iiS[ԖI1X<J[aypIڌ7i2Yi7shAoc >0k)nKNO),9KG} ^zIkҸj7#:Q$ʻHT#L KYنr ؤ R赃+C9\dњ<gx~`Ѡq5Vqn".)8լ0˾ -K.엠{0C ε +w5p|se]B® c_ :G)^' z57-u\Z;1kh0+6*0s9M& y9|#JXRW}2h/A] *ysE@5&=On0z.z^~+C 8탪`YX{9npk» + H^eo~%FPh9ި&Q*m"@ H\( NCN_rP>2pY$?30$`8[gH} 7zG\ /s~b:L3qM-((G3g'O Tl; Ccjxb]M詮fD$u~5JP - P̳8kt'P#?!+L#oAn+ HBXݴ|6,YCoN&sr0 .W'*ac]=A -eO& .$j6O5L#nqAV|QxK j=O+yhK% _D7T'5LoJ![endstream -endobj -3758 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -*}ݳB!P Wj#hl/@[rHJbN% P13 2v؛Hm]j-<[tRp}e.KS//s_ qS&h\sgю<~uUr8?#mq0?h"$$w6kϛm$3yrfh*|D J!~:xwQOΣLw`xt4MMi{]2{+)^Z>znaQwlbc!l*S'_6$[%n H0bi4a)-=]_l3=f)JG@͗rꎟ - &9)n3_ hG_+CԴfjNB7(cDuH-ޜ(%M}oͯEI:R`eR~X}H&^)z+!x^ P䓏lZ*WEt(4"PAOS^Bx "')MWM̶.NP:lPm1T}'xZo:өXHXô0up\جo}f5]kxF)w:=B ah,2w q4^@ -A>28y/\hFKO["1xaɘ!:&9JvF:.6ŚG*+)f耢T4cbjTA^JlQPX$X'@8㝴 -wّHp+:Mg= ۈGvZA*oV!fm]{k "%€z#Lf7,S EFk8K$}ٻ駡 -NPa^pI[ײscʍ=d;h\zշ큺|qOe'7++y٪kRTæpgbA:]$u+C՞' h:Ps# r?6 t夶cx|Pl{@⯓-6L4 L61I{12*΂_{n`!R6|ƌcz%IY\c>bwf\V{{heLo* 1>+P除4<ƴӂG `wjv;d/:L?zkTA#BQ7pr(̘,: s#C_Pi\aV.$Ka Oy,GhdrαS qUz_=Nu}\U&:#kᜃ 5J ե+kd(rPvUOWaXM_ZR bЖ$`5 ,φy=Yۮbl{|a':x"2Ʊ[tFBuqHa0S*2d8舊tJ0')(1f9D;P,NHD(>s%OSua"}ZnE}ͳS@HcWϭGUp/UUQ#~UjƜl@(MG,l}I/a% ;UpL$GW2 EQpH_'d viXЮ1P³1endstream -endobj -3759 0 obj -<< /Filter /FlateDecode /Length 2384 >> -stream -]ChOʼn]Z;iR6.b[uy=<0{@ЁuN_oxZVY~{2t}Jޜ887[ʍC2qdw N -ָjW^4 [~Y4D^\84bbP.&E -PdnD˜9'#(~hzmJZc^Kj5^ux5L -5ׄF9_\"70'U6Ln!|%pK}IaP1BbN=ݽm0Q}槲 cH3A~?It8?l <:xUDV~6d -!'$nJ2gҐ>m,Ĥ1t?ldEQb[̰}5t@y׷q-{KH]C%%Wq9~*՚?j>TKDƥuk/,|3;(hGY4m~-G nP0񿂸}jx E(|5mYD(],hJwB#}Ӫ:[ǂ^/'g;G#DmkB:(;:CĦhl*O߀#&Mz0[A<,ـbȃ[bd#Rt/>}>C5PfgKONYpJV}Y)cw+I 9HU}a ӫԡ_h~SI\M-p=$G%)F=%Bs>ׄ Iv)A"?<^5[ͥ#I l8#а/`YP8[?-N9+suo\S)&OiJ|, ahvt"6v]]Hͼœ=6=~(%ųA)Y%;#kxUnmB~Y\T]+3 OG |aҤ$4bY;u|a QJ;,WH%Hhiqlp; 1ZFnZ}pwG?pGNiz⸤ϩ6 -RU ܉?9xD -<*]iy +u#B& kpAp -UO4Yv9 c{p\nebUE5T~ )yc\DOYAJ-*^N5rb'V_wԽ.Hn"o'T(wy[$`yB4ڱ5&9GOuӮſ` Tx1kX$e׈ ,3[vҸM;tUbC TAxrEL[]7'?H+uX8:N[Οe6_-v%mT#KnzY?oRU-BWpڙǦ) tx#,MhO|oЍ92,DĆP/1&i i0dy0?%ݔqGEգ `=ڹHݟ3Ƈ݋O*0򚟯I_hW)Y`{ŦyX\s@0iU;O pȉ={s%7"%ɐ  l#әI"|'P۴^[_Yj:endstream -endobj -3760 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -%!=) 1(؉@EItYsq#_!VޗGhkYQ!8CKq X19xM̈?_sK T<%QQ O^BG#;vz=C:u \ ˓]:ybEdzq3V7N.(͑8bj) -n3&)7+g$sf1>aX`$3Dz p:+zOZz[Yl1G"=@w@ɫ#JsGm^q_%5&}~%X - pt^BS#]î٢#$iXRK{ 5wVwL:[Ž0#R7M@b|jm/{[hCx&HjM|E\!]r3G=,/Ĺs׾x} uÇC>"m~x\hPf:q?9E#_.Ti |u 3Veas7+@,ű)kQ^'ϭK W[KޒnRGO˶ld$?jrPB"f.{{y1{sRI3'F3yᤅ EO$vG1: @F- xm\r4t 94#^ݙ;fw]U)M_E͢hk+5s!QkR,-|eh&ɧȎ\9Zu!M:MLq{ ~#  ?g==郜m^o+HdhȾq8;t׊٧43E8IV3AbF;S5DJ n^O_oԣf;CUqJ 7 - E7he ) _e -h{HgaOa嫩u#wOF(zŪ]L3,<Ѓ^?!2ujq |p#~jRHXЩ& &6C2jGKw96y`D9WFXg ׇ^jPOs -sYQ)R瞝LlCjigo4Ƀ7,pܥ4αYP|e߉Mj~9TyK")JO%JQ9ꅤв{uM~,8.YSnۗ%3AE|ŧ翄N!YiA)Lts'P{ P9 ޚV2j67h:00+H = -:o5ٚ/gZI`35WM -*/vx AYooP^k(Wa)w<| -nBJ3pw`/f@g;i?7 %廚V?:ɮl#Fb(ձ:20Xa6Hendstream -endobj -3761 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -'x8H -כˬYZ/[v^ؚvOAm;n"!5筣9+ܑ[݋l*F1|vOSD:AVv`\Z8)r%M#48zttr@X٨zOaP",F YyD{9zxj텠,!4Lb\{-т = 1`[SbczaHU *ƭ-%ZF]E/2\&Wmg|>Xͷ/ :#/MmJgax/(LnUrlIP -e@V!\xPm32asj=.zYLCH}$ -B!qJԜې'FlY#dV z~"_yC q1HSh~)ݸpp5vqZd,,נ+'z{֩%:B1:[pc̿xTkX#UBMC 9.I銅F=SyDgƞGBTȅiV XDLyb(olc%Gd,ʅ|p!vgOsdDFPs,jbl^X%e`|N}NO7^Dҟ!6iac&46'+> -(Jv( g@[lZ*>QON̏1b(A\x aљUiZ}tYj7< #VF4}zk0x X>[n#~UzFRNJ\֜9&#UQ -CU 5-s(e˥b U1MK`EĖd*jƜm`O)Xj&ӂ& rdn:]QջYMğX" -'yoAsMI&&u1CV%R0Z)ȓ} -;)ܛL̙rR'lmoP"2mŚT>um/'e3^:Y -S}dUפŒ5qn`ư$Lj>;o$AKP!*f\MDEQOMNe:6endstream -endobj -3762 0 obj -<< /Filter /FlateDecode /Length 2800 >> -stream -F;7#KY[ѧ~hwI*wr\B"h -c'tF?ҲͧO&-HYt 0$mqSхܫzQC]A!Qj;P.ZTr*АQzfF̍^IhO3I-s;\D+}@3qJ;O+;nkLr:Ye$.4,xEf-ksn>K׹B=u]0s6-{[Y3M9 Y 9cX 7x0t4`ve"i᲍g:Z "@t1K_eM~$w 9%EBi -Z?a "\^2}MC - -HB16t{=d{+6N`ͺf:E>PԦMg}}}?ConXN Q|nrƩ m[g)AVTHX"d oLm2%SH䇾?s0TꍤHl9 FMsuPi+ ՊƥԵ?ItZD ]kZ,ӕgHOyPdm%h= _(P2#I<exk6<؀j.vaS?q 0DBk_&\->v0ۄSprGL~-dt_̯2 Ȼ̸ -y #cd~ b鞫/gKc~5)c:08gXM[@޺7QTي5P|܋ O='.뢮@uU24,S ZtIɡwoX+Ds.M8Hŧ ؕ250 -rFBW 6Lh? 6^ 9rsKT.jOkn&ڂ/+0J&ĪǨmMJE\ƄauSn.!kW٤-U: rQv@싩f% )duV؈ק{lB 8p89><Tuhgc }[R5 ^Q;v[:H5s?y~ ;kzH?:ҫvpM7RGKq&F>d&?;S'Nxl=5jf"MM 9rf`%WE"lSX<7Y,@8&=_>M)Fxf 5 Ogv8 o(:sP#G}uxn`4h=Xa3{*޺J:PZ&*{AdF+?鏩jF/"nM-+kS\e5|ɥ ;;ZHP˜' < U7-K鼛㘴S>IU(ZI^4,дkA(d2,/|"T9Q_#pfOLJGӁzicd-r^=\?'mBI}}4Mll>Oɓ"!JɧCyDr*7MCҥ\VU<|f仗[zzIVjK5F_PwԙezӐ2?I*ؐkCn+ּfN֟,)`UI7D> -stream - ]D8+,Lf`c!U-kLWD#4*h ?qNޫSXcPV @eWъ츺v -92>j'fgUul؊(/Yk5LA )k'DݓQ{S܎6lo+WV[?T_dlx߶W;կ糚c.噛4{,vWX,ِkP9*L&Oz'ROGL%))ѫI -]xVMkQN e\̽Dj=臨l$F?RskB蕻JK-V1r>׭`ϵSƂUw>"%)u8߅#4F9|&@cbq~F- -0zӖx.@};fw&sJu&x o]YG"*ۃR^I+D뗦i|Ygr09֭k/I<1JX?-_^vD N>ċߩ; ylf6F#yGD}2`TQ[ rA5yR twLwp1F`_鋕ۀ h[:Ks%v/i)+\N69i"m*y!fCkkYga{6a -Y1k #&8fՔ4+seT+$@h{GzG|E임$qY: -^zX22nb5-12O_ -BC/Gjk:g"ܵVє;Z!]8[T"Q`[+5P%Gwjkݷ-W+8:8W'06!?9f yt $ D!1u\ݜØ9!:fb(esIof̝Ȇ; mof=01r^;* GPyUKma69+R$Wk9vGogfU}dc%M,-%?\Jż 2 -9J~.zͣؓ?l7F_!+CX걌$7;1f(< ,oXsl&Vg~Иn'AV)Am1%yRiC2IH}h/tb~#Mwý!,ArBS(χ!!qI[U1/F49H-q >5/jL]Tƈ-7g Lv~ d+.G%gաqcIud1-4ZOZoH&c8ZjzU[\ V‚ZN`Ea>njy9AtŷUz+h93~tՄNs)&堸fHCendstream -endobj -3764 0 obj -<< /Filter /FlateDecode /Length 2336 >> -stream -xŐؓ~SnPd5s!0B鳓ˤ^cEٵ֫_m* -IRCޖ> -5a4m=rKIҵ8O}gVAhD̪8;&8e+c#9r¾ ϟ؅Ȕ4.,:6W{ sI o0 4kPx×|2tt&؀s@=<rQ䂾Ƈ.a:Sx?Ω!Eoc28 ^ϼ PRR hC^{ZDh߸qc¢[*ĠUNsDM˭E;1t`3 eVXحx eGaV>hSusӕKٲ:h &|r.ל1S/}@mG]Cr5 =7/2k㜕߶Th)U M W H -.ȚO}+C4er7TS1`z,D^<3P:,8+aQ #ʣr!j6594j>t]ڽZ2mV@ji_ :`XC -G˨յ֟fqx& LDW43oo|\ XeܠSHOQ+5%l#);" LG׍#_kBs u N£2‘L Dػ~rnJNN!c#shH\ˇ t6kq1A׋,G2?52Jj96fWAOe/ #:<;&B[pya8^k,7̕QCmFR7.ѩ\\Kz1DS qiOrJ͓C2[_ikICӴ)E֊5/p[i8}]HųnlRm⇺F9~& e>]K$NDF&m`mo[iC -0jkn=]ӆ;eKœ4&g;|#OvR~B/6EnfC?ÄD=bpܡ*'T9/ rRo듉 --֭(Ÿ2cl<(Y_^JoUAG7_-oܞzX~aP -uuqm0%uo9$m[CoݹeUX/?r 3Dhz"z9e8$M/pk~)&)a<6Sc ULccgpuɃH:45(:&q[15]VMYy^Enu ?XYw\^ǵN>d  #L)aA6̹;UgCN?&:Ç|ͯ`&r*bTƭ8C%klᄺ=YKYQGdVzs m&~^!=p>998hs뵛u,738 xF띶o1l ;^Fmlt}=Ể @~z|^l:K1 V)d@4) 7uJR{^އ85ؑ*q -aGKVdRrk x䱧7 T㕏\t"7ȌB@w|B8Iǝ!h -ZM]W6 /9뵯qVD8Ho? ;[l -OZe?gX˜B}'I Q}tyK:z)όہ<<:Tiendstream -endobj -3765 0 obj -<< /Filter /FlateDecode /Length 2384 >> -stream -+1Qv/WG)zh3%BR眡>;u([oЬ7Gٱ:;=:-ζsOyߌ˄.'#}r(KCJת"mA[?W"Y9}RM| `80'AYx,Χ~ kق* {_✑|.2"r\FA]CF8H9~ d*.p#z˗t;t -ƈDƇ~!1pDunS"~*YpT%foր^7P0nؘ hd)&հ0! z<)lZj-}XSŵM ŏPvGE$Wyu!F@Z̡#x W&n.{V1C䬹!颼φaPKj)a9^loh,ݞϝR͉;oȇYc5`mpa> &q5$R<X(ٻ2KIJ晧t?|P^j`'&d}~ypلy7 J^IaLk-mO(07`,h# o?pg8A}\Q>(/ zە@*[B+su^yEjJVöϻK;̞ -,h@1« -Vtp)?F3ً7=nSSBtb A`fvw9^n"OXMOa6S΀O-Ǵqb-Co:hG- o&g׈Oe&l)A M:Qu]>i/QQ>^?oy򉙖7@ƵxR?6Jfcuj|/! xŒoZ{J)k+~T5uX,`a_W5k\14Mt;_r`ߔ[ߕfnL%YBn!k;o@A|;Xak}s5yV^|UX (ֱAׂ Ti`m㹊:эt$5*mɪ.t (n!1  w HtL)Ú b7jx2"˽ dÕfZ%} PJ -WNA?+(bd$+)kzȧ=4oTVp)|'rG)EgIɱf@b/l]UuJfaqtt!O7)i ^_c+JQSv"ʚt%GRq[ \d}{ DJp孏%$\;:+rC Dj|L'ؠ VGJQ9 !tl= åL6uEA@oB8GVb*6yʤ0̄6~T{T憵X8D\Yԯ|Cl+k!ߍ/W60#PQ^qNs2B+cvVjmz"L` -ێSـVr qe#y⦍ _켣hv> ? lpE}Q{BJ/,wJ !3TVw ^e;@XYSsփ!J m 7&dL/ph=ئ=71A$D܅L }0 oD/8Z`<ߝda'Zg_s(ua(3/Lq&'jE Hc_UþLTouNvI}^ -_{ƨ'>'L9,-xg8Cތwzh-mZ>W9u7e e,Sq-$"TDHASa90׭O j˖*j"\lTEBNJEewx2ǥZ'ǭ//P@Zendstream -endobj -3766 0 obj -<< /Filter /FlateDecode /Length 2368 >> -stream -kv^d7"A(nY j` KHvD;ޔޥnx:rBWPk"4T1,`1SH?c{$0׭-#Xñh;\(U'~&52ifldƭX^'΀Qcʐ,~|/8Whe?{J#&p仜QWP(+Is1b4,Yv ʋ&#1S58OM,ASpk0\>Ô>-&[XB@uyجOh؎Goh=IŒ X=Ld\鬪 u.ɁFZ{gftxh;?u h/%[5ؽR:sʍ3riFi ]11xd^._pԆz=ě!EHcnlD%x)XwˍeTH]}%e8R%ƒF -%.(m!B}0Hkبsi-YF0 i|eHFկʰO -Jm*+ fm3ȿG> ,,هhܭתL&H% yl, #ϴAֱ1 E]: UxlӇL%o Ix(M$غ]b^Dh<9#2.@`x TA;,iXJ=( D㢞&/u2m$h=f8?6Efu:$E8: v v_".ꆢ)u`28f:Nv턄T̫2S/WWֻð6͑ӵ{hG9.҆}.W=BPΚfN gߙPn a\$Ldv89^׍q(ET9[O_qf]_i󊳏{?/Fԩ 5)[58K5ѥ>IJlv NLZtwAL"5cy8ɈW4T5=W_7Zr͘H;5[%4C0!~rb>i4v808UO%뼬?DAp[h-'ȓ1hoI̚WsSp;ښڠZuXn{e\\ryfAG莌絅OggJg`_Q2`Ǡ)b`HtgPW"8OrH-0/?K\(l˥~I$ITAWX3 d<H,{e?"; wƫHł`cEFn&J8OZfŸvPKCB_24)/E7}#iYqPi*#XT|# IJW.Ye9}ˠW73&2qeos_}zKIyzֹR;@\Yۣ öR"Bhpr˔B\"H~țmCsK& C-SQW3`;XEpf?`ĺ t04;kbY(A-1lG1"y/ -!2(^]j43 k]J`;t V.RTP/.#&-cepד% -2Mo1WfAǙYK72踊[*̈́dgcV xZ -Tu@eCS(#:{KPoDiR\@; ZEgRB %WĂv(˩f4GdZ#7⋊VQ7w]^4r7Ȱ1ѻ$|*O?JZ(ܘVSSb$tb+4#K']|P#P?Է:pG3}h\\*NV]! | -`CV[4Ӄ7hp׭,")Zs,eOٵ7VCcᏹ$^my㖵p,A|JRژ\*} A!KL|MZ@wǢ BK~ύa$MpڢlSJO{endstream -endobj -3767 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -)PC~>Y%B$%Ix~y_ډײ^{Hy4?fQQP1t{zEu*wvkF tٹ wfA+Islkk,3_X6ӕgFh6{v&}p @ny>zg!3uZ'81cЁs&i'(OHLSg1(_!3U=^3i~$u 09^ltկvb%wx? -*T$cnݲ-UѢ{{Ӱg /%9wr.|ңʡy>g`RمK)vT R |@եʵ^M4Ns6džDYLyѯZc4Y#~R܍?WgF TY\&ofW)/rt+[(+R 0tC`a<.BpyNm}yW䢖G'7`j*3D堪XHJ$u]',vJ=!Tt{:P. bpeuYJL[Mc#GZ8\;mZ]f=0h2`Q?vW nHڏ Zܲ:Tnj ->ϻȉܺ)w/y9![Zy]?Ϛbg'qw|m- *c/llpok?" 46k5J8նH7큃 \Vz.թcњQÌTںpwCO,811!3Ge/$DĈuǠa+j/7Yz ͑9cH׵k=5H5<6+/zD~?מ07ΧuU9PmL.´|\?k)́biaz~qSN>,=L`e9M&/u 2^Q m#Ι, Iv-I*40*S ܄ysL+$c߾ڂOԙ>$fJw֩鈡Je -{}ZGtoGr|e$|H5=Uymi[NR(z+sy0CdB!^ZX#ο^|b[Fr'b9:O1Cc=.GOgjSH':_oC9Jt]?w\Lz':KXŏϧ67QÓuo9GQa)ۄ"n2҆(x^s.B6=0oHC]6],_,&DMIzLhW_<<`zo -b?ECqi"r[)[UJ$B_K^](š5j.4>y3>Kע=\li3_j  q(L0 ch![=\䐚s&d^Sz~? 3.^$7Nendstream -endobj -3768 0 obj -<< /Filter /FlateDecode /Length 2176 >> -stream -8:׿H|# FL V7nT+}bMZhk]x!S 1gw2j("6w@M(tA'DK%NF!B `1H].fC )LjH8!}3;hޛL&0{uhɋ}.撩$85ENM'E <{xxFd,&_gUb4W7RLb q%M,6KN!eA}'>cg?.iiçasњ)W2l);OGS/Cל -TL~LTlC*B>θ* -e%0q7h<K)o Tb0py=!9CwP!*OX{^`3PaF{%f?ݳK9ѵxR*͍__cAjde-ޏQ2?!_@:g)LZݿ-ɴn 朠wEYKI2N&=t^NW/c!e|1 K `xofiPCmHTZtN J`EyZԗ/?R6,Td@$hNA#0nT5,CdKؒBo䆖dl W\!g kKm\VL}k>] -+3oci* ١/{fx4T3Xqi|IbZŁ|ﺧzR7MG|_W?Nͭ= _1fA!YETnvɀ6G &Zb o+vayӧڶЄA(Aj~:fR6Jַ -n57mCAŤ鼠;S"q5wr.@=> c )~gh~_)&`W{,|N|N/O_X 0Cm-\rTq o"1sdALo jdlDם8WUϑ%I^u% iq j_b#x0t޲iLܭ 㬨UV{ -2 9XHDFbYeoJfL8C _}7ثo/{ -ijȱǚeswzc"ޜhᣬ7QBjL=@*WA<@*[tG^NBG - HRǦ(DX\O%*=Gjk -Cog4׀4Ҋ`usx]n4aQЍ(E;_F"YbMTKvZiOac3V՗7ലzk++ .iU FT:&KBIݡʚSBC't*443Nxd_c"lzիcF;r94c#&m==FYnPC+T+GCק]j MrRj$Qؼ2 /.q)nv8cbȢ1{ 0I -k6cpLE9=z6x=!BK؜*Q  !ɇ%Mn>x[0%Bohs_kZCAcL6L('ԓ &KZps#`rd# 9Vtg߿fĀW%J+0:4KΤ{a]9˜R2iQD Klf$Q%uFPwӑ.P|HO2Ѐj꾭Б,&5xtAeb[@5 s5J !1pQ6>P֕4Wt6~TUqnh X'"L׍6IZt΃P!8*Ǭ6i4Yu~_ i$ue{B’yK - ^-1t -+h) ߃X6T5Gĝդܣlߪ0 ҕW }F{0|1Ys&h|Ҩ]WFQM&BL\He;[ˌIb)H_Lv:rǂϟfendstream -endobj -3769 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -o*[dRT%Hf=K bv y -ՃܢK͜tA) 'ٌH{1c~;p2᛺!zVmin Ҙ:ne 괞?87N[B >QdòvMv:cw0-@f!ؙ cAͫX#8@[Yj{tn܈.2A=F&U.67w/12(LͅX5SlykQ|j S\Eg^EceY|g踭 [`46KY~S)ѨJfIX)Ec)fҊ:[8?Gŝ27N:YaDhi辽.9gY -*_2 (b@QWA<CG)XnmNM4씙|l>FM1{Pc[C$2SҌ`w}`t;_p`ncOj$+6()nj~Lқmh*"sQ_/2MEЕywlӗמEs caq4R2lو4t'ѨP d_x1+ނS]?psFʹ)?:mlI~lh.={M\։'߷\vžfb<c|hC2yxNֶ_L#w8ZV#qr70>`oI&g\82KC`fRAUu |M ln,?gq:*ܫ>C @g* Z{ 2E]i}^bU᪎JH!pG[bG$n`,U4R~H6-ס@vcbmy2@u11~c*:rq Pj -lu% $[T)j2s(8,]#P]:r^..t!xxOucH9ĉ#Rmw?ֿq ,GU)s GFIMVmt۟)27˸IWzϺ> =jw"qa|?xpm㵛8*7n('s=J4*6:Lj?\67O^i\q40⢌F =s7Ҕ' !P$ÚcLWA^D*?_-i|lQ~Ea0Êy82_5_*O-ALj0Gj QCM W=ALS}KE3]qw.5pK0ŠEЃ ;q}\a]/Դ_g fd-`@eIm;ZE|Вpmlt6Mx%ud]GŅh ;rg.?bH ãU|YEk=JjHo/LWR>huXG vuѡbӘj-#2a],#"!(4r`CkӦ}EC`9%Byo]m_HȎZ7_ D_Sv>L짌isTendstream -endobj -3770 0 obj -<< /Filter /FlateDecode /Length 2528 >> -stream -:?+L6BRZ5<{uYXpR<Xڣ.fHY[L Y6C;ƁQ,ڭQKmwMLE;qȟbMO5,Wتnܢޏj#C+"eGk[&1Z_1>#: 2?.sX;c9I)#0ʏq0Q1~KV$\C+_mLP薕b?iDƤbr `| :;"hrc%=CYyj+G_7z-.'Núw(Ky7P ~^g -z@0Bsu$򓵌٘cOJ=w3s :1E ׊X*h_Sh3kvQ Tq I?[U8*~? >< I+EE@ʄBJgYӑڴ ] lq|V#wZOcH&BnI4%z|}wKń -ZyĔ60 '137xPB[}s8NHϣ:lg#.:ZLgFθŪ³Ngļi[;)'NDS79 Eq5 값bs+ HkRE j?)?B}dfRLNtBgO]LCR8mD":8cZ%lЩar%=yd %!;-gXuzznb\K&@E-4PKܡ,<2P:RdbdYWIޕQ1Q?TJ??~H)jKDnYj _EPLm৒h-ގ!eZmE]Uml2rlahۦ7xMn#ʇ-%@p}!ɜ7[|'>,4! 0 %&_raS6ZV|$-a^.9*b>;#2f iA7@#)kJWN쪁 KCaaD]y<+|0l@N4rbϭĮ/~ % ]ȓ>([͝($|)-@٨%$:'}LD‰RH?& lAYCjbH&Q*+};ˤz(7  ̤ "yq;a@ĽF>!r.|oRwe9.-(b>yLgZ[B0P׷K *J<nLᢺ &}gy 8?vNKh)_nWo=9mH,.&zf\=:H]]Kgc.h`3Ocصڧ5Q)4Zya'Ks]}nOLͿ[Nvrt\r*WQO"b$‹KzcV@edžyJZU{u,KA8XGYZ®\\~'OnW.ݵ Ң t}_W|)yUi$㐎} - -AA͉ -1zeVtps -vFΠ?{C7!H 7Ouqd/Fty*!6O_v 9[j֞rPf*h`L Gy(ŵAKE=\៣S&xQO6аϞh>ldz xqh7I+!#Wendstream -endobj -3771 0 obj -<< /Filter /FlateDecode /Length 3184 >> -stream -4=ѭ@u\]WJS5W-)ku\ U=ԛI0le>O$!_rOj+(,/8 M:ֽ֥Jޚ։S -1B$K)dxiڟ);+SDT)XN{?~_F_6JB$-6_ a%ZcP_X5-w͚n K쬢)[=5w0)5$Cg'7 -]#aP"Bż[ήR_Y2},yHK y(Fx%mp>յZ|}Mx+2@5,cK~㱜+MEtٲ1[$FZ -/лV>}\ZrglXŽAl9@ζ%_[\qRCL+{0C7esT|*X -+2~‡ gf B^wjt:l1w -I~R҉Z:)-e_ex>"8ZS.#+"FU׹_;j d+dl:9)]P&LThnH sw$a-bp%u5;p(3$-![ -f_$TDEcd_e1.Le\SMXz\8Z,įmTfPH~oVb"bMkR55f(LbܫWT.9#s\+nlKBEBh"8D^h|u%܂ъ+рʔ8Mi6 -{N.sG4Ɗ,"9[Am|՟L6OS] -2` -0޴_zAY(P? zJ?~"nVE4_݈!™D߹|:LN;y*N^i|GHH+"/L=rHXWL?ċ갼a2 yb50>Բ.Xf8H=&S߉yF< _^J !Llp2jr+'w%i>XͤF,2:k2¥ H>o, =NJGKn7Y)3() 4_ QD`;x/ovu$/% &>W$Ѽ2V>/=U}Dz -6R6(|,NOrx.j ℥ߣlrVDA&DGl*d5dOiֳ.7i:0$PY1҅*c_ K-NVX@֛Y hY&axvu 6ᗉ -yY z?peZxJ:;P{}zEjkL ;l|wp:.P=mNe΢/sG!sP C}g> -stream -'0m /+ϸ!KWEk01Ȓ<$S͗δ#<ض@S xr3.O?@p;:D]0.Q,0'56´+%$LW1pcs[#Lsvǭ?6DâBGw~g؎I˒FmvpZ /f'k+"j+uc.J('o*> -stream -,B Zu)ٖ~X1\g.<$d}7J᱓lҷ\?l%*MTp~f5!|Xsv*@נ&8z\9d\^ǰä4 ۓ N#%."UsBߔLOrlf| .}!|鷫@SQN21PG=4(A&:÷bJğl }7lbBf&&u,gvUNls&.> z?2-_7$^gL-y;\Y3 B qj&9藃dg41!x"{x,ſl;£b5剑ūb̚ڈI"V+|^9W/- j^1P+jeDθR42KzV؍QkQ0BeV♲nKe9R^wjW+qs?'tq4񿓠EYU߰f+w ~?__(b_#Z4V8V -[md環^ -mueZ8};k[Hdtċb~UThq " MSyɮMs~;ej!%|d:ȘM89̧-dA6nh{{R4 ʰ 頢,.s_O./U} 0ŃT0b`fBv:<>Gd^٭;sYw/?mnQ'{S^@C~X -@9bRF z]5I]c-b´=^|Rn -Xw~ƷN5-)#-ÕT>_/؀ace6 |mU(" P9ngy!{30q^qfvp#|8U -"S:VmiKx% 'kH=B(7v+Ą! ,B!0N\^e=`fH󨝔7+rn'v +SlPm+leV\X) -,lsQ7ڧfDKłp) Qơ0a޿Ӫ7CXU4gaekx@)T*ff迂5@h@hEf@Q"[O8v=U!V}+Ic?hRi7 $ZýS{O59Si*Si<$Lq>"0AR30 8M@(9S+LM0?R.<\ISu> -stream -"΍X8Ilg wҺs S%>K O냌%/>)) ->nqR`OEfGD\z,NMAR*advW<;cTeVr_IvA~ǕHI^d=߈'.:-/F Zjbݎ0q[g\do{jBSp@{H%$k*݋o9sj7w >ć{5t'`f.MI-!jymϨkoma6)z9Yҕ#ZGm~25Ztf߶tIGU\ũhV{%6sSG~9<#TVݐh'oI +*sϖkCo8:O{]w$hӺrņDN'Hd.=U+-쐆F = ,4K~y2R̤5S ;.Jm&. r endstream -endobj -3775 0 obj -<< /Filter /FlateDecode /Length 2800 >> -stream -j(wOf؅jqJW?*xJK4o(Yݲ SeM_)~U^(ZA?qOf>RσbP_IDo܊ȱ{3CyM x;ohm7Py#ɐڤW;NSȫ@] mR!R-6tw'$i?w9AHݯyO.mOFRj ~!Ϩge}_0⺶E -X;.WGC+a~GI%_VƉ*/X=6*Hw{j_4C?oP Va2];ٙ`DwȬ|Ňu);*xLERle:Qxq:4MfsY-$A:sxPGl?ÛlX`RhTJxOB(B3=Ώ C`h&.uFĩ+ஊCdg`A`ڸӢS!C^*Pf`~F1Tp6 z>CMz iPX>/pTH|jK'SImOĩu%,b$%A R4e][^w܆VU:5`s| -%{zBg'p=N13+D+Ѿe_pJ!9!3n4@7I\=pN\;wF(Ûin${-=VFL&p7^p/Z&^í"Z|Few%),; Cx=ކ+Jtd\!*CXZJOYl*&B (]TRu"9 -}d zڮC'?k߿8-?Kq//` qKEWR?"H -C;SZ\W M4X zZ;\.g ͱ Sk ?~i3\[*1 "a{mX5%d2*۶ dbJ0t+DS DC3di@ʦ,-|.M$$4M>1bG7=/'ASzb80ҵKաȔp8 f^:OjyjVnLLh,74}2ό14{@7_9d<"3sO'5`E7EhpN5:*in "H!/S6C=9" \x c 5#\2D@Z=yyYB~KnRWprޛ@SsbfWgW6uvoK)s {yQKM^-\1%tL?˥MU0>ˍLG -fۿij+'9%OݽVby;zJifiLnA#Ī(泉,%f(""#:ڂ p>cm:!/mS^]͏=&)fa3vrFYEK\zl A q;uju|=&Aŕz1(T (]ICD1eA26OP%aCa-9,pⱳ²fwӔQE`T?0 卑:3P c~m*\1I -s -c8Gyy(^B#:0tFX$Хs{8qd[z z9Qc0݃8c=6/`9I&h-o9w`N곯ق##]4G5'Iq,Kjr]H?|,0+ I/ -T -_Wai'P^2^9YuHw3V -{^?LsȜ]ݭ'W'|ڎ}fB4=Ԉ%/҅0t+U &rLZc8,]FЅ-浰usen`SwHtwɋdUI.3q-r;lagn$չZFendstream -endobj -3776 0 obj -<< /Filter /FlateDecode /Length 2624 >> -stream -O8$3`a잝@'Ðz":kDjv'y;75n]*hh[_MZ.5.#?v/9+^@q1A* oR -|SMY|Trx:?տ,k]Wƽ_10|,$Lvŵ4z$k ًsR -ob/O+~:՛_JZaQd&i^LNZigs\ 'é>JZ=#ozS l{_:~؊_=GbgX50dz,/3>$BE bĄ=:`usy@ӥS9 oaHKv`U9^kW賰ш"H7H,-uDe*w+[Cr<͛$W]M-oPEkqRm[^[ک>^A^h}Z{b@MCMt,[Pp)SܢK֔5;2:Bͪ8 L窦sǣ`ZlFV E_SA} - NK etЕK%ɵr⠵Z^߇ U4<nقʧʂ!ORZ+!k ]#SV~w=f BgV QP*sLD-0ti1Svd}hm^5= rV襕j>[jf,ZsqVV*d(y)q,PN&j^ q9o*A5܄9]7R顏 O y#pxV ~ab`NK>5j28[/.ͯl)D7`RDQt$ -;><|+(yTF P}mpeKoH{9d5jJGrh >c㒄ji죘"o6bɯm$j/|߹#oR0 u1|ׄRrRiIg4\'@+mp57YFPE =w~*͐mM|-/uŗlUGAGޗpAݭh6ӗ,L]'a ߆xwg~Atmwvһ@f|Yr'%;)eRGDQyR%9`aзMĉn?z!|s$5ekKuD4Oy59iyBĆ@ġ4qӞD Oj\7WjkZl~yL2bcAkǵ&W۟BZMV,I ^}˗*GK\N(޿q*{~t̕vSM;Nv:/ +X&W<`6Dvq׷ߐ4M(o)l^'1iMN}fӜ;qӚ|=>p^48[ޒTt:8YHez@ۮSE*ul"?je7"!2oE5FTyyt0DpA[.]P>:`Xvn?$"0|jnLVuFz1?'m)>4KH0bW]? $Nݔ -/qT!Pj[ԙlckX7nFŦds:)ıcPO 0 ?zs秐_4`@$7q۳˾_& cy4->2ZɵG)ъSs005TӅ3EEN\_΃vdýS -E{V2NID1..XlZ-Y4rd//P> m6pžIysx XXʲPj=c#mI]pa!qFzΖ$1KlzQ +LS3?qmOendstream -endobj -3777 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -X"imZM1.Gί-DUVn߉@V;ȴ-+R/t3W`HtRbpz9CX*J_{g ;([w3W5+5GէIղ9^4щRRwSIVD!Ͼ)_L&;lΘrdkyCLٍġѯI:JhZg썿5@ö4 -\~չD= -R$[yBl8vlpC9#{6_?݋SvD{F0_|oGsmE^k"l25<3Iں'߼󇬺)7qizU*;eQЈ34J=͕ -$vvni%2stU7Vd36[^puG,Yz;R-gk.Mq 5Lk ڬ8!l}V]̩?CŚ*q,T#h7W!ꠛgd1!%F3,J gp>!ԟZwpflʢg^ 5yNk=b>7[WysA3d4g,)W[ū[.la;#]G!lPT[x/Zw}ɒ'XROig\\(2NR%[z,AzS>xۘ=t ]8qL/9^429,n OJ[pz /:S5@W~6>38BJv/C 9Oyt-Q6Kf`X\To&g<%{gi l1:\} dXފd+|ˣ[6sk J>=9L48, 7@ -crg.$lxa' {0K;n_Tt"K>yPh`-گ.eH+dI!cųv)Oَ|/^٣pU9[&5tNDCߔ p +~%hŅ -%3K^׆~Q aǛ5:'" V4"ȣ/] yOoL=^8"vipl ֊N٪z עޣc-aqzOp6[- "{\ W0H -ӈC* fƎ@9>ґ#lԾ_Y@"rPtw_[5I0̦'rO<}Nplx6mR\78"Gn9@.17{9Un@ -7ꯗμ dR[dTLd6B<ט|Vzcй኏?|>`0VʀhO4 ֧?п s#㿯e%ԭl.f-;9emmZndˬ Ӭ>CrVOi0]A@oЊendstream -endobj -3778 0 obj -<< /Filter /FlateDecode /Length 2864 >> -stream --U_R`!!$@bPlчĨ%p<^K`ߛWa[RڴLڛyoZ^~<7 `>|m0 KI$WZ5?X2Ff̓OF~s^b66+|n^ !F475,8m ?>$&Yk& "pQ?룂yzj-J\k]S,{9_eRkү֭Q"8_Wܱ`ݯFW. -Vb8 - -߁7>2ȥr#n,mf%PX/ĘB -!DQ=jk3,d1%j:A)nyS83jbn[5lrc#aH @dnB_>fX&P>a-f딭btDc --~.mJN ^݆>1?mN'y%@CBK<9?# u1Q&w}xx\_.ґW&JF%ib;bO20AO;T|& GQUYn.܆叱( w h<&^ J˄h-zK'7B5Iw380\?S79HfJ[' uae6wx{gtaD-37LP@Œ3/L (Kig#Aø,ZM\P֏&OB3l(WE6䂧#d '7W0d%|1Q,[obt%s هONٷ#}61IoyRx vKQC^tQZM]3)ƣ ~}b߽t -AiUZ%n|o)v|[f]JEM^ni*"r#kWሲ'%hij%W ĬWVEF٪NP2pC&4MxyyZ$ĝ!~ -"pǰ}hA|Z>PE2Db:ӣ_A>b `6K]`a9qQ`}%cSs85=!!BEz,^룿fn uFP$qO\c<|^E..a\>Kww/y | -?e :&Vƌ*?y/~hhq΀ׂ5xwS)_y5 8Kr6s5Rx* ɀ1g̗p@-ȊZGkVkiu0 <_Cj"`+W2揺Ow[ y9yIY6<%.RwP y,̉ՀT9{;FNػ㸗x j;9%?I ?QGx]Y6em'}mL$eY 09;F;#A%nEñ PFZ (hSnshe0u-!W<$1d< 8Y9@뫜|6M 6$9K_k>]DTSA>/˺n(EPBJg/Đ΅pvŻmQ^JnASikI%Uf;s1z?,/| TD1BN\lB # .6^ņ:J"T~H~s6?ʐ9 .n+>ؿW_Qƫ"nǞc4floeWG7BgT.uM[U ê3AJF k*h0j{.$"8g-ʬ9cr ӥz( [8,.`(dH%(caLtaV5lkvmlVGe ħO/(ȑBmi(`}9z@E4.ВfJ=>Mbρf`4B(@$Izo -H`] JҺj t^RW'.@?O%I)LGF=>/*Y{M-ϊ0=Uh [fe3VHK1غ$֟a?dg|O]_&kh'%@`p /{Y y.yVM bd ڢM&CI:kL/gѧL?@7 BX*ria3E17t̄|M%G:Ecrؤ?=uP,Ku}ܥo lE:E ,a? wop`~':o]7ek -<|>/|SkgN( endstream -endobj -3779 0 obj -<< /Filter /FlateDecode /Length 2816 >> -stream -YQZM9BS'sLc H6Yy{F\+j__I@f9ZDpXH#Dw8_i3 9oBҩQשksFG.=+pmEZGh_Ҵ>}D1 df?;Xb^%ۺ[(v&e}8=Ҩ!e+[3B1&KY s43^@Кs岿ߖ57ko Q(03{6AdD2P<9_ԟ4Z Ue~QL'rqC~XL+~W^P;^I_VZ(Z2rI/b^}q{+/i1:n|o+bSe㮚 -gv#[Q n8ܵgaMhlt* ^ 5/;ɕmWz7Iǽ_#ɝS_ܤbNT:~IzG2Z!NT%'nuSMu &)pU^jg7\{/\1n+҉}at`fpǿ%ƕ|t0trr+j-k RLj͡FWS* 2gvˠ}R^WPaRJ^AM!TV:ÿsLgLǵP+"J@F6G ]iFӻDѨgH<<'Ep[G!,_%an hls`lHC؟[Fc)pZ_U 2/I(3IYIVYӼog3$ gڔ@xLt(>5"EQu t Y2k$ ܡh_aMP gf0Oՠ^;_yw`i!xbԜ3()ت/Q(2m+EP! 7o)>T@.TS֧c~*XSa5eTPZ/pmҊƑ=`giJnu/R _]ӈ%y+dR&ŴfW2OlZyh~5:;ƏKm+Klf f6_2\(ɽ ul&U6bj@k0P9Pyzhim盌i5_ݥ -jmkQabe8*oEּEER1OsmsoinQ+Pǥ{zͪoqpe41ɠ#p{eJ: &#L-̨,/mf9ȞՓls.U?eO(Kžs?ߛp:VӠOsOpJ.]q[!@HύtB{Tbl7lG -Ú6B͘($&GŦ| -~62dqqgwE3 -Y0O}lv#6b'8sF2ѷ(2F NW߼",F`키&^6- ,x,#{lOLM]6_.`ahp\h4iKTHUc+\V]a{p}-b>jiёbPHn4<+rl|7fdM_S(yeZk;`-w( *c`W1%ͽj71Yʉ >U=_<3D),. z#.bTx/__0 \ˌeHso>\AõW NZ\gr>ҜHV},}2˚P(8-3ϳ\äiAJKN0t^1[BsC1JSUFm8뜼Cݗ(EF#44'" ^RGCґߴ6\ng5 $1xv 4+? `ԠEWѴzA6 4Mi\}Z>26> -stream -qw0tB*R"9K^7 =`Cx@2Y^ (A!D2xŚㅸ˷fSn=X*BEzD6ʒẼN`9lf-kу6$i8xk0OhYuCK#fU@fkD|$ Z9=u+hn-,$GUN9 ڔ0p)f>>ꔄ9nyJ( 'hlYn!m&p|<@N.G tU.YPq?ne(N V<^Z++)VPr0lpi?Hi:Xs?VsN:%mQڀ:lmTܳra_>ṗ \lï*Yě=cFJ+f̈́ A|'D`k@K"U5ߌeb%㭒nq[BFL2A/dh%.NEc]hМ'?OH; 3+Sَ -{Ξ -pMus}.M%NZIDJ",|4|.#Si qN42Q/Czl g;Z}NMh)3ۨ[>TZSi /y=+tB"{,UG+W -] Ukt#F "=ij|Ox?/5sˏ4 ovHʂs khaPmcQwg"\ öCz~1a;βaSX9 ٷ-_PY]wUa(GIMLpcE3Y\ѥw&Wv72SIa)4,cXj9Q83J魓K;^] amQQdUʔJ)8@ӭ@eEMvD E4⯣Ksp Le)% Obj[8{cr7i+&L~f5f6 z/iPv6+X` -=X.u4y6J3^O@P/ic8z8 Xv7gTڧc^ -?yysZgч$WC,DB**&5H-ژilG.olL3p44qONT9!#g}5a?$-U%} - «VzK;l% . Z bmH"zy弉2:{'4I(HIDO{v~W_{~=l^UD=y8u ڋߕI>2%I_QB-O(oy}a=Ⲳe~=)(dA$*h(:g+p$+zfA_T@%|:xt'W@m -_!?yN!w_1LK\_MIhBQ4lb׿o-endstream -endobj -3781 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -VCq0RQ}h}?]pA,@$"FA +d`/Scwb."r5o;"f7KZ%]7XANn%1x?@r~BU07lI@6U7.2Ԡ5 ظOmFL&'#0*`nژA__h:hkcpWR?'8V4wü,LVGc^`Z`Yo4PrCa ^Y=wcM]8ӯF, /+ et\OSّusm[Ӹ,.CJXPyʎ%$ 4\h8$o0U p4{ȄCt/@%Yky(g2JfZXgHD1 ]!~Ixh٤#auҾfk@Yb?]@~fؠr ץc1_+25?"&E@ UJqj/ERoSV=$vt |߀'h Cx~4§Tq:~pḁ4]w `~\o%3Icd^es~{.%9UtQ/4kCJ8NzMoHn ύKB,_>bWwpŒdw.jAiV@'ԥA_nCRNŤ-MVDfLKڨG;xjLp[ J%cs%`2llMRNMl0TZʖr{G<*60l:ІaY-Մ_7]C14ˡAfcIfb1P0wˈSyoK̇@Ζ@'Г;Ex&hWHe<сmc7JA(wh -7yQ{WF1--yA7ntA}#tu}?GfNDB?127& -9ڲOF1MCKgV\ -⤰IG1 G REoU߻gUPn`޺ 䠚EQ -ZD (^qFHE,炭+*<\CfFs&f`],S#is_ezUBľ#S(jd}fW`lxGS_syJmhߢkZGn\Ӣ+irg3' krrHO{,p=˭hfWAHO`k͎B$ ЩߔGΉfW ۍ - -ܷj铈;"N ->YD[m~_u۹Pk: m@Z!>}seO[$ң 1w{{72S*ۘj$Y:$b -Mq'2ĝl=u@OYZ KբhtexwcyU3ER{?c%ͯ\~+hL/B XE.fLH,8(bHQ4[0#'uv %W1A;OdA\r#w$#k7Y#H YAuw-Wc]}>ncRH&ȓ?* f"fYL(m O3VP.^O,oO'GgQCW(.D=آךn!iNey"*5e6,bendstream -endobj -3782 0 obj -<< /Filter /FlateDecode /Length 2368 >> -stream - L%LS#pQj1EvQ~ND(((Ńjt u?Ø,klN=X,#<% RI~V&Iݟ5#7B -9r4þCRà£Q xXG[߯bMoem"Lk4یIXt!(ه g@t[VQ DC ʙ`>QHD+*O((}_5,iut&&9+%uBJ=emjn{," ES|,eᣮՑu7.X1.ud#M>qxo>ΩE%6Sm9"xmj.>~VP-΄g ULeU9 -zœlր9jzHH&wW4'XpZ)hu=yWq(>w^5َg 1þqO.0!cWӖ$.=\E҉fT&|]m  FQ/Z4W=?΄/ __C!ꉮή^"rJ]ѕOnxpmUfMbځxGchAq2$dwy HaE|8Ćlf[t7 $~vb-H)s998جlgxmc*z=E;zlRUkx8oW0¬R 98h\݌a,+.¿\qO؟M5Yr1[+M=S9-[؋6 -،B΂=B{meTd20N, -r_cJXGAD4鉏Bm?fc; -vva듹ҁ1Skmʙɥ?V7kAw !nU.cvZ!Nd[m@Д=^W\o(`цXg6pLoHnϙVgAX>eĹ\4ဎ$Jƽ86VLζEّ!m~D%puNe GIurvR[UUR(> V9 YdEtTŘ#UЬdS\3`KရVɏ؊˪/mpxVaA d !W|ˬߧ wwo,6߶YgKm%/Jz!cBd^cv~@5m7|t`ŴH^x.@!9-O+1E⏾KVᗤ=gRFĘ G:=l*cGX'( _Hc.R]`6O*_߅s̄w"ˬѿ)t0cH#NC505h!(V@*v8r -;{+VJ@>չ|=#w- qn\">qi44O5h{ŏ1A ]X.4l#:aCkV$yYn^:i1I~@43UmIOӆlӷ:n~j<~,2hXlwcendstream -endobj -3783 0 obj -<< /Filter /FlateDecode /Length 2544 >> -stream -l5bD(VDlu&XP͋ -G;#r@qg .`{_VҽAI2̋n'n%tUnEogUI?] blHz2dm$bs)O;yR4mH-OZ tpM 4LTU:"5GFl>P yaWDar۹  -8MGdũ F*)cjXߔ앛>n?;)mf9gydA>稗$D )DHyX9eIW{/u~ 'eRZ,iNfj݈ܤwwҖ5 ʙ+4 .ZD<؉힖lgG5hw`b@ -CxEgCpߏ-juPq <[r(ֻ OW[ B* zsP#/`|[A[{:Ly 5IS̡Kkjzaο払~WƸ!QYh69!Gb(k(~n{lAzAy(h6O~]"wEr*9)!a1w/a.5gCVecHf`Xn0DE/u`vj{6K㓵 69xFM͍KH^ UcTY K[aȵ*N$d͓xl=6(]h/g,O$mOjz&/G -p RSᅪ\є)HçmeK/Dgl4l•[T?$$f`%j@ 7򮾮>='/"jm5hbL:·蚐xZmV6̡e k$4VvBmAp|$TB}hd)"Ԡg9rZċ(z|6~N;j%=qOiSM#X͒ϱs6 {OBᩩL[6^RsPIiSM Ky.@1.Wx!ot:Zէ9ɴ 4" }9iMvZ'wl]96]|ݖ -ZU0#`8 /qyHΐ웷dI7qEs`Θ,V&*)煎ϲizVyIf)PI*͡A(2 W}j_wXeEkxk{ 1.ֳ4Uba4\B;3AO6x;Щв념ILUŋ3g/ -rHV -ei^ /2Wlb㳶*Q!V|47+)"elj Γʦnϕ{#@ȮT SPTlA؁M -d|>ve3W Yd~mG,تW ǀ *OS1H8BZ JdM v͠HPn= yS^0*D:L3*P`H8 c[:c(ܥ 'tȶIU:ͳ?"].]\5ނ',endstream -endobj -3784 0 obj -<< /Filter /FlateDecode /Length 2800 >> -stream -C`]}=L5ġņ2C.o7iGZpjB7咴v'8C]ڸ2sE'exPwXy+) '-bMQm%l|Ht}fˠ t $ll7f&A@Oy<8yˎ'aF`Tщ o}ߑ yԑޓ++}VGM荇6:E:{>+f ՄPuJO0-?PPFy kF DxhqnbT;ʬB -`9<ůc؜wu(خO+1Ɬa%t+{ai4L*C5qa_|g}k ;n!zw+ݽSU#caBiu - 냆$\db | QT[64HbOrmK rGyG_d[ XIXL?CS|bɏ¬|#uJo}B]NnE8*kĄ\Щgrc -H $j{A^{I&2ɑ\Ȉ>0M֪P[\ʵe -al쨌vUe<`Y]TSp~%B_:b"#jSRP!]VHR,T!(MMa3,2*Y邫ٯ=9ܨnK$q|5Q{:HP/G5%{7\ʅ$G\;{li qFOC yAB\uVv)8hLUjår'EP|/'CurӬ.R - H!Ń w8*H_ pK3$΃QҿD1꬘týޮ}1t eE,bsjʂM<3HDŽ10 -pFNn6tOޮzS@“-6XyP|_SXlTj$id:kF欚ɛO~VX-:7t^mf̴0!u[Ɔc7{`'vO4t.tc+sIG-'ʁu{⺞<L wB 2yy>&'copx0'B:~ja![.clyeaոo=8w3SQ \f[I/C:+'K{Qw i8ת,A8X(8ɗrĀg7Qͨw6rLJfk"P5 2BpƩH8Oe"ڴ/x:o ٢8Q!ǑK #"buɊ.)eM=qZ4K;8H}F!\K/n cKD3򑁜 Z#i@endstream -endobj -3785 0 obj -<< /Filter /FlateDecode /Length 2208 >> -stream -( -EgkuRXn3Ţk'h g, V‰^`r|W)Nݿ~I/#B Z/Q#ƳZjPV>>E .!,x_7QY DĊ -ƝVw{Pd4TI~$>J*ԩD0yAN ۓ(yϏ1GY] :Dc`~`lLh;kyv be75nY3dFBt O TR˹:ׁ 7EPVMy`M=E6h?}V9yxdX>S͖9HSׯqUqW= +Ā6s#$tg}!'a˟Gۃ), /?ؘcAM"O8 -[_n&9UO}=7cbޡ:MK/.ɿ s$@GТfT - 93{i!|'{e~\,9y9U8gh{Ja,MߔL7G{>wӣɑ&y{Ջ-{>uB n!$czTAu^qݘwI07wYB3YKFԑtI.R0tZS_=%u E 3a,:5ݿǷ7Tv#Q4֙9+FgZ6( $#-]ErJE^Q *勶@*nw9vRBN;%wF#!CdGįH spUA;Y2򯇢8"({|٨'2TQh&qUlBfgo=Tâ_s2 ٴM?ncKbRf]V$Gk.ak- vM R߂i{ ].ݐէ܁@>D86M-n)f ZN2v ֓:[3-aGcǹ?RppJ>1rgd5γ{L}Y4Ωy,vn2i1#it| ;U\6S$:VuE2/郑Pb1 5ev -GL-"I.`,Byh>$aHxq`wAW %Q?ړ.t% 0EĽq:=3FW ):g0.ߔw;:9ut0.+]INNA y~`d+ [,[NmZiSY;1GO #MF}rYN4jE ]CALaS 3?ٹs# =z|'tw7 wT6FU5l6Z׿a*¯` HH;끕4 ڌGoYgu5-EWg(7L|֧eo+ڕ YcB_᭒*u~џ8߫iPPd!~! 7^<mhB>)31^=DJ1*E"Q$5mY["d[d-PgcS=hD.t_)^(I˨6&yj5:rݰb;AqT`ޓMګK~Zm>)! 8limlr)AŸs,1 -t/p"k%c -]25)NӺLp[٩i!@0rT=?FGRl_L.u>XS\MX>, .SdHzl?].(mթ%jrR_endstream -endobj -3786 0 obj -<< /Filter /FlateDecode /Length 2608 >> -stream -&O#L,veg$Gyԑiz_$~X.`8.)t̍Ma|`ٌW芵.4Lቺį 値]BiՏ5`CЗ_0 !9h7|߉PT uN64f@}.W4[EǾ 94NS2wY='V:8GۻhLCRG7%٫OIr넓CW<!+FJ -zdzKťfLgv{&;5y"8;J|E(h1墹$\^/̪RW&uH[W{h<8MIwk.HvjKAL%ҋ 6TFȲ09t'@ɵP8p=Aj~`tf -Y_{"s[v)b A4YMxYw@ɀ?Q}0hCISbQAG{WԯUwp3ḯkV*rn%\Bv1vuvFg~ZHnju釛S7Tɍ*+Y?PJ=־?OZf3O{hً遛hi݁x -_:TsMcU-j/JC]Reug=_4t!A4%/:{Bae-q>b kh FɥgY 6B'OX5?K6t <ۏ%AޕZ rV}A]I[T{. -V t兝>EB,d(4YyV$Äm3m89-l/ۡ]aQ/f:h1zhv@ x3/P|Jxjݰ<>Fwk",X*; {q'i P>W~c[◹. -B7=Cwc,!"e]c}2řWB ^)@R):JAα6mj0ə3#3n5wpP󻻓"F/rbjn&tiBX"*9dt*Kٴ3FJ)z8q4 -K = #gxQ1]SMZF]m -S&L=P HL -mx(6 J,˜m+дijkȹ#5򳴌8OJ64Cfy^km\. BƎϢ T zyXJ.)k],=頺 :S!1G# kѲM&+rOp9gVn?W}-Tmf8>; *ȟ$V -8Kgؽ&<7)TNyZbn $!cޝZ'L.L~-2Y-LWخy4t݊Oɖ "n:ǵ{5c߶b$b!p>{J)SF::P!ۅ uZ\S`U$|SLBѢeta9f+ؐ$YU۹J}k<8ʍ2c£ -ϨĠZp`"dq2Ge> -vq'ɇ4p$1]ώ:TP!c9&7n  d|׵c^= &R -urS:F7Q-$̎6ⵜrd  ܱGт6Wpm{EEϖQ$lP*rGKhDRE_jGŖ -S&FfՐe9sukO`btMt\R0-x2,Θ@oR B7b맭'>6[tșB)@yr?<o1亻߫~ޫJendstream -endobj -3787 0 obj -<< /Filter /FlateDecode /Length 2736 >> -stream -un.̀VCS -o-OQcm9a6.R/Rѩw9G~!!_z_I'F((eT\@J^!%Rpҩ1LINA\hi$C`HRRec=[i1XKL[#=0:A#1FqٲQo ~aKп͐Rޘ9_s0 \AtʚЧS,ohy63"4k!Rsiw|hPoypԿabe2v![OѲNΞe^h/\mj׀ ޑ.\ђs#W׎װ[9~[P+oy%(d6E|}[ mM/)z{ \UGvbyX6UN^J_ppp.20PYjI"IW/-|埅 F.C:4ϏLBũQ|Yd[L2nIᔇN]e_FTfL ټ@4/M.z*W%\5Q ]<<=~Vc:3);C衋:Y66ksyUf> Lbꚿם! 8۾ TX/&9+}ድ\d1#as%FDdEb7O|pS6v0Q6h{8B ёJO^VMc:bD~BDp 09࠙EMgx#wR8~gR:K10;{~/[ZQ!QYУwA9J~ޚs*_QiG,W!T:9o^Ȭƫךs^SE390*'rc6fA'P|*mCSsF.٧''*hx 2rLk:/ٕ fY<-L4[ۣ6=j^,Y4 !{U*ܬ⩃$lYO -w/-0FdcKJ-XMo|4l~8Buc86wߦlF5.F\'^$ɤVI)nzkUmqjt0%GFL&p @-Ҏ aӵ#1r~ը(Ϲ2Y~!1ׄa@.v/$~GH<)E(JXv$~ii64HqN }`TF'uʲmeΛ*Pgv Z]zHjޜHҳSB=)m|7Cdlkd/d3/Ƹt0<"8B|h9t:Cێ(n7exKŰ4YWC][2P(_SS:h_ZHu;J]ly O3MEopq7e3"*kV̔jcʮCS_G|RXǜNY%v][[s -΍FW: ~H?\D\Mŕ,ܐt-ݢ/D^3i'H^+dvY{dLʗ ޣu=W-m.<2‹͘Y#U>oBo -ڽSr&JDӡY+!"\_dNqwD?975&Ő[$iES "@Y_!dZ(hӳ Ǥr_.C%*te\y~!Ja>uj -r\ˋi6 -T"W9{vG1mlfHAP_5T^ʭ`ķE Ⱥۦ:> -stream -;8;9Ӽіy`*? s0T,msZ;է/JXb.rG%FO}تYˤK2:i{*-a<>R%Jל;%>5z#$wgfr,C:9 )=Vin{Oqx̠X?%uOo.FJ0K׌ ~"Yx -j -<, ,4]%vˁ4y}O~/øRg - ROx;;BpM?BR͋ A, -/⸨bkJMQ3뢥rD+R" Vi5tPDsrާke8f:W9[ѹEC߷pxh-1>+ KHZ~m; FQ©MKi4|u 0,zة2yL@z4F>>;ؑt{IpL]PScB*KԾ1rʇ9(C9  O2 @1J+HAl1PΌTE)W󞼮 ocMŕH*2;n隆c #dFl;X=-;ǣ3  >4m]D:5&9ṕݔEh)օEz }v(ZP<緸#1 ''f 54mJ*;N@d2y|9wX><ќZKmo?U`'_ K"cu]WOH'8iEVzwl9J <" JJ>PW IcSS _%=u%w@'BjsT06sq֛c-ًne -Ш瞥j10|Tk$6F$qNr.@/ - XPp|fyɀ< LжE Ƶa.ii46pSu3mmvT< AFKl N:rkcEՉHiͫPl%G23 bo[ڄ,8m]]VaGp{Ka' A,V!ÂlwPvYsno(is0¥IzӧFA -y'@`2i7KQs^(؃s9ᱱrN R3GB5k) `ۦx'} dsQxF؀*feSNK:>,&L0r|>O2":E9=ަm _H^.pR -E{3 -\KZ}&=ޥ*_ ?M]A_q9P\qXHCT1X~ܘ'i9IoֹDn>x$`3bcmqH JBZ|zt뛸hƬϕ9ZhnѺWc1a"ݲ5gЋpF F}чhy3GX)k$ @ݮX#6tYpyRsҌjq8J6x޹ܝ$A 5֨ |C 1G̔ :GhbN}_&߷HL4Ob,--s+PLbG"+&χ.GldbjxT[2ETa>o"#8C Rk.()U(RcDKW*_z7xxoaƦ듂45]=ɗb`2l.-FblMd2=[4>OTʚ@|>8uU<}넅8n݆ ,¨`&?J?47N.duL\/*xO,~4 Am䊚% M/I=8'5 w)A!?|0@Pc.hrW63~oէpP1z՟uGhQ۟gԞ ƃRdG^b2h I:&Pߪ`2}d'@ݽ -Ou_h+Sy*w[HM0PA)TfՊ P"?"Į5b9懽"Q-򦵘;frP2fBӌl#h --~[ʵb4bb6/-d{{RS -{B2=!^ 1-nysX[(aOkn0Jy/ 3uU'V sؼqL7tsEymT  -ZP T{o<,"DEsHXjtTpM,S@z~4֭*/=(;1 endstream -endobj -3789 0 obj -<< /Filter /FlateDecode /Length 2816 >> -stream -A]Zpo55e(. O|sCƧ۟7BL*T^,I .3a 1nG~xI MÌ}OS_GKDS3[,\? pOIm{՜e b0tKMgէ@nQ̣l^%7 ~s AOs]RwM|5-5í\Dz\ʓKJh>(? ?Vʌ2L^MG2zpQNj'ܟjWcy&GofBOgT -\a(Ny*{?[X$x{H啕38(ކWhSzGvw{ىꅷ>5UF̯})1s} eW˦a;~拞N8_t߈0<=V^ vv_/e#^VX]RFnѮUF0# [\ '«4d/7 J&lќ4PE{E:\oa3{DqsOKE'+Ž7tTky0-\D,K7#ٮ(ɋbµ'NC,qI0^LڥQ) kDS_r#?Ǎ?$`ZF 7# UH7Z^sDmd )9/MR屴sB]5{bs۬%+N su -a46VbId=B{-2!S;"2{AYMOhe'x%Ff@0Ԗ9Y*mctLyZU9U-Wܐxs}3idEO۽n@`^(uG;"҂rҹW8T-hm -@ڑ&z3@O᯹Y~hq~ьZ0o7Nx?v!!XlI-]ECap6 azmCg71 $G }Pׂ!ma`HٿN9+;Jre`GVC23Gmz(> Ad -lfZf$\|O ٲBfE|qVMu ؎KYǼ:wLJ{SIt7セiP?aɥV)剸* gcC/$y[{S(m91 ]7&(JU*@NP`$p͖ -ɀ7>~t4%^fb|1o QC!dU cp\^2*[kR u$7䚧MP½*)- J s(_G^X޳D~J@ -ݔ AWSM$GVz*Lz0n޴<h3nEDZ ݑ ׽H`[P#tE sS^Iq=v8ӖBPs#`E?֝Hd}{R#dR|z>~"ʰg ~#xWЁr>(D,ck\0" ) -cCL1XCyX~XٕKub̓Ei(2Zs0[7_+Ks_/Sh-*mՏzy|FZM!'~iS]wѨ9po$ٞ tp6p?mƊ Pߖ D4o]~ Emmx-d88|_W xKcs -HIz+Y#ZjaaF <'|]"Ij*g5Xd[D%`V1J<,C)eM% Tv8jo_^dl{GANPLǒj/r%>ަ)Xz4!6#_8O;I6 '=O3VV"J>y(G;,yb>newe& -Kb5tۣ)u Tmo:r؋58TMqwf_ԲYP}.}h9͛YyD*_,Hm: -E:FH5w]ݺ@vS,@g@P卩\8GqR9 ?/uiOpʻK׏M.t5BVR)< xj3&䘖NKK+apIFS]fL30a-B O&DX `k+ LILbնx2M ݮ*Uއ4U3ipH9QײNp[ur,˃V? SHh &s!Js3"D>V@z/ ڄZc*8蘩8^u^al[\[Ù?}pN=CVrgy  i otyr<'}_dš탮Eq(XdE'BpAㅣM#7j)ܩE}3g_ !9t+o;C򛀠rx#$Z$92TCWdk 9n`ʆp{#4),D{4r/ 1pCPqF wB>|(k+Wڢ=xd2T 1BL{ޝkًwH-;HU+4! \)}?WhN\gv92!F?TM|/s^hSȅc/mP> -stream -c3o3my94:#FXƦC$v;^^ߒz#S 0JJи[ŗP0D8j0S$rH- kc}Ax+Z$ k7bo=>kbtθ2){lF.7z%a'^5+B4wMWY=X^sc'Xa?ar90>r͖+]zzk3Q5 ߜHa O%A=c*4~c=.=VOy+³ )lr+gs7G'60{Cec *8FH#P hm_Ӓ[e?z3%iQ -W1T|HDȔ-T@~BĎ]}= |VL"VS` aڧ`\n}=|P}T ihrѠ V3m̖pILd࢛aMq~fbNv+A\vW$o]#+Tֆ*+Zl*'b)К@tv9$D*uY)z2Sn{b" /&8Gǃ8r[7æDbob_T"' 1?Ȣ; 96}ō"h(ʈc֭Zf;Q,7{?V'Ts%YAn"7נ4!!3Aa\OIQ=DN$(2w -}w@Ŧv7ig{\ԛcG%Pk:CLI3ia_3}1ލ:/IDWǟHFbZ=q*Iqko;/cJzve` -]32aK -6^ZԌ`NjM I$Cxէ 駦??-sV.c*'yTԁg9!(Tӧx2?=sBXԴaB^Wyt  -o#?A͸R,rԛpB#&?M`Ж5~ENw,o!mely0OL!-ЍKZ^{ "ɠyF;7d{=iGk_}`̕T1A`.o,0v[=9ӤG/XBtaanX\Iq"W)DU+ ^ut6J翤e NSy%K pD),b>0+WTG:C't:G-77&<($Gί^XUfBSd@$e6R촭8 DZ2\,p7|ٹ;J4h>Pq$ :q~(,f8گޏe.,'O'{بd*I`cmc-hƎ -%p] 1ڢd  Oꚧ`EmgVς!0 { .VNO.ekTo{nGoT#Qy9kU搇u~adn`@Žs5hr"|C:j1`:ƫt?cf&3mx?A#)Cp(gk -9[IW|Mxeudlq1cYd]v8Q۔s .>rҮ5[zo%M8KL'!AU߀:E d - V?9ϥ>`?f>KER.C\p?%} v#fdKcF}p~ғ$?e3Nw_qxPz.mk=P1UVUEg6/n!XFdCvbK8*`EwU%Xn7B ^`ٛ>JHzd$9,K]>\I宎>Z9[vvީ$v^"#oZm9;Is7?4ӒcFdX/ԙqr9eUJ> -stream -ͰkfǏ` "4;;e\haABy_gb+@CEd*[ar4x;qZkG )!`* D1@/oHϫ]> -stream -pHk.k'Bg8|Z.XdR2P;.\G2W@/ПUOhrAbf&DQn03 BmohTŘ,U$|Z$3:ҋz#zH+C1:W%2ÝW ĿE3Z>7z&I%Ru3z۰eO5{!2&V.`rc<L] -[(xJ; gA5D\5ȟ tBjm^j#Mi2'(ސ|iqT :eS/= m/h^TZᎧ`Rh 5*3dީ Rj]Wpv/+4y)D0nl̀~PŶ6ˈǃ_rѻSKS)إFxExGdݿ/qM! ek;l9VC]~qz3Jz%PŌndţxz}d׷/ 72jҕ?r 2äPy5<̷% 4[;ָ.ĈJO@CqhunƠ/]4iUƹYXo/٘ B5ÍU>›jC͵tŦ?,Pʌ߱Ҕ^$}.ͧJ/RbOIg s?<$%T -D/UYJrgi:Y=uf8ĹD~FDz҃1mQj c&]86R^.BqQ)9cWX4i uPK> z~EKkY[]W㋟Nt̃t E ⹀)~h}1G$9JJϛП)Q!;r;RF;uBf/"7ZkT~ם(82ULu/TurǍ:om&&5Qj0<ӧB:Z}xa&+ݏl`)>uWIbrCYC2dLlxX;0yIz{=6sؽ[GU]5f,|TjP),3 ax2QrU@J.1* NgyyQل~|Wv}yyzeQ}%NuO0^R=8_kE޶3#f8$OV(Jb1sEi(ZvwQ%$^2qU+%%93!@$`n7׉0^MmTC> -stream -nHFe`<Ͻk . 6OU7y93> $ ,lA/"T`TQ1Mߪ\9 ۤp?aJ៓]uvh8T.?io[ aM"50B0/+xjpt"1gϮ=PEW#viZHZ/&'[(;:@g|9m<\|TrJ-D1*Q~߮N. SEhݛGى8,wq$l(+'J# !)í:6B]۞gL hWwM2JtyU> dK2B(/ςm8zJCL4W %~d`21^i+O).qԜ*dS$%(2f*4gAj E0dsgf1J(Mot}CǛ9\7(Ѣ]DIƀ`~B}U:ZFx M@d:oV8LPH~[񽩾 -ނ·Rwۨ=EncPJYbps C;;^XȌəb,XDI<Xټfj\cb,! -&̿Or^˪nx "¦I/c6J""գXmGc3I@̾(?ŵ`vvk!IVE[Ya#0Tj x.H[W2ET}W)?YIOrtnV{3{΍+$<}-)1 -l\#\f2[{2P`0M_xKH;6o2)/=M>ف?]\/,8tlqlT=Yr D%!$\֖P?-x,/I_c-0"ِ`/Ը_UrW)\ I!tz_QQݥliz~W^yx~I?qHhm)]=vL*QQ%AL"%÷+8Ke^Ք~٨0 - G:wФ:VjZg=^W3|W* |n-]^_R]` -%,6,Xw3(ͤva1%-n?xBRtK4<x,ԫb"XcסXvAnؒ+_!jx%M,ңXRbQn=HnjRs\,&E`rO+OH.$^4a;:=u%~gnKrL6bj:pA\~km 5@=tv(t,$e;UX`;BG c+CD;!61.։oĩE&^<*lgc>5˪5x^Xjj@ȯXxTm)_d1zQHH:'bE!t(8VаTSw'~ A%@H K?@U)^PaVIDHAߝ&>71EXS2>;ithma O#Fp_vX=FOn1HW2x,řc\֯ o,@$~5w -BF6Xh[IL+L%kX^>>$E^dUp,shm &wberl, -0ION \89BWШR{}#˫~6= M9. =']5GLE-]uQi"~6/x]ye J/ݑ}mҳx9f0S]K^H|ƒ!*.$x{f}b t,,)zJW^%d"phT‚v^m`uU}s>;U3B}KL|ozʙ/f4ÂXG9[C9eeMVS+v/W- ">ND:bn) -__Xm!f;bv{Nb:H,L>0H3&AhTXa;x UYOΰ.>~xcC0}2ۘq5/ѯ& -s1|٣g2 TK6Dq!z2 KT[`O|pֿ --oG-<~ Ž:n}L|dfn#C¼U0knU@V#Auj1cCU-',NNV1a%t2xĊІ{rk: ]cܫd~CҩT a{#Gյj|9y%i/:GN,1!k.>%m$0V0bHm7VJ5Zl5<Z҆Ea.CD2)BXQ;Dݯ3:0 &"I}i2> -stream - yu`EmԹ~E;? ʡ9Ž -7/G ͧq kO|݋\]s!y13LĞTQhYjR38lư.:{խ%_e'E.ARJ#w 2nj4;9~ntihR(=JIJ3)7t_?Nb.qFĽYaܴmӒ"}ʖE4mVZP)@Щ5B,7@MBǝKM~.w=qC ,TO汈#CfI2 PfW2Ao˨\ᕶ[Mo`x#Ywb j{lb iL'/ۋz - ? -jw@VI+Ou 'HK$}bf -x曈Ӻ˙hgO~+DMٓ\ abm~id1gPWG__7E۲YA*Xp\H,<:q2r`F@, \%(d_CoD]U+ǵ k)GE]7 BpYWY ˆI~>њeAq}45W$*,hIu~:H(ls J@BѴ/jIt"v2F#删΂AɝQ?>(Q}E7F[k@Y#mX\n.7'|Ge ]a(ݴ,)P`.ͤja]sXǮ:fM[#7:gp,P>|{RքEǏŸW٭4G<,p_7~1o1{U7rLL 2^b{!"{+A<%a[qK6RڤG3%wpnW)^:؃,PJg$#޶EMa;"I*yN<ljߞc[tYQHzB-.0ŸkDZFA5G$ݴfYXtO㤦'$1Dc7ӊ+ ٠ Bڋ~/f/bwmܳ;M`*Mш}U] =V5>4xF]nϻ{ڃ'01\2Yߔ lJbMlqԎݾvw%Mѡ1R1]v@{ļ@ԃ6/ X7vZJ̘8[ux:|q$=9Tnp #1~afAhq=baԕZ\E;qlsܒɂbw޲3r>j&cc<hTބ0fȾ 7mnUyj tju7W$v?k8PTsp&RMN5xDv3:^76hgn7IO˶KEǼƏ&AlF~&4zYl-U/>륏`0u؟hݛtՕNo|E4h(NwNI|eY JKz 3kXp&hM ~%:l/کQf'):RW+p`?#}j+l"jnn= kpe4ҝ`!&mK\·j\\($> -stream --RCJy1@-"1ȶ~ݻ"#=XrRSOB d{]/tT6=7׼|_K1t1dHؓD F_%ph$eDIiC.;men}[ZGO'BQ ҆2^Qx\4쟝EEgi ->齝f}ͭSހcu>7Af^2L#0vL:~IFjJ9|_Q.wS'j+!eifoIcJ4.K蝉85.QXB n k1 QSPN Yyߑn{2~/rB{$GNLiA<.DyZQ2=E;IH͎T`Bbyep`LRͷڴrbikyco7[.D";4L҃<]fȑ\eAWs( HۺSh|S,w)T«߈!9{3v.Vl4 ^`sle|ԕj l9*{ IP,w&FgFۡ=Kݖ7Zá>[vʹ*'z 1H$˻Ī͕g+AmUCA㒕o1@m=LǜPVa>˾R{/֕gx.!CPJ-x4dϓfϪ@ãHhMbwG;/ZJn6&XWW[&7e]/*Jr0fhXbE&J`h=9n](MzZlml*stL? -~~P~m,4i+$m0XI2e = sy^8nXjY"L-]QCee2X]oQ|͠kzNL)(˿ôm Փa1wB4 OJ5¤S qR$%\PzqhFրΏ}g][7Yū\łvtMVjEO3ߊWRO8R9eASö̔LC5‰$C et_(3ڸ+%3T9 c 댔eǻ%}G^c1T5g %9Au$~8HX8O[><ɽcPDj??#QF8) bt2UBɏY %\5׻bA0L']pU@zK-> .'c`O'On@fI%~!-&Z9̐4(Sf=瑠B3|v%] ў*&չC6;o̩i <6pp#%+ָy.{c6?3V6- fR>)%J[`]So bOM L)Xo@)e?;h# 5Ҟ@jo#t7ʊ@a-t0%]gg4cfl -w;DLY1x(͉X י+"$hB-ſ!i+s&Ti|<. y[d#m= 0߶"D -w]Zhq3zfJ_P|Nї )jӲg`Fc ǨRۑvؚ6JWp܅5 ] m:a*qcIO _R7|>+DڹFΠ -a$mN( *s=U2UǥHc%?> -stream - 5ٽsO*-m#o兮$xH^lStsE1#'37ٞy']v:FHJg#wP%ZzjVOݻxuNE? xeAOՔOĝr()t d{uԗ5ji{ |j=鮬[TwEi - { ^yNT^"cq9Y :Z; t}1Ɍ|=;e6/6[$ǘ2GMۜ 9vS.:FS/h]EeA$NȀy9 MlC3o|dW!6@Ч06g֤+/ښ &q[ʳ 븳0泣YH3_ 7#=_E,ʤZ L抲P*~+'$d@Y!N4i@>h,->[պZY=B" 4@ǯuэmU!> ?< -qࣸꙛCEVN~/ z^l&:gQ0Μ -U#HL*,hspO\0@bl xhI_qhd'-&߆p .h(?O;@zn^RC 8SŰ%hص=X0l1E<"Gc`B0S} 5endstream -endobj -3797 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -*4mr`+%ۼJ' .5;LnX}ccGF^#qνh)u 6Wt&MmRCcN@}:&%W&k-->_ZfRfejAnXj}p5%?6ͳ<b۫>Cv77r{9/ve ,1rjh1 t:_ )vpSpԷ_sxo >q?Fpޠ4?i49%Aİ<%cUЍ VnF;mbEv -.I(VVꝰ=ÙTzeʫpb\J;ic^r^8ҡM|]JG9E=eS0P(2%16Zڂt"O00!xy@.Ru# ߠ _s2KjUB䘥EkĨ/xъ;mƥ:+4 W6*J~go-{SfQ*[g앞Llj9lz+yh@ߍpQ:|BY gСv`%=[edQ[0=Zi̓Yԇq?,{ b:vlܴ(ؖ687895?l#cΞ ?-;Ey>֫DY (@Fs@\'Sͺڧ 8k;ln_M5&ukƠ8@Te0G  Z1agxAKe13qۏ hz͗U[3d|RV%f[1Kbm)Ib\THqyu)# *=HIs> -; NJ`PG&S/%{eqRVabS-~,XJ^s,B -iPB&˷Ez[.d]hzKZPj+u͋aY>Kl6]^Ya9K>`URF938Ze^.9C{: XF^ :4u`H011(7bHoۍOŨI>)YCY$@IuV/pԆH -Ç2@M6𭢱n/DQMJ ʭeLs{sCpV7ں7LN0N \3w?a/Bm*֣7|z &'J!%{z:ì,Vje}?D93ǻl@a5eDaSb~0(ZF -<tOf47|k/,r q̂=SXx:/x$^'!%ns z,pPb(NT T;~'ƶt =nS&;$؉2oΈƔ]mC'FLx<XLfZkyyXQ[d95Ste3 #§$& +8ӴўbF|)8ػ%̸)-+s!fW D:0T<:eв~{?#֜.;G=cбOJVc,AOҦдm;FZDx :"纕fL{ 74<e1bӽPNjKS<"EA`ʙ~endstream -endobj -3798 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -g̰sHжEcx( e7]osXt};Ӳa# Yc.8MCjJ6 -+kQUMl4(V,kڋMANlZgOg@n9v7GܸTؿGwO>K !:h腵 i4޾l)+8 -̪FB+~e]?w J 'k2iо$Ur{ܻr \.2 0[ -T %`&~Hg(2SκkA3-BW֥K@v?$a?Hp!o@oG~-·N|e{=feECZn%F: (+_ݴBۗcd!"tڄ~.7L: +(f,⽻ʂ&hDȄA j4#wJ/|#Sz*h&Ss"Fm'DBR_ -'ks;AĖ̼E5sc[+(HMSߠtj6̚IP׳٨lW 03E&KE$M+Yi4.gN60p=Eq$ѥSt:mDwzN+;.<_-M%~&:5w"a r YeA"M` {ck#[׵T-MF<]b+`P|=G7Y蘄*h+,{qZSsٌjHGiYڧ:7 m5XZ2OaW6q@lqsW -ŇA)K˳ا9S}q+3mTvutW(x4/G@A65eFf"r:s8j T-Ih?z Í\LVRŔ <8S(ݾt{tQ}MC惯Pl&؂#R݃ ;[=wЛUw6bf-0F#=4vBRCY6$v-zb\m@utZE8=*TSHEp6jƕ(Majb`~7뤩׃3!ݐ bE8y5xjGW8̤ރ8j66} -`OAWib{.RȆc:TN2(T;LL/UٔPA#zP -y:=X9.]TXS3! ~ċ|6P%A9L7]XSmx;  !_iI[h2j=8AOyk0+$/Mt5#iRM'[KV_"WAw+8#|r&آendstream -endobj -3799 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream - oZ 0~:&hg:<j?_rN~__KڨY<Weo8&y;QεS3_B8ZhCͩ<\׫{G Ju$G4~U:]5s=n|A Yh'= -HfB=s-**\/r[&~*$[,)UVwc;' K%&/AT"\@sďrjix*REcDK>?'dԨX} l͏F?!} ynT }Λ/SAk LE/~p=eJ'zۻq:yj'@!Mg@W%SKI!/Yǚ'>468kҹ[гVg"CɬpG?;F, qr -d,nyMKش^AayKrC=~hiKzVD/%6Ecqf@Ou+P_Ӱ69@IYR>6)ÿ2)Ie 5ed9Xÿ< %RPEGLz䄰9)H@ a kj@p2;n=wa?[.6|C ]N)uY^#Fa=#ߨ= ,־>270MlFxNn?Pxd F;թ&K~R7%D^Qs+C(ݭu6+pY p,(oNw`W6/ZRgSO$&N0`N׼FQ&į0.CáF -sQR! I 6,6Tc(b&AqxvbWx,܃.:| \ -I-_*ń|aW:L q&+ԓHjľjNRl0k}2PRewQaPxbG5`endstream -endobj -3800 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -(k^AՀْP;޳7gӆ-8iAQKWأJ^-Hp;('.YqE/JQ.ʷf'08<|kPB -iWY07s:6=ۼem'p+ hàuQf$ruwֿh3r UC99TMowoCs9꫕Hl轡:{чUtjpS>_+|[\ufpJj.é^Ï[K⎦9kOxãdB'|D~䏊 "։0XD*EhQB7慅Cg\%ؙ헡XhFaLpr6cy]!OsS47<3t2eqH a {WW4;,N2 j0{fLT"C1`bz_Zg(R;ŋWg?V?7'rXoi -񻵇,!<:y:=e* 9jLY@#&8**jT-o'w¾:W-P$_HDg'O:pĵU˶6 w Eg:fO -!9(!NP92KTΚ*=ܘVEGv RUO%o>Q -&n1O'IS=4K;hlF~mQ-џ9IF3hLX2sTZ<6%1!j麞endstream -endobj -3801 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -b WEiɨ^`AΥ3_ -A 7}IX\rY/cRi -{xC9+k@b]l,I_^MH')KxgVDwM@Ol%@ -F~L97fir'2#V C`1(wTfA(٠b2n#Pu9as/!s,\i:۽u`W^a]TXEWQ2!Dԫߨ$q89. ӣ}"1L\K+@GPnK.w'~ı9xÛ4FLЉ/ *$l8TVG ^mom%G+ 8#qY@#R[fN8F<Ʊݩr@J4ĶDJ?Kf`UJ#p⎕DJ@V _xa Đye,[4t/Vgh_4i|"Fyjͯy31ќ¾gJ*nKCYB-sme8(ga.wbKL9q3-]̃xnQKc9`A:]Pޛu ̌|֔EќѶ;oT\hg'6cYN ,7_ۈ ;m٬j{st%𝕪.JNmccKq$E/->UG)"Dny.T'#f#a<΃э2b+k")_>Hh -ex+͡;I'̬a8ZVLgfZuqAeQ x_nCMh$6Gk$nYedVz)R6l|<%Vv} -_:!u~&#>q'#O&&%i)xlĞwuNssMt Qr5lAɺhq 5f2rMS:AJHW6{٫:n3PUvEyn3TimlxSM_K4JFܖ+ -6 :]?}[^*>Nf.ZZOyꍘ0m M2UF)dendstream -endobj -3802 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -oSOZҷYQHFQa@>"ά$#tTh'T>W"!%(Op:+qLY*RdT8ՍOAoP57: rTl:LWpvGvqA1׸`gE$#J28,1ŠtW+rSS#ӡv1Mp5gr^<;毋t ]F3 9cl,-7YXhA~IÌtZK>ܝ'>qftT*Ii$'Հʕr/֑+TYELRG(P0t8`Oœ)jQ#uV7)W`b=8]MED˸?2!H{>XB*ph\= -NǫxS1i}h "yz7 _R KD pʲYV!唓JOԌ@l 9[e0AMPL#N8'uw?կ;ؒь'ȕ!$M$E^aY^X80QSF9%wڃrKzK"LK;#b;wo'1 ]׊ǒ8S(E8CZ풒2TodAed٩LkGc x8iAJ}8z1hpǥ2#-:4N.y+Rv=@dam pН\Z<KCN_5Հl\}ݳs// -%;Yfz/>*N`ý뼩=\Sc (s=W^@mitl$@}t.= 6BVSN9?.W@̃Z29Qx}=>SnR8^xlJbfҮ־`}ON+Z A={jIiRH]wj7"yjLB - -ggŝ,>򌃁g֡;rsQ* k7J6yx-ϖue WuMQ:8zp7833W«endstream -endobj -3803 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -Fr6:\pJ ?;r[GA"#=HbD-B -PE,>;0 $m kK3x#M?=u5 )hg%RP2k3&o!~LgsVB۫LW /PqL[]z | V91#P$ >9U7uMbaTؙ=ЫIH.B`!ҠP8}jQ=L qfX87hh=2u[a S*81A%˖=h# -\/:s\meL jߖRiB)r}/ h>)\V&ʊ pcB" -b숺9ZdLk0p|)o)8uy z )6QYȿ$-sRFa4)Y'q>`'Μ$ #zrHě'=X4L%@q(^K>bw?ҳ7 ^ Tg= }o?>\k%K%A`dYXM 7İ]Ljb'ˏm/WoOqÁ'7|쁇֟tHk(1iH_ka8ف~7fbge,Hwww-@W6I9vf`(Q.;d.0MժG{U(7'\ȸP5ҷv,wdszp*iPBr@٥}x3E*!f:V#JЌ0dU`]cuYP8+ѹ4%meEilu<>]ug.8$,=]%K0ʛ^^TNO=H;լ%Į\dl{;$'Lu;R҈DYS[ #2vQA8 +0yRʜ -2(*ܤ!%`{:͠\ry'7r ֯#d\n?| ?вaqZFxՃ̥ҹBffOXuj.d7bQF#z2#F*ےPAlɐr ֱmQ `FB-Q0563\Daw%{BV9W1J7,wppMz:+" F˄iI:t~L;57 X&4qr"+5J$GIĞ;U+^H`(aHN\nxqfY?gԷ̋8a7|ɧ- QaY)?!gOH߶"Tpiԩg"<*j, uN -Z{?}`i9|!׽؀'%_?:v'Ň_u屉f}1|*'Q^#{SJ\('Ҹů*7keXH?!a~ 8E,n4˻dgfԅ8NA k;_xܘKPQm$31H T7(L:xb13x2;iendstream -endobj -3804 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -'+RjBȯgH Dklu'&\ZNnUp2[ {P)#LpB"0n+ѰxqP{{qSe]GAOX_oU{yxhtQ?dGL{l5nLk5dLh:Uiѫl:e[K?HfgHvV9+ eH/d!n_[k4px6-速u,|AW 1 -5Jߑ 4,sZry3굜p0j$ RgOim2:l,&rX$RaFF dZzӘZtu#TzSN.)v~JM{םol Ӱ=A68pە5#-6P\qe46p` $ *vJqF%9xvh( )jlN >*qi-foCV+JCH:DO{Aߦح$$CZ4b9bb]ssޚ?Nd=UD?\ۮ @of\{'i^>fA+@t;=L1٩gyēHS(t[$i GLINh"uBThl)$tՋodzIb -ef"Lj h7ųҤwzǽ[I:q2;gk*z.,w -7ލ -AV#گ}:~E+Slk\DvcoҨSNMd-Ѧ5۹A'J#PF5%WI<=$ecؗ&#ݶTT @i$ͪQ>`\=Qn2Q:%ԅG5FP E^õ EѹNI9*u7)*F[*rM\*Fby8t64S 7pi4W ~d2z&P:F b% :U/ 9b莘~e V2ok+ BK[5CKb<&k ,zƫ,11,S"7];3jtZ^rG6@6KQ?E͕k SzWLa`ӧrXx S\3]J[@{q{TObS9dU_-SI&\'3qĶ0C*pVt\.+biGt>yH]Q,6ӹMp!!',endstream -endobj -3805 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -|PV ,Rp^.#`9/ѡ[w*0B$ԫ`ԮfFx^v-5+ W4CM 0_;5nIg(@l3jE=5pBbgA>S -V ѻY;_A?^ȢfM Dc]oϜwkCr¸;] QV\0qVuլri4Ec't#)zE( -N$;K~aH54sĞ'ţ u7:a{M{,rіK 5Ky5RK05JzrGbө \+#fpͩ'9ӏuެQб!j3+ _H(eCFTHw[㲄NŲ\9̄jg*7@ضnF@W$ߵOD?Bpx2 -^du>-edeSf{5Y5A5ޣй)j2GIUfOA4*ۅИj|tS9Yz;BA_qipc}M`-Q yE glb:Q D~2yngwVHMs;X5;jZ]( ĠP~>OKZVO;jAu%uƩy5#DyΌZӿ?H+ט5Lƍ db69w -%pKs޽L;KwU-Lҩ[:˾ F&k;9nB홽m.gNUQ_6 ;d<"8ိp?W8eCFWGy&41O)NP$4<<^_nPWQendstream -endobj -3806 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -\' Sz퇳(тNٰaKuVܓ'0QmHfǽd|gʿgX -CP3 6kFNWC>>LxRMj%U8f"Oɥ}f `ЯLJ؆JlC8eR+÷;9wu,T|<ps`.\cEpTx&VUoC*vk;ң9Jj~椶Li] ,:FTDJx1.\9F1/s k".M!vL_Uw'xd0!,kZTewA6 |6nMV&22^5%ÁR$Y-X.^Smr<0&5@ArfK3gJ. Ȇ{B3n0.\9 Ȫ!*;䎲`?f kf;mw]:߭t~.&+=T5.g?<.~b9{h`|WջȚ/6j:$H:baG{_Ux8.Aexp=J\]UKS3Մxg,-oH;~H cd,?7~`@k%v.۫Ta0]P;׳%|؎OxGz_>znC6Qe!1ɰ:bi}WuLr z"9`<8[ f4 =_KF7~~&.ېׅoXszԄI9RќLhv(G^w:z`~c%#CiZ-KBƗ (}l_Q:_C{0 -{8",$3hzqe,HAɌ]jJ(UhJp;װ9_@.l_F_z?~v*aucρ$ڳ1#6~iKBS®Å$M$}?g>!\33VRtTlendstream -endobj -3807 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -$d#*نɪҏ2F/:A+ P‹dv|C'1-4fK.wr_#&_lXpzNaNBi*'+%'x({sTf!2L}6qg(b@[>{L|tWQ=[(9 -4nю#I~ O.N2Hu8G:@0HVapgɓ5GD4qHSsB$myTϳByZ'XG:R`1Guh^((饕'qbq$4JH[@$ #r6 j{&<3 @Z4բrOKNZp$MJeyrI&2 m-h:J]Woğ'K#_b"R$lUo徥L*.d7h8EIUaM؊"$}99kj:g|ڭQIPHHR>QzCŭVY DKEDj̻ؽRϑ12bl-|2dš.i<(1UR5oJ}6BH}nT|6)Y7x] -(`Cm~6W+l'(ʕsQggKǒw w諸ےpQdj~^!䬨 )29 MH"<zT?IÊbf/|:ɒecrR;F" -8w\Cmqnpvf\KII\d b@^P]G'Wzj}u p niXS?57FK/.ܓbTԍeH+o]i+hvQ pz,%]^ XCdXx7$Xl-jƾ](kYXz -_Px8 Lǧ{kg1}cmėIgf+wPa1u Iw7,fQ"֭\p],ѻjgF -;6 _SwTpZ*4Xec߁Z޸p6cEn0< ƘXE0(ն`Ֆ{c3s!$1 dJ\޽b֏m/V`3>(%n2j/Tp; S&H>b.72endstream -endobj -3808 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -YCOyjOhC̫9Rd C' F! wQB\:l;w'嬀.8 -x=νf._nxsѕ!:m}q'g0 Bi'wӁ C:=e-f|jS -bw)CBL1M>vN -!hS]А93JqL,st!֊^{2R*\P#|j5St 婱PGRf}/Mevp <瓖.͕u#-KB=HǰSX;Zz,fXBmP$tq%Z5-]nǪ<'pǭC/,6z Ma/rz~Wc?2ıTGJpR#ݽGjrA*CUS2Sv9i>IF8twqbt X2m-2̀5n>,ӓ9溦;9 -n@?ۮN* -M;ӄP!|z|qGE .kt-"cy66p f`Z۳zG*A*g$/dyw!9aQo&E⺥Ӱ?H(磐|HlUisCs,TǔDkCF.rOGVx?p5CJ]zzg|Fu܁ʻ B-T?Ȥzq?p~rz[GSG'G!C?SC$x_U0 "iBtXT BabbS&Ov & p}~3V"WG2k(I'>X“#\9>xq:*O %cc qu6,k-*4T'x,m˚"WqiBU9d&x;Sɉ<0!xj=6z}cbcEhmDM. n]'.%-O/-@Z,,p9T%2!񸙰S*K:zRG-O>JɖE@J~=YΧ_>7$su:lbZW7jUb5btz؛WRB -EIyt4C6ުXDX#g0t=ç-Py7V#z1#]4a1yFf(.p룟C^[k2 3Jnl2EYB/!aõ2} ]n, -~/Th 3VNϞW /d^&2_>Ժ[ԮQՎ޷pť{\iN&kE Ũ\!8GF!ȾrYp4T]v{''RI[<U@9LMCHjܪhOR2Ot Q7p255zifE `?~[ .ãΙzf/y lendstream -endobj -3809 0 obj -<< /Filter /FlateDecode /Length 1968 >> -stream -%A0i?H.=;1Y W3uMIdQKc+(o]|G3׫'WŮ0.b?ElWЅSq'CVHpBƻ=$%-Nt3ӎ0<P SI4bמX@@y]kzsijkrLQO -{I * iBʌ B|nM C ͇]aPhbӎ)`n}hWzI5*,2=T\ӚϥD %B%әBivYaM\ $:JeF z{Zo679 -/=t. E"u|bϙ %FxɋjP(J EH .<u&==Yj#P~=8HufgMMgFztP:іW \8Xqg[{PFanqQ,"nɾ<[HwP +i\j+GId_U٬)@XS}\s#+B0ɍySu{-b9m75PB3iERiV.z[Q |WסR 1hD ^#k_|ONi|̳yn ics?Q;K)?_旨'õow<% `:" `^4n{h}kʂ  -eG.ODvF -f)L@l#x:X V m{~#Wyo*O鬙OgW  <pNX3rSm+h ~e,)*aa|13'ʩ=T4Krj[ڛ9(=2:_Y-T}fV{#+$pi{')ߩc#ڏBw'Lp3 d"oBn^›0==+v(P M!#ڑA&xBYs^.店+"%ʖBH4̳ ʇlQQ2ClAxʫۣ}r(Vޛ }itq劓k+])u?Xx4'C-uQNSP&?ѭ\-,6 MI;i= ܻgt^][,9j̓>YQdީ*.$3D-J}t1/<^>6O1m~@lϬ&+S7Gy8KJ~+Aq-$`M%׵㕛P"تFJ4Y!iiaeݓoa&_Ⲁg`_ zL?sK|_.9b$i(Ta(G*|5;3:k\4SkOtڍX)]Pi߸֋D*Ue}%>9/=@u(kY3Ǽ_иp?@R -)=&K ጶ 12ȔqcFzoZ>`.WUH2endstream -endobj -3810 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -jDE22zzhVYSf$$vM V N}vỠ`瞇]',2.'uc\.֡i4[r\SXƏeqW9<15φ|ؠQ"=w*Cft.e Z -raZ)UD@( Ul#4$]jjф vȐ6}|8l x_M_h8 3_N|ۛ}ܷΈĐqyK+D -0UrSAE;6nɅ-Jf%LB4V[]%#3JaS!~c -zl.:X +uC]JPӀΪ]G,"P^(SUFֿϑĖߟWG!?Am6:>/q6䂜dn5>Bkv࿴;Alөk&K4a^nE|63~ʆ<)*1]10!;p̏Tlon P$g - aQ8̋k|G6FSƧ)XӪ&p~Wt4:T6Jb*\17?:-P|S=i#e~9fW2'^Ӫ"\@}gTMq [kȜ %(&oS>]FQ}2ĔF+` 6G|ξRɹ -e ι ė걟Ӗ~ .9) SKv3F)tYf~]l4 ߡvt:2NC G[#=$HfM>9ID#c87X.tp[:/rqІ-Z֙p8;:\ip%m&d&npo{XƔQ!;-9r0xD|,#.Ż&,vG%d,ܹx274k9U.f+")XJ=53pbNQebxkJ^!Y, ٬[ݫ##l{:}/ @ -6iTfvʖO~wW䑥< |4gŇplX\g2䧁C8洊3\]wVh>ŸDz &3Ml=X:4'({K4(#t AKGGFv0p^Gn̶:Kb?uu;Gv:X֘F5P^-jl'{4ᜥ"9Qq<>?B@(tU|aK-WA5BJ̲ P*F+?³:qxj|{J|oX&py?0 B-ク'cY듻Jlh -rHbVr([X{yvp -*Og`ɤj@_T$vA"^7_i{`Ҟ0#ɃssS 錑NyUo@kܦZbyF)鈧oF\L;kk5l 9.b-(-gO|"EprXP8n|W835|Wo(z> -stream - boGN6I4׹ 0U>?%3M+LC҇^EN_fbxK׃|.xiQ>v_AaW.h^ɊWwWwѰ%"oWV*n֘ %/y8yx;,oDO&gN[%:݈fv^= -ݿbp#^V1vʖtS -)V]ǀ/* -7NDYela1á CYBVnarXH-t]tG͚(|TR}IUQ|g(G#zs-KRp'vhRd1f[>οPE .W<~Y%Ue+r EW03O RtDNEtVա9D%a¥0NSkV6 ;dta+ͮmS -F3F4Ye3O=,ݗkBJ>Y,"Q_C됖9[r'RF`. lDnsa;)F eKiV,Q! -gc Aҗ_ -Czxyۮ 6FJuI Iw|EJ!Zo*oݮy6f.Ě&垾B0.Onnh/Z v3Q,Eȴ_V3rCsTMEX./1?RʄmSeXesfcH[y-; DXxCYD _/\gZQpfz/fAKFr Xؙz"s 7;6FbH>aZgfE|}8u|a8|b{*M/->Uz-֭v6pIНs r7E"yy4[i0eNJ0yzcnR0ωSOBkZk¯Va;0b0"d l/MJv|Up8螁7}/\YPF^dTpD|3h9#fO{j^ -ئ0%Tq>yե͇2F"Ӝ#c,tوj+p[,^d/4# -@ʓM#E#8W%L#&tBR07oI2qBiaZpRC w30 Ҳ. ({Y䜙UCUby[k߁˱v*'fX&#rлN0ȑ1\P\0V׽~~X"v_`_hs #%F9Wjy4yyp}DFIG`_fWH+LyG `Qڎ0Ik|> -{,{'bfG!Q{ P8,].|J4Ɨ!R* VR{YJy$6׬ļ -K&~/n,yX%ίoB31nBtg/ wJ"桶 6RaHP4:y4a36N&?879-,lV<<ϭ0k[Ms:d2dq"&.|{d}>'HZTrڜDendstream -endobj -3812 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -2PSJd2 -ŏf5Py9{39🮥i%v3+QJ hRIY*n, Z |66fn1 GUb7͖]^)IA#5[h2)$ j&mF -On|9-yb@@ԛueWyUܻ3:;%9N"3{=ܢ$1_.e n5ZT+r@#âM7@& n3D! /A\:-:M삔vBԟ6aPţF hz H 'rd~gЙIun5ݺ&(S>^6O3[eme1 yo{${b,4e5=~O?~1zz&)Xcy<6Ӛ[}gg Hh Ps]MR4cɢ ac&Hdf zZG,(nQ΢w㢲1xs逊!a;MS6cY!luNn q.RιAU32)V8QoK[Y/'4 9-BEgKKCFm={  7|1eԑ%t9/h|{FB}S1Rq9sdMۿ -hc-`S"G:ǯ E` -+/i t}bv!9Ei ݆2:m=]?[s -~-#bU?PP鮾G>klJɟ7~ԍ)ڱ#6㽚>b07J.s7 0+<*Wԩ#&ʴ@H OlԆ -VƀG@gJ=7{cv)<9FMN}܎bgB^wAe>EzCk#g2lDVf{kVXsy{kt=*źۗZ7$&+l'AƉ俰peNe2JE[+@ާ 7uΧFF♧O> u2CJn5y6g{EKdYAxގ!H6p5.E dDAlڟ Vkm):ܙ$%{h7#,(trZI_Ұ{6Y54C]YG%kk q 1ܮv_0Jڱe-T9/t;`5endstream -endobj -3813 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -/_{Ԁ<LWw+\=~pцr!UagCaTm Ŝ)qJ1cWrеI0`ۇ>?H[k'w$ xE5Ѭ<;VŪBGVcLlj&ѿfd` -Q,Wн%SaZީ:vF͚~[}{)٤p'C zB }XDӇ:;; aɱ|/yxx1HOLM}F`3/K/!NCW8-Q=jT3d{YhJ~{M+mHMeQ7~]wHY]sl5{.gПpX0)N~ox寺?Nu,'94f>$? [X~iB@uuل+Ng -շߒdNaK -;ڝQmSZ,y3qANOS?RBi0 sf> U r~uI:*RPjGaѢp]'=endstream -endobj -3814 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -T[C sfB'ҼȨ#(8>q/7k]>rj67.ZךL^jS$wTNZ_JrC}A')FTm ʈУK\]>$[위>M"˂6vmz2~]U5FB7h|+!n[#yPn4R,2o*?j&j$9 |PW{I~@R$݀䨈DQ}v hzpv {C8]%cA6eIR;=&za=L> =,mϪ㣎7IK;mZdvIA#^\TQ8Cě\$Y-4wGƅ MԦ Akn|x#D *-*h=3=Z[Yoਝ)O1y:f -s NYCL9#El3~D&S!* owS:&Z[fYu5ΰIvC nH>LnYC[+(!CSQ$m&eH!S=QO-F0^#BnZ5svm+;U:rsϰKZ_a4}@xQ\2FN}mSO6i:*b㆙Eʲ"p}8JЊvH#!6(l:N x%s.X'x{O"6yZqB*iqA.HZlݾKBz]s0.mQToUY'u=xa! CMl@DNX@SྕKoN~ NR_Q%Q>Hj$J"ExホNQI@Ss ST3\ ȝ*ޥ&"{[X{ Xi'<OKi7Y/)a@-9j<| -FQZ`TcHlz,ERO!dU/՝0MHR Vz J!ѻ#{*7i"?6\:^фGs윰3~]:s 85$_:hGbe3C:ɻu6NC{>+>Ff W[ɦǩ2@ri1:n7Pr1lB.R٪\#}$`ޜrژ\zQK*@">(CO\؛SSAeGi10_ܮ;>ģΜlClTA78հ)Dz> -stream --+0 kti%;P sHTъЋp_F"\ 5 Lߝgmg'$#E4JN>o{ ks:&Hֱ/L}$ 2cE#R\_vFܸ0WUiٛ|{%%WM I oq'4![Kk+ӑ.}H ?;΄I` 2꼒d=P;s  ilx~W#2.pw -K}Vq|Bš_MRYͻuv|PU]4왈_b!9 &21 qo6ȕh⡋~­#-G7sLf@ ?,4`@2zB#;wM81vOѢm2VW/>'h`; "gO(w\u]6R/Nl>/bO"^ə6"٩>̍ jS(Hr`aLjͩ6&=%yVU9OzRM\qxgRP#"@ -A-͋=/ -iJIN0YodyNC،zl#gؔbq돴^&Z{4`sN|z{Tx(kfI OA? F'2,?{_šqI$ځX-_yupĝ 黴(T ʫ C˰d#sYBWT=9N\+uD /L -֭B:w?u& b}x6!1t> -stream -#&4LZ^M4(qT v ֐-o#XL7qLG*e"R?r票<0IN\‘j|"&ErNHicYrqgղɷ;rC[; 0d>=fضUW{]Ulv叱+mZTͲ{Wg~d` -pM_$D"_R/Ht%dRYHū,=%=auQՠ:̄!o#uCBkt٭V7$2=p 1EqbV" 1u\·H͡^k# DtFCߙ3{y&Tņ/ο\k,\(O$ WJ@! `5 ]N[`NB&-vAGrcdL8uGdQ!e'*G]0265 -a j,L# 4i6_sPoЋUڑTO\{Sk (V&k @T3/9q_jK$;Żw;b?žEƮwyR\pr]v"Ys;/YՑ^,UM`.SEb(ŒE#H44 wxPq1^85P01EA]O/ϵ{|*E r*y7yp(R0m5_q+B (s?o7 -ZIx1U<4ƸUĔ0?5h`=LYT 4=v@ިhAUE_#8Nqp̘/DG WB9z gC:DÒ =CLK0%])&cǴȬ_*k p:0uEE^V2;S(,0䉁>eznhA!r _=)Mל4g$HR(wUQ^uTMʼnK_! .o6+Kj ̭t&N3 -Eȝ$ח+l#p,> -stream -s2^ -Y Q3L.q`/ªFZll79N -A1j}d4#R_t#lMJ3&H)MhL~ԷM.P 7pk%e)|xyOW(,bE"-d z@~zy -;Lwyko~24Cĸ+aˆwpB*2"{w>N ZD5cd)a ◶"C1(A24;-rb[91&nrfNcúi&9SvnX#mZ{w e)? i7Ȑ֊zS71:A$x_K+_ątuh]I˘\{OYf3t\3T.w`ļrt(v NV 6qX"jIj -ާ!n|ǗpV.u{S_H"#|8cPZ -HDT.H^ʇ;Ŕm'$v59bR/E??y%p\(T%OMbJ^=ak p`*ڱP{aD&ԙ"j%59Nk, љpB|]k#ϓM>Tyr&%@u$qKf-,na(ʼnWXtOTH#ҡՠ1rBOfSAW%@R7d-qɧ?F9 &o>6a(vV+a _Ya%ľvJD6uTLD]#6w6d{qoiFMU(S] w6:;/f/Rd"+eNCUA~F`پ_8WB$>U'=s)ELA9mt LŠ-t}`dj71ˀ &ċΕ\3gƅ #K,/F}E01o#u$ey PE8S#4K -VQޑ@&lg9d(.wOG9Z=mW.fG܁ .2|Xp?}"3 ->%PCX^b'yd -BՋxĝ^: QxxG(,UǝԸ Q](9g-٥/UmcKh0Ưdw &yVz4 _q -RB+ N0߿{e2  -ߥZdR -Bm΍ {Ćcˁ݁_)*~C&tj8AX6H\ڳ#X]((nv FYJ2^},BPgN8JH?MUy#h ⛀ófvV ¶6Gj^cB > NjgɶzXt/ g|b*ЯN|wTKW*cmh/JGYB7xal,˃|E/ D;,VᏟ+i,\x.V (G/a0$dt_RƖߞmfG?YePE-Q8 -k~`%Y[K \ G&yMOiR#)q1lس5nkٯ2jGpÌky恰g*3Ll;Mo>Er`݀.<) U493hy 7d{Ka01+ OnjXx"#k$[bI5endstream -endobj -3818 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -):G9Qt{u,7"n֦c}OWeh` M; a< USChq\d]-(5 c*֢@8q],ic|iKF=.&)"8'nFje{"_@:} NٱΞqBNkvsH)1ւqa?uJQ(79ů_EG[3exEA7tو v&>#vѝzp A4tY^̹Xv2${?m~yŭ 1գOb/R]%CډH!juCh3-]'>={;?u~#Y'>e\fn0l6t7Maѧ<'LBND2o'tSx#ojf@8³iB9@}g#cHm~8ͩGNz4oSւ`_#PEϻU]B^A#@i0/e,2*gf30.*/H*=:LoA]{2m ^#ǯGPZe=5u@J{Ui&AA*d]2IYD ,lH;\kð;tb7h_!cla)xpJ-3QűPFKɾS箜5֡Tղh'llî; ⭋-`DY  -{e2<)sazU6W܏.)4 [`H"6Z+o$ZU]A+W0߾#l`2YJW2cW5&+W.7awH -d'i ;/QesɟM}~~"?!xeF:&ֱhjo]inHlf^_p̏ !d> -stream -A'qJYDy;8r d՗ ye{as\ªmé faن_XOcգN8Rpmw6TH~c Z3*qhMd *&SJwؿ8k*x\!)L-8OB?æ=~2WWE*$jg#hVIj&!Duz8/n[W1[ ΔL2r\g<qPM56AbZXH.AjbdG.2mLvwɈN9=0@Ju )+}6nG$kieB|yh+"[jTdi?SJQ\QOr -L@{*Y3y BgI 6˖@ћ7*eZ$DAD'rF3o'jzGE izaCV6+t(r` PEf7,;CRU*1KNng\=*W_Ww-;"B ^Bzp|Rr϶hLrʮ%WDº!` װ -|0,V|I)^#: 0"o$OCn.xtH|?|/m(kN?n.6߶2TqxaܙBٴ<{5y5=O۫EGd0ڣsϯXnyg_^2tt]CHchN8Hvտ*]gO \:!7؊3[>D8xg(M.%+MN Xwӟ e -i@GI%2^j¦#E&jRendstream -endobj -3820 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -jETM=-I?M31u:ӽE n^="x)hZhZdf9Rq ДlV]L_ht`ڎ5%pGtn#ω,y"_R -wxՏDbw@6+!cH $_D7ÁSy3eNb?|O9"]~I*-\qϪBP$߮ޙB68 +uh*Xzv@+ ϋ-ƶW@qJԻ|Ac ϥ{m.$v7cvuw: J^̠mc: V+j8t6 ĈG0*@|"#'U -I6uu/TYM̈b@ ˱xٟ)-M.K@AJ&۽ -6Oa -39`7*glh=nQJpaB=HׅPwxڒz)a'57k\K$6I2XmR:]W+!ē]L׆(h@XC I z1dL-&IxUNb=lO9,XF O0ɒ/14ĤZ$>?֚~#[",KK.[  &{;{I>]]PX ;1"`;-zhqCe k7%9ɦ̣e3ɇp؉"IUofJ&u,RIa>xM Th91V [q~wɳ.Q< ^W? HצUx> -stream -gHPM}jr -ϸ6T;OLJPͽB3WYߐD'0.ymka2< -aA ~?cc W y :m S/4}ه/@ͼQ[Bv';΄oUߢ(wd;sh0pWCsc{" 7P"0c\$Tle @R 6cɞC'\&IAe%>+;0ĸ:k|'eE3q?BZ}=|Xy -[nlXlg˳FԝvA|˭+ 3H"qǠSG7Z'B.H5q(6E˶Ruz8rbX 6C1 -/⩂pč-Ҋ wUAdn`PU 偁u{ g5b}7Y_ƬUPGOō}}G1e}!/V[ҟy6;Kx+vf>n+"egr<o Tgl*3&m;f|"8~|ť6s?8[>#@ |.CtSN =k*B-!qφiKIaE_KWhˮlhe쌹u"#ֻ?ː@jKNd4R69^R3WG`D$dSOA@nE|gCpc\#W ^:Mx3s1hXKO;gαv/1\Xe3wˉG6=l8쮯JdqUlgeBXĒXn0e~Wͼ ]Ռ.+Kk06KWiҝ\;OMP4Fr;ۙ!րoT#:K#hiQĆ9Np[>Di6L`W񊡂tȿy9 (P.tb~6k~Ǒ - b|Wg^cIbf5Hj6㝝PAhH,FqI&Jރq=` Dɼx&6")(0p&"& s[)$I)2' LQv}1Ifh:?^j}ڌ!`9cbv} -=;%Y-c -k>*V~r|ȉfYb{QOǕH(Bsc3SL7~vs8g_8>!VW* (ƣr-?Fq4aUK&W{\b@)endstream -endobj -3822 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -/1ņypLbGux .DQ+6rDnGFGtЂRrpڣ&+pUkװp8kkr'Μ&=2g3KUT,X?{d>s8 -ɻ2*3Ph+*ȫoYr"#)r)ʷ^v>pw~c]~3Vm1MrBՑ?M[꫆\u%¥) l2bQX2SI0"E<Ho_f>NDta "\KgiD-qk_+b;[/Td6ICR -6)W [-C&hlłY%ʵ⚯j@%:Y^6rzu.Iva -e'LT^ ҁȞF/];Jt2nEUvpFIer .`$k)1`Oi .y P^odT9?U\w^"c&ڰh*}+M*f & s~Ji{Âk] -ڀg rֈ$/-QS7X\y0X[5$IB.lG5B붻gp`](ruh\Zx[Dl'`9`rsW eGP7dy)^ߍ<5MVwi 3Ey ^%2E4 ;ͮf]ibeŻߐo]Tni2ζr=F~@%*1t˃fOX?A/גr1Iܽ&Blw*8ukrHr i5+)4D?^kCɡ&ΎSCPiL9__Uj5Kǐa$e=|eJXIrBFqUEI]Wk5驘X[`S=|V*Ѭr{쀵GMIkp`,mՅ=4͐I2![DC> tYߪtunVG÷QE3o0#xv䭑y&>SA^Ly}$g^OaC*icW6~p ,o tkht@*06N'[I) `^2&v"J7;<Aj Mtx=GMO"%}:lQwh P՗G'zgAU0z}<$ 6!L, O;#tgTzDbdFԃE"|YM22WB/CtSMCHHLȃVO.L%#X7! o"B_8Ns#hk4}oeg> -stream - YOyJ8^lyݾy)RdLܔ ʵ5K4 -.Ec"R&\S7Grk,dͤ7"⢌.$ȕ;"{A)'(6^&pF'@Ā̈`ʡN!j}+8)UUٛ""YO=v%ye%DG/D\"G?GG I@ Cx8H^"g[{!>;Z _] & bz -x7*aRm['>% y<,o"D/Me6/հu&%jp+MLa#Dhή1ҸQPPVo -i8E{C7_Ȥ^$^ceXݤ>F>`!8wyp)FҠ<|2__Hڅ)rZ@jL"@s*%w1(i?%;?t) k^KC pJj@BPx43rD$.Df :\@`RѵjA#fLj.jM{D*Zo adVHz[LJF(Gy\&{W=dXg,HIVU3eaǘR-2i UʤC Ԇlj>06:ସA{"F^!E<G6\l0U[s8UK I Ց*iOHlƹ(uN +÷s$"+VS䓾tfj[mt]A݈H4taྸ?ێ\,qlS惠M{@JgDG ݽR#07FQDrWY߉OTWKeة!`¯evl0RkmQX"}.2Z:smHp ,GP_3'DhOO -٧[LF4WD@tI(9NZgZʗ2Xl z/me߫|Sz -8aCURa/ɡ?R"`T~yƆd]jZēt ^ GAy0yo34VĆI?Rce_ -K:Hի8ֿz5]t743敓=NgATA8@G^*h\4KP(cW:h 씊Cz\|>4XtLg2AB q0ؒ]US;&@}#Y*ɒh2"MO0Ny*{v7 ۯ6endstream -endobj -3824 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -^n:X:EL6Њ~9/Lr.B(uoDzRK1Ҭ0:26nrAa9e^a`i~RYR:,2Wv kd;qJ.ِ v:g}:h)k,;F30I]̿]=۔o\kP}Lcw226`ҋHHykSP,ϽTc ƳLGU]uPd'b>-Z9ËXhr& UYY{ƛԀ;v,Df݈M]ӳo|%NT߬&UL)Pkr7 p0qi/JGl)<3'd1*% 봰uXšZ(+ЅF nGmoacے7Y5TDDEy}i:ŗ\Fqof;}?jbs93Gq wUNCИYJD۰TOv*yfujAUþSFt*;fOGQL맴 #FOn -u(qt]/k䴴-PUfb;OR QȶY@ -:Tlo}k~|׽>QD?LOj(gb%\LL3Ju?ЭI\NVEjB{H&,ku=G<,ndPI^ZDd4LR -YD5&$VlI$MUI1D3u=ˮpY8qR7{k­mfl>]CY2XF?xkΰ<~3/ri!*{l[km\b]]gH" )7Uʐ[ ?&qHS38=d&JCG(Ub|H-뼐ƸB;WQ]>>a ,)\>Q2Uڧ^A=rz<;VN}׹…- 7;ދɃL1GWVC -Oiv+ %gP,z݋$~`ٟaco/ rmBgi+XÂM'OfH_ @ CZuܲT!KS:'I!|$G_ -"wY;o4\RA h1H`dDc9f;mucp1 3#b R+0M(pGy.jjKFwV-Hg B-ϰΞ& -'#+IGyHk6PJgZvVٱ]+šqGUV/.qaR) j;(tpry;qx:Ny;`9߹S9Ӣ.2 ^bG"~6Tfsw=kܟ|}<< -+ُo/Zz )9YaNdSEǃu"}R'Bu -N(~Dh$> -stream -("\S:?i׉ti+d_@014hfp4 ih}7c Sv~%ֱc' ff!{T}fGND@JTF* @vouO8Ё4*@f6_4].IDK#N|7>-}6AݒC:dw2o_mh>D5+ѷ^7Ll݉Lo2f}ARYWp${[(x"d8?lf"1* V_殠 X^TӼMu^JW -m cd=ԟv><(L2oC;T.P#:kŘִy"{J"wy=qt؞6RŨ'B*ZXK7~Xׯa?7DUݐT]XulTB]r\ v6g >6.ogozo mRB2;~Z +@ݙ-Y4l`ХWAav؎1u 1Cա,#T;`ksH|]-Jn+J&y94qe%CԉIt*_Xc65O -%Z Ď7 )욠PПW5{Ob.OD9y] Cl}}PlkODm~'mODc|`s̮s8;E)4=2Or2/64_L8Q䛠Ӧ.̷*NDbfKˬ1F>;gC+o|ĽIθ%(oO̤)qGQ6PxFR[[\E/o̻OObS73J#kh#]'ᯣ%ЃMuSlmrITqSDzpV{Vz$Plt1qMB,bG׿7=pXfo]7d/hY16X4}{_uO@K󮝤WRG#h>g"jY0S)1&}q\:I⿋)͔?͇/DY.} V6h'@w٨%)N|0>rC BW ƽmG7{j~1Gudeendstream -endobj -3826 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -%wqHD=$IjiZx J 6>mp],3Z,9**Qw&v;N*_w<|,3 -嫿ÑlW"L_qگRQZgs - `NM^Gn(d34>vTvY i# KqAG?(ܚZ͚ySAǗ~g1V1N -\YpHyU]%)Lj|^%JT{_Dl{P1G&tL1r*h=$Hc^&dOO]APfܺuKTmt@̝'kUGkR"( V~ -Ħ?}U]1+Yȸ;YKdd3]ٻ;Tÿ[=qB8~z1x3jH/8ymmS2u%(KzNb:ϙ`l9G傰̥RP#mУbçKyz7q:̷n%&:X&+`)`MǕOHu̐8pŇ1s/zsBAl TGe L_v*OYQc*-M>}e1keF@Z| ]{Q vZJʘry`1.VU;P[m͂4g&uM14dxyP@gԪLj5zH N 0t\贖؟o{FcL!FFޛive@>ڄ!TMә6uzbyhi{\I=: ?jYY@-v1YW -'z By&p_ aS+HH#/=x_{Z<E=~Rϗ\6**nړBkLkA[a;|?zw*3tFk9eɟ *_._d'\'fe> Xz87ilʗ -.z9Aa:3"7~uZP3x^'Hƪ -g>Q1hą'ej -;̾b7ӗH -0ay0\br|ɡ>3%GN,|iyRe*IW7GI&)mV KVu&NvȑGn4bӕ +z3endstream -endobj -3827 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -93:W.s _}N+P}n*̘6 ɗHncSW-lؽ+9@t&,8y2YG&Prn?H; .c;YEvByŭ/@m>=i@$X@u.p}/=Kq;ڍ݊5iwS|*jN"J1m"ŪDWR'ۙh2.L/q虚"n2rCF.r=WEl=ʸojeV'ӂѳ cI6k~x z>[&_7?: 7 - (tIE%-G bd/-w^'p؂_'&+ <)RTm-gՆ|BfOLS QeBj(j^%/ś UMڎΘb!szS>~>SN+'qDh@Ki> -stream -Ͱ]!F6>k/mB5=RwmZbZqѼ0K3,(a8 sy>3b -҅/s^B%I$ݑ 67d2wM&;dWr "JfXy#CܷX&D6^y -OF?n?FTf05҅)qˀ;X%\Ϥ~gZ -.%@&RuU@/${`xd}VU2p`'J?5:sG*vg9h0Pӿ` }ɐZw3}p<I1 -f'~]V\7e ɆC#JV'UE$gdC(={;o [Rf$$v xieKp[>OaL]s )^i8[b8f.u^QQ311BW;5PA/cxQa̡et:U#cqTxsi 6#܄O7goчOB[;O(E:="}b@XRrޮ.*6;>|yT3<DN$nWmboQ2:;` -nc,8N՘'=Xd.PdGD^rFhZBhpgd= 0w!1 &Љ{e/IGg0u}ȣL({THHҮ-k{ 8KQKYE8q]o}ʫV3Y ~Gp+ij@ACRBȍQ0KC1[*¹{WTF ZxZk7ӑZA?Lp_8 o9c:I^ĆL,b?Njc"AKjIR ߤ"Bu ߘc`BDؐG/[#*cy<.BRo[T3#ڽKZ4,^."S4o=g CVq,r; MեeSsX]QeS|@}L\GD/! znh(BL%63:_ fnAQSdit -SW|yK̘Ó|vhB:`E;Tendstream -endobj -3829 0 obj -<< /Filter /FlateDecode /Length 1232 >> -stream -:̚NC I-r/mYȍ(ȳ`qKV~_m +YӂHf*V=ڗ+t@&*<'>fJWܥɷkiDYc~f:sE2pf}+U u˞= h*nrAF@8J,V@~"J∳T&,R .@#>ak~v=`vыfz% \|Qa:rڂ'DA)uV?S4C&S6bB9LEuSYw9g@Kt޼),I;X6^^7Z`BbSku.>|ح[EA7tt".ΕT%h2vJ`tc62~sF$hOYgtg\)Vֻ!>p'St]KJͮ<#(@)1rv?o&KJ(¬2vWUp~5u<lt< -L84n5k 7j@YTnj%EW@qA4"恥 vPhc M̅[}2ќJJhI59)l۾E%OSY)ӻvFBAPsAd,L٫MȲր4E7+63Sgva1 -捽Q NlV2<)0af%S)xL~pUxU!vɅ 70=] Ů\e+{ŧmB1n0=TE:S:X6yQ.Hc=xNsм"@ D^cn,UKb=F֊ m>3&V?J̾dk(ƽ(xVF[`<5z6ZL^Fa96s4RYwS+_Zkx֧=\АK^<*XˋU=gGY8݊Zw{կ(UpwR47]X ڴμ%eqaBSZftʪAEd3ƮP21C^ꎋvfhOh(00U]b9M I2k歔& /+_b:Uw,?>+qw~ -˩,@L `ԗKQ׎|^R.fъfendstream -endobj -3830 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -Edf=:?G#+syc:ߣQ%Zy4'ǽvo{i˙tH̊޶S;I`| )), $G,vyI/Ad;ifJd[Bo=~++O̡^*m2^5Ijs̐U@n#T+iyK"~la]lRȤ9R0˯|33*'gS">.ڽF!G(_Y/O.=(ɘrDGEys+/WWu<%iFvzfvS"_r5-iZ?N1H&R#L\pyGjIr^(b5k~n}Uowy,5QzwAoiHBIa2[J \iBhcf8ϡ&> C+j͇qPصfs,[a>K -P(HH0qRYjBGÅkZ룣j-f &J!w+3di p(W/$b k -føtk#='?S7(u >eHZJPv5n Y9嶧>oG-#$ѽs}"pm!0'w=ʌV║"G&- a}n&)CٓųTdXMFEb:XS? ̏~z--''sΎhZH :E~Ә~{kyG_u܍Q/Q)xA87>I$gQ@^|Uf<ӕ{2.`+(Wnboʏl].~*O="x5\S N2n?m'bo:汍 9gSC"=x7)(q#)[琵lj*IȂOYaQpU1j - gK+yZ]"ob+ZnHsHd %Pb^ 24)̲Dm lCqne|wq4:p:?;.K dD4(=ፆz&nfi%`ֿH Jǣ Q!`:؀`@OW΀ :#dOJSu6g,U긏! yrj]T]/;mHi4@Ĝ{+ YI<7GJ>b94vW_Zw6]vlg᥇MKzRqWn';VhTj_JfۢxUyK% (P_U\?hCU?s׌f!)iP%$W -J{\ݜ#7]>[صҖtc<0o۶ҜxY m?!$J;^:id$_j)ɠR)~Ϫyk+~[ƑiLۧx|jo(Q"~Z쩐^!ygP1JaMG`Q`|0,GbC\B\[mendstream -endobj -3831 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -oQM}Q3c>F{Lo/@cZ? *l$kml0d@5iTaD.f*0z:ok$/l#BFLխ2~@3!PEb7C;xjk3L߉XdSLʋ4KJr.)x$¤\Ė3eKuQ,ڕGOf -6pJnkZ}Tμ"*Sx -a@?~\=XƥQ{T nJg[+AkD={?mm0Q=?AJUčҔfUdO]SH_ <)\mE3w8iYZGq'Uvl~y8EGýu-w\0o(ҎNi.cc_(sd=Olrsr14vv lf{ S`|q&v_J`z<Ȥql݀MȀ >.4TuQ'PNNJc6txUWzų:_C\خDii&F_nf'$e%mx\V2ʻ~giu4Lgs, 6پ]Fׯx=́yXG*ծה~~7~02-9v4*]uFlqUz:mhKB$B9e)/d.)eaMɤRX_mKOq G<uσ ܨ8_tֲ -zU2yK4>czWh4.iGhKh_B+!Rh}e(2qs::@/П)"oIֳcP7č '&H(̹ "K+3ZؚpwQ,$Z3ykL$9KaB c(ip4b{f2@K;" h`QpAoDOKqwlV`4F*)UB2lR'ā'+b=c:m1K*Ҙ⪗Smc=`:$C'q%"H& -ɽ+M0 '_䍞(ͥ^_ W)w*ʙsw8pQ_KTī[ F)/x\qjǣ#SJTo :uŎĘ1 3M p( rV it&>6[K(9w&01[0b~QZY3+ 12\ѿ)d:a@a Ord s`VG:ggBC;p +e7q'a]7- L9Tfz &endstream -endobj -3832 0 obj -<< /Filter /FlateDecode /Length 2304 >> -stream -Ox޽ȔϢ :,)6eGH?Aw|}:#q@hd!v&GEIB>F@^mɆ4kfƬ~Đϟ FEiL>(}yMg)܊}u\R*fj c )n[=/y1%%kE/ \.O]/:J=<3vbbTд-yڿgPAΗYC:<>Fƈ.bsjM -ZQa%>m<.ȭ@+2E} J'l@.iFUF9lORFӫZ#wREX? A,j%pu|*WM놋yɬ['F%[-񹫊8]}UhEF1.'dc4NiҎ_ռv@ 2[SRLT{Wc}ZD% -̛JşX1A؅q p:.d^$/a>A -uаp8 t%sm'jK~]abP2?S .ګ ʊ+IXϟ@n8#n!e7N{J]h NBIdw@=.a9G TX|n.3zOw6ztRw{֎]/1VCa m& -2RD`Z`U>5*ٕ4o H\88j5~Q׽R -GuAsߗzd Sm9uR+yC_g%j&^:o{.=u}4LwS$Xe"8:Cnl gd0 Wo=D --sFb`ػOvbzH@b l}9JQCC'b{D!I%hv;6=u,;, :=ݸ&1 Utfɱ0VX,,}?Rg^`$䇫&ܐ՞ ]krO~2.wՐ ,ɮ9JeEx&s^㓣E<ڊ^IFv"ɗ<~z/Ju#q {O׋@<& ,a Uqn=LL ҟrP"6Kn)e)6 _>RfJ# - -8$_?8ؽvy<Ϧgf_/Ѿ,t@;{e'usE,ڟӿKm{n\GVq,%4A;lq`&3ihF12 -jbXWvx0ormx}L֍Hщ ",ʳ³=:ǁ6;xN A˸xdp~VZ uUyyÞG j. kyzYdtԝσ㺾U0LoWu>' [;a> a(1 [;,HWd4L! ;2e.B<>ZA5nؽPy(֍|.ȗֆ.]+ǰx{7N!hVC \[}E̪; YZ yhʼnx*gkm߉DمHtxv $sFa}TMWG.O{lmbׅͬ.:oWIe(Rzb[P慶ҰlGfO>D@Y:I3j|?!ynyt[v~D U c8zos5Ruv)x!n -c1̩[j񈎣 v̜-) -c %̣,4 -Omc8Jhޖޟ~$'"֧1䝐AxfiYbà-,-!m:h$Q\LX:)CiO $Tj6?wY{VX{n>Q{~L.̚,a߀x8W~^˜@xhc1 tcFZ -=B.endstream -endobj -3833 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -9 "X_F!6pO"4ǗD"W='C@QÑ'-8Ihlз@jƃ4Xwc-i,|_̌{CV"ע -2-Y^)h zZj#~m3y!FjZOyL~|Wg1dl5y['j&851Y{ P^^NGJY+\!5t ev׎ӻgawlI"X}$"BWSx8@k1i%Myq:pŸBy @:l` `Z%ՄlsJDK RZEĖINxpM9y"Xx^X<&mܿZًHօ.s:hم=3^omd#RK؂%YqF 6R=xdxCYg_!ʒ,h˴!x^@'FN{% =HU|N/[ T?{Yڹ LFRb.*jsrNJW+ې6NsU9 DƁxRѠҹƠR -2)rU੏9T7ۮO뛭ڒVlX~)ZJ{r3PX'a;?Խ_ΔH?BYԉi5IńN,RtߗeJHIYِT^#fDXc@e"uC-סd>k)]cqx>K HLj#t{ BGK/\Ī@Ւ]WZ؜xWKŎLN1g''ih4gB+a&2VݟvPi3&" -yTu"yD;ܨҖ~W@O18_3?ȗ/u|Gh¾DIKwo-[ؔ[$p\|-ɯE\s*9jzd,endstream -endobj -3834 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream - -g8bg V+% e=iq;q ['%gaPFoGGIF>J>vKiaxfw~RͼG13dhidB9Bea4B`ȑ{]mm򱧇2iࡈ#%v|$Lx)[Jn^|KbI2x IӇNrn^6}6Q2kc+M\H6A"UyXF[uhyݛ6gP7,?hqzh:gF$~0'/<( M%ށtK^ -)_@ꋔ+gSp]8TyrW>@` - If<0SDKwq_)O8CO5b4T^ۘ]r_Byە!X򂶻%{:d=]LJ;4uWxJ(hƇp! r -<)WC!EƖ戆B%>],Ms@+53"~)>ĎNcyPW`J\ZV̫kSWh<i?>qop\fӕoEqL+MK#T8HRO_ *xŏnϞ[S!%PcB W0=>QrV¼"mBeN7'j ;/37_R@ab{ażCdpj,/"]ήF7f<h|߲%;Ȁkr$}9M-X]0o*LBjV1zfB퓯{9P 2sk2js+K\Q2]wPՇjSJapaWR65ۖxXZuxǶ"=קh]˛8E?n0>_D _fŽB16TTRv@_Qj8\Lwz~eyp"Xs1QKtАB*++r$Bl +y(cb}C1LpkO?NI_&*4#uJ ~}B(JجOdwlr ۻ hCZ፤0NI6}!=c`ҫa\y$!%0#7 -k] baHuHJTᲇwhX[(6%DŒ=Z($k~!Kh꡺F@ڦ`]]bt38eśai['&9{#+Y,V}|U9+y%⅔WoGfqca͋ TWef&<]@5V)P✮:Y Ӕ K%9 -'q#?ܾܿ'Ⓥ(kV0J(}IGo qıy^"*us,XZ[̍T~;;QR`@?a$ GLS}endstream -endobj -3835 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -LB|GX&Bl^g RwXiXr:9mJSK2G9 m:@pI;H> 7MICBy>Z-\8)4AziTyՇ ׎1 –=ċ J79<@!gstDd1<DPP!PT,ӣbXw2-k.xKXV>n r 9 -6Ttbfs(k 3*=z6:Kn/2xt\]-kԑ m> 6=I8lE`v劘.s_^\9<~Qd46m=knΝHt#?!ٹxB'2n.vҠl9ur;S1-@XR0Ekzu= 1쾺)qJQ{) endstream -endobj -3836 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -㱖i:C-wYHdO%dc~*xOL˻Cac1azEz<^rdT跓pq:o|<! h cܮ 7abηtO&mU6&\z+}"p1\5G@+AQX-W[Ɠ*3jSgsл>pZ ~I~M`J$6RHJOёOٴ^PQ?-Sr㟳'@T8fhi!~;h&2N>R 1,8cQCK?yZ~o(^6Fݦu,˸[BmPHbtX(ɴG>5iMXJWzTBod~lAP],tsuϨD+3 Ы)>{u"sɪ$<~^SIU()K$5PfwT7y^#/:*qJxW+3Dyl k a RF@F/~2sYl |׽8H1xŗ鯺%i;g*PV`?phZ@%-$aȤò03q(GB牤_& &v#z.^9%>> -d`==m=Vt~i1ru 422Bs;& -_SK`߀0H|LԋZ;-ͨUE.w5X{tfe8):o/o%g-Xb*~zV~? z /'"MST{a'`e|dXYel2Eܣ;,`QeyF#uU-c#7nVܠvy PuƭގH6םRZԵn>d^eB1` -])}6J@ ԩ&KbErN-0h"bB+DFgZΟ 6'O~}6G" 2κ– 5I2"`&E?oh1PhKxINyO!:KC4J(ԣ.ɡG?U8iKO- ͩ7R1HY1dCa:IFtܑ*P4q|% g=s7R9n'3{3*SHT -4~Wȥ<>RvK`3GB=SqA֢@z6i# -Wj -Nx2q#ݛVf_v/Ryݠ,=Z⥨v%X1@a.nFW'`b-,H]褔y4Љŧw웞b"v#>)S8Oxo>hU(,ܧ=+b|l̓6'{D{qmRAg1g)l=Z -ݘZND6usq7q endstream -endobj -3837 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -ϘΫHY:m|aNe8b&O8G-'`ꗥ>~J cI̱ex:g]ŁRe`4ᕈG~}b'4aC'A7OOvlD-'I0٤Bngn،0dDԾHvyv0xY5h4^ SV3'a .HӉYhk-NX[4x{&grЊuRQYۉ-ԀG 8̩E#]e5B3_㡏K7\ݛrQt&mi/o]s{ y4 =熟8Ue #Lj$pߍ#gf -YlF׬yRY btɿK`ߞd_]w(EV1,)(!0y1Ɖ*q!Lz7 -hJu?p:4‚DxzMݿbx65CcGg0[ ڕET8ǜMnHV>(Gu~D7a-B9uor{Q UMUJ{uܥ+bȅ7Px2`$LRѪ;'#Ejj%H汭H|h!X'cfE ncSְ=;]]KETן\|&Tu.cՊ5[D`3|i @#=c<+Û AI5`HvƼPhvn O9xvt㣪cn0~@C{4?WGZZh.~V{$޲P9-)~P77O5 &үe?Xyȱ73T!$)-t<Ԝsޕo 2c-&U/@ ]Ym]bT4ZqOmNݨ(ybP>T^ SA:p18Fg [Ju=F&Pf0Jxk4?5hjEy5T&ƒ/Ȇ_Ԁ9dPC`Ty<`: -vJ4RyiBd'"Xh{V^m)Ur#cbw# -GO aI9}<z\@E@ﭷ>cr(L'[&MHs ;+j_&Sp6$gf06[Up)Г'_4 F⸘e~L 9gmʝ]{'Dbf\9w1ʘ& *9((Y?P<۫L@'+2(|M⚉˖ڧ) Z_0]׋?0" I-5$8j -v.BO+oJ ~GAzސ1'+.q}zM*e oACg*t$x,ԬHkdr.h` omoM\M@~6 b 0=_N*Zimj$S/rN+9biY򤆹 ?P]lٸ Wu8p }GWH;956*uњǎ}Fds!?CN1Q.OܓGU\o8QN/w@ Ԅ,+O ȂNq4sendstream -endobj -3838 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -lV,ϢD+~q< -.;4PًR0G@9 nVOw~y39k_湓:#~IviC[zf JtT -o3 ^-r_-jL7\[ iVU洧2x_I#6m h*[I뚦ILG6$9YfIkOn|># -Q"Tc^r*%=~,r'-~C0./(r-Bc -!~E:N_׭pYwBk3.Agœ/A2ϟw+Ќ3f!Hg{w 9u(1p8-3L7/N^oj/ܪ N3Ň鸲jnI)S$eSRݟȲ3|`j48nD!:鹇/ʟN^$t}.p7YR X4,4W`~|[߅u=AZdQ8W]EJvyOh9?;C!rK)|h?"8@aE/wVQ PP+]ؐ;:빬#U$|PcUF;nڨN^Id"qQ 'G9qc񸫦le6Rela\JĔkjEMs -LH=IK(VDh*W')p 6QrHx% n^<ޗ:J5AmA8LtKϮD-M9$vn~X3rhU@C{J@iP6®橚/g㮛8Y& afwiѡJҞHpf僪V*$>=> -stream -Ό"7" -?SMsq M4#CU!X 2H.2\O@FhndVAuĜ9d.mV NZ]CB{%J_2/hZ Yl L Z'z兽zr2O'@uPLY$V֛#|?h~,^i$bxIPHE%GL`P1FK"$ՊGOXVe}i`\,2q>ӭW} %Z?f UXEg''~Y7-T̢-/(H -.E{/I"J\+لh+Q8vѠ^ЎDu~wc}w! W}Tcw$dQw9O\6&Dgy&^хBQ>y=Ks-?\~Oh2g+܏l~N$ZYIÜ;xW؟Tl]H&Kq<8аUGdDXb NR0 ,1:Ȯaw=~dQ.ڿ u%žFjÕ;F*I owлxyBtj]l6*2kq3d(AW`hI,n1< AYIa|+Vo22,K4> 7<5 -A:~ }3T6 ufp^^0iH!>MV |4ұfs+}8.߽/-iE"yY%MeQ6 r+HKa_I.@x6iPWڹċTArT>}X_m^`9S=iO0 (^x:#A7WyL&N;)M&N7P¼p'.j&<ϧhEgGd!q_FK\B6 -Iq >)І$S`CZt2j؂)<ӆk-cD/r;BA+,M^P9.3XjӨ<[m iRM0_N)t:u [JXzBe3 "gU[Vb -j&>^.-;tJ^Nt5VPAa[P~`wMoxѠ8@qoIGau ȮP,7R餇 v\Y)e5!zDUQpelO^9+$"yD9 K'o~Hw[Ӳ 4Qlm -CWPʀp~Rb&"eR~Cj„"$G<+ l ^r$5GckjCGʕ1WxYȸJX|5IoS+Q}ZMKح}V!0O;ifR0y -Ev:VT ~LaV֪,kYO<~9MP֜ӶMI PkͪPMp"ȉ)bª -oWa^2qT~$[M=gWfm QdžCJ,m1\Љl/xU&G+kZK$ͥ/HZݤh=e\ yW1y ؃+IY7/ʠYV] 3('q[ eYuR x[Z( ϰMӍs7NP2ovXōATNEcr}IRxJ)t20 AKX]LdodX=+V' !4]Aendstream -endobj -3840 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -m(rmqs;ԧ}q:MO.$dRb7gL J""!WP<(*;t8`n>1?vk7cypx7ZӽB5&P؈#wOd6nOlRЧsGF3j{EХ\@m:fѝ.]ẃ77'!&Y2u4'qUp/V V~饻a ?>㹦)<Ìdt&wS!F5'<B._U2?"O}+О- GCMSKsޜ̒ W -YyhxiHY a/r2Q5E]Xwxt cvr ptxY} f=<څ%.,2JD'Es^X Y`0R )]MXlݧ7zÚ]<Lx6^hebcLE. Aa0'Hzss# W/\W; R݈ ,RT'"ƼL_V­t[ɘD\ER<ʡ!Od' u7\J4X)vfAw*y䒶oʸ~]D+xy%sބ5jA!+)O)­ݘtMRKB6=' xsLC1';Io`:l"+T<ї -nyz;t°GĚpF#g+3AeLV7a#lKLqBtVi{rrXZ$HdfVsqTl&B +[;?n!TJcik55tQ֜z2nuPm"y{zD.^@/#q"ofE>NH4; E X͋q6h>rGjU!O̖^N{jnLc `msL"DR00w3::~_vLe1)xΧUtIGds%M M__Jq{dm=Jĸ<Ydiʸ>?l MJn{mbY^)SBNhBFCՃ@(20Hz#O/2ϲiԺu,|3Myf= _9_EtIۋl:dtD(X{JI݁n,HrDBz ȯ $C;#p\= ع7_2ݭ (" WI8v> -stream -t\i`4w 20hS?aͧ# cqITkrp C Мz܇ZZ)0A~~@ 7o(^LV}%5'п>Zhm+Qş6^u\:uT"2ZϤH&W⯴ TM;^ :M[}! CWp5\Q|`Z+Q%$w[:s|g(X匉bOj,dt^6T4+mYWݦv 蘄ԕ#iYXz9.{y;3QZ<:o,7!Τkr3Vɓj unO#轚9Β60ɍsw*a/WPfVϘ[L\ƪ2V>5'#%X/'Oh%ݨz)l1(p\bu9PڕxkO8 !:0$scSz:a nqC՞*v?GUUuH` :1I=(* -3Ŕ)k[vzVokÉۺ3g*ɟޗ{7_f+T-~ع˿$ pJ/:zPMKh aQxzy;`@̭uku70vj\?K˴ijG!^=v>}Oe^EsDžBz|h=hM=meqIJ9?A } S?j]vvdwKZr}r*RR>1.܎DRtF=2Lu(ܣK|olD˜#b虂*L`IX_y<φL[țEe qL?ց4kvYf먋5ZrPo侓3w1/ -99/H~'(Hf\(da3m1YePXK OWFq{MMoKpUŮLͭ艹~\$/DY[J~VkNhGIXfk=Z~?#w}G#7})Ih]߆Q5wq3Kh j^ZDhS'Fj+ |lwE.UOh$wἽ$z-^sؘ> AJU`ӺdMp]FXe^#E*٦*0?VXi{OXd>Q$Rc526N;]z_°_y̾Q|o70=~ -զWQKkН(k}TXavi^uv0E=sRmR\ \gLK`[|`2DopUuth뚀L@^'[9X'Y*kM- -RփWHGk<S(w$6^mLn5"7Ԧ"gV_4s0hg{#2I`_2SA*+ *T\ 0WqRoI}2L4>d-D(ZF[&!$y[ͅzׇ^R6!LHح&%QaYlËÍS?BxRDvGYbf rQMjW@6^^tP@oendstream -endobj -3842 0 obj -<< /Filter /FlateDecode /Length 1248 >> -stream -AF*= 2:8hbC"\po~|d=YdL3dcU. -L!Y@d@ 9 rԨ\ϣo#!Q9k#J{ < -5κzccuHoEtɪzMw'v"h~^OU~_s~%pD+i}GOO -Qy7+ +l_U~ehIl) YU$2qo7S_;`eO]ùo:9[v*V $3cA=H׶"87O1VxtH 7VUʑJd"4y.ʮdE8y!;m[zM:;iP- -R;|XkŊZ$u%㭋F!N~יP AFT |d4-h`}.oփKscoʀ˱ֻ>яS{֐Cd G)BLQZ:g͊YX}^U* ϲ,-*wZlT̀5o&*>Rk+X]zM&I\NuX='mV[&A{1<{䏛%i.P*)Oz[J0]Rdp(BnlluvGzU:tMVŖ\5H -暟,0xTE&ܡسp:gN|HȖvWt~SZ)3&nvڴ CbU uV]x&̎t[U]4 yV @U݉r *+> -stream -Wdž~gƝ^S&ydٛݪ;=#z:V3^j'(v攍6AVnT)Nъ<@R{xrV|PQƇiPWx`bD/zQW.Oz'=tN:B4LXLRрPX0Zb|$}8uUiի iNDa2W*]E@Uq)\m+r3T?n&DX:DIX-`X -l͂zVаH_Z3~W11ښI;mW)9Qf[0u2gA^K t-<6qT7V.< '~}59+qQlTsIYõ%)lM FF42TԱ22_YH.mna)Mv0ޘOmӧ#h*;E!l`)n.~]2!l̸ePg"%}&q -R{a?nG6YjH/ٶ0{} 3]ǘEqW~#Aǜ"LkV 6MI>af&(7>bCNe%lpC2?(4< #nؒ5>{k$,T3ذI쳏>?UF\aGp;_kD"@垻bzpy^7Ƀ-P@Y̚eBAv14VHVDhi]!78J HPsECmT(aӑ~#|vOjkxRGJakzt}Caz(I{;&V· ,l`wn| 3@tzÓyVaߴ&e1^9+l^4?,1p]K,N2iSYFM7ۻClՇXySq\C { dRmw6,jp`B>;#g{泥 ev(s|=oeo1չ]+(֯[:``dZڏ$4͖kuHG<[Z^8r,^8iXk& -RW \#*@c8p2#b>F+[/#?<ÆS:MJĽp!E"YRA U>Jb#t -R@K6ڦ&sZ YÒR@hw\ -a7 -굓wez"6v+~Y[Mɬ3pRT|$ƴ2L{YJАpNG;]A{.݆BdFr@iaNeD SB FGTF=<;z#&c)XȆc+; nTOBiVn=l98Os>Hس> up$ZrOM83_7 J&@Sl\4o$'ڪAو ͔Sy/΃4I0HuT^*p/'#?# 9[tiFZκhmfM!*}nZQJ^ك'#՚t<+=;Y)0uoVpb8Li?M^F`9ۥp -ti9c  :ͯrdC5Zendstream -endobj -3844 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -!z`%ۜbTǶk'iGzT>^8zV닛Ζ=Tj6sb{lЈU31 ˣ pFs[,ė#b4Sy~C*|b^;4iGg9?.^m]m1,l<rrt+a}&l{8C9+1ZsÝo@N[,̖ʧCЁKCe{z[{/;Y `#J:L)F6ZV|^"C$KK4IeB(y6lk^?xa!gɛ!&h=}@xI7K{9wb|L1|) )Pu Q2^- ƣ/J1-;)+j?r^?ts*5i`Eㇻa|sui9rRN Cn, OK˕Қ.~fQ)>(Mi=È$)Wt D"]#kuuGk`syikT1bX5(1&qMKٖL R(_;RQI|+xt:..PʒvDwTڝP}Κal67s~mLm<=`L"Dm}TڧD)LUHaz$9 ЈܽPwv9q}km9큞+v!q\D,W_sH?o>ȫ8{11OLja]):P` N>Y?Vj2$$aI oR֐z>tN{hF?ɂ$!liA&M?%{ze{xZ'P R>5őrCyhHk,LMdGwo!x&)\MMcFRgxMg9 s1dY ->YzNM"jwsiJlv]>-_vƾ -YsQqp<:bi ֍dc\gޢ7fEهǂ#qX߂H;>!Zo<ݲ}9\[%gMJh''Zʸ!5)柉`M9E@> -stream -rˇ^>8C<0N:][sO^Ew73l9(S2)'º -aB!W!^$lG)JkW(xՀWҸk~ߵvkZS-b>($n͞#gPѪ-㕧؁bs꠽h>[#Nt6]t/1'P_ڟ3>W<*b eE$ -]..:ڢcLC-&̗3>EŢO/Aw2q^40+ kuUMޱYgL|Mz۪+DQ]K0z7.ꪚQK}c5>y-X8afӟ0fӔjy.k^/ߟd^t~9n2mZq8\]aDf$Xɩ0hh&HaõтA(TT`){"m{r+Kc$C.P/5{D~:O`f2h!SI\-1S6_ F+k@ġ%VTnzFJGSΉ3~V kAtJbqrcB -Ήl~*L<˫>iSI qsDFvA-}72v[R[Bbk0 uȌG;k灀GR ^bOkWv.o -sVhEeۀE`,[R1i#mEY Qs9TT^YP/uvVy`T#Tcb!T]UN͗7T'V*zk^(.>C]ari'>E(^g\pJB{xzDzG#j3prձ4(kړqC?XVBwɥJ[fC8},W=WLL4Cd}ߗ8ܻx~Q{S5rkJٱ;E"BXf2RS"507$UJr:LQ„ Ng!U8 %Vdq3~4*Cthf<6t@\/{Ys@V-_Ȍe#[M1ciӴysozvpAWʢ\TyuL$OBzbǟK7^E Օ2=LeYm0bPa1\Eoytȋ.G+ cLǯ r nQ /$3M/2 S1QF0^~[)IX<)~u%wQXZأ.;8Z X|! tk.Gkql{0<CRI89̇\[nv7{ȟ䂀7"K-',eD,-,z]JC!Az)KBߤl|>*#ڪ.>tqjfKkn׽Sendstream -endobj -3846 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -qǩ=MOY6h#;z 7N>-NSF\!Ͳ{v7^G}t:u[6SU*<2Hb2՘Sh| M@zAlcq(G-BE0DͻMv8eMOaC7dVP0ޞ  -]Ĺ[/'K*5 6Bfz VcQ7]nUqV\{^XɯcsHyeH|M/t2:|,%֑y@I# EMM -D#ƝLx4zѦ(EX:.7?n -u"e~\~Ϣ>2wwS?St8gv~x4ꮵSح6$nؘ8 KQX;+ҹON sB[''T8z_cY Òtò -ma sW}aYHW5w;VrI0{3dwʩa'[W9@˫ܿfIDJ]~X!Eu\?/Ǭ >Xc -D&&c hdd\l^T2TU2`B#JO1(kZQK{RG=yoqrhh֣9Co( E쇉\OR<-&EZz/ -kUg -!(:8H p1oʝ( ](O֡)P} ӊs3۠sNˢJ+m@QQwCJRpI-/lt__&NaM@\{>w-=n-;!PtpxN.(NIm/^"ՅA{-D:fɒ&['`D92 - &s" -GԶ`2Ȏ@QM5_mphV޸ڀ3] -1-=P4; #*FzרztA -h>q& -UT+,aḣBUsk^uɲMt#ODB8 ȿ`bzNr֎+=LR y{Udi% 5zg4F7 6(Ov >\Tʹqpi?"|BqL U'0pM>Þ*`D?[InġwE@UѲZr`gK5n9EX U_قe2SA>";X~(¹'m+elļl$x3U@ BglVl> -stream -WnQ/῅] T!C 2r~;(KVɛ!6%ke$S"{]6Ɲ?X8\hg0y]AFu0U1W9j֑Gd%?$/ݮ;AWO3_2wp^Es 4&"XB%4ZяpUW€; Hb i/VŐ2d—x9{]],2߹ ) $A?|`n:p404f3TFR>U@L֟a)Ovgu>#ivO)R>4h e2fiycY%CQeЉYM3yEjw`h_?8R>O8m,yA*.g1H q:>r/u%׻gK=q0uwHZ>d2$Y &|JŌ9bo3i g`$~oe&$12(P`k(+]{rjwX2S~T'!KűD6{Xiwgf_"9u zS0@Junl-^&VyW HM)W"ԎMː̍*#IYV$-0!#62KiC !pὑO9"GBr̈%HPΉW4߲JiDol/ECW bLVKv'I¨RPsڄ1B㛞}0[ҐO   E Q37"p' -u?@$@2r(eǰjnї~ :9c'Ff~Q).pRP%)\ D Wr\Dz_RV$}u(^*┼ϲCr6(?=|ʔ#PBŦ;czJ}f)\+Ox n(W1 }zM(VS'b`z$rս~1PJQG!&X7)H HZdW"ucn8JTC"wԈ-'QTٲyT3bt-cg;xX7tja{N S:m=\ʶ2t -7A1 TkD '⺌EWK>g{j4R浏U/s#'?˴E79tl%y>k^j쭔UOCGĘ8endstream -endobj -3848 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -mhZgcZ7`vqcte-V *w 7Qxx |J@V -1}q Zz,hڰdkN<r$ -6*k"+o/n Rvj@id1B@`4Scv[pm&6ݿ˥.hIe_T+ (7 M_ g`g$B*Gp$_ʘbmP!Ί8B,:;Kdi*,II'M§R^r蟣>iH,QIXPnK.fG@RS?R -=/[2ca2&^=d"ӵFV -~ief``乖"w{:?7iy. \! %z]kh8CO4NĤ0Z5hz)GVO]);4XV S1wA!oV)TEs$ }`eY’\ eeX7N1etΨѳQ?TOP Gj_ z:YZ:)G Oc_ɬ(c,25Orw7?6o}kjFp߄VIHTc@]$Bza/Rl0l)ZC;ǫUIgw3d"92);[Zse6r{R@B/.s“dc|B& `QirMpm2{M4֡Fb"\wOJ}̉&ܶiÓr8e0t*ZmZP~xf:Z۔OAVʹ/w(L}q*YOzsH >'H)g#!HV TAP -A&NvFvE 9O7 LS2<5̔iS"@Gcg&<覥WzRT|չ i\.;" EXyV<H]Xr?ѩjϒStgu1zRcw4Ġ&mĎFB15=j{2.lVpu?9FB8kl9!E=h~ -xa Z脏y˲?1^}_4 -Kx[ .Sb#7IV܅L+M=Z,SDH5c홓c-Aԕ --5Un+ }k  CZ\p6{μYF#.4>7'%9 ja]bJ#0ԛ;vBQ4U;G4UmR!83)hZcMsendstream -endobj -3849 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -?4ߛyI^ 6:G!(Xj S{sd=~:2-KO:WcW$,:gڅp=!LUL>E~1O8Zbzw*ܻİ -S'' -mU{m< "*WG'md7 -$M)F-a J29~Ŭ$/.PTRw5.wI[ܯ#EJ -o 8T|혏(a}fDyyyY=liRÉQ9)Pu/qgSE8 Q|qB :Fke)].r鱇Xp8,rn/bLF<x3jFj 7ڙ!\I݌]6(+AU|My餃<^?-4BoaX^{fa2u}hUus<,97dD%qja;^s)|^\D3?|ƿS/˗6CR7HsRrBG8zi ՑҢ3T5wKZMb7fD ṕZY@%}4k(衈 8L9M1tAoPK5Q"mHX1z[*]to$(RԕB+a\aSS.&zKEj{*>v=)K+&9XhDBi8>3wA _;)m$]\ǰ?: QڋB]d LK8S.}\ }=ٛ0q=q~H˓$/8&e;K9oZU(݆"a)sk$:*dRM! -j) meRd'm'pT// l%rM'IDӹ$"Ƕ]v8 )_IL+\@iPZKl8Z(Rl /rBezs1#$>,ƾWT#4I7) -$v¥)$h2K?.HkzXܫؙ~jn؄L^FףF dYD酎 {F,-2اa_p>p8J1Xj"|:;dX7GA ӯ'.Bpj֍-b\C܀arV 3obT[} -5`# VcbXB1)gĠ2hIMj46@5J8|q]N~J ¥sTk%P-K-: Gɠؾ+K==~e - -c2 }*4dsKa!o`S6ez`'#?n(a%֊J:Ʀid&GfoF.«\rendstream -endobj -3850 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream --[|z[:+I8D<1m\r{ֻv4s/ӈ n y}__BWpYKjM0'kO@S 7rV\Q#NYUYT=+05D%?alAQ`C ʓ -*T$Ҩwv֫[>x Q]Wix&X `NX+c| q&xuXFS\WyZQ^7[4 2jG-ݢ ku)4r䉢T(C2.ԙzQQq8cM?)!#Q%54t 4W(|}JF|H6XkL?"AV19B ԻJ-?"$xD77G_, UZViaaɘPN+~ es磞Hv޸kQO= 1LcEݔfV]).Ф0Lǡp8\HJE##v H h _r;aXȫjh$aJrbΓ q{KڱA]tkKsGSp[3J8}ާuB_y%ϝߥNTM\(]\>!&ywgbX^6m|!|fW;q_ `Y=%KaϏ֓;f.mFmHVBgUܑ{G5^uW!gӀџWg zzxf8V1]ߓZV[+ @vz^6g`rԻ2Y*vT;Ѳ3c<@A*rk=`/N$q;:C^,D3,fS@-?=xh\0m-;θ3[AO:Ept xBk܄1OXǏlQaD|+掛L˼jWk::3鉾b *aA`jgt_8rZQpm/vHȐ{Y4-XIp(+F鶎SwWZCǂaݑH%lG(]츗(!>دS-z+\JpmAƉh!7zwzyEK2X U(>w$P#O\MCkFH>[<8eL"5p&m]N. Ҥ&:SiC9jF,0'af.J;CaP]R7EA&]>OGK 2W=/Ug) x>r7!2;4,-]=HI#}KN9[a3-ďF,pWUlZIG^ɍd -x)}F#CPNiaNΜ;xN!msHkkКiuw[ǿO֘X̛}yM7U\ )y4C<:ؔHҐ@?< ޟ o4g' `#lؖW1~.}PxuEV e> -stream - w7-ҿ-b4JlZszECQ.Z D@y)i= XJ -]Xht>s"͊BYS2Myy -f:[eٗDUl[ג` +:i fy!ћΎɻ=ə.y>$oŖ}/ Mj.N\͈IReMjlys;@( |yhu&L[[6NLɳQcz+70PŗFj!tדc ѫ^tGB 2Ðtia-:L3pAW`pwnoW٧k8]cƥ@хN@L싱:0զkUR֭09lf#u‚;lܙaGX"H4$4 JNh̊&rb]) -2@ӎڝgѷk|Y)`ɭ-cZ~t,b< -fN{Ѳ:xu{+|ɲg-i'ރ $+ՊD=̥G͡je:f4i>30]BpDcB;(zL0sD,^<"Pߡ`-aj$V*k}@=ԕR1TYrfze;g;p#dnJ@jh H 똡,s{F/[ z)QfF.k,@`@ʣΊ=f 2MTa3ׯ9lW/v })dGx܁۪g58\T3aoY0NZ>S']|<3Y>Ԙx,) -FBөJՓ9;ia.aCl>' Fwifb9IIUxsKeL톳C[~e$Z9<,1(#O@Ixt jxOiߛX>r-|,siRl6>)E3@W@]l!GW;d~ -vKBC(Kцp}_)B 2kM.H4v"UoWhW{6%\GPhF h7lFkRJ~&RmgHAfJoj>9 c= o"y"clсB'x,endstream -endobj -3852 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -X͝3qw8X7~sKt<% )_E6Zl"66e3k`63dF<3cH' ]pm˼Ƴ-0~/*zRaߚ|ɭsJbf^[p!32I]Ϫ(1${>#º V{Nmtqo`$wzdSBX*Yߖ<5N_{f+ -%bq_烩(`~dFCrPeJ>)"[ ,F߽ T Is hO~L۷X0XShp_pe@䰮 . -y)4];+|m j.Leҍ#LRH}-\? L;H/c~! 3i65~ qMDEÐI w!\%1h~O< _axx~da9CZmN**AS ->~?+*UZ>3 -~?v(=֖KV$KEVt8m -ʸ(<%b\[^ g)*1'd)p%gxMM )S>k:5/K-R O{_ACvǡJuT_cR%msHI $>9>| ݁7B}YG[BK6ecu|VOQC2X;5br?Tٻ+s{4 4-82yס.(-gP 9!OO6'{@`TTg&(/nHMЃUE:FlP#SYW|?4.SB?ݧrߞP} RG,]먹GhK -CCE#'Ƴ5&P0pY&VbT /c CCm׺*MTD0Ni>U¸ՠ]R~0&v67K Ƴ!%"qƎ"{uukʾ|rDf"o(Q,=VfTm0˜BP\WE'T:;4^<f>0(e n~h>왇>ٽc<3.~`A`=.TZ5MCnʒ7>-{rM&'#twB*bf?| - ((պ_5 P~Q7`uFN;ڧ\%n ǹN@" ;xx_wȜp&=xa0aR&g4aס6 ֝Nꪊvn\PnҾ> ԦoΝn0*aXC3aE@-zkpI6]a~4! Ô涐r -`nffn:J4n}[s̝բq ` '~&{P|G>u+NkړR1EL‡sn=X8R&"̡9!&ud&"P3|`eL)U^lSb2 Z[5 ƻx%8 0KHȚ-DjJh5v`Z_ 2\:ʼx6="Szm,#>DsH^Je:kM6D -0ǝv߲V_NH%M\װGW.>_>uGK1|2ʼne jWW,Sl<•>9*豤u=[ -!z}*9H>MC—` e DqUump9qq% A>2BŨ\?b3ӟendstream -endobj -3853 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -ڿН2䶊Fq`tfe w8R |fI2vpjZ,q-mc%+Lʼ cZ 1lsb^FbM7RT鰫qT _FCYm)15O,D~ċ_W]ѕUeRDaKEmM_*6"䣹SׄiV'1_V'p=mt# =2&qw; 聕]0P\bMs\?/\E U/Moe5*Tzf֑|\hYIe.]8(&b}]ozCߞUc?iDA]9ԭ!Ĕ!L{'=Bas)Z7NX! VW%5YN ǥOKL5L(!zD+}z.OE(zyL>H2]Y,{&^3]*g+ërr$rc"+"f;HRXY NnWZSb~gHGj]xl -BĐ{$ln)N;3K׼8wK\-h­3~YB[$56<-p"!v;g=ɋ=޺)b(jG -15'\ng~pb&(> -stream -[Y./ϻdf--'!O8CtTV&>K8/3FYf4 >3@~.tCu_@B2Q!N|<1 l9^ғ:8E_Qojz?Jn½2e6 R~kA4oi4bԢ:sNjL~zA( -t`xz&CYϕfgK#U7+ `!x$iX–T 7Ckv qNj&ǼbFH:@$CO_'E9֤*!RBECC$i1QoI4 -̥'x<\NB>EhOpp-T\4~\=_/ ׺#)M3ef]-S<:V+3fziYRnc&e~%ڄy s]L{9ڃ&LF[eT-rf9I~״B0z3ynB^7m|2QCLAIX\Asfh-,o wo(Z~E*ľFB\5&%1֔'SNd=gny*c±&rʗeΡf]=Ś\W+2l",YMs'?CV\&k,?!?|Phѭ *awJUYb;лYUvߓeI&Jz7Ch7b9!K1BO7Anۻ| /ZqBl} FݫTAiz[OypVYwQ|yrD+.LvUYnGcS+\}!8j&\޿A ])y!z4ViZ .$crDdJO'}%s\V8qE>4A֥0kbnql!PbΑ6JD&=En|1aK@s%yʴF9IJU nJ,r AxtEgp**K l,xu-.#J}3,]֋iCj*{ (w:U|BI/,MlO k_eO>"-ojr."MU"Bt06.YM'} -I -YqsR~BMbq'kmeC cӭ^GNPFނeP rQe; T)azS7Ý%c"b|sv%Ir2k8NS^i$ #$U#J@ZMhg7a=F܋ !g|&'<YҩAf(ajYD͑$ת)HuW?$udB?ح8q{Ůp(ϏPX@K`% UIHjLb%ϟC,^ )aP_/P?w?֣*@F %^ͣ!bD?eKVYSʍ;*rH Gʮ" -6/gsmԀx3%efbXU/OSuҳq΂_~.}p4pRj,hnŴß\p.[=endstream -endobj -3855 0 obj -<< /Filter /FlateDecode /Length 880 >> -stream -%Kxp< x˅:辏cc̃|hQmN$&$W >3ڃW/qػdtXl_ k;sGfe{ ,nǟ@Y0gpofGt2 tiԕ-W"0@yHujSjAT6dHO[RKrАSh @%OF` K+Z]J')S%i8&!A]C nn.Ѵx|_}M`*UR*}yT]wOnÍ,dK ETę\[Wo2.-X -}RbAF*KR=Dj^xTVap9joda\Hf׊RŁ,<$s-NJk{#ST> -stream -:[[ǔT3%< B nE~D8:xNȿ^Ei"(m_=efANugK_+HL4>fGۓI/Vmhy[irdW2'A7dSCS/7_㳆e27\{x"xe'Ka߃!@j Y[^^kVi X=Ef\ a{fNQOjLiQ&1TMyMFl۴ - 5LܯrC/Ї;<* \Y="JcΊ{k6{ Jf1{ *Y^3 -KӞI8ᥐ_uAgixV"#C -u_8[;;eKfv&. )0W N%FT)6CFDڱh y̻<=ecd>A4Mtrt$T^/5o*7J1D#Y,h+J#Zy4zXpnoC@ֳ@:WeCX]۔H: ] qЩ }V =Yc5uWR%y -|{Vrˁ4{ONk{w|~9*n /ex*wv`%"ߑ7Z -$[Z~Y,eNAf׽uRV{3 x蘐cnvҕ {탥2x>Fx -GaVk|aBTx+-Pz'pApd,!BEmCBkGDopuܕ)D{PPƒ"7oi-UDB>a'#QKGF -B>T(kL\~w2 o8R[N5]q>jhu8e;?XS L{*PH*oo^-Y`Ts9G;%6N[/Uf]qr{i +]ĆV\@di8HBKõ"Ȃ%OݿKthCvQBLcuMZ7d(fl߃,A`@\endstream -endobj -3857 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -DIVQ OxGH}DA?xhX02sd548IIKzㆱؾЅtZAt :U[u$4 --ZKn!ÔlR,JcK ) L4=cή} L+liI PF/ÂN~h',9dqwEC.'fD%>QoٚU# -P²CdpάIU;Sţ);n=!8(W&q̔{4b@0bqgf ÔK|Ӷ6H 1!oNiguup:U脎S^Ft5 -}#=ZˠZf~k?f823[9ֽb`A- \&/{# xMxJi(uMn˰ij2Wޔ HB|JbTqAyaJq6RE08Q5$K - +WjmpUjP@xw9̾m?'qP@t{r]1QfPwMЇwW tN| 'TvK/o^gpafWt`!QfnI/չ_bzrgdݲSaA\qٓ5 j:n¶g |‘t4GF{QK"x9vtTLZ8 y@$#GrYJde3ͱ,pٞ&͗%ҏ GhXZjo 9<Ґ׮o4ַ#"؅ 6up@> -8ڔjT_'>Xć6%\4@ZRxU^Yz17;'A6AW\ӥG[tOs7m)7X -$R\_ )(<#n1.X]7]c/V&w/َNVeSU5=~g;Ft3,ش[U7D* endstream -endobj -3858 0 obj -<< /Filter /FlateDecode /Length 2160 >> -stream -ֻ'?nIދRJ!r -ݷ$!:h -Oup~_hΫ]b84|~(<.׿ĐC7exAP&̭B+ō -9~B5!Ъ:E!3DvSJӌr LnfOG/y<:F.fhݫ -_}YLw #錺K D]Q!/8jعDb+؎&iyFGYot :c;NRy cj i+ -Ô:%'z4HQjTi1Jv?(h+ d  -`e/*6GrCT l"#o,nP[oP945zKZEqu!" 9RGEIg24׻ȍ\>o [5Ca$d-f+E*GцEf -tl"f^` @%st6R W7բiB& `9݀pE#T 0"Rw/VlHR`f"UyhZ.LXq5_ #sgt/!9vxV,RdNyFhP/FўqɐrM=ѡ< .\_-Y2 -[l 54T'$WZcbBj˳⴨"/FnV %OJT_ѶFaG{P~4ןo3of> -+0 FXAF-`ZX=xFB.;:ow$E{4KksV(uVS^V"mdd [̜k1:Š?Bd8z̰  P:.?\qL|vyRv[sK|t=e텦"&lv31(!ܐ5 S:aIJH^A `WW~تj؇.Yf -*ba.'_DVΘ6@i`Y"96/Kc,܅#OG.<+~> -stream -{ζEK(9ix-ld_oAv;L˾X.yALr~.oީ6r)ZzO:{@y݈B!r&QiJgY7 dc\RFNlE4B´ǥ3A"Z~)Y`i>⩺?5䁧fk]ၼ_r%5+}~0BI$7MQص4 :PhqM\CQN9y3Ćc@9:nFcѵ:&7HZ. Y:[W2`@FպW -]%A(ktLclK". U/p9F&=Oq_R-Õ4i&=d(8E~ۧ4؃!ռZr ܒX@*HA8wŰ沚7%kYc_Pl،ysGvBP7l"O= `@8:XVW(%a0؊%`x!sLnɘ \9&RbXqO"J!S`FROtcХ<4ܮ@~XxC^ -rC=,b[ -jsXvd"@$s1xsnDPͣ 2_2&2J{(#B1nwx%E47ɑcVͬN]uH|! m4 D:Mo$Z?J)ۼ[$W,q\4DCzgΙl^='&/s O]7}A4s\:u"̛_OįrCMpI*p[0mIdQ+ŏ Mhc6> Ƚut`-['Ҹq[HQ9S">vcK2Pb$w^6L Z BJ ;-MuIrXp{0 qW`uE +gf4X^X6X?U8{,RJD\S}UX8olh~Zx$hg[d77hh<hM=55lƱcI"4ХxF9ZECcD*-ժAp@k; #ՖMW#a3H㝽^YC<J9Πܹ@<wO 1^  ͟RNBL}=, &M! -JG9%5{EؕCz=Cb*-n7>+0nNUCW&߉C ə42:`DJsGd - vyB$qy0n -CpWB⨰r܎! lWh'i,c5™8av>`21;ˆ\}槖!ú\C+YZ|dn@q^1x* TD`W8:lx9Gi -Q䏯XvQ);ʝMQ}Ҡ7 /OT7DBJq GN]zE`:oɯŨ27xj-4{mK6zNjQrmHPN;v<f@v(<^V bW%XwU<nEc1oMή(UV> -stream -'gdˇVe`Rhri-E*Ft)*raayCMt3r e1θ^ PU/#>%)Ŭ-|ldK6blă~0ZM81']:Ft힖FJ[]EFFG-RiҬ^nDλx -R1@ -ML#dvV_:^lSA%*vjm -{o7ٙ`㞚0wtO^.7P˼niH0ۚSJGr vsxCVhXUߋz nHE5H QB6+6P$D9 5whwGUDL7Hx. я5S{3}rb&MrʊJ"|-FA6̠j4⒮s[V.O5bdrg!:(asv ЖإbULȀjsZz!j0s -xׅxD:`2ٖɯ!kHZU m!o + -Cچ;~ #l5'>UV^^N#U?W v&c2NHE:QKՍ8}"w٤+i]kl{}!|9Sq5/ P)qF:6_[Sm]q!xwprK²kEջ嗝6QjjTge*$5$SҾZq?{: h~|;3hpbnsμ~mZtʠf{11=,a֐)[oI" [\ akIT`B"e>q%wBLG`Ï4. 2"ʉHO[׬YFHrIՍ ױYPJgtEh +ELTW!@$,-ֺye@WPD䣥; <4%ޥh]6n &h!IW,/<xj 󎥏 f8m\)~* u[@@A,/r`Gn鞵S]OFԟL*}ntmH/b=o{D&7p79GbF e0uhXRTdl˙]crv]iQ/ w56ipuOW91@PYp֗ی'l| - ˂kxJ&; MԮFs H,YXu~x[o{άfGԕ9v{Sͤ_i{ԋjvwX6joNwǐR kaskǡ^)8+h|K5E+I{qe;I9 $s+ Mۘ{5@'C3jb2LJ|-nr1<$掅?`M+#%_/Š8xHX47P}~:jbm!uy5_51Q~Dk?1K'V낚$9s oŽ~ b Ӷ5W`ȯKik cgᛄt ,]8,y> -stream -Q D?n/YT2ʎ8kU nύY *m⭺\$#|F,ܴuyҸpK* ?'w6HN=OIzfp -V8[ N&\_(~HTKAy+y =?xE*4q |^f!b, S23lDQ'?&;ֳYC XT,KxXySev*,xk7g> ;Fuϥ6; J_1 J_Uڭٓ=fJ.N``$P_(Ȼ851tO\"<:i'Wz +&.Pj1uWLCtlT v^BǏ"=$ݛh;e O[sK `w -㣒IN9'^*!OsHe -#)cVֵ,enAg-Bp5 cdxteN -pOX5M8cG#]z\f%jE7="@OmP|pTeCie6ѓȑbG ;C/>Xi>Qwŋ.K(CqPӣ/'7%z]F w ;IW3fL\E| O ;MiZ/ңL4x?O9% 5 R}<#$ϚMJ^ +0zD/U-xIk#ѻU78. ]l֝TT&|%̊76endstream -endobj -3862 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -<%em5hTj'o %oh*te]' ے~lPUv XD0Yӛη'inGGܷZeC}O!yT9 qc9.kSWoeG?kXg=$f.0َFxɿ :j?ƦߨP| Z8 '(=|%e0d>ʪ%Y=&dFaĜ@Hs/Rtg R3cnǞ --cEEZ% >ܤUgԿ>k`:XLM!I~GrBĴo0~eSuS<,x9}`'CR;)4kCɴLH -*:^"[J2(Ug4ŲҗA847<%<9t*Yr4  DZu,[|m R!ņ^p }&_yum?K0RbKU1) -s#FfanEkzJZPx:wO83Gc#fĽp 7>voiS=xDA*[\zAy( qK'/'(vκ -z.lk|Hk`K2k:(k*lW{eX+N=z -z]F[IqD۞{q9&9ϐKʿ7֋NI7g\fjk`(Ȗ ӥkYy}n`*,Cee>[VQ -8->"Z3+64Tdh=POQƾ}39|Ř@g7QL00C ~z̎RdQ(e2~m5S|F?_"!lF_YhyXkv[iB-RG-pHs Suq7;*U=Dld-@L|GAqMCaW;غ!#I(,dzNolrE|L!?4v_2F*&j>8!VԎaOXH$ EAKfs 7m+=}<5 q:6r|Lٟ;4e;1Ż#.?UM'6@9e b=re#6T o?c\H~%+L[So+)W4 ftLQyaخSM6FYǜ74.Iy|5lH{itKq]GPgo8!o-"݈?}.i>P  -QHЊ ^>״jv.քm|]9Ţ55mk/&(۔1vifN*)[Xh @#]ב|4,TRߵw=23G27&Ь.I5.)㻫֡Rݯ_#jp`W,G#˟%+76d 4"Nڍ++L(fH%{w - a0 1n9JOikhMr)hlKgendstream -endobj -3863 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream - =#AZMa9ӰM 7NCzBFJnH}f -'ҡ=f5&Nݿ9'Xb@nI&3I`F!,?4eRro{xR,uw6G'g_ sG0ʳ,SM0fMH rs׶R!ܗ zo{z `BS<.Y5RxM,!a/ J{k*.p YdLC x -;gtO*ʻ j59wp$fܡ>7.==:![TCdq~\]@0Xz9c~D{Ha# G0Y->7E`vնGo;Iu\+L%!RW~ LO0͗ ZgV Ɔ=`e`` }?Eo XcfG1^Qnzi3}wi)w0k55qIh<⬐n$9lDzZ7e -T -90ݜ c'Onz7'䟻Jty%V1FsJ0Fomm!H -#A@T0qZ\y &F-͒_>"a x}4ϬT?hbxB^y3JxV7bR? ٮ.)Y;2>H Ϫd.ㅼ8Cqn2{N/ZY-zBi-+,ܜ=.Y|Z)evE:$fPV tЬ40H3qJX _}h cbžBdy> -stream -ifX,)&9rc V5[YƲO`P7Y.%"gRz3reg9^ -Hߌ`>>e7L*z0WF6cSreG^C{jz 2t8}t2e?6%D9z> -۰ݻpggO,~4C?BDa_ y$CM'b\ۇ7]e@p ;g/ѥ4bg)}w'l"YVOh恣eyXˉINa/(l4EdD>eeAfXi'0!M8>Lͷ޽^i9p\[; MYiT* -Wc+EPL6&\Qhi ,.D mԍ@@yrMh7K]5~)LJ{.7V&9 5k?j+Q>[kL16͔ ƹ5&z^"TBhgjMM|I/\yOh[)5Q8).f]e-"EksJw z?p1T!!e+_Bf^={C w-j<?M8J+ S:yY. R%[ Vܨ̺<:|֙\/ZQMHA2NIUqiKd I6Z.._yeܥ|yU==5Ÿ$h)<"C)of])]\%Hk/k=B*448?t"O +cwvToqU$6Bz8\ڌT!|Z|8az}Ku<$9Lqv9Ox#>b2V͇͜tEؚ(Ñύ"Sbb.#~4Չu(ow+Cq&yn0I؇9"bo/llX4yf?NޱB^A~OC?aЩԡ1}S1xY{oѢb>zCϬ]H|SdE~u|LH[Hɗi -q֑㠀Ƿ@S6[9%PHye-?6,½"$Mzl-}8\TByÁu0&#C$Mњ]S{aG =q׋FM,nu?zz[qG@(GqywISpQ:%Bba@r0]$Fx(@巷J08oqSx|b'~:e=./A?S* (C?X,KCFx+cU?de4$KҲRTTendstream -endobj -3865 0 obj -<< /Filter /FlateDecode /Length 560 >> -stream -9U) }bQpC{QMZ S9;gs bNs~8<5AsE߈5AT\q06NrVpi;~$~2 L`l,e ڿ+ul6Nm B#+& Z[߷aH .[^`!}llcDbPk<ҀKóm?J<ՈobŃ>2 -O/@jQ!UtAџ ʨj\}oׁUFp-8+XXpa98Ѕqјt`_vON`-EMe5j6gw||pO^& +e˗J5da6p?쟖endstream -endobj -3866 0 obj -<< /Filter /FlateDecode /Length 1968 >> -stream -"%x{(6*wl"2Şp`Y^Ttdьs>DIEE&qy [wۂge5zL0R)\Q&(rhwN$S+I^sElXy$GFtBT7~@i Ȳ/-Q/38 `lDۻFƈlb=VȮeOh^TX)80}OT2ˏM$iMCTA[<<{`3( Z {&I& - ?KÍT,c00Jy$qT= ŝ -_t@P@;PG/vtJM҈łmk^y}|`(.Z"V*/Q~fJ_sa֭J/wAlʝ8;f<̹70.o,;}a(֞ݪyIIs`87D|CM\ڊhV)Ig$;ڭbVW;(Na?罳IF6vJm osvxTi\J}8',"qOH\R}%2? ہҏF\Bk+ E2",e_M_m< Wgti:Ss͍s+sp32|_}EǙ}V訁X%gm"]5t9.l)Z 0 TTrwca0/ңs; -=c>#0"g0"-P⮷11Iz`vtYLBp}(=ƢAfڿ[ bo~4Gt_[ E -'oUkZ'zhld6me%CvmtnUL^Fsk/Ttʂp -R``dz/"g2GחW՝ڍ5Kb B[X=uWZĈsćkU8E=MNluEz"T[`~%YK4RFk6+I77 x%e)UO!->A{iQ` -\݆4:}:, 5C6-s:EJ|,,L &a\SRl]bdmHҁE_OjlC6BU=QګC)_xzShr;y0V?|W^g)[^s5@pW^.c'oݝ DIъذMUuB,]1i*֧"Pe8?i{bwuoqa Jxo|$L酢)ΟXMZY(d nV.;1#BZPeRH@n` &d7INhRNou"̬z -:\DgCL/*9y2mDupaLqk3kQ2AsUs /ɷʫXwKԮ<"NX`/d?W֟^JQDJ`7;t"#5 C^5Rendstream -endobj -3867 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -:f`O J%˟%,TLU6^0 5p-镂zK:>cS0 -"7|U',emj8'-m}P[4uLIihqqn#T[@V? --oBiv c0o h6LZ>vR3FjmC -@n13dԋ]#Z`Rpb>Ozb[J6X-.xؿRbG:NP@Ҝ|&(3`g֎n\Qީr;X# " -ԃaAb?ym4mK~l%`gvvQl"&+/|k <;Xm,h8'<ZZw '/~ݬKj֍GR/'^-K﹀\d^bsGX;Zl fdhbB ';̺S]fB@l/7gבi>4R -2tNG u?RS%oǣ1۩%q)Ruh9E\YWhh.S;EWQ: -PN)Γe -T_h J 翺SR*-9Z\@.n@;UsҺwI^qQ ?|yCH;)pQR>2 l 99VnŪ=m -x͋p^A`nK9t==2_xtŲ7<\p#GWg{?G;3&/?t('3*x. 2M;m6~#5b,PLOdPU 5=}L]k>)юpH -t-tsdžOV 'Ly0D I]UzutYrMnڳC0H_{i{&',Wj5R2#^3`w'tA;Yռq]ü|F5ybs_?no`x:UUqyN48}29'$\8g?^ign[Ea&8/"ֆ:1 aX%ހ536Y1/0BEO̕ jh0sڌ g uWR.TA՝|e܃GWuQ"UӇ$ÌR'|_q2cLe_ -:q\i}u UdHWDL8pٺ8v7>pgf X4^̴~"97%TGUAXћendstream -endobj -3868 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream - kQOحA*םJ"#  -Jޞm@Φ6^1ɤ9/gŘ -م%gWBcy BƟ -].ؾmQlY\3?E2'M*50(""md)^;.ppE-5X$S6s~x3`[!!g0_8ت :  縰|ZN: K%8m>o'QYLኝSғeXu V-) NyS,T,&H}1bUCef8i@jkj0a4ZVRGrS8=#ܔcƥO["d:@--?e${K-@@9SQRŌbQ浑N`F0 -wOX JzgȄ+T./5q S:SўusDf&P1\7]vP~AlSzFU7_q 4!$%7S&CY +L^ts7׬,>nR3 CɞT-ryǙUu%+iP 󷪧R.z:uEPhϱ"tifQ^绘AHޢqUS$NK**3ܕXѿ /C=yUjTA -髷g0O*ap㻼#V`$L5 7|x'Trׯ|׃cQg{pƚ\WN,NoEbT(B/FfQs2k4|oF!;m$;X *rxfX9ۓsDi566|FG7:25Z=VSl8 hMBt_Ii21U C3y;;˶!֦f1P/RB|LY؉~]nj_Ya_GfsDZ;=UA]ٱV8\ebځ7:PEOg5;\I~1~FE^~{ ֤H_eعvl!^Œ7됪eDi5f-gegU%~thVMJ?Rg ӟTT]+5,b6OHW$BTJ^WUq_x9[JkYPrΆ]a&˯1r~Q~YG" WafߒXk^^fNNuZZv+hk - D6?yeM*$^e8@ؙs\?!s)+fҽRvPǾ*^Y6PGW:tX󆄹Hv`E閏TP?|E(&͛`zi&*N`߼(RJ'Ik;_Mf&{̑6,_1-s?kv̈́b7'&q<. re -j6„cvrhrh$H1 4 -NP(Oģ$;8 "t jjҩ-Qw~a#p B#5 uVV2Hr]E^cXFOK;YcjלWkIb˧Uh&ﴀ/S)Tr3z5Sr}Fdřd2e7*w U1)'He -7/E(J*dl!J (#N6r -˯KhoS{!/Z:*5l/endstream -endobj -3869 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream - -4m3 -//AT]^U<6c҂Ng8rq78*8YkV#I ['d_Ou ²0މ\!@QE쒉~enr؍0"x0}0|u,bð:~)5%PPy/'񱷓 qTQN""MiSe[h͠"AկKCk̆vhA5=Swe"Cܭ(|q݇MFt}!m0?Ϥ-=ا]r-.}X fm>< -(0hrd(næNU Nqkӓ#LޭSc bЪ:z|C, Ƥmykyi*^;3 #:6/* T: 6vP1;51_I.o -@lo~|Xz%c?æCMx=jX -oM^3(;:lr(y}+N>C<ѓbjw#a!`|֢v9~༸ ,y6 ّd˓ˊhGN%^NGJIc04Q^~װt ͖W V잷"oC)#mr,:u,L,GHD%i"GC4ƶ1g=qBT=̜̅$@,I;;aq8T!np2ij6s=l~x`\d WwQqǚY}9Ӡg9-C #/y1$-E_%M76x{,ZUID.:[ 5a0჉Ǽz^)u.LB/-fR tP9($. 22s<(ʈ2&BJ=[<Bgpֹ$*/ 㛨 &1HCq8ѧ?}fէΗ|aE%zC[of]k>{_@n3NܭPSaFv`RUO$N bAf'eW.L=C+K`o~Ȁ9mOv.'qnY3#bL1@,VmE94CښRc{ RDt1H+$lFّWnumN͢OX`ٮ'GvbҠjs^6(\?)$oAd뜏.WBewsRkdC-UӬD yVU:mFw3{W;t7w.i)`J@ f38ٜYQ'i1!>"*ʌ[蛏Ө.eT>[>UNgHuCs^fHO4]f2Ԧ$MɍEendstream -endobj -3870 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -vUΔ~$glU;#!ab:4!f1X=3˽Tre>V$KIyq.I:Z ~lyʖMey&+ -//XS L Pm .?!rEli`*RIjiw -/-l5OeN[]^6HU@VzF Gt -EfB1}ymMGơ{ +=۰ݭG(b&/Ly)>7 efK'rB]cTbӭۮ+3BWD -IC-RHQ zFaAh?LS&''bpr(,*.HGN]^C Q%`uͦUc_=ۖqB,K">+r$TI@"וV+|WAuQ% g(sy>YkulPUb8#f-'aZYR4_Jς)xKvdRdsn4?*-}참tG3z I&,pט$%;0;iUr<X -bW}Õ:۫ -jBI=غ}"Q< @wq%h漋v[ÉA)8YfKv#Ѭ?? ea`[B}a أPaI[ct=Ӯ 3:' Ɇ?1V5G8yF$)zn\|q4$h~”L+f23ĝf[^xbrNfD%8Z;J!53f0O3C!;FDJ%]i)i;%vY$eZSS.HlTCC8 b5p% ntyMdFj.͗%]ꥨBD -pP#9,844VwKJ,= Y -ij'qYd`Qm5="zcp,4y5alG dyN6ǚ%(wL7Di =저 /.|5v8pXW)z<9(Z. cW+gJM3[j_WJKŶ^W=CtyshbD| n;Jܶ -fR!&|Z73EwCo3A+C:1Md>]}!ؗUg NeЯUVXDa|"O~Ʃ;Y"eMcX#SWc`\A .0Vafjʫb4:ovgʋ> -stream -3b`J7݆-Lwx}{ӐTa)!Y5`՛[-GR6614&?_%Gj<1$xA0FD3*eMχ-umde -ڋ&*{gذq3i#kv\y=X[u ۧb| 5~z{&#Y vGX]zRq-e<=X"C?zh$xK*_A0 nG>2X" 節+Dm>Yb,Fh -d<'D›р sL-2[ <vŭ_V)5 ͈?T4,}(xѵGlG> -stream -YD.]U'J)dXw0 Օlc]&kؓF( uUBWM^v̀!swήdO38b/b_,rnpYf؂g_& -ĩHڃCɅ"A5g/$AcŲ?! -e#K\jCoB*:Ȕ`aFs'+cY6rsi2dilKUv)<r5"ɵMt]痀nHޗyc?Q^U1x<"ȥ1h -t \,5Y 2D˚cfow0ƙ箟ǧQ`Xoj=Pǝ#SDmJoRayTJ~X^.|Yq#WQ5R?4Vn9+A5Z?ՊVXaa=$ ~'Ŋ' >Z5` nW.A } R`EUYWDj.q;ӕgxQ:ƥ^ÃőBxgƻ&ʣWJ[dV;Fr5 -}S/qڀYxF˺U*G n0ǥ"&q.21j$bJ,TĸNttUc^ι}IGͽ'Vb@ki pZ/ҥp ·mS?l%|8A=9}al`rI)rZ[,L>iQd@Ryif9o΂UAmȷM%GːO_iwBd[۾F,8/ouqi-"h݇8q%E +u(xk] #˔2. 1bgbRWx˿l+'5*:4XhmCѺf<2$Jw~ũ5"Q bJ[V5>Jx(C>_"=Dml[m+a؋} [ݹx`-_ϱɮD1p֒pWD})l=4DEp`5XK3T@|Gz{ XT 9xZ:Rڙr /| -NγΙ)r[ey N)~~RΠ$d\Or#Qdxk|fނ` ӗ^ O$7dLqe5O[v]<1ۓ(k3Rz:3ͩRJeTU Z.%d cHvsnsga#RUifXwU zwgIZY+Lu} Z .&GŦz̟G ՕX'ʍAcMv8Cf>:ebTR-!mg9z,9)#}اJ4x1 G -%t3GC;z/K/tdE cL"۬CbQu""cih}ɯQ(coͪ֙MA]1(WMg:/&@~k -@ddR3COSW6td -ǚӔ)0>Ǭ;Av Ac-T25endstream -endobj -3873 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -IA,i5Zٕ_>@g|iӎ{#_6 -e8<U~q~ˇx)g[df:br~S%W9tdu~ 1S*Z=F&vi ^`rS8C̝had<m$ Ʈ)(sS^yɷQnS%>K\X*Yca>g, gF)RE`9͍dz|f8a:φ7TpQ+29!!%!aHx0m2 vgb~ڟn-ED -rK*F iiWoc!^k-U@8Vl.氍xh94֯t}o45yL^+Z5 UeU,lqSPT=OG GyCOO, +'2@nH簫#jTDKmmOQ^5g yxQ]cMopTG̳%i0(Xh1KzT2S2"O3GŽ|Mb80m&:(ڷT6t-Y(Z6ZkJ Ef6J w2VMv2&oCI.JWmx>{.Lq#2 _ԅ@(U'Bݰ6]A"|JthIӾODρP1MwumPxZ]Svy*Tх{(GYz͙3"\9x*Qj${;_I+B5bd`:!hw.>kf R:tHztBE HN +ey7ĂԶِ#6:SĆ]hfI]V Jr}ԛ@}2'v'. r^{8uF]f~3cIo ~\F -ľG_NM?< Eͨ+'ƯQgd#E-V?+|DBiu#$)a܍OZ! ];$EZ8ǃusD9]vc*:-^!K -=av(Lk񫱪mr:Z3 [E.'dz -E |!>dѦw46:[>z('[:i(p}O`Ϟ'u (J guP/-.y0ٲbv;8k^B|;R!a7 .pKr* Z'b:$H~$l'@]}hvju}irQr8PIW]r5)}1_i։| ~:8bv2{1*abٚCN-C6iI;WJendstream -endobj -3874 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -Cŵ-C`ǜH{M)91Ak`qmަ5m d nF?UA~78914ĺD0⃶(53`Rct~m֩{zx lfx/KaȳCv[XqMf]|Q3Zvȡ"J`@!BHod .3(ƟwbwGoD{t}bRZ~k -0%\ͽG*JT1Zz'ԥz^ {AS*VAڮb>y|Nh1>=HwQIŪZ5=,A}FBwV0;ܶ@#Iί2U Yw igAnh!t<"P U #s~ 5׋F&șC$z0^_$(0㝧|]3!ǔLZZ}ʨTR )@o!^pQ֟;[6#4T)#ko~@e,tzbڦ5ܼ2$ԭl$/ub􏧪ʋGu|5.6/=hZ]r+ -D}&g:BJN 9#K{͉a18WP;_ vGA:LXبF&i{"2b2A8B随 - l]@|$i"1m& Z~~?RI86 -ٝ]ߡJ,uKuwJ/p C9{XGO0#*r+A=x>K`STM̤~0VmM#3]1{cm,q+GCG`E7VzyEÇ~zR 1f'}6%5jdFU^JQ76`8jd~FeC+axK2e!Dy^zsӹ#骃V/l`=5V0 V"TK } "|4N -@b货 -G{ UȈ?9(\^xPZ+ԫQ%{g( <bKgTSr z߲> -zG k!"M =~yeϝendstream -endobj -3875 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -w¼Y)gd3hbBN)ϐqS_#F*sj}t9XͷC;T a MA012 q³ՋZ5 (zv -̾-%q99ip!%hEPZ0zBGs8Cx_Vb|"+Mt4?G~r3R:IĶ`(j~w~oDpxrɺ wY:Zlof'kYm-o܍EE8<1ZCYiPVz݃5)Q/J+{т%uԞ\,y/_gǶevR3[HJmQzbkjm|5F>򱝘޻$ ;£f)bn T]+ @fXR4' Xh_e HSY:ws%_^#8e\7ȽAW4h׎ze&n>972z Yr8-W~!Kc[dћW^G5k*MZyͦg!urHQ|>6$DžBmMn{Ϧ"= $F ɚ2Ȃ}Mb](<1}QF}列qYbZ k=)([d/2t;;G.5o{udvy! (\穲mx[Nmz~~עhhu﷫`>YS6m(TlS2&ani3J2uj49\,xCf(ȶÉ{}K?xE -tKT{q KRH,=}%+hWu/muY&̱@uOIƚ]=.p3M%aT7Rai9[߯6pWGxWI')[L&R:cB #0|$TǧPozd$:jͯD2kZͶV+3Cy<\1DNc   jFG%5y嵺0@??8&PcPef^C$Y> -stream -VjWק&r bxvf3o'SYȊh5A FL= 8j*V,/ztm2`p9e 4puB>^tq.[U Q 8wT'Yyt>Y]0Sj 0QV3ޅ*.$|ub)mo$=8!nԕI_. "B$=$L48j/LSKS޹1|jtTmf{l "/BP~iyMyT56y2<84^\JLb+yc[H W G -Z0c{3<~>MGen@Dc/ tU|ٱ刺Lt+aDa,9Ծh;1$nJ!y Hl1Ѧ yU0[H Fҫ֌z>or9Wa LIᇲ .ECYD~]"9ZF(Y+.ج~:625GS= -XRX -l:(_;#Ah4Rh"Z [2/p8m'B]ć@y=F/n?X|ob{u8["7zw~xBXz˫~4 "Ƙ -FΉO-l'H~ qgL - W -*ϒ$R:|ݏ(zy4@%م6PuRĮUgxT?"7ٻ7K8 -;Od&-N$9 0+Q}o $#ݒ>w2a\`NmKl `1gϳKv k_r - -yJٚ?-ٮa,# Ήl&驃vdv W(l{jW{(RAu#C*=2Yyv"PM6 bR}sgI +oL0Q%u0LpKd E7d 0)KK[bg$7|Wt qB Y>降n19֔"|vb6h2/~O<ÃfQz)mͅKP+ We 2N݃ nW -K5Lt^!uxy[-KllŴu"/_ReqrPfcPK&숗d܇y6G2Xendstream -endobj -3877 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -)ǣFmp^fA~vZ#u;q&¥s>"iu/;Oj2!ŋ8|(61%8n!)(be;\ PJ[QFF \KȮ+$ L;_ܦDR~ )D]8rN'[$(%#@\qI՜;ֻA' nVccK&@|FnY9cb_/':~tjG'yZz*]ÄC6pZJLejΒ)}ݔ3ԅV2"BG}'&n[8sةЇ㸊H6bD1 -Tz4]Ƥ?fشi-eSs$[n]=`ڶ$9 -Ijl9c,F`n^ZbLS h=Sbvyi\I@eh}l[%)OEjDph_й@{)]z^nSwO"Bztʛ^!Ɣdn]ݘѫBu Dm'Jr6QJn?JIO˼WH+II=bvyԝU^}E6vY5VI^ksij= Qtd@b pa)_v)vr2/gEɎM"0ezFЦz'e+b#6>޳i˕)!r p#W_B\2^g:nN2@Wo1iRUnu3YpMXz-ѽeō!<|= H=B( |p<B@#)zmW"Sp"~ɥct"S*˘)Stڇmdz&p?tGjBf7?8`g-7=dO~6H1[y n Zd ){h R<&ybYM̷yjV歹Ú5bbIաwIL w#`/?FHֹUfpcn"8Ρa]v&m^" __ɳ1@s݀PB<DL<^);!$6`cok]S|r--Y #L8fu|`g%ԣW=^D΍|Bc(씶4_'kiE^jU"M Pnf[endstream -endobj -3878 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -aiB%[`p T}bxBG'nk:*!CU׾vtH3:2V`jHې - =$$2 $r2;C?Z@2MjnpаG{BHQ:yَf #a^;ۚQ -2JɠH`*^,ՒdmfΕtX!dFѝZpSsRx;|rt%*NBNL[aSqILGQ]GO;cbeg;ڼش⫼7[M ()2aefܝKxąSza૬ -fQ|hY铄o;ҢSLj1s|+//Q`D? -یTDzm9#AlǬXQJ L:{nR)C Wsa32;ZU%+le3ao!n|5YpQ_%gإmR{A*GqMpg8Dڋؼ ܖh_d㜍b9,t 341GaSNN0W$hy/٭9ȉ5!$? = -㳖nF/̙Gu\85 R{n 6|G?bgܾP !i &`5nҾ%;sb9>3]}'"”y6Ȫ7FVI#oJ]hϪH_p,EF0&Erpe`{ rяjΙmExlġۛK5 )b*gƐ oL-G0h c;mw/gBlJXAܐ B&H՜4#6O;oj}٫uh[L պ*s~{t!CnB>/_RD$)`j+AWrĶ@Սcw܎JW y1!rRHi@e>5ѕ*FVK_q^Ve4ӵ B)^A d@&~PW W2^T+BNccˈKbxGj0>Lw]8v:}YuBo,SLtǑh*Z~U -0~KstMaL~Pךכ{< R#7vo`+z?x}`f_vUUS6.99Btof}v-=UUQp>\/t -'Z3p]Ƥ: honjKyLhl) K W P4[!T `,[Uu6y^L }_C ϯ!G{ ^'J!*k36DS E?͚Q[Ϭiz8SVxqEGey3[if_b`㘫0relnJW -\Orh"~a_L 5%b{sF!뵩{/]3_޳QbS$B}w~\[R+6U vKs3l56BqQ5eg'=7FDR}X&.2ԟncҟqBfjAʈػ jv쀕Dƅ傓Qۗɂ;څ3Gr#]T]Pʯ3x{40 jԉU~MtJ zOW7G-7i$m1B r\fwԕk(p <瘦^`xb2B6w7ĉ،$2^BbU> -stream -d`kj^p7E72 -,"ə/ps,7 lK-)% 1@`4KҘ{lOӯy%r3>/72XaM)9gj=v\ mpk gr<OT0"_H- G{(&w7$jBb#EXQ؅"x:*Z +TrDuS=aK6\|WY!*?'Rsa@ݠeA>?WѾ>L\5'elIq!Hu^w%Yg {OTg@a!m|^ 5q]N}۽GIJITQocVhV|Л݂x+\3kC\{}t\2 -+!mN=b#*Qm;3(4.4;kYL=-9UpRO"\! {y'[ b,w&Z5ڽCbLzn]RP$&L6ȏ%^7Ξzs/\[-yjVuL#7 &V\(t,DCH GNkZ}\ސ dco¯sPǪD4hPE C:^Rc<B}é)e -jrr&/w}$[[(0:o 2)\<&y2Ըϝ8̟O zj $j"?-č=^b0ݶyBnusƮa&TQ -30*Oջ1tJbL-.b1Lƣ}aiBEq@;t_q'1ĝzU @k8`,v%lHdE^BRÇ'IhefiC qK7g~CNj;P[ ( mGhȸX.bMƸȆLxN%5Hԧ{).Q3gyd VįvAEZKl-oi֜HttRX+X{];ūY|o -$ݨ{$lG>endstream -endobj -3880 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -QɹL-wmwZظsh=(5MFI -hctA'ɋFr`>P퓰MЯm4A4!bP#vA"f)|s إpstyq`MۉnNbOnրgu;*NYPR"NOX"zt[+꨽E- ~3jG^k64ZŘǬԸw/[s_ع7ApVYZVu7K(<|4Qɵ3{dd4`(*n_E?=akKcov]Yiƍ 8fN%=|'XĽE8Z:GXso(ו'I{;p0:I%q} 5w%.,tG0ʤ[r|IJvyV >C,CZR>/%?)G_LFݐx&bsh>`{mCP -;Iwr]^Ō*ZEgV|!`O9O.ϼ4()'d]͔VS+Zt4F^Ɗ!0 =gC:x-j/XK|=oL%+{` 5=iY$nS6CD<"qVMi̩ʇ]G>u "eERJ37g.;ց٤n pG13Vhs)Mݢ oSe&;x<@QkJokNm6tSaȬD %@jτInRDu|}.$cX3# _>MsmXYVL(o)ml7$_s'a8`>4IW -'m= -أvg]!U>] -V2)o)Zd0Y -gnQfۿ; 59kO\ےY[ߵ"m}5OE ЮT1j"1"*w`Dx=; .kfO ;-W~8±Z `HԎ\ sUɻ W:kNSm?RWHendstream -endobj -3881 0 obj -<< /Filter /FlateDecode /Length 1056 >> -stream -~k-NԧF3&[1`ݎ.TICGP|fg5Јw\TkЮ~U -3а #4y^z7Έ+YRIFM~gw6.wjՓbxjL#aHx~3^9?O xtoy#I\QVm풆}EcvTl6q9ZR՗B?km?9署rp=B@nMJ_7IcIe &,GX¡TyaUIKAYU,.Q1 >ao3kOQ8FS]{>WbufuD_~EpZs -#Aa Pb;tMTybaKͨ׷AmY b-yD̽LXs|* -*kn-#] ݝOWFq7>cTQ;O2޹zЋ5Q̷q;ӟVYƥd'*U(*D+E줅 p Rؼ9i[Ĝ鐮-0/cDf{Ʉ];HoĤ_2?\=.on 16bx]X]J :rf'o -'saendstream -endobj -3882 0 obj -<< /Filter /FlateDecode /Length 2368 >> -stream -LMjDaǷ'(̗{D=pIMb 7LmbBe+Nϙf㪈&>V1XlR'7yRH0xD;vyu#JA=L谛1yƤa+=Uk2_2p2ԯMϯ)%]z`v:+f ~R`:3l6ƙ$ ] e6jv` :}?8$gy3s˝ /)'HG,iw@N09W1E kTe?گtvݤRR$5K5iM4*l1Il%H8HO֔2S]7CK⺰<,:'jsx ִG&$|1R\ 7[Rn%ޅ@Nk6\?DFBNw'?/b'p@&NȩKRC r*{ `o[F6"_k<4TZLdh ?3a=akfԦʴ,Qg!'KT_`}oj<&WE$gi 7qJ,|Gno=hF*YrBwN.^ܐ4LǙm\+kYݺ~aaRD*),H¦ֱPfHȰsdmc"g`a]}.$8죋vz"8ҕj7"'F2=EwC♐dFvYdd&F!޲5÷TWIw7Kc,MEd xke6JI6>cu4 ȕ?-0^i/ Wz;jeFb#e'+rE"";BJ7֢9tp,Kߔ/>gpWZG>+}Lʯq*0|pO]Hvxze*1NaM"u -#fR<::\ VH)Xx7*+b*Qyh\R$ 춈%kWg^vثND1*MjUc;W*Ogr ^`-[T:>KТW|]ĹQh`M/T"2_O’rF]ǚ65" -2F;UXC2~GXӯ =܇<}蛁j5V|{HX7%zf=64RO* zrbzs^4)Uq;oTqVÑ_CGCGנ1Ҵ3 j@ m_Wrj72X##w'0%Y'޶R{]4(c D6>tՕiơVڷR` i[.dEVC3D3V8BTYl=U} ,tEiżl]c$q9,6kq .VkHQ=ާ(jiafy^n8?}DZc0{xW=Pcz#R/T/סAy;TCN La9_'lęZqUĕ-Y=9夲Ҍ'\ZH,,[34+! وs[N58]?0$wPx012 )<3y%=}R8 WٵIvK$"AK<"ы8|^@M{jm%2g9]"Iu H?YbXbro~xXkaFH7F#V2cK#ᇜaRi4lqB7,ej -('OowN:pjd'`)6gpu9mg|<nA+HҤIzX{6!1{e#7?5%bb*(VMilKT2;3|@oˮ9Q\d4Z~bCuZ4V -X'eKޗ1GoO3``t}%vD;YHRœdo0Lãヒ[~-k0R& -3SD=MGڦv-d0@q`fǷP!NEYendstream -endobj -3883 0 obj -<< /Filter /FlateDecode /Length 2512 >> -stream -00MoԻopmQW椕CG? ^|gc6=$H4k>~iZp@,Y տyR{"Aj|ܚq{8rE]{a秥[z([CQk#4pP娂LietVkOb?MI) pj}G07zΩ*VԵ^iU6KwQ5Cg.\E-n@xkYEs:7vTzM h7 N vV1 'X`͊؋?Z6IUmW=E}*2o䞽As62_! !*4D*;+g~P?`-rVW0{!+0qR?qdsn]C~w} b*~F:V0Tws6kNT丵<;J䥎^ bJyBdݕ:b&ewс|,,|ȕdVAN3b#P$!s%{{3z=8C[Erl28opdWɻIhk^?C%RKbB19Q8M] /ղ;6=CQu~J -ͼL,e QO[קeߟHVPTbYiNuI i>j |UAC N HHg`&znHĄ~4%d2E۠$Js[ok-1ɻFGSH4\}hD%CBڞU7*0z=\< -e %< }V  WJh&K,>O2ةdLUV/Yk_%} e.بstfgVk3maA>)ɍl ֞t7>z;^F$ P7Qttt/D4ns$y"t-2Sl1;ը䧏:?7iRg\ɨ<CJ?$a8QjkD\ex4ptel~+bpµ.=gw:7 -BFkf Pƀ{i^*-֚a.Y{$ ?j-PpVZ`dX%Rر~{47$ ۨvKTҸǑh_IBAofHrr\~V0Uˤ{UG7~zB$sb 1Fk/3"[ZJaS-t,6@^ % ,yt\i7.Ae( R/0wYE(|Eg^BdbCA+'FL(B1{e* q-φ3H2mیUʭ"d%_[b皇?F!̄-Bh@#4i#Pg%'ˍՑ$-p2_|mx9=W<[z܃F"\L#wnVn4_6@x-i-8B?3F|t+>iR O3C?4օ"{w -=[f Xf+B1K<> -stream -KϴI_J` ۑ 㵴&|@VH7jM8#8Ȳ6 [TWgmU( ^p+զ I(n ɯw uhs!al5b'~w2o^$80\i8G{, Dϫ)9AOɌV m\/30u9]'i@˸b$JJ2Ը K)E}ϼVi_ &+H- p(]cr6C=nfōx"B c]6B1Tr~32 6^1_W<-ӎ .iOm&Rρ$U$!ȳ@}~#A.]JᙪFܾS,:w$dF2K?!S7vYG_e -,oj~-E'Pz \=m19L$GEZNwbq0L!v3Ko'JmށsS¯y\~ с<2 s?\Y\L̰NQ% FXk GOBj=ǔU%07+#ʲ/$~.q+*f*/&#ˡ㱢f*ߞ ]Zʤa$L-a=R_Z7/uS_RVqcSlKKr@J \>L{+[:joăs,vhN%~ri a#PμQa,i`Uk#0`olCEx VaШkK<tRx\tѓGhP`;'0G'x|4BB V.oh|S bVVڀHZ0rNׂ}3\; TN@ 0>.l]z#?pp>soTn'[6N~}?i=$)H*$y9WvĨ[Ls)/H>JCCz,CvH' ^/D)fgc`\-0*W关&[B6pT]Dn|9-K̤!K?#@6ie~EJOJ@@\' i&@Rk$yH_ۑ@"'xH> 3s@wSĽ+h┲`3qd/,/j6'056j]C4.A"hd`#+ ]X<5ak='e5 |"5O]/ֲ-nCd% ϒv* -I7 l.(4_ -{,-%{Nr X7xgrZe9Gh)rh]AǨE0P?2fpULF&:xz=H/`bSs6f ˟5pk܇Y9P-@K|RҾ nwU G_Wxb/@bH)> -stream -K Ng4G[2FAcjo$`GYѩ ؙ s;x6ɋf뫇CAD3{ ?.РdҜ{MP -F ru|, c$b^SÂ=+0-9<]Vv,n-Əfdk(.>Ll!n }QB}@n67(gP|3vD [n2ߙk^:6h9zmcq4 -:,G;U[jU:cZ$sDپ);7Fmb.4tG|<*Q -E”|V$۔F5kh7@P}:7ܗ(΢5i0P!Pю<~<>#pN7Tc!'9 9" ?eT ǻ nim(\z0Cf9H FR!bhZ,a7;ziY=?i5bbnNhv&!U_;h -}qfClvsyU)YG3XG0oX8υUh,n>{bV?&ǁ*v>CqݕL¡{7b ]Ǫ?aVu'8ŝFDDi/nภQl%L{?Bep/Qiс݅&}D@/bo62 e 6ڳY MFJ'лj[ᜤ 20v/w5;Anbo$Vޙ# +pendstream -endobj -3886 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -8qb(R Z5 -:ɠcjbL`CZ}x<;PgjQu)(alέxbp )K`)@frhR0;z~<n8 #~n~\o 7L'W?Asș ]=1,w:Hڮ9+jVѯKʴ9)5h8Dt\g# n Y2&K?u!Wrb`i"|NͤAT#a:8_ ww \)(f'~ -98H 4Scθ۰3 3}I -seC ȟX(§~պm~ۙۓɽA8GjocKP@3qK`4_O8;>!ڬ.|WĐ p`U`@x\&kJ`Ή0VF'9y|&0dׄ/7ɉ59}ɨ!7@MJ; 'g^O<{yFPmd,n]btSXt8VՌciG'Ϗ6L-^?N&~lU?gUT/(7zic],,\L̟ =yTw&N[Ğ K։^Q K>B``G(yJ0x\CG;P7I 8TtML{z f IB\I9!oR xFKr ZKik -ޥU=e%I׫rbSt;[cwj]A=sCuKJ%BK -+5J0h{TH裤n&j [%GTWwG& Oō\3X>Tyߨ 𠛹s4KxkՒoMGL'$AຆAE} ~(UܚzJǓ8euķ#>62|j%+a];?_̂2}-Xٳ*>jp>ʠXf͠6瀝md *F;P^,$T; G^;< Աg+LϾ1lzyK*PՁӱҵkE 4ZFlCVAҦS*k)E_Fis;O_!ER`dRM{sbʻWWG_Cendstream -endobj -3887 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -_"R[MtsQg{+\_>eŷtnmK 2/6sqn<YlgGwu*!=ًHb s#.;BC#'m1F?hnky1&{aϖƑH 6}|蔯l$1@{$PEۃc9=.`n ^1O/>U_3t|>m"۳JKGk; -dqj { -@s 0S<;$۱7 72}V4bVc&9(BLU|Yc4uyD8ugv'df1A.bhjb0dOU4(@䩱){`k5Uhl􌀍HHNb[߆)7`e%$G Ь췠(*l~Vw#zGg:|i?i%SS݀%*ޙG $:+dÚfK"" Srػ]D LFjN@KVp@qaWn?`ʨzZ˓0}*$S[\l//u/&sǨZ%0@? BEdz/+z!Y?H[%j3%#MCEJ)m5hyL?HO9 KEV h#SS =dHs='07qr_5% %Dm#u0q&x/zB70`ݘR8fV7nP+N'X &\@`DE )5+̯kE[#Lq*ځ%%Y "Xe5aU~!"˞Ow P_OIP[6wJKCviV 5?}VuP^Ճgn<$y"Dݦ;ohHfa0C|_n'ĩ 9q/Y5tq.;2^@OR -CVY`U2_x=^^0&ʱ1t BԖH"SuמBƛ\cn-NM^9zrEw :1 X?~72"B׉/'5elތ%d!7g7WtNa7˱d`9q{!T`$ڶ1{U#@}ٰ~f xX(^4]p$:_}xQ+V5EȮ3nW.X6ԍESpHza˂ AVO&f$f䢜Qx?\8f,ǭ9gӌ RQn7vғ@㏃~0L\0!anz3)x䧟77+Wr98m]"CԷhW3TYS3HB^ף:U$,51"#18&_E=*.mC`iסhi:{* /oHv:-(57ER>w{㒤y Vßo'K9Xn7ȌHjzF\66?ZUIO'r+{Jj^k+S ~j98Ҙ_)D - / j*kߤ(s<0QmV`\]&6Ey_ d4(s5jaafendstream -endobj -3888 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -) Kp3x;30@9KPGMJvY+S,z -qkwqo0n2F;/@8F 0z* /n˕6ulo1H4Iq ss$&sM]"ҩ_4$A +j*U!liGn@1k ;ȫIw0msRaC* +Q)^|Sy5lh:8E8tT8+ -R| W{pd穇~KD\1?ytq˕eCxq)2+R[LDK8łێZs vjP[oyu[c) Qn"W7 ]U:hSx)oM U7bpTYI j%XUvf&j/XyL;6 W=>3p&Q l !5e%PW` ol2`˘|tg%?m%O I.MjOuҰ Q)w6 g5;%fZ`*f兹E Ҿ&aL#u F@h1g V- #&X]CF4`$K|<->ƌa$лhP$*裩 銱X>3/D,vBa``I5:w4yBH^eh<=00ȐBԣ'=AӐaOG]q0\`ΖNb7vlLer_? SmY5~!a-{_ٖaSX("cˍ;0* -ʜʚ;Tu4LȘ* OVmq,F3"{.%#\kzy'Vh*\"&/fyZK/n~|- 0LpT=u9 @ FKv?҄16^KFcx(QHu\"շI]G)mqendstream -endobj -3889 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -s -#N›Wdn~}l.6B,o1ġ;1xjn2Q;Kܖ÷能i -]pl$ج'G)h1]~9JYeF(̠6/)ұ*+G -wwzYY!3 -3p@Uw;A$}ɱpRe˩/'f"tþ5bewe{tտ\;xV:cmKr84pUP\Ik82]A>q~LγȈ[AQjjIիr7^JCrϚZ, O[O\{Rj.lRBMFlE^FءzWf\M\Y7#(&j&1y--@G6vNJ43\.L\$WU@`#>Ěv0b~54&xt\Ÿֈq!Oqnos>)wFHS_GkOUDUd{aCE冄"2(Tz}V-C@/aZ2zgFCLȁ -+R{~+nlW;S-h%gUO"o1 [Q_Ffz/"˹$9b#D"{ۧ^eX!rE{/u/A5Cziz_iXda-zuf(k I䑃)_ ,rOXK/̀86fz0S[̓RkqիL[kXkY1F!A l2ola% Iʢv|_iHv˄ 4( Gn 5fsm#3ӂѾCWDPN{6@Un1jLigA`V,^Sn)'$~"]8ej7t\kH!l(zOZ":<.{ -u -+Ou񂃧!A+͂1>Ϸ1Ar3ަ\c MߑNA`ݭ:{x0Lv`ʦ.H]`"(0mFRyXʵsH.Q{HC8=ᇐ^SrS%ԡzyE?"agJ\'ل* ЙnUeqBAj`в9 52Ѯ򯸔'zԔoVΌqRpN#Q_4;xwB6)SgB2`XJ;U 1CfaHj\ K[Ȟ.e#Ek_ -h+cibjaXF-C0lKE]֑zoT5c$"ٵ7+%ZSZHtk$& LLz(Ǣϴbx; ?]}c?]d:Ku+6)M/g촙-`HeP4O-TR 2y'οUf$S~|u&ۻ gWhl;|IdTZ V7+=$NVl8V7594jT0\;֡Y./[p:Bajr -=v$"O[u^!fAcȑt> -stream - 52a0$W\*vqK1dHϲ Cȑ!ΨW ص-/0@XϨL_ouӽ/M -zKyi!p~Xʙu7OdO50)chJJd+g=Dd'ěj]p3 BmZ-=(8s{2Ub9fW絪22IX|:^SSl+l* Wegv3RAUX G}n"q%ΠzVFN3ߦZE3zKfȑ@Բ ցb%@a)4S5R3y5g#<GSB[cY ,گRѿ|n1uoNr7YjY8P:/ҸWmEiW5~Π`X͗R ,js`AJ7_ -x@,:|+,Nv`&@*OAKu0|-Xο^'MM'YQ\]t -"U#LY(÷gm# Z[Г jB[zE#1㘱#(i({Y.f=D8r+9Taa)r.' - SB<ڹAT;^Ǵ6_0[SHD,(\Tmٖ-9>K;XIjMN4dj5A"]I'9\fVjNMdB N3ub5h'nMp&;S -`ݒE=!e:Ro7D}Ӏ*1'F -+h R7QpZ}~Kν5}uXDyAzʧN$GNNK%|w Q-`"DkK.|eLM#x]ۥr"TE/]@oཬ@m})_% 2_czد!*~]L:VcGfB&=URg#ƀF=kCU4ղMZٳan,ȧ5j籠}M;MxZHULE[d] Wn250W;Áew0rw=7A&A6s]jkZ%`īC5S,a9Yw?{} E<ȹ&׍cl31?7HOܙzj -pɓ1-"ba">NPyQ-wlOqMn|T]Lrr 53eX"yE-g{mtoPWC> -stream -i.JY`b ۵$fW0: WL~J' ~߅10\f=6ymZkݭs_ m'\b~}iSp:$L6ҁGC3"Q^wg`ݗEsy& W߽|0C'3LqץFT|6n@.g]vvk`UD/[CKĒG+6aFW"4su$ sWIll9|w9Bfe\MnY>64D?D"Qz $uvӜ!+`AZN -H_fK1nEհ_.$?W3S/`a!a3û$9endstream -endobj -3892 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -UҴƜ.W>X},s(a۽Ĝ9Oڍ;?S)aGVů"*avU~A7$Wo+"{o,ӷy'KpzvN,σ.dξNfNԧgV_b^x߼bR&4Πb!0#B_g")phTuC"`;YS/3J2o䋔 yہt/;a)F"Lnd\)Z;齘B3"+Jdg % -9*›$xy`;p8<%_D f|iJ|#_"ҷQ5ڳ~N|^WmC%^"崃:-,JH/66(9pB8ޭ϶gSq6⿩ZZ嘏PǾkp޿ 12NEF~n6M0FIVbED;g"Wߢ NrJ~AvY <ޢ5ZRZ.q!w4id\kd Wvg̑H?TMUՐ?/3eX&y O֔yAGF Pu6ȣ,{ ":jp_h7 O DAJcy[?EM8Fn)u_'Țv>/,ƷfX{-wl- 6Ω3h?Y*vE+U CcyUXXqJ9q#X_ee^:T|@Z" xДTП~4i}ɣvP*v TԂuYVDahaZ$1(~}# - -` FYiVPUNMiy^ _"nm2pjMQ G̶:aGH-l=H}8] 5&|J>7R̗֠,㇠IP[mJB1 -Ai~ Kl=A@`;grED.dZ (wġJ#p?U{Q\ P׉k# a;'4ڙ&W"S44;.Z#,fמI1(MnV )e8{M [{ 1Yln&*jM_O.L_uS)rm#NO#87QYȌ #idyYsTHU*N pua`"{FbG j|q9q쎣γ^4Wu=}YqSňsendstream -endobj -3893 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -RlU tN@Yf'?,w:,D(,_ P 4`dvNڟ(2aay3YˣF |ddgjܯh]{N؛aݪַ;'LQU'5%:a;nYVHؾ-CIsKa$_ RsX XuVΓ-bxnQdϪۀVŹ燂QA<,l_郶٩kh>XLrdi1&d3ЉwXLX#)m% !2 vF5_ 8TRT^CH:PRDq{[yU46͌hA7mR@IړAg0UY #y9gߍLja XrI+VP9dڷ AH4|Ko5PhH!CUm npfZ!}?t%QX^T6h01WN+9!YS9M^ MYyUߠY:vKV,j|GybjZbr"/,=0hr⤶eA"o3$]*Mb0d?Uq"%G a`do?[v |x2 lfDOuz2=.Qlˎ3^AXL; -Ru ՕRVM23pGu)VN¯B]f|mcWvYAQKroJW[svUF;T6MV>DK1Z.%fdLgU|m?v˼n JxҤwlBp&߮]N z=ے1R -F6{e]u(*7K Dh*tg΅"Ƚx,8(MrO5}x_̫r|0ڀL -nYL99RɹdgqH"ņ\%M`{ho_>RE/v'0S0Eo|P#}iCiµTWXUwMTRhb1Bv rmIa[zT*m"@5n?Y -HmTN4 fC 7~r71>dpVֵ߲`LCLq-H4?T5`haѥ _3Noɛi -xɊ.jy9*c1c/g&)jmksưVtk,|F~C(J.nwA2mάGק^r':x,xz[G -: ?|C+)]bn/[s -dmPޜR #Չ"FЇ܉ծMAa+bx &F9uY>X@OuEPcN_@3!OMyendstream -endobj -3894 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -72?kX{>>2 ^6[_*+7zD@р!R %&Ee}\.sD61GN49A#gh'\BP y!(h0DzC S"D< Rv$|hۿomS͜-)^p4J2<]Ms>Wp$)X{DIh.J?\\b@ct@75&@閰n% ],۷T=d2D@/,Re$;$ j=]d'g/D5)_՟FXA|{+ZdeΞ`HËr\ -PŚז ۔\`>vlWwdTۣ]F^.㓾Q;i -,.buC0\D;7z\ )\XQϫ5 -/xO#f]3?%pb6tBaMs+w -WLuʚE V@2ƇÛ|pof?R.O~XRF4j1?"kc"`O$#iJ{ !cy3+sd[ zնPG0 ֧f)lo bH(ş@Vh~ `-z3b ؞F9^)endstream -endobj -3895 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -=fB욂dq2F}f=&r(Nuɶ̈)ae[' c٘ -?jݫyy'˵"&SdP н2{Gȸ*~%8%ZT@=O0SŒ0xc9h,¸tx!{D?A2z+x/gr:Jk-t[Q͊uThk V1ݨ: 5e -4MWyLiY^3ث).&ќ"#ppNl#w:[5a+j ocDg{bsX?dhبDPlVNjUyO <1cT^FL}[ v[jC -j}|jsH[ ߮"_b٧ߚ }( o Ayy:_J -n3S: bt_1Ȕ,5pR "Jç1꜔H笋l -ē@eT8"FRR&դ绡N1Uպ8Ū q%2/1W$Ua&O ->JCƮح1^j-i%}]ΐ%/t~"`q2ZnمMRi7$j2.~U{4xcw {^,CùjGY^KcݪV֝鄯MpaClɓ2]F &441D GrEEQ;NPi^i}L|W H")TgT`] |1T'dO%1֩-F'8#@Q;qe#6%J設qF6CJ -^j ֙ǣID?me0k̸fN$9b?8w[tdԸcԞڱ*"ъFG_TO3S'w!25P^1|_msT uhbeL7h-&0T2.V;`&S=bq_@>\aD5U)uGb rc$/&'$$Ste;A aN{&D!SAab>Hoe$qR/g3woj*I\aEKS0tʄ_-Н>pC#8xA; 6t] XsYZ#'✃i1 D~>D9Nl7e+EKXOZ<0Djq|$_ʇ$ -W{wnyW 鞈6z>,Z 1-!ahc(*Xx.&p9@ȀXQrctڎ*7lH֝d{ -IRt?}M?ȿzh8ƒDt3m_th-+ _7$>}w7 1f>y ٕ_>.Adv'@Q&hIu"endstream -endobj -3896 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -Hps\;/8JYSɍZuH! y3O3<<2T,uk ivl MqxML yv2p P"$mԴ AEfWiy"0֝ۜ5.QUr9Or;%} ez/3f\IK7VмUo϶WuAy"/F/2>Fф?tDN~mKᨂD<|@h6EAh֮r [?\.Zu(R_-JG o3 v7 G4w~շ M/B -%Hq| Ġ;T%bh"3/;Y4i -eドRgf_GAP_缑 \ǧ6a-d#xdcR2{Vg3.ʰBo.ΒݽBؚ2|zGpx>B+ރcdsk5ڞ^iL-U۫;/ϵ$r]1}ZxQA PF~laK|po@#/]%L#e`^ Azش; \|xFDy/xb -I>irrp  Anoo6i/U6/:O1v1AI·X?ܫKQZnt gxpbFp0{},g,-S!h/L^<0I[uy^!_ 5؎#CqMy&h߮dgrC$bBO;`^'wuXtd.&Z6i%Ϫ{^&ii8+gu/PmN'w aiOB$u -iEuZY@4 -ҴC>Ewl-Z}!Zr(M Qŝh JWW'SQ}q}05j`''*FF1R_)t//BEϜ1;)1y^ =hɫ9Ax#} -w??u+.:pMb2N -IJ6%͒{YϏpսT -9QCbo79!=GGx -D;I]A',:3F%1QtAX̌+Z~mW?m|r%2 EFU QwL - ͂9UïH$2Sdwo{:ځ<127<:{z#FB?78fQ0a ۯ{V Ÿ"uZ,c ]UF&zFo8cP|dw;R#ahI 7|,j4PVF5ѵ]gg@5ipTل`ƫ4I2Y -3`}raɻKi ͧgKDO(nԖsێěp网՛# 0UTTPξ^dZ4.!{Kn M4;RGWrq$Π3nw024\T2EEp"k_8=_RB  UeCo!ot}G|E;&[~xxXQ<7EFgMs~m%9zG 90_қ@D Sb\g~.2 F8@R7 >i]g- a|IWendstream -endobj -3897 0 obj -<< /Filter /FlateDecode /Length 1408 >> -stream -jp;MQ匋UiEuSϘss(R=TmwY_`%){&I1eNdEh@YؽB3kp1 -,ZXT)1~{^,;9,'^?.i똖 -㊲^&7!1OTTq-3y dڽm0'xFTf?""Ԯ6zc]mMb̧%5~q^uvSK|҄8~XRM#&”X7FA|HGB@kXCM_>VIgTү}~5)&]: oK#Ʉw!Aڮ8^P8o"mK ~GE#'M<7a|x$Wy!5˯ß%thL ';OD2+-̻9&(;(N ]\|xQSo,0Ȩv/mHmp")6H6}pgv؊ Nseq{yTdf$gM5%`gK] -.}vhR&*{2m8^=u85(B+Bnh -@v}BiPּ/VTmOH_W uTr\ -X%`ZPݵOW޼;羻UFLL d,Ě -)/?0{(sڧfa/)xnGYq[X<@Qtn:F7w~"6!Go(cǔ@Ez07ԧz$x2icrÞޮ|+&70$5TW\eyȱi38ܛRb6]x3MtXҫ͗[X5eo[M70 -CS^Z\LaKoLChI\;w(r0M/| -WXa)@Fq50*W L˸T`[}pV^W[})Z4[NXendstream -endobj -3898 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -i1EcN0佢m;;"X̯qPNhS?\C@R"r?U,׷.rѼfU ^f!$WJe]4x"5}SEXCA$ͭ|QGCy{`y7 i}[62Aqi= 5w6y)Cx. 7E/yd٫]^lf`JhD\z;&HG,#ZCcf}‡cz$M7"~ B)X?Ϸbdee|?ؠPxg -TaURūRsbefR+wP^*n+=/҅Zjoy5W Ocnp[P}[v +oƒ ?Ȟ(hfdIyWkF/E$wS$h:Y7,[9[6m -a|fvxS!ssqn*rΞZ$պ}΀unSQϥ W{4? < q0x -b[gŷ"k kz^ ߭@6q -3c~[B}?ِ׹ UD: LlWٗ`GU?2"JaEu_O@upϓ h1/}gTRtcn+UQxP#lx17w܆K|m7{/Z`ww_t,@F\[~)^vOsٵ \ikZH䢜pj!ӒR'ߪL ϢD-KKwY4[Gw"[Dg}.eģ -ʋ B1QJ#P1\& -Lqd/Zy] n^JejUOJf2Y*G;,V?-1d(fK)x -7_JbC\ϦvH|?Ex]L^N:K+vmk^"1o$.D뮿G,Tqyu}Ze޺^0sº^riߎU1 oWOxRV:SG`s@ sz2!2"RS|AU\*X oeTۗ~%tzendstream -endobj -3899 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -&$/o[&H:Q)s]t.y`4EOs쫊ǙK4nhJdx>TFĽ]z#G5I၆M~B8iNuYRMw=MJ*b6t9 XTeai4RW#rF']/NQ=9^i:ᏦNwV)I1yj< $,oxi`{k$_Dvߘe"bQr5c-)um-|vZ>DoS ϶HG9-ʓX+*ic@,C)t#O:o3䠜HYpYWU޵WK y/T7*#`bs>|sHKE>ca+a*]dw=̃۶OPwizGb Z׋ V2,{u^=BgGxtLS)uv4)Y#3!0N2Ld1A}ms0.*c`.YbDۑoV(T[|:kZ,e׻RiilKx÷AdVqdb|-J,?Y*ʼnO67[;!/k+7R`Dm 9{gL7&&0T0Rˍ8F YVbmu04>7$. 嬛}{r55ejSFw:iiݟ;Xor:T UM3y!1BJxU4f^l?klSPfF;<%RA}$%D^a.-Ø?_-)AcV=џGObs( /JI@ޟo+rm>bW{ -yl kh?&%[бH w0ҥoa)l"2{88Qۘ dO1d9wbލr"5v^EֆPrFe(S~O'O?ѹ (XHvkZ&>it_.NrnUr/l(ZSpr1,Q# l*^SZbtyD]w7٥.P&ff/߫.«Gy=*Zès_+8LoL5mhgwـ&'8mm"2$}@{q!?ģX$x7*"m(Wu_9 1z=)5q5jKhG9*nz@2 InZ, oL/76elAVq (T=ga@gp4xܯ34NNx$a; -N`hg&5> -stream -4 >"ۖtQc=YkNxۢjYQ*`MrҖ֗cYeB|*nVnfDɝI/sORwй#RK,#9t1bt`YǙ=NjUEcc(CfC|>7N8Kۖc֮&1y e -y~:,?;DoѢe% -W:[?A_{R?Y<]@ҕJlyqTٵl$N/,V2o3D>IKg6tĩ.][KͺH WsB&v)hd[#,`O|Ǯ{yÆBLabբ#T%^vdx6Bpo1.ICu-V椟ؒLfNOpA$v2 ĆDmd,Ȏ}IE2?$CNmu P[gnm!CE  n0%ϟMdpTn^~h<Kc>\Mof\_uN,LGx_D^dt1b_}pX-hO"`m2l5KמDW:O|zGuhVn v%T2nɑRcÊl$}PK=ĺF2 -|u;#ׁi$58lj GS3rE7댣$ϲs܋{uj>]hWdۇ.v⹇}qοU~[W^w\9 -SĈI" h__۞Qpt UU~<촼ՔOluh"akVmɈbͽ:MRNH; 5. +L ߛt.kfJS"XfiPCSn0;8y -#tc1.!VF4b_/r P Xُ/^v/qoց1y6' iP۳ -"c:{U}L}EurΣ}:M`T}!v[BCVs.'\@;;O=q^DО0+9endstream -endobj -3901 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -1Go=}٘`{P 2'Jt֧,|_q%mx_Ba!*]؉4Zj2a3e/H-ȷ,J}kYwqNdg*V;ׇ HV'7 f w0%ii~$qNF%{FtpruofN2BVVwowmf="ɺi0q(g/U敥;6_Bx$NMdթ{9/-z,WS#U#B2BlAgv34V1E6}`t}O\DE:-< mu.NN}i'?s"p3D9U0֋Brw@M`lch"K%8G0o)3Kb1B.-z,© tF_Z670I%;2a/yOLIήv~쫛 -ڠrl K#^ WC ;L-0kAS{bKѵw>4Qh@. Wo`eW:qm`j Κ +7I`xjYtmMmwGt#/@ -|RWtҤ^`jjkL_Ґ^}p\KM`='ձhu XpS;{vx9KC6dNX\vɽ3*ki/B9 )``cySfݺp;FEW'^ kS{C|c\!iEބ;[.TڔJ=@|vԛÐI~kE@yA.>jfG:*O\KX.Os/6 .ثO-9$KJm.#amԤ6|E\{qBEuv ;h;NwĎ9QA 2L!s!g1w;־} Z^_vL@hI69608g*!!1?~|_9O(~%ɦf +TK+$i`iLQk|J LA9pERΛz/kb -,)[[es̍.Kg\?5 BEPqnBk4-뉤3[_݊IԀ#P=ã貦RIH¦\|< kElXF)E"~:/e}TUl|~h+2(:Q -O7VYrj|`_j?/FM9ĨR`UZv4=/kfШ¿ɔ]*IV }+s Mm4WGtXendstream -endobj -3902 0 obj -<< /Filter /FlateDecode /Length 912 >> -stream -A >){e谈5P@؟WJOݰ`hy;M/zcau@| `l59bpt6<}.0}L_C-4ڝIݥM3^{:鎓Ҝ#v *KVYy8j8¼<9,V46q(Cܮ>h ܬ.GGǻcazglT4ٰ-c<vOC߂Er.:gM4DjґE`54h-w8幱`ie8'U՘nQ:|YNL,rq*$/ZIpi^h^C}( Vw^ ٢ )}532@%S+J^JG؜~(Em6\l، rhut&;^B zzĒ@Y8Swfʷ/Բdj:_| m*\lrt9hv%rsSu9Rb$RN@EP68*jdendstream -endobj -3903 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -Ufi&M3)(TTBV0=Y6Va7^GM -m#x-oXZ_I(C{5V[p׎4'. u ^Hg%d #Kw.OHZM'Mhy8>?9DEC.,\UHk6 ,n = \M!,ϗ酸&m=9HINN]NQ̔|ψ. <ɜ$Bl'Crf0 c [\LwcZupwDVb}<k~J`>7VI'9NҡؚWaT - ߵ};崖3:iXG8{!S=vǕM!η~&% O2E?v trn*#;G)}ASi?wV~>Ip^GvnY&.CjݱLԘ^P?*rY<0Aպh#B9c&- HU;p>ei(t1RM\8X`rh.]t-S+- -PIǁW!c^5%"t5U;W̼k0kKS. Sc[.#8D)3wX;u :Pq>Sxb[61eZHmJܰ7IUzJ$,BA˥&|#'|vp]_ʋ֦ryc>\FEF(Og$./_G%!wJFߕ 64E0#/}Nk1(5Ve}[a=&σH极SI楑4c"d&^=}n[T@o=\k+I[^]|(K?u5i䜣n썃8 -M{ĴR22v"菼g~Ti66RUS=|EQg = x,@s@u5cBy~k -`FPC!)V:~a^_y9j-÷nNG8+W yuud;:LS8vaܦ%6 ?ēo+ :P{_bj}߸fJØ^^N*n`R ﲤ9F^Zw_Ǜ@ ztgѩbAؔ87|EɮDCz}*r>ÙnX>Ix*&rǔ4D`>'N_"ycfbx´ z\LyBS7ha j03H2&`Z' u.<@p1~] DAhQ 5X}i1I'ʧ RbFl]=z ci# 7MSU e !=vA7`@{!L2n#yeVIy~AT> -stream - , N2ںђQ Ғk/*=p=x&!yckDe`"tJ2=o-oR\'xkhkz_}]FVR>tűmwkKOZl(- @Uf5 JA~u$weVl0w<=j >YFMhuUưj]rVuYNHs]u0s]U M#Wpfi3rR$ƌz U}x Ƃo-F>Wɾ --kW; eXQ˻N='FCVQT` [32i}#gȒGStt`d0 MbXk02} _Cx'H&*IJ-7G94MQ ~I@ăws3,5)D+!<=[e(fANɥ? !,I0&7 -;/މϭPh1,CĦm -8DniLD_X(’HM*>s!m "lϠSPIs0~&ގQH.[1*ao=fhʺ"\j.N?@ -=_LU[.{:A eLQ+t$?Zi;L7y`EæuֽN{fqEDomp(s@1.⻴flco+{BG#sб,=@UW&)!DzL* -`E*O1G'N֮ٓXyOǘU}΂N6|17UC}yuN{=Gw7=g6-&y8̭}W'-{ȖAsߖo=M,g -oH`H]LӠ4զB`\ʣ٪7c!']]@~>/\l!S'uUw*ra}0L LvX~NZ?TO顪ί}Y@3 C){3Ki𛎢fk9(,B? ۓ'c }ҊE&Ia(U.<ޚ-~,E,C{ HQBF4v2J+E^^؛Qƒ {&?Kf5yuxX2Ev(KpgCPeӮ>XO$pQC\K!,r .s)H- C0ϻ qˡa]݃if 9i?f 2nLlS -7)l.<~Dendstream -endobj -3905 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -L2O^|ϣn#)r7C;tEܧ'+&.Au`*\" WlI|dL uO}3FJ<^'nD-gXk_?$nvbCB;7a._ ,$O 3H\y}񻜞2d(*/X[šs)8Ґ Btp[T9˥JfKrxrALʊɤDNd6ԉF66GPڬxO'т`ne¥=;y~,/r)mau1[)yDplC^)֥Y9KL$ճHM^NQf56}8/8n0VR,#bڑO{xT Q0 ^=ԟ,J_`ƊxQs,JI{qry2}0U<J[D[qb0+KK9Քp9CXnIG{sq0J9I9a=UN+(q:JAeI:ҽV7ի slMeܷB1y}iR@:[Y2K--C jy4Mf l~Wq))F+p"]\b5ޣha?+=.FX7e/VQKZiD9ϔl NFj1FoP f̅YfsX{$ꑴs)z*۰8_I)al2nW0 -g3_IS Η[nw{rwӞ|p36ǕgOmP Qxj6gZPүlQ8ג^< - -# ]*CEq2q}<E c^tAAUy?zg&}ȴE -| -qQqXP<Ŭ'BM,W?(?ݡVWcRE#67# ӼDP|-C߁QKMiL|JEyfiI~"@@(Qr32e|\ϵ=3j-M<-VC&M$jg4 vS?_LD/͜x Bߩ$W:Q/6J:\d/VқwsuN0 -Ik  Y՝V#l ObZ u3H!SV:m^%3wi򱽜FkF\`8#3n\TiF_;$͜",n5&I! y$B0(:83'buGՀiinZAoץ߼暛@ -L~ s/6oE$LǢ%$D"; @صLkR$i1T%877.?D0Ɠ#<$uM }k,,4t+Mߝ9o#{V߯@:\R,SQ V^,j4endstream -endobj -3906 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -"+y[K\0pޅR:XȂ} -ԱExy2b۬[A -..nF -6ڕ^Ju)~IgBzxd8Є@]y<`=ug⢍]3ɕJqv&`lFi[JԳ&hO_v?v!M쐢?ng툊>F?_Cqt9d1YtvAN]Is.a~JSt/{EM@9͞h *m!ĩ2ul!,?V8P-=&(`K&sa.7,cnU?a"P<i\%ttP noB8Yjd[uHA2[3v9(Ӛ3.-czb̕K҅B d\ð!{l]Nfh;'xGL9ݏ ۮҏɱAI8g.NG_Hk]hO KSxmnzc[O|UɘJ$}D-bDiHQiK'J݈Q,З>P\F<7i#.kJh*V<Fww%IzvɚT) I~sp9vZ0# ojO UD5xCWIF 8%u-kl)sSک%B{t(ޅ Ohl4:f.E Q5ëDR+$VRƇt|8xEݞ0BS7Z5Iu6xHXN}1'y0P w eOO &HBMtcylj:$-^q&wGwo -ˤrެ): :vgvI> |T1#b`+_~uTitkJl>]'t8Ɖ0vďbқ%JYER8H.gmc,R `7ĕ+-rQNRҌ` 3jF3ͦg-Orrs/lNDșUdV W3vq&t/JuUl4FBݝUh>nv:dHF,M|$!.A oHxK"Ɠ;u}3@,B}] HYJr7R"s|-S0JPf!Y]ز7W]ʞX&x߸CR +L/qE=V?qigJni> -stream -܈5I\l0J8_[`+oO^e_jY'>:+nCa·5).6MbHMp_z)dB=73Z9A##. MT4SDpuέho(8 $)&JT*C5Zу=Ǧ?iYK)$IXť^݂> zw_ԺтTW8X+>Ih3 D}z^Dc}\"Y'%C2bĉƔ!Zv$z?g?蓮!V`<_J8UIQUwEZ;GŸ=|7[jDY".Ism͟L_+v; -l&U (c4AB1xDv ={K6n8zcV;|J(Z!LJEI -QY *XDγzDl|KFsD ha趪?',N mǾޝ<4OްHzF p2ͱVc rQE ԳU} -j6m,M? Elwqؐ߬4!|Cˤѝ' ؍7Vf̠&{$sVJ#Lp+U hs+I`$dUW\6ght9F}/5VhLie1 3$mQɀ<Ս8duR nsV"V~S{M/&$Ң$e/FP5/K#_A#`+&,"9XQ1ծ"2۹f\k#Gbmk) *9#j6@T|vS3::a:2eʙ8 Q9Q.gWtf|tFO339wd54M!mݟIz]@ ƀjEx!TC 2,nXa|d+OtC9s XoG%ۋ续**p43(&YְWՆrHj%mADá}AsmBH9gJ(ڥJ4bF7O+jh!>^>lK.(8"w"dH/k߹P thAĆfR yjG^3Ҁ_|G~W^>^2'|ҳӧzg6%; La&V/5}r #1;54Qdq1&"Tzk #Œ@ɽ%rޛ(q@ ۇ˿5@K8iH665ں\ O|;^%luCs5ԺaϕmE-Mhrkf}r$h"2$R׶?!-HcA_VO m*G% IBSr"ǖ-z(_IcvexdÊrY\OJR~ƝЖ._uo$lZf#R79xt$&V c֥Xg0!uZ,&YGS@}׵螆elo!Ë.;}IPMs[jڿS;h#_H9yAzgl ׮oH˦tę4jVo>T%m#6Cl`/s9"%Kz?+3zZVWMRb %9^#Aendstream -endobj -3908 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -;alߟt0kŋއ@*Pc5Is~3P\b*5qO=)(&5@ͅV" 3N/Up: -}rЮ_zͷHd>|z}RN;1z!('2H Eseۅ K,1F_RU%k#>,mT/% @& -ZBuLY3lM2V-D L3*^p0Zg^A.k*ᄍ|=.FTFU~ׇ -Usbġ{]4hx+w%TD^%m 1yEy` 2FQyf#}x Lp[˒,Fn EOb].BqF5d]XXǷ~@Ƭ (L:< 2lW A]X| ʞ9"$;Y~$oGs-Le`x I?mw -AJdZbsx;9 XJϋԻ&Z-HoJ-_݈f1[,Tv$tCD8$TMzjn@Q@BnO;"ABy$HIKȳCْӟX(+Oy -r> -!W"y'`rhɓv"C !(/ ʼnW&"hm=tJ.L{I++#0^@$TcjnPtp7_煣D -Z&QepS?t2o:QŅ=6 -z YkUO} {o&&x!2V +Z%S]Vv/P8Tƫ,Jq4I&_&D+ B/(kWE]oR&`9l`W %^*윪^ZU Y{ӼFjd<[~:cV&I"6y?R1Т|1" 4|nod.@!h.bd-{GXthPB@"LqlՔtm-q\s4,Jendstream -endobj -3909 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -—"gd VkQyT 9zu̢Ѧ/PVH??yV+ueMIDm3jxNNv 4[wC'xG0+/t lz-ѷSb9)7s lokqdʮg.ElZ;zR5Pu-WY7]p I}A 7Y ѳ98C,_bWF:ۓUۇұ=IiɍuucpsDa-YZ'T(Hýkk{mQR8NjKpkaw> M&.3X'BEL~bcK:>Vas o5*=:*mwr"C8:zu3Y+f2n?aDb$Yq*fĭrY}P낝WFe;[M.~YN t{=d'g&Oϑ)/cwh@dɁ3|}RX=8dQZv UMHV]֚͐dV\~laI?M.6jv[['f1źjKOsδYE po{u,ٮIu2saQK4XqǏs79/v' jUfWHL<J|2.2g\2䧃^э"0|ZGa@hpZ7n CϋֳfQ?#Ej_0=觠lȖaYʾGXљ* -- I7o 7^O!!p7CІ`mf$ lŧCo;0u xwk+%->MAkGKw暱P 4\[K7+}YK:x|pwTX_nJFeL'n!ii}$_@B;ɍTX(iHP04y,T=͓͆c:a>O,&^ഞ8ܽP9@qlONL'Zz9@,v8Gwv\a6rӽ3Ƚ%^ m4H+TW~r;h> -stream -@!H0#BpQ bn% -QS0g%X65h?2D5lRCMP9+lP2MӗzHtuIJypE 'cXd,tu -ǜwtU{ -EZy,L LYBP|!쩢HٕzU`4& RpvFNcukj$oߌ+-<v=-‹: ~0O*$h!i1v^`WQ|,zS[WN0C_IGocU&8݄9B;Q> ni+%𴌰&Әp7t !*WzGrWP;mMhtjX؝A!$L5(4@_Ll·ՠi&ރ_沫nhr著F䳚?kU44ɆZ$& ^m_w$ni<Jѷf֣udö?ƈ7G^Me)Ƭ[2ar([&g ;߬ 0]->^''*\5MDJ(M;:s ˂C8~eNDZK1\yzƀX!:-)}#IH fJ7EqΏ^fU_-iUa%XtQLCgW.x,pj{9"nkN*)]iq0&(zħ\OYnB73, -48EƯf-ͻC+En`L֯5&ŶjpW+7)$ ,{N+hҊZtoF(醿\VT4]8H{4ì7L82pS TַE?}qf5H3G>hJ2wAʰ]K2Jljm -&0U d6pQ~D;S\R*afבϹHW[Fgp`񻪒p #vwH>S)Q5uVqDhIcf@i>.\Fb`K!;G 25ҙ I鲀 vtu* 鎈nJ§)hPD4l -R -* .Siָ-/>}q _%!:lwl a.g]UI2k{Le!6 Q)-usgm.ݟꫲ寵7ŏG -4@ۊ;Vd'Ł41 9P4]I]8kYKSʵŭrzhH[a=ePvMMWɫ{AEBE/oK;S4{XT,>=1?ޕhB1Vk&q1%͛n!=,endstream -endobj -3911 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -:i*LTX_V-帷77S9d_3`v> *bL{`:˺YV=[bSaPF>*÷[I'Nb |";T"'~aAШ{Uͳ]seRdNb5;2 hJ_& }A;k( 9 -#G= D]j٢^*θQQ 1_HK\ؗP>"^jR KXs{}6cM>| a{ˉ)rzQ?No!~jȬVMgnyo7%N -u58:A2m7-PXj4I[D牂ʊ2=RBf_uZnu2 Q-7}U}S1p+olĪҫiGUWjUJ(Z򴌞g!cn Gn`xVuG{&z{"*ZE..n9D&n%3꟫l!MqC\iC$ 39HGb:J'jGڡB1'V^C5kH`b9no1hO$]TGJar4PF+l_ixS]o"A&R] *z&=o}U=-=c;3zvb<9#!ģU5̠\"4ǍIjhe8f+;@uecW錋KUz %8Y6G?cf]<XNInF ݱ)z3X-ggPI/S:JQ qxkI ^/№sXYr賅?]?]TĶ#BXJFǷ/ b<6OFSL$am資i"'2U|={m30j@k>`(xBm|=,h~+LB(u`g+66&_rTpAk:0>6P \=,S& {džzPüE|=bє -*kE`›;ֵ)$d1_1Ĺ/F?A)#j"A@i-cKD);>(̧L#q]WsCNhZRS;~ճz{Y2-NLZX#쑈峺vTTcA0 k.k\*X8_1z(y->5_|$+:]d}S_T$WT;=݌'T8oJxe5/5u6!r#d8Pendstream -endobj -3912 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -*P Bx  r?R צTmz=yJ"F=' *4CDqA 3o{糇aL]cStBtZJ霔N^x $Ag:]JL^1l7a{9XKig }]9O;[fawGm;=@1i$ NjXekчCyZ`2;+ -ܣS[FǀtnKy>5wg_|]$ 7.Icl cAry]SMY.ZF2BtR*h@_F*;@?:CȶtQ } -Hשw.{}7B|AMUG*4O֒?Ĉ;[ፐx0^YBh,]tGQ"#;9:$V6fwB~&Iǁ4x" ڠ͒btAXr[P.E-%ib(mkB{ހC Wc[c@E#m'D@^R`4EL(պ|]٠ߺ,g#+4w=%:)o_oE90_DO"Vj̸ ȍvtTy#_&fo[|Dv3Br;;V\':ҩxRwƱ,lM| ˒BdӚGHs{VexA&T*|aad A} [۫yhRwK^2N*bR}fe[;AsۜX ƅ=c+zALfwЬ{,U UWBMn8j=$a#7f[9 (nplg@Y hS$Du9;s?s-B 4W]TՊ3U-Z'm.Sd-I~UR: x,Su5yܥQvסbjӒg0wSN[Q/Tm%(N-k>9p \$R< L`vETXz8=Ih xҺ]E$%{oIZ4{[tY2(Is_vs.]l1w2rl!OJ \'U3B;y -z1?$h-__3?s7-X-`@"EF9}18أ/(Uc|J>Rg$ =| J@seKƆ+?W8jJzNC5$nJ5wj:ҒE*z\ӢaF0鯭q 8&) v˕7A'a/YbRJ >=E~!mzmrE~.uasx)]^e4ozCPYo*9~\ w҅ EpꝎmd/+uu'IY{;_;Kz2QF^Lٺ5U&jklZ&4r_f΅,~ԧ>a. uI$5^0ݟ H`(+r0RQ H}ER3N">'ZDEZ@]ԗܔRz0J<1|L ͌`F n/~LfdCt_;K-Q 61K, h۰Y.A6Y~6[1W6Iw:3^7 ({t*IaP*rc@ WK`NI4WbB֊]yfr> -stream -BƔ/f3{FfVD<3E> σG" -ɋ"@  gT;ުQOз:&=tQbv3 Ap)z]lxXMVGƊ 9/4we~r~3|L[rxLKH sx~SUm=Hىl-/{=?[Q:Im8=UJaD'\*SpT3 -7O3>JHs֧]YHy8YVl0VGM̂[&$;37qٽIJh,c_ Ehg!Zf3 |}1}rZ lJzþ zh!a`+lNURtR|um -? -/ɪ"*M2+Q)^1F鎈d -闟^5x-JK5@19fbջO|tdޘ5sfH!|I}4Vbn -jl5C}Q@`Z⮅9aHRW||Ĉ'D -0PxЛn4"Jn;fKI-Uõp'X[^<'R>?yM~A ԋ$IUm*0: ӫsA?s&!ވ)@]rW*6jZm,[GC )>F\H"P0#9/>.s }TDFr}BJze؂vo\GN;\^hWsIQw%\ck sE0税-&hM:Ai$|Șce 2"T <-ZQ4JhɈn s:5O dFI#Rt.F^ wx9><I|(1ym9Bp,I`si^7WD@i_R -9GMIaBcNz^ԇqߦOW;xx{'JQM փs KK(/_Yf3_41S$#yrT6xm` ׉fRP!l!HRa,J]@XG=YmM<7?=qr5NF FI,"Ϩ_ѐK}X'BG](ljtU\}?> -stream -LIюL K|eVPOsꞑYٰ5l)QQ"BH&N9ׄVLԔ?ڶ#6sqNBE31 bR,L즈đ)kТabb]V!^{nMކ^PK3$5*ifxї!jȘS ֯8S8SšVo|cϟWU8_.=^.l$<-wq(xxR{X ͈}y.[F򚈟X,<XFz\n+=)8#%2[>0ndze[Puu 9Z4dDp)/Ce}(K[6۰m$SF̫l}lUU.QV΄`ܼȓbW JB̀fc#/WNǬLnҐJ}]CҋE)偅VS;k qK|aY% 0Bvb:IsdLd]n,Gw6} YW޷uwQ md$#,cdf\;>5zpC\ş Ʋ?ofZ rN\w`)jup1Tz LScendstream -endobj -3915 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -}k%5$CR-GlųǦG[2MEsX/%ɳ gR $j`! -$Q72ɶ -Mh#kk_Vx{~Y^N*o݁'i>c頋h/>Mzv0//3wOa)KWQ 0gM^;p !<"O:u=-dU']`PviTY0.p[\DOlMU ,֘TBdgJ[c5CQ` D_0x(N!穂pc,et-s_:Сs0rLA!GV7{9Cw -Z' BY.@+&V$5nS4 4S%TxbMUuNӈԨhӬ,x1GEdtrpsJ3|o3䊿`2B7dP%]C-,s~;jgޝQfBň-O -̖')ىy\Zv"ﶻ}BlpgOO[(q j3<ۧDV<i$f&9B_~h|a -Rlh(4 NןpcWN -b w{@NQ!pyƴk8Ē;J0neDgL.ls1(_Co@NAӀ4뺁q)0 ~fS-sdwvW; hˏGYVRnF1 d݉r<*ѭe)ȅSg3*>6Y NuWPcIŨ2 |Dep*#8廉~`z>)ː!j8}][V 9Ng2|ғB}BZ\Ftz¿Zt:0>fA\r|0EEnC]lzJݡ;aZ=gT/BX?WǐzJp=_9eR6'HEEјx,jq -5opUVZ[ޗMJ@gM BﻁR7%Chpd) - snnmiO"z'k&[eoQ˳ĥZzR.%vG0[YPaAa -r(47M2WFq;'$LDm0':<[=hMM?p\[ 2 x:T?0.2x endstream -endobj -3916 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -,&ɺ䰿:I~5 3 SXtq>{2L| -;7̺oڕhP[{qM_v8lܕJg=4kؿ^um̄|eW9,Ix$4v}Z/^ah -^bq"*m_kiI%Tеxnampӑp~n,Ab_r(S<dyUcR3ܘ+:׻oG)u%x,O(1"lܲZJ6RդRIq ^kLʲr踆Mʲ⬮BXIъLCsW_}u7U2 vTًfzo<īMc܂U*UxvbGںEuH;2LLO?`G09c -"ag:Tgc-{q%Wr*)DF<:8J>3w)H/бŠT%AT߈ꯦ-MuI^ Q-(/ \'Aik'<ާ#N,˦>hW"5/l?G d)mY;lYyplqpRs/NF  )8(0ԂewK_k ct>fP͞ cUIm[-U[e~ls>K+1t4j\UBr%y:gma< n"ٜ9*Z;n&)GkPʳgDdcZb q|Cn_tpwibk5!޳oG\0݂u:Wsy j0XDiCJax2v3SA+9/ȰǼ*z%=6h^C/U,] \ҌlD:m+9ϾJފoBA{h3 Wm1d3'&Ljqw%`SPҽ -|pt@ JUO/a|iݨq'vFD+@;b.//tΥv-l u=]sqR*md-$V,ʎl?'c+ZWR?#a&CEzL2=r Ƞ&w̙PX,*byHʄTh[,biB2Nq0V;IbG]3O"ES^?5r"= m,#ո?dAU!BCuntp -m>Etl ? Ycj-B26=goE̥UZ-6qIP2=S^i5msE>mwvE$31Uq!5wsE8žwGA~k 0EDݘq7w0WzЏ:Rb qF˷A+[) NOZ5~bCelFr;/#n:lIvcm֦G{! Ӵ3Q}T@_ī:LS3p>'~F ƿKI>:e> -stream -}̖yHA-s,F+P۱RJzF4L9נtlYbɮ 50B >l=:ڄ,Xo;݈ɎuU#W -B4[l9Z 6*;imopdw ]HwQPMzZf7S Lj=hӕA,G*L+~F0>R}M'VMYc&bҴ>UHeW3#P^ ^e)ׇL񍄛S'@8}t8]Fr8Ƞ`"]Ysނ_+n£_6`Uv61ÿ3PydAe&$5FT g=:*@-#O'ɔpwz7GBA}ST-_A6%'?rj9]UʱGqW$:/m3GNt+ 4PDi`IVDy$iyr:AtȾ6R7)HjF9`RT%-<~i;}ǔ+~i-y9wOujKsV$\:o --ё@$ S$pX Rp?:F'`~ ?FCtJj{=r)iu@BUKcV $>z X>T )m(>UiW1'm{yĕmB tDXe֢ -*rƛB^/XzҪ@NɶɴR""Hybㅣ2$tt✗D~FzzNs-~^Q^RsTw2DeH%l>[J;s0!op'ix$n&9_䋒~4cי|@T<=`(BV>4]⹟fPx1`|)i<6T=x5th]w8 em#X7HV?QUgKLѨX"^Ln=w|Rj_^MrdzKA5U2G:bs.=X*6قaW}oE;NS%ڂAYa}0wݡX'{5W -M1tfJ1?mx T*0`G;^ُB.q.Sy=5≖#z*?QԜJ~ & +5233f"B ! @PsnoQ SZ{E}ځJ63li1N v}ȹjz:svG_HRAPqM^EjȤd2WQh fL2c,+-endstream -endobj -3918 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream - tIYjy+BWKڲH;OnӳE@+IPOU)Y; W$>3SFբbImjC2-Q)Pf4E1Ч"3f28 -jq,4 Fͱ?U&+IcdA5mzjLTgeH~5dy\DSHIlhԏ0 ~г;U>CeC^C1ט3EpF⺇F-IJY p[}EOveA찣A+2HgLo)&II -Sz.fߧ뒹֖q}#5 >,E @(tA9Kz9)i?uU r. &U.ՏPvcCMp^'5 ޠyNR<@yW.07*g#Vdeiۥդ Cen8M`ĬSPmmZO(K4c4F9 -X o>.0pK+Kr -B_`kwʈC//LL,^[xGmSXY kWfJW,گT17}SIAgLr=l]rdNoUm ,,v_]ITrJh"z^Ye@xьez\^PCi.tSR9AoH+ JlwyttM&ߵՒBm[c(6FʧF@#O}FsxT׻T'sʭ$Q /[#Ha&xxNcZ̩K9FQ4b*\|bYsp)͈djo]iZZC?.M:1es.l<$6mO'\ !фWcpDh$`(Y*h%y}(R~DcM-ܫhPY+r`zcwN0Ee+}5!Nf'owAƥrhkހQfڧ>L%L"mVs:Ix陼#GQD.L7kF>ǎV+$L\] l@#ʹR%]d-o1TIBծaDﯞ!,v\wq yJ.އd\l*:7 "-v*d GV'PPIg(9hxS7o X_? 4OcKN{rkԎKhV/3Q6r'HD -i|LV030aO 1V@D?_A9ޕ|v?[4|m37hq]rJ.S*ZQ`qi3d[A$"Mcl::[?‚\3ni.~)i> 5wh&O&~x({\X -1=oendstream -endobj -3919 0 obj -<< /Filter /FlateDecode /Length 608 >> -stream -{z[4,&2a3*-kNf"+p9lT׵¦0>d27xv+; i= n|lcv".JZ/4C\Gt{jS~A2HXw*ȴ;z~vNJAAG/3s^J)T뷨X(j1w7Y=tONϕ 0%LEÏZHg&|m̼zuf2CѦtTzBmnY 9 -|PGC_UDߋy.¸'ڳFo4ʱgD f𨾡;j~a`Xmq/LAǿȪ=+ -*M:ʔlV~t-Qx]wyQ`EQޫhxLXp4ـG235 -54 >f6 v{OE]:;Qq$,=+- -7rBn(bwK*Hkf,>mpqKGw6px6+#݀iB*)}ꆱ{Xt:0x\7XfRfedҸ"=MA55j|]M-Jendstream -endobj -3920 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -.+o4m5• 7ǐ:0`pH45xJM b.jƮHj@6X랹i%p>] ,9G^ngAܵ[減*¥,M'qlP!)W֧F.;ʛњKNqM|9M4r6'hzF` 5ֻȣu41m6p0I"4_[7Sdn3C]C %esfXH$} -O z:H$)&PaḿTEP[`Y$ +:r ,3\8NnΦ၂ݴx0HvWo@`}SG3?OML,CQ/^_Sа{yCK١:vm{ "4k #:;qǦ[N۔Ϟ|f_1~3BCO Ӿ\H5ƽ#.T .5gr}N W;OL^wz+XK}d^]JN̙Zl(҅Zn|wMT/' OANvց4[橩XE0+,}F̖|d:l]|1švwJL]&|SȿGqݦ ْHVTBAUD;UJzBԡax9} -5"k'" $MY#]ZcUVds6sT -͗>wI򪽎_׋xN1^~JvYpO]F:[|43ؓ"I=?Yb=9 DXxmCUdga-6ZqNn3tfVപLkjIRХh y.u,U5>~)nYlƵU1T?ONw,v =6%TvOHu5^9Ph^j1f'kls) vr0ub!?M.DNK̊+Zﲶ@4,{\_:꽖wAi8I&.TGcއx>3Rr1˼Z5s*T݋ u]`A8;1y ^`lxy>_E.a$:B|Xj~ -_Ӛ;:qy\$TQo+0)ݕz4:D"HWlAp,\endstream -endobj -3921 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -W)L[6jGb}Üic2hXJ`]JF 4UP)mU3|//t;;~rۻ,UK[{)Z7sm@:;YxRk#c2?kYw\@S3j cacrp^jHNwZ(V. 22x5O4}O1WCEB -\1# ?`(4}X.7Qy#MmEMiMf շ^)Κ%l{vLٟ1GhdKGE٠Jp@!q|+EUL<- UuRB P.giQGMY+N$N05E'`p,Q]~JV`Վx -aiԕ疛^TAO7yɫTC'' ]hDG[R>LIbO KFJTTr&zIR]<VN^FiqpCmf},U\Rf'pw.S\͉70aӖ$Ip$вhz0fsl -zGpt+t7#Y$kjy"K&exX/3 |'<˖D3&p׈na1'OUS" &,J7 [7r$[d@^ȍ?5wc0ڍd].::pD?wwH2HyG=uNtXej o3;TIu6E>l=r=LMx +[z'~Q+nO_#aS%AyK}Ԟ F& ȋbܚ:50I] -Xan9bzWa wJɺ-xMcSa'6ea_'e3s*YOcLA0,p 'f-('۔&:;IZG~^&)b$YܯJ9>JR)Lv 2F9bD+׉g - $&3lVA$B꺦UX?!D@ᴨKˏ|s;x;˛5ߑT>Omj={s #5n-56Q8^t#l$sS25{>֏^g9V]OvLQtkr --NxfW7 -LP&fXJ׆F-lUN^g8)+7"`Fڠ$pNj3Aendstream -endobj -3922 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -נβ'1jX7- -.ɐw8~ -?F|gjMEA& SًW"i>l6J ] Nb ٺGrrΌNίślj~eOJ ?m܎Hp9YUa,N"8\pm-V妷e?x}7_8d r5Ѥ=@T]e`tu5FL?}%b0^qQLL z~r"Tri2d eG=O֗0)м6ܢc:B.KZ fKCš(()̵1Kg޸/8̫jPiZr vL1#/; c>ئ R(%kA"IDKc{^Q+81A - cs*FqvA^5J4r0au$y|_ܒN=6~^HuhUP:dA4=HEERҽ}麎{/LYЙc~vٙ4O4 }HX'r"LlI1*JWh%dY6]bf%oS-iZ]'χs2Z⻅ntD햍ppjqf#{( =`"#7@; V=w3ɇЍf~⫍ P0F~o\ѽ8Mc'#jYR<#dձ=wұ -q`Ozqv<۰o?Uhx>u߭,Cty,y/vYh$ , -lSRf(n~n=6Hrb z-bFS Z2DˑL% ͏ɂBVV +`p`5:yՃCI#n2Vz8~3'(!([3!Ruh2=70 ܠ-zVܤ1變bGֵY|v]#0 ,ӅlBA|nЭ*PREfx#LJTLq4;PT HrY5+yqyAgo+W_-nצQp=1Aƀ]G&,dzendstream -endobj -3923 0 obj -<< /Filter /FlateDecode /Length 1120 >> -stream -|y8,I%^t&a?y!r.5C*QID~aN2f6KxFK+$nAc# -H 2ynX:64I^~󍫆`M3폢B1qFqn\|މh<#jZMpJW":ؒ͹l"qF/W}~:_P#7%P6tH8|}3 RiԔS>􍅇2yxDoմ#g֚|#a%Y9H,Mʙ>%QK !?Z;jnOhk)jttRv,˽;7s2I) ]{CRV/š {+h -xdqY4+R;nUșOj҅3e+A%JvE+bN vrZojzaEQ^c.,TIM/u8:3؉5+&ު_u 31)y/Ki8%(FbqJp6H(W^ԂRendstream -endobj -3924 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -򉓲89e2qUo0E7QtQiA6&OHq`,u6 vej+14gE <F]=g0;TmyX Sފ\Z 9zHl(_tb;0{ybL6SS2?"6VܭDrP]/#1[۟{Js W$ygRVˆ{M u$_kwVDVi|`+`HI[^X{X/eF~63>% % -&JIGR8+;E<~a{v`L᷺ Z(#!VvMs ; {4H~084_|U\E[;"ۿBSKvoxpVI'ye9JhM'k#mV^mAmqrV!.={'}3.J ޥD][f3ɀP[njrcTs {;eB10 f5qߥAfbݚf|܅L p$~G)Ëղr -];D#Ad"o$r) 3%3kyFA`K1 J߀T= .&fXVLD>gIfl,F -|SȑgIJ2_0.«yo!oD9^ŵ[Ay= -d{Nú->99&˔E>5UmKl~^Ĕ\K^]ZgV$djDUc --%eghȦȢ5 -}4CUY9_oIsRQ˧2S}H$13OIyv9.äCm ܸe).tX#iS@!4{]UW${]E͏چoNqwzWX85Vm@d7'NY.w] *#c%'dC\oj뇭=@!'H šlR P h%KS5oSg Ő+ -y‡sO$u߼ZXmTmtвoPٷoS$Յ\u<pD_VHOo]n ߤ?WA9!3mvJe;"➂D8cd֕ "@]_L*T ͺUeyNO;QyF5O'eQF SMǰ-)6 3lEFt5sX4IJj '4+ȗh<7bU }W.cgou`߮Az{] M]Yv1cVP FmnRG+`^Lɲ18GNkRD)>W5z9ctaDyO!5^2&']&DI_w1P$x"wX 7987Q+@DA; `"RVnM"-.h7<U|ĨӞfdCߊ4.ݒclBWյ!3!5Cnfk %ZT9 + <g5s#Cwy OmH t?ʩ@SVHͭ}(Y{؝mVgendstream -endobj -3925 0 obj -<< /Filter /FlateDecode /Length 1296 >> -stream -g=BC蹻!Z~ -پwhI:ƶ책$Z㻿k6SPIʾwX[lmQJDCS4w-4%)aPa i+/eh X? aabvRS6f¥FF\ѩczy< iM^{ {y$!\3R0Pkg]J0G"z/SvF6ź&ztLܟcF5ا]  ׮y{;4pGv}zB.3;?P82W :UQ.W*U'ñ"5",?[JO\kvu -Z_;{}NB~>`)w%hq0{Ov7Jr`z_ -%ԞT_ p"ӱf -"YmOy -k TIekr8U23=8e_ئڰ*ڗ3ÉcpHc-Z4QyEg"[X(QggN!*bI{UlOƾ*1b{} wI[>5lrJ֢_#{aU޹7j!=d4nbl_4pG>'ش~G1hd\At÷,vT0^kmz]anM*'˦8Rٲdr sZT ٕ^ { YCPAe߰ZfN52u ;8pbha_)z -IzKB𺊛.gR{Tox2Ujnk'uy3`Aט -[\\S[˝EGxcϛ6YBʑ,_pd$ ꪱ]q:Gq]bٛ%(a4XOy~lk \^D]jh}Zŋ  u2'da/nR4,?J OʧtJٝ<,k -w -Fe?T!^/?rVer4D/qhf'pkBؗ8#dby")\.&a;%ezw.$?]=jM\]EAg6Fendstream -endobj -3926 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -H]Ni"I,+"BH -VwC˻Q}p#P7οz10~Hm5jcҐxy4s 3t77 ٚߒrhb26hޔǧ{Goki -;D?~ !TA(0CuȰ .+ڝ٧-{d2`]ymuCgT -w|@2 jAm]4 RDgIZAά!dդWyΙ0f#5xRN{9"9:u9N{5LXhyQ`G7ágRhgsXUx,?:YÞFk`M>X$w_tǵQY&J"ht - UIrGyŸ+Г:)\~жJa.]8;ڑ `SVn M)ΰ}dlϷh.F%!b~0G^Bw>q"*Kǡ5}:UKn'?wuM\jhz *(~Y˿gY 20ikFz*TָtE05Tb8\InqP\nB kdIw*u9l PMq}ȳT]! 4.^re -9x@ _AG觬M77/ 7dxhT͖8l.czqHGMCsy>>\Dh8+I -LV+ 5IjxMLljdw%:\(S n!O2GuzU3? Dpr¸4B2 o57.8ůq~ΘSr5ηi͡jWThC@Io}"^Li~SukjQaCq!.=*zj(nkA`V,5V=gf-r*4dǎ{y~n)T2- -g2g-iVh$_swwK`uldI {{5_):9X&ܾ~C6:eTR" -j3=JLL/DblܟEpϬPUk(EGwh$ilnaȹb -Ɏ$BQ]6| S[Fkьl~qrMimk8ۚɶG.MH>?Zmr>gHb+Ԧ؎h`p{G!prT W7ƿj1Lw=>;miͪ͠U S/ئ Lm - R+U~L{.#- -3j@6 -8J,ҷn~S9x>-endstream -endobj -3927 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -DZ>{\E3vk6n wUV"`ND.OdVLrN̸B²r!m} RI;w}bklPAY¢"*hWI鳶%jԘשxFAqeD^Nt{ik9"tĴ!&ӔO8M8uzKKRD81k/ -G8 Q.| {H¸ OKH##S92Cqy@]"e]7-ơ `(-fg:w" -z8-Xk5F SQl IԘ6)rؾcwjP;Q4?IT+GE t^\=\E'-IQxRHi -{`ͦcN3텼HQ0*5¾qbz57f&5N&d$2計QyܑlvɔF{1جd~\hN>S)FCGk~~. -l: n\ 30'r"`oV6YwHRIV|h _9\^6>_&ǺԸ h~]urXÁjB1UY <**B6^o R?5!p*;H^@?s"R: " -m]_^S@6PS%A }$X~06܅Ƃ }R;`W UI{v(Ŗ,fߴWi!Ir_-h"UըnDDĸ~T&ln N5އ_&i%\K=,5{}9TS4![O77K&aefiemŌ\hw•rE_oƋ,eO՘f\==H4{vrsװC쓰$(1A^alDL慻")6ީHَKԢ\αn/5>%q^-1dfDcnw0tkhEBBr-OA=uٿ)(0VBlPŊ\ѻsxvcO'sKe=}b_ӫ~NZendstream -endobj -3928 0 obj -<< /Filter /FlateDecode /Length 1232 >> -stream -5xJO>)%4,8 iBgB%49&vJWPwS%Wo/9}mgE_!8,IJ$K8`ŊlƶT:BDT%ٿ1S,r8/ Q.$F\RBטs.> CX㝇8fPy -}?#57"2Lˈ~>$͹ -$kkhͳZ9=^ȹ{tI/X|_,?FB[U(bXIN Jguus V#,X_.g/ -%62c +ϧUҎj2O Wɕ0PlzUm -H£\`R!ccn.o[ Qvܳv1$0k<zҐZy58W$+2Y(kdugF$`pj('M|tb.Qt!X9@h[^Wu8Crjj}!\NOMql9x,~u,6g q;Vw\0e#Qqk3ɽ\8c[ F:S)nחo46#/fUs6{ [Sɼi5wo'x:QuM )<@KYw¾9PWqcb΅`cGdHcc\[hWxKTqD0~yxJ5l.Sl_C|J9-ⷷVT7֊lS= 5TqޯN"`xA(qG=h毠8\fpAhaO]f0P+in4QG3_0~!2a w :$<|l;C0nĦ*<﮷(RwhWyD"|*33=7lJ;&EόK%36:#}3bsqp`}b1I\\1SADz&S)T$M'+A0X̎Is=I;W43endstream -endobj -3929 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -j~(Hfɫ|C } ͊M5E$-<4mx2%uA\n=zbr<$::F,Dꑲb" E qxJo-gl<, ~'P1w `s|<ov]HsHH4D F"oOY@W}33kgy>(\o3?zTWJ.(%O Vkj$y_hrR?8ͫBErî&3[8~qC|iw)⢱!Ү[mDQj^glC;H2CR)|[WuOW1>.PJ%)֐` o|Lv,_Ts^T:H_WS*#غ'G 5\j]O݋k;, .!co#]mT 6rUK -MSGj^C'Y|fj ҤFǂW:D߉,S@!s;m~u9:H>tW4iR~tC=8{48VO\d -Vx!֢ATRZ'%s,^Kp/nt[9:|W h`HȊ:62Ҟԩ*?*4]D&Y-VCM:aփm~^ VTJH2ls F]Iw7ۍqq(r"P~40Iط` U7TNjzm%f^JAxX\p{- ~GcN7ጒ7=5A?LH| npyQw~ftYQ҆T8rk#tZtfY6Y`姙( -q×:Ccl݄Tm^`Y* Ktf=dTV#amK/H1eSL)-WӗnJx'C6bE@Z<% -);&͡zKZ|4ҩX,g~]c dwE`NG~;N -H)K)}Lmgݥؗlؘ詪3&gU~`: ו*B\zsѺt5.>J#1Qj8Rn#Dmv/F5-tw2ڿKPgx˪iEWx0Z_HN"0uhپRežYz{g3[!t6G?m/:L$C]ܕK$#BApٵ.L*B\+xK>*S?;};خ8 v{ۑH0dcn{,!f|v7aĉ=dw6n^d3R'> -stream -}Ѻ?vĔ"jY@qg╢dZ\&T4d-"@* -9u#CEfDYDYeGg V^a (@jYdz|J8Q}mf2ԥKp9A6yDP5[#}k>j˧=G( -ʍL(WY'IR^m/5ODF;LdOus9qb[v;M,.[jte5z`H,!=VR]'6p{)VŸ },LxA0e29?v/h-ue Y -ˊ\@whױ -͛X^&X1~Κq>9);@UJ22z-'H ͖R,rs3?Ҟ6y9=wޗ燱e4翿=-p`xu4Ovxh<YuCpaMi8OcI ?,qAlT(Ih}BӇGbLؔRٽl+S~Mu$.1X_?+)_}#f*ϕ5+9.)Dd^8lKYHh-N0{׫KClZtje7@ςǡ*ZZ%{̳~cT.[4EX I WV+Kp]< -%pzڛ1E7 HX\}TdI@Q+řҨysM}CޤКJQ@XyҔՅZ1)SQ+ -#kr'Njѥ[o/F2$ N FFj }զR7 ; $ PjҭF$Ii} -3G& *iE}QM_ o|}yM6sPʕ)i WIIi[H iz[iQG -]2n ox'@nۛb+-Qa<:b7e>gbiճcS†K 㜚1283(E';yq̅вZx9lbq{f>&?,݉LR!ȯBJz6#W<7( )0f - -W&oܔҖqHy4sߡA /)_@l^F?=];Ig^8I2ңJU?}皝'Ŏ҃2yorܝH`3 $?H(!ZÒWV\3iظJս+떰̸*`nRIo^!g7`hȔB\+OBendstream -endobj -3931 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -gpb07T2d膝8_<{_`LWزj$%}ѦA*侪{ t -bʽ}BC+!UBѣ4!СiE6ʠsrQYh.!X0)\ᕋ;DZKNFV 0@Vw@}X|j(ق?  Boy8 2xǃ"M]ؾٽ -/1Qw8-9X/ͲdFry1Ir +WT0φoFUS?+ -JSy@F`cB;`smIد9jVy,l_HKع$z =mysRɼ1 ٺ{:JkN?vV$)w.<{ʝF~π5'LſWU n)[LsՍ3k䂶ӣ̏6LS9_=21@u%ri6:;;,봌A.ie?= uQ;cK9\qDL"D>̓Kj0yT]&EuDA7򘝔iX&wmWe`/531-}vgD 7،@ڽ" *֪j/ZcBY_Ǐ+Rg_5l_AB(a(FYQ$?{h -B.Ki6J  &akSl0Iz:BSh$l"F7TAo$qw0_2ᤩ.+s}VI尌46$!w?V$)A,c\ncI<"dN c^~ v# -yFe~ȡhGQ&R}kIY۩g%h`LgaC4c$̗@&GWP^%\b`chS:и1 7NXQ?.nekyCg)Ry7qCNVQ}.s"b'icuE<ߑ ZW)\2m?])hr8CȨ{==67%,?bA@h)jl<d4m,ϯ: ьGQf>ф_H!mjx}c(SKm;|&Dvaz|PRpI4AJgOY8 'yZ> -stream -^u>W(9lljj.;&R (:OCbV b24gz2g&&Ey#c$Ļ@,ä ׆]M$o$ 1.rg,Hv܀F)@OWmnkoԇ<CQ+S\gvjdb -' Hnm`96<Us4jEOLVwi?fSfݿ#uGOL>( GM߉3G[)#Y p@IU&FN&*z=p -ќ2%k-`u ^F d{3&O: ?kC> -?%wi$3}0.ڥr="A -e\lMm '֐ocmeu?yF#JP嚩zMuCyL~.> -Lfo1foO`>l>mp2M&[Ę,$\ =vںj8bbQY tOKNjlϒ|L¶衛PG!ihy!?Q dryXXG͒攟dL+>jUHIv v.QG"A#/f*5!յik_8#BINT4icecnfxzs䷬;+&{K1)+4b mLuNh9L(S) 0%gBfŦ[E4>u#F{Ei#OZLR41j풎޵ow[m^"ŗ΁ޘ1&w6E#쟂J4R\9. !Ε4P Y0wr0K()ѻ|0zd$nzxZTᛲcT mo-̽nex 7/;qW.b ZǽKS rh)j@Tmm.B`h=T$e2 -0DrUٜ{N*56k -ֳ2>,m+ !Kvj)E.1^+WSF.~^Yݏic\ɤD1QlgbJ! )a0*KwHlt7@2GԺ]МJ"$6S -8Mڂff>$-j+ k 1(l{d|}m|' %Ϧ79>R"s8ڙmoz ߀Y-tMjۣEOT2E&ڲ;ܦ4}IK'8wm6\n7]j&Ҷ  Uhgx!C> -stream -I!zlDh?pjO:"-%Pj5('gMz]! Cs"%51*Zqa! K:'IܜC иZA6~PW=cmY`@~#ϼ uOIબ/x@X/ДB#pЭ^]w&s[wл&VO4v$Dj9\qP\y$Ig.:,_g XZC 4I_F }EUCMg_a?3ĭYs 3o`I30 OZ O el [?m_dv\jHx^Ȫ4#z^cgq"`9;,Vъ3' xC.#ɂL4NK6([A?iFabB+';  /oMk=}3Aup־4zph&pHlgsI)=4*PU^hX42FI~#*(zs#M'-=[7tΩ& 0#xkdSK,E-Zdi4Pl}Y+"ur} . ( -[HiU̶ܰ~|R| 9A4lzh {@ ]F( S614raZGRČKŌOl'1%lo -JAָ4xr3 D`ē{v +sE\3?r_+\diL.D9^q\<֗"lc}BęGsy'I~aE+Ae1+ kJp?ڸ9m,+fJruUKAѻ#*J!!xucvm>O-{lRZI`o5YGL ;/ݭ> _pm >·؜0g.fMb}\J kes -b}ˍendstream -endobj -3934 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -hk+]vs)fWn3k_JGVM#` Gծmx{_2%E2ưPg 9Md3QнbrTLӹ#Ͷ9Ԯ(ؐA;rԦ9;L1IFBOXWx`a-ʀP[4+y}yNKe٧ܡO@"B a(Lo{b^j O24T -'.q\#7=n'L;dF[C[7'q"¢&4# ¼ry7-M_bmpOUF_Ǣ=z& }]zw\*֑_8~vI{'R^oG2#K.]d@$S/9X֘ ,ʴ<68(wHOiĔ9rn%V,LhZmAZH^G1踂^p Ht_I,K%ןдRB uA sY,c]aCQIhZKnH15O(lrKȴ,[^W(iϘ81Ej(J 5:&v-q@%<3~gX -|#9 S}:%թtCkmu[ NBQM 5A*iiY<hdzsH -Q4UmwvWadbA,x"Q\n"̽$8 ۣZd?%T;˝zjt] FYgIjȢ_d -+i^΍&j$ Ou# d}RvcU_=9s(7n 2*$D_0n&R?Rc^fL -ɶ0uiQL.4m{KeD]٘LA1gc7**UXm0A<.m97i(.:v2 *񁨧:jD]UO|*ܹ=;C}9-$Nˮ2{(]4J.V0 ⴿ-[biّ1;*5/Le#{kGn5irVŘc'|fkW͈> -stream -QN*Ww>SA8a 8.?rޱ]F[F@5J!+UV㠎 yaԨBԐ)-S@NH]{K%PPt6d5#k3+c%0?js|wkQ8,|ċx]}WD9@v|BJbTOF7psdHJ??E/r`8rć*b2j9]{q2RaagxRҊ@_ἇ3Pi2n%Cm<ڛݼd0MT-Жcu zH&B۷e+| -C"zQ([mʀUbnEZSxojQtͺq"YA |dJGV:]$#b^-aTA^δXzDXUfnٻ1^2Q\vR)fk\BO/[)3bię$O$(";;ȥCi@Jul~IX|%2 {7Z-ܥ-I (Xii[I$'xK' m4Yyz~Ԇ -IxaLgU 8Q}\U^wߌAFc~J*oW8k$B8!'|FWC#xq,aѡy*/b5~94 Y+"Yjb1Vi5B0%QTWeK%JgRg["zP0tr>pTDٽ$4WNC|1B'ř]dy - ?M1jbRށ?SXET>;f![MYA{pK(H].S9t.DBeL+5E<4cESsendstream -endobj -3936 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -*?P^xVW t|FXu?0eqErhlI` 5Mҋ/䨢ԯQVRiZc>&kRPHLMJBۅ(TUV  t6R$9(UZYrm=LJ.Җ.qD+޾`jxl\#FE-i`ԓdZt"fFJ,PV/dJyOԤZ L¶ĭ&JMa:6f5j щmYdqc6$8nQ -F1O> -stream -|JOwXFLH$c.WpS[c| !U B}E?Q(E4g79oI M615+c e$"R<)FC- -AN`ѣoW= Yq0_@T 0d{{ `7)5՛#pw|<`8x_m[YcdSg6oVa'o#\v/ 3FWbB0QJ=瑗Ѽ aAR:քaι/̔,4:K l Rc;]:&ECKQ ?=[C\GTa$ }]_å}, 7RN5nT9ͤMrWfSr86:,mv!/԰"bBcv<=Qbh)L:S͞iФ_~$t̀L󫭐S)Thc ` Ԟ+PI-9'HiT_&|a~Nⳕ&='+7Kr;{EGFzjrf 'X1Gq^.ʦԊf J`ԒQ#ml4h}B#?*EKt_K9ÿY({uOÈ'RΟ?Tu"t.]BpVcBHu}U{l٬_,I4#tZ3lcP0lg!0G"oB(%.&yagAM2֗mU(CJyMGqit&͸TsMցJ=L+29{O\1|I%ik/B%zb"&dh"] 's#v,o8.bC-"A\ ->Bc1w!b}΀Wn$Cf ^Sb*" *>9 `>-PF8U}Kk -TQt cO#-:05Fa«T¶-A8́ktav@i첄F4^|w!- Qp{L-DL/Q[o@x x/T"t,22ݜH> -ٵ+e&ث`]y9}{ePČJgaY8c9q%KFd`c[aEb Jڍg cD - -LAvJ%-X1nׁiAH4ZDEr0:AKr|ݝe`BajkE/}*u 6nu,Qu9nw&D,݌Მvh;G˗A>ڬwR=8j$Ooq 髕saKks৹RgW -Qqm⏘egBZҖh% t2ٺ&c/DKߦv @>`rp þqJ\s;X5|n͝4O.ˆ[S1[.|Ϥ?֌g걟tbendstream -endobj -3938 0 obj -<< /Filter /FlateDecode /Length 1424 >> -stream -]x/8W.sԺ{{6bY[JW2&JB!7'l/~s3 w E7a+|Z+P)`(r 9FN1.ocj%To֥ccg܊w]ˁf췥0Ao OKM_[kC|RЄj s$cmy7Y8▔4Ť>_EjfքcC]\TR)|G]/ޑc:T*4e&uEoYmu+ue"r+gPPˆM7fMw(Ce 1;f՚F5p1R"3i s96hoڼ!7rt}GNv./T]SO%W0Iu}咄P挢8/ID6,}7HG -*y^&L0;$Q kA(WBhg*ҺjXxrg=PK *)x|kNx'"Lĺ9qP"ۛW+Kkx1fp -/ϙ\*C2<1K> -VsX՜WEX6ʄ'sĿxu?aa㿸P(ed{L'&7B#06J!SF8/ח O/K+ER1vqb?&ɳ"Vm 9:nendstream -endobj -3939 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -}JJIJeC.5 $ѓILҎ#H8}맟 -iuXvÃAK/ 1f;|T0B·L-3ռ$,Gzb2  EATQmcW[GV4m@жWܦ÷tR) --z\vr SxηԜzw|9~MjhsAo`<;Ի^sDX}v[qaگ#C14VIsO@iϦ( s - -Pa*JP_=0h pS Ċf8JF:*ڱOJ_#n67H4=:m]gVrQg윺"FEbA]9t?J`/nƀfv2Н]<4/_V񈠁j,噏_b S0pGÊ.!% )|3[~+#>l]*H%|4A^YՊ\i ^Y -ȆP"䔐W dqYqk|-TdQXbꓝH|n+Zgw;8\Zտen^5J~kylFhvXa8wV/\BQqb|sLy<e)p2|15N\BcPC`?=EJjRvZި]$O"8| -M(r%l=@m tgnbÅ"2F 3 aJI:*3z*-[+ -ӗjNx>ˬ;4y9Ɓ΃u>MӯK\tQW//?hԠCgqx*N."7j%Жx: Q=/O5:=ƒaL-A39U-n{0%Eπ"ߖmS!1gr7,1;n^#9/u?# BfBGlHϲh޵gׁ-10KL`:᪙^GOTendstream -endobj -3940 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -Arzƣ 2_Ҹ+7GC^2 *@< ^A*f}&Gf%׏w!2ǎ!KTFێ# -v3؏ qby4M7Wcd3[)ۡqPhRAjU{zTv|]|ey__ j{MvzHbT\ K o IcbU y6-NwTo/~*Ɂn,`g7:'n_Mu<ȽlAޜ4UU]E%x${v+70DZ.ؕLĘ!FTǗ@ʻ̸wq.vl\%0Kik\aSAшƆEwjCx u9D7XGT4Lu9R/?Yg09 +ygZgFxZ+B5]Vݸ bF#0-QkN6RP@p>!LC(k *J 5wUrP&XO.ӰSN s|iT([FK-$GDC -i5|}ITISV7)$1*m;GQTehO] ۉtuXr[%o%6R}xpHa 1l@* gzX<~Y(ڃ2<`% I- - -,Yqkܛn<&B8Cqu=4p *z^I .P.J/|_c$)綀0!/D-c;mtceSD2> -stream - STj(<0"A#iR# ]0JHGL7 Khfku1Qi1Ij@Ң,ֲߵULSk9->[196"7i\i 3GWmúr +NlOՌ4pv-OlJv h?7 -gGS~w~6( k/Hhq|-9 .G]sjVUֵ)kHVj_w_Q<[EY$G^AuGwY9!wَХfۄ%ʢ22lVujY 9v)d rL -*oYlcS@fVkõUwnɤ_MȝƢ=z#y~Y6=^lVn $QM *RY?'dp4a̽Nn'r9-ƒ }njvqh.\[+q6S-7E>YK~0RgvF֞8uA;&ZO}= "O5o| 6>cpV3oX{zYu]0NG}p52< έ`%[SB[ltbl,q1߸Ƿ3e#>#خ@i s*xPQ?f,]xCx6j*ܴ -fOx-6\{W"oW?莓L0 -BӟצWs˘II+&~_H0- \?JL36x'tвX"ŐE|WyhRxRӨM -EWg|j&[eoU F~*I^reug -'ńX2@i 5s^k"je|1pV|:oV-ƿ8gTQyYra OW:9sVޖ ]D]-V"+oѤ܂hpI25F~^Cv+P1v ܤ8*lQ#3?Qπg -6ùs 71n/{.YC^}͓eQ٢CFBWutak; ͖hKfwB\`t7ͱ8Fqw3v";A):$TœITd)G m]dIlS0O -HWma"i$! }]=ۑi\#䨁Ӡc/S4GWy߳5c P: mf-ᎼBbI{ ZlRX4wYxD`,% Xb4޾=ՆpsA>߂\\S?v;g݈Dž4lzefH9=Q"Y7 3;@GumA>tOhיD8xtX(T;Adq_w!8&\i <!5endstream -endobj -3942 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -lqZD+!,y?ScМ ;/;(lfaڌ&M'/+[f%\k&,VmBLl5?e>J Ic/[(^Q|;=lL"\ЊavHPZ}9كa)\ׇ~ 7xT<\u/̊B9*qHW?HEnA4m%hXhܚVJy zO`Awӿ=Ro9DfIf(\J"Òp*8:lD$q۲m8Ü x_UmR@P E9jÜ_Ԋ8S6?H&3԰'d⡕258++ S1ݼd3 MXk/ |4'j*PdDd>kr{+FisP~F81ͯ0Q5T p\>_h-A;,p6~ G@TQ:ۈzp_}(ezL5W7*XHUhŰBsx+XLՈqGl3XZTfF3 -yGۣ tT7%A3֛='A1)Co_Ntpzhg^@P:C)IC$kɡauY؀CF7y b1莐E - oG#Յ6u'A e_ObHG -}rF.4}u!evRe =fqb3H Z:'꘮M(L^sv[_٨L8BwhW_= -JjdZi%6dy Agc{ t^ꋴ-wp.DNp'J@7ﰗ-6/NVMI"HsԣQփaNR谏j߉˄ȝHw4KSS*7$mK3&bv#wЈm&ܰ!uJF\MyH2{9}Re шQjKj! 'Wn `Dj|/2Ú 3qI9'N>3ǘ^ˏoG4B>@hˑkZOvB GsV4 pUtnE&~p2I/&3| rtR8*^/u֎MZ",Gq9-#*jI -WЮa7eic3"@qTXrTGqMWg΋Q圸ݲfLTVQ](p^n(ւ+K\%la?o59.KSQ -<[/-u?O@~(ce%6QaOo019AGڽ/Lhͤҕu+c۪m|qf (JU%NdtgS[cDy"r8ehC&endstream -endobj -3943 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -̺@@ -v6 -Qٟr4oQy::KEA-}f{մXna'4^{?oX"HyDaߐ :`/ȮXT,bMinÝ_/g/A uSw~OD`[>ܦE8GV{޵u=)kK2ere.0bkG-rSZ$#OїޞX$-2Q\`s2{ -k̫V]GŮRdu!Q*x_춘z ?֊`+`Ot-Xr [?RJ~5N9Nؽt/ew{>TKM2+:v, "kpr}ܯy2~Pļ#.B7Ȫ])AtӃBu:BSGE\ ր'x"Y F4;Y3 MKxt67xN2na9:7g]'!ΙQarLfʏzr]s9 \%KjbsDTx7#.At^TN.'DF_}*%Q+&<02FvѾ,,q:KФ$~>+jz䥰Wf#Klg&sܒ; Kd\ nODP 0H -<2 -65".t #ݣ'jiwr\o{ɏP@D -=YAxFL)P%| 2`X/"/B'PR|.ZfJe+Q',|-K > -)D˹A$gsuqW-t@8k!:ciBZgRb\]z&;X$0#G\S\P`8dMendstream -endobj -3944 0 obj -<< /Filter /FlateDecode /Length 1056 >> -stream -5H(GAx⛏ o8qe9d|:ۢlQg4Hp\IQm%bK|cJ_0_NwWo2iA4 y[! F.C< b^:<=+Լ.w--:z%e`5ǃ1!g -} ~5f*<9ރ-m?F>t;Qhsl2K}rr]I5"W\i/ XBw ZLC5?63 // B)Bk^zD)1ӷLC蒫y=Z&02E{<^7$"kVr#n>TGIc贞Sp3U[VZUE8N$q^ݦ -FUJ  R 됭f~l<Z{VTw6 =gap>1}dYo: 4y]KʮBO;Co:$({_m$$~F{U6[I[d;3^JEo֜B#S$=E8:/鸞5Py0q@C=!| fQG1hcVNRd>_ԝf}Ɣ~N!K_%}3(Y1|şQp'&2* 2'l >4-ҮSЙ\IgP/; [ƖHKrdC\gL#h9P&SM _|Vsx[&̷+\I y`Xn[P&D6 pI?IQn{TY>`^{ pM@|JFc  %1tKڅupкD#B 8[pJuw,< &Q:?jl[&A haE‹l. /'B*$-R\^2EUW@,59endstream -endobj -3945 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream - L0'Ab^/R1yZ0F-t-#AV)3WHWz=ZPlĦ_x)]p2QZsbߟ)Ԇ>0'VCeG̴ :"yg%Gbɐ,ϓ+l\]6+?*%˙<>Qԥ B§#R&WlY3x05v{>u;%s=7ͅtX{Á2|VC)Hv k9t>w+a2FH?.TH3隣#MWl2m a& :^`e,vQ S>4#JR%cÌ̮%]bto%+`J7~&F\r#-\b}*9?Qm.^x`hV16KRz-sj"mP,]kj!s{:Ԩ0paA5b;e&/-f`2YIaPD="T~PZ,|]5_[>+L(  @hU ɱ"6Alvg`M _ yMkixZԓmj~#NZkˌoʏxuou]ZMݎ~D !F0/ Y0gEvN>~:i ZǧڡTM L7C8Uʚ86 ;%o]Y(bwko -u3{hQ8/%JϤh_FSܕۧ5A?G 0N&!LU*'"BAے$]l.x4 S -p=uѰ -"P) ``d? -ccsʔ -#_!dTnOq7ϋ>*nrUInjH}3tFݍ/dc%s,9SW'?'z-0ZbYI-p*ZMoM%~f1vlCrsԷ0 bpa;V|&LaeѲ 3endstream -endobj -3946 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -IT/ph OB\=qi`]զ_'NmzmDŒpm1" } [YLIqQ(=fɒo d -s϶Εd9WWA+qy.MTbg[T[k{z(x0E^?87*7c* ć_;;Z(8MDEy(Gbf>[ugX$^ϝMWm4֗O${MhĮ=*.2 (븎w+H.mAJكx,10%P }원]F`%+qi}x2(i 8o|Sv{HQ Pi+? *m~9LXEgs[݅Dwp٤pRiO-sS%XІ.Pc`ƱU.ĵCsD] -2uza9 ԢhJhQ&:z#9 ֱ@_I'r h*m.&IϩD|k~P'Iqq`j5UR]yJWysdW ^ 4,Ƶӑ*'!(nj0Kw3d#L|Oa z+z3f&;] 'VRO˫M"97eMykb.hztb !ڹB<^+(Y'["&G?'nCfgfs~*u&%#J5Eb^;H"{9Y&u,rb%+|۱"Ra|?ci'2@B;^ēmwƪd\O" IqF`Ǥ[*gIxj˔ -W!KG<c75UϝWN:*sZD|X/@[Ӏ k>%OkZ&?Vt@wJVW__D> -stream -={s]2fU 뽊L!7*#}/AAn#pHԇQ/}ؤȨnݙ Ё>ҪľD 'TS -浔j(q#?4M% &`׼Tճ+m.~> *䫠̾^]H4R[ɒۻ; RR@-D$"30G!`6a=*R/"N A?Lv^2&aӛmG1|$1ɜ=Hō0-?g6SEQvVOc[9I7TnxQ'w#DUDܑخΰ*cɛT)RF)t^c=,Zk}BrGz],mz4 mXu͌1;i8i4wU+K;GJmYR|rԏ;'>ʜ4W}eQȜqhVYi_ݟEHDGpc3%8X |-ϼޙLiguw"dc2F;SHPt2|}]1\ Cau@QUCB6\/˴x `!f[Qp7ŋߗnuYI=,%.u!" .)JsmvD#\,ޑWm(K{)tjtÙtCiẅ' q[WoL*IfUf9:UмɝF#$%崏ϵxtVGf] urFV -$Y'T:3%9M>EpLdV}FOK&~wPLukhLdMrfӌPZc&nvܓA k0*#+6Yl/ݬU{ fuAC1۟2j،mU\0[;Ud.7#`*KI]eoULLl. G{#o7|2~jSx+eɀ~/Abq3Mwr[:ē+O:1x[xGpW$,D?Vuk9FLTOMY~˱Җ S'X1H\[c֜9DMK0_Z؀38d{Knr+^aChKÉޛa铖` (vF}k[b6tT#&&K[UY}r/a4,8Ha-CS0 ;2$kt"bmEmX͓CAKxT("O-v~޹"(coɦGK{*㩤gGWE|”uΪs@%~-s8%aUD%E/3_O^򯣠|>J#@Éfeݐ)Fk[y%}G5)LA5 k.I[b7Kz§älpMjs+sZ?Ecǭi ʧ8纩gJ/8jଠQqN'nqoOYGp> -stream - ok%.|vG \+תR:_\%T%#i\1ީ2jG^bX͑jFeKož" -UX~&U:<]9{3Itwu6I:^1GA -e?a=HMzYC% k~3<q2hoWG8WZA]cτ uiQ]_+s>lI[ ~{U )ή|ũ C@ϵ1S\)5![iZ&S6F$HviX.|w?cMƊQjX k8YvŦL$ct%]HBȔqS  F>cv%bU]̞z)#U X=J bq`%ɼ2ׇ.ױ>ZBd |PMr8&$9h̲Qх}FN9Ɏmoe3aa0 i>7["2bkl"21( -Y BI"#'-sV'Ww3iLS~e!y!'Ò#6), -LE&},J^;%HU& CK z%ID>!q -ROQ Fp~)fq/)*l:џ[ }n W#0`<;Y]<1ꐾg L -R3ۣg8; (F:cŚf$̕:I%endstream -endobj -3949 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -)U}p ᳹@#0(@v6{!۠Ж=T?K+^=U]9yC_Ef!x&S{4r7 E - iSF[@slg̫*udcrQJb^\2"PRI}!¦-eU"):lJ98,ט#otih'|B*R(: j`08>8Y8N;m}l-ٖW̻aFJe!ɝGiNW-5 -)0h΄f -u C!Bf|"b^n0G,Nn>RBk+&$9 -ċv"K+U@h*rJN@ʷfx\cemMhXAI {tN!⒞T@cVI:]? -Xo cxB;cFNH|OSg^,]+/Me~ Ȑlv |j -|"j(д346*B>T` ocQm+XroCjF!KAŕV 'YsY&Vl^$G )ǖ,c#q9`T۔Ll+"6pqfgݶ&ҝ#~uCe\bwr="jC+kD)>\c _S붥sm tEz2k262!Je:%'V!#<RlL/ 9t{&:V N#unM:Ր.Eyê^7C.5%wOu@ʯk''9ξI):h,|D^EI7lǦ,"$̸T]Og G! ܲ)RDD9 lx-\zaS}ܹ߭~_ ^;`G[,XGJ>qWȉo}?ȅFMe=_Q8qkf77cYT`_o=q4S£s>kJ1Cs6 :JY1-) G;ؓPSxF?ۉ+e/3]fu'qq57eeF$~u& d0AG,Կj `kendstream -endobj -3950 0 obj -<< /Filter /FlateDecode /Length 1248 >> -stream -h_1I -K"J ihz{(Ni}FȽ$m_W8oALnK۾4lz MIu@5 yq'Lp<dSQʚ/ԄH/Z&.Ex+:ߨ'p}~3z"wӜ+LJ -Sӗ9o(+XmmOS %nUKo+TmO'c񫨸I(ב%Q=jDŽ&jBW?+C߳Z8yН6 g/Z:) ْp'|:+N_G3"x~S|w SmY]Fɗu"v<""5D:n`6~z"݀٦KuSop{mlg7߭YX@W:0D5|>D\:zV~\)G=7zIt3S\pYs^'2l8B2 lQP `duޫ$+gCZ@/Og'Oio"aVmo-E_'BB#:7f?䔄32ʐY8Ttr/r}:8BG1^uQͯpj$=bw+Kgz>V:O;NyOY^N&sÌqXX#[>Ӧa-y0?َ3./GZ}Rf&<}\|TugKޠendstream -endobj -3951 0 obj -<< /Filter /FlateDecode /Length 1504 >> -stream -jd -,`(x!C<2!HV[bLԮYPk P B2C˪"8![];gN\*^݇7j}:T-WwoC;k)X<%SrKh*j3jʁFu=UzFikD7-PAnCӭa_)lqg:vkU^N6v%8';!5= -9~ $ w7?G3ו~3=!Wr5cb[vP5Lʣz(bTOX!ATO8MJ -nځl*2QU~BVtbvP[6BV>H烺Q7j649Nځ Wۥ5[ [r#X$kyQq@rZR9+,Br2ـMC[-L-q >6ʠ I=xUO=D1Ow<"d=܄B[M>4_o/ƫn`E&%xvzngsN{R1XPo%߬mc$A#PIoB0/{x,|?nc^6璂o.W26>Cи;JGFZ먹8 ouʿ<@J.y3(k_jf[jV)G^TuQF1 WJ@}!Md - c9Q:u3X)d$&BY^F_. i%לUi)gvDyǏ4ؕ{ 7$D[L -AŒXY2`j *= \Rk:)+;0G &P&Ζ}@05 -@)[(D$ X- | `=[^SUv3)6]{;&aa]ƒ݉PG_ɗrDAm)&U Tmuf5ǺԒIenJ5>`7Kk6G tb&50Ι)r FO%g2E==t@8EgPL[DN5/K& R+t/Sd~/ܺRܽZ73w y~Tߗ]{!R;ԋ)S'op[nhLXuW]Tɶ.M6P+QGEB$L fWӨ$7!/x8k_%;&i)B+<"`Z_R C@XGj1bfq51'lR) w%nlYO5J`ْT?HVh҂%zT~EFL -sTVendstream -endobj -3952 0 obj -<< /Filter /FlateDecode /Length 1408 >> -stream -U o7(*bt(U *ebDt:OPkdωEA$#c2Y8/JetZ;ˈ5g*{Dٗ(>g>v('ظ03 -/*Ά!tJvGF*\ܯ>+Z3x0?'"#h:]V!c_P e!3[Q0 -C[FJDU\SŌuOdW -S`=~--"IZ IIv4.[1/,q&'U5z>?v%xFүO٩KZ\8QnιV׆}A8?vpi)˦ٱ*-=zեl5 460BPOh\N|O{mo1Bw♽H* {ikkH3b;^*,7.J>Z(Xdz-`\?ܿĪf Oh)Dn\eqDJ]_!o5ZoF's  -*Np81a<];* {yS۟<+<8:Ug[{=;H^_6T 6 51Ԇ+5"w)XR;_VJ4I{9)59l]d~K-2Q@۔Ēl_>0i,ϱ(c䛩T؏1Ѩ?TP`?}@ʴ -<<`$v~b cxT\7Za|_uA1y9g3/ ,ԚX>yѫ+v'z{ix1F9NEHы#@q9"PXjs[GsCj]̥$3ȉX\ԏveH|7))<! -r^3^uO :^#'A pc&4ѥ1LPb?<^ce|fތ/u;3.zv^ٵBޣ )BYG O2,his?دJҀ5_|Lf3(Ϲݯ-IyC&ӣnxQ ;L58F8By[/ue$\G9>z9x{nCك ju Sman+A`=ܩ3ґG%endstream -endobj -3953 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -ć|+|,cw_E43m<ޮ(#,(vPEz}1H /O ? Y|ي^9p4K`˿Y.PHIuJ+#Kx4oH.8cR -y"ymm2S}(bM4+% *={ɜ -푓HLAR3Y^3" {E3j4pkHS9\ėʞ/}}楜Wvjk^ +-~S+7q@?K(9?lv<VEvP-YtŴ搿4sXʺa+B>g;n2#;\[̋1"qS[Lzіy|%Wh͒y{:aS|ҕ -4Ɋ6fȹ`z/ 7 uCu?`"UyRzgSNfR)Ɋ]`EBf)j˳"s"&vc )-{>: -+.MCV'|L8FAdY$T^5lFgޢv6 ?EaHN(l&-?mhOW (zc?r t!SVm%uZI]@aiRW^ 4Uts 91|){D znr -yS/c@wJ1dse6nzlu:$%{ael}޳@7o$2,o4&B.ʹf6HЩu}"J5آF.??j yQ$߭OsR`)juqLWb;%G?e?MسkB&(q^sƾ\9?aQ]H@H {2c -Ud.XDMGD܎&hȔ,n֢{Q R,!BJ̕IQh ˫ -Y~91ĘyM9D^J0YD[:oE3H/B塉vhE -z7`FC`Gru<=Oo$o$>~Im>& -o3Ŭ{=M+SJa@FU`ȈOxfj9uѡzWfy> -stream -/kΧ=QZLV *yX:bs šh K k;A>zfo{GbW1v4hv0p@5DM -e$ZL0mՆ;C5Yuwѡ[?f [> 78Mi`Ԍ.PgA`?zZ@i8|w0epecIq`F5ۈϺX0Cdĭ_5Q}r} jxs5f v]DzW5Lt 4/x +֘j%KE"bħ=+/8xhiBƔwӈ:ӱfVmŰi(݌!'͵5}2#=mPq: T7m;U&60J@k$(ˮQXZP༾Y`_[˔IH~8 xLÙ!-mjBV۠ߘwϹ.4[ L>gi eQrùU -K0"on̛OE7n$tkLS2dfpO2yYUGU07AΑ!z( [EPd=\0E;!m"ui{_Xx'$Ƌ<zM^,}:sx - 3oet3&:π9+e`JC?qBETa':=&< -&)'$N)7&h]" Ǝ0 js"trOD3%%.Q`&?}>OoWlX~{ W#Cɇs\B -RGM[l3JS=#m(An$# $ϣ!ř{L>*!ˣe9s/6,|ASd?8=-C'x -[JN4u _UH`qϭi3]4f"_4V*\qs"{̱=_?9+iob/[T hF9)C[^ z-+DԹԠaxil s${ B '9BӜ\) -X߃G{I:-49qjCo Ԙ\1к\ \ລS/(|%kt9?X6/#=h77C`D6'}K(_o__e$jwA(\Bȗ '>j%]zk4Lbkp;?ۑ_<P+NN ztwۡnZqț엖>ޖdaյg y  -WUS?#!'Jhk\WOklCC}GvLŬEГg5endstream -endobj -3955 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -urv@3!x֑.eս+ZbXՈn|*Uy:ӉUXq~k4U*aV ,qT.uЀtl(Gv݌y[W\*'G&}BHJuYZ4dgop}Œ.r~?F+a=V@/&<,-$bρYnCTE':@ 4f"=JϺy|*2JfC;;i^4-x׌GCI]}ЩBppVqW*ϰ W>|70`MMOoR΃:ddx}NRIذeo,GfX'ngNuSuf`O$7uL"o"cST.lB1fRhn%Ԗ_qRkԡ96 ,! } I6s |k):;,@eŀп\(-=Hy1 -hXeĭ:gluwr8`.v8~I.S +?I09PRFaMW|*+Oh S[s"QIGis嫍oJ[SZ P\Wu'ghTny?F)XKx!&`dr]$*3yAyC74D'ʵڒRmq~ n 1k4ǖ.eLW#xacWyLO"GVl"ͽ] -N t -QЃG iE͐S yv3-b=m6NUeag|-t°`G+D5B{g4}H{%cz~(|Q PG:W?}PQρ޹AiZmoJ&$Iu(w#JȲ; Ҙbjsy?Qб)x>9Dub̅rN0fZ`څnZ|SH_YaΕrkK8"MaPnRlj[ ROؑ0FN>q#鹯KZ'׿~BKǒ(IJdV!M,*^ <@]5OT a/x $hgSA'/iײ)m;Pi2{f{s2fvx (? nU'c|cv8ޣh ^Q۱l597n }P`M\AN4O -S@Zǟi%ٽ(ձMMBqs;YrZ2~ydS ԇҴEYЖU; \K7h7ֲӛA3rA/zn).9kK?VD}-eCXh\6+Z_ jy˷ikRt$!ԫXѹپdUc:jaqONObS7 huvc0c> -stream -4dY|v=:3.4CJN3?& M|KsFG+xk-duꪎll&obcT?=gt>0eo4~C7 1IB =Viy/׵#CLc4GJ-ޕwksAGR9)+oQ^e[ BT ̟h|/1o /̊ẖq+DHYݱc\cҐ:@q5a4H5( }g%II[DV;<#Z7:+h(|cURb _R*yޞ߭>( 6 -.MYsnpa=i!_z*G$؆\y0ZshB 7p?$>jE/^&hIuVwLE뛇uXZ@ b}/<A5qm[0 -J,xjeS`#@\P#tcezfݳktkPAo& -Sա,:>3ֺ\`Dvwi3 9ЄP70$. nV Mҳ(2b"k .v xru_ -c`bY#,-\'9[hFPI^}Paw7YIH򠛪 Ga’IMÛ$(2њ*xSv@y; _7 7-bco#EZ `=wn~ӻT!)'Pic2n6 {o9LltN]L]|hfxʋqo'AꎺS٢X^#f)o0#rn"x86φotuk'CL>=hn떄tʾwbV||̬QI*xsWiL @}Gzu -{<0 -^崸&!ck^ ,7 !:0O؂k0gY}Mޙkt_gҬe^|`oaطV^J.fqg ֔0e>V[Tﯕl(p$e*scfU~uC]TcrUg] u_]ٿ2?dcDh7ے4i7j*V>YT'q%gO]}yO%ٚ`9o[.kP2ʯ9+; dq~w$DW̱Ύt,B TyqVzkeL#{#[m c %#IkLUSr5h;.XV~_m&4ۘƄrn[F@@2 {v. -;)2f^y7ܩmҲ a@ > -stream -GmCCg:CN\qY^GUF6sH0ghq1'ncLG7&/>(ji"+H"r -'ӍRS[;mx6޼m$ ;T!,iO_8/ Jfwp&rFa/ntpb` ~pH#N)pu$d``2^.w뜥<6rn`to>z)"V)bPff|J=}H.|t=<ȿՎ?f -M)[1ް_f͐dU Orv^jh/Y}<Z7n~L.dxt⽾j4P^o΁Gt'%v8J䶶jQ*"h]ĩZ\|gHşB85XNm"EPd0uٶN@]GZW 2Xw!\??!L-KV#*܋ ^ v x5wUBnضa_v9bk I__8oi'5eS}n"YkrS-4t1]NaX׊8j8Q0n7ؙ9i bGT{d?~I $EQ\%l> -stream -^\Bv`s1a #kCKgӀ`7^ע~mn |rrp \}O'63a"0po?-,7xOD^瓯ٚZt%4%+:.fw`l4ZQ,ʴͿeIS΀ %?ڸgVW͸m@ޥ3Sr -ֽęA)iq}">K:ㄦ.B#"~L5Aq{>FIDEGf)\l7X.U+||[sIP= Aqa3z 0?G`,]%_/Yualy!`Ox戒VO;$zzXAg--Z9"HgeV} agspE\l5=7 TZ7o -B ihVyUm$#Z9gS~v!ܲbrzN/_),J;lFtV$@YRQ -{FH@dmT($/ϲ;<]^5} EIe[0[rK;cwHn5Ze9Py٣0Ix0. ^a* _AU:gendstream -endobj -3959 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -!JzeͿNYߧoF_4+V{VB]Tl%D- -8UkΰK['ʐ/1u!Ds@vM_%n`8x:b9CA0{1q$axAmN4ҞTmK7so3jy sAR^cCk9(I+J>BQJu_0jix{4U -u9؉Tك祶 -l6m"TH^]''ӐeJ-K`''~f1 Д -_>Z"&}Bp9wLN=Πo "S53y( iPb 4/_eJZqqW 8{܅ ,K?hA5XЖ"]/Nլ h=u2Sbnk_*ÝJ*UWtv>}:}f+^Ϗ¤;Q\V Pf[ ZC:#GdO+2u(O:UVh*MEs3y8;כҿPh1zsb,+Vwjßi.0]YfA |ŴQ&3~%R`RM!΍l!2n4X Oomt:ew} S62f{9FUmw%>eQ'/>w|M=:b6R'|HEQx iph}55Lƻ*.&7t>$uFBsqe(V|;jWq/ZLcQ/kAi&PH -G;}t9JNb~-È2EuPTb=_A#Łn8FH;ъF#% - JvwZN&L]Yn }q%wErQU fYH{4x‘ikIjQsb 7"-^!1K.Q{9!hh1?92Mi?4.܋}McRwc!+qP/bDP$aǿu+d -*d{! ^XnzyBl# Fnб@m&L6Cm˝u -[rFY3D ׁ'WNȣ;HM*cE wrR Lj'΀_^{N9+67Qտ5cJX|Cݲr8o~+@ -ot6ɊRqdGGvي" $̹ʛq$9+XGUa-tBj$APլ\e̮CnJYMf4> -stream -غ!DZB=0֊d,ǁnNTO=T+a c`.쵬ЅOFCfMeXS@#@( /;פe *Ӧh@ 9h8S"<#btא}gG%Ҟa{z:ghffۚpt'IR'?~a^}P?_܍ 1ǟqs_sqĩ.z؄hP*qtm>޳[HaO&#Lϭ4%ڔٵizeAZVu+kͦ;nV<g"q#eWjmcukYոZ hNw>.j\b -V`{s\ ,/jPŲ)8TyDv;t.g<{x"./eSF9m?[@ z-P526' 2 ަ N --S8b# (K noե >]*]: kf.AX>PH{=ޙU.SQFYTT,*`QN*nIόqN(KCв9!/⎺ɚ3W˴ĭ<5LSGIy/]{"Ì/8HY.*KLX^ \`zo[is2V)"OηT[/{cS`qwZLYz+񑑮!&fZw{ -ToLVuTclX> -5;|24c$bRz,;#k=(YV֦4^j}P/gdžs6O@ 4 -Z7` endstream -endobj -3961 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -y@6u@\L)]cjq«(;Ȉ%F'<|f$yhS.M㏘lҩ.aTY7 &wW]fPbf1tx墮^ŰXG9dV{R1<취cl~OƳh^T3$׊&cS`&cҝ{h -ۯz`Dї[U[4x$yGe6b6]w_Ҿ˶5$^hT #Fc+0KP9mGl60|?^BGȉ.Ar&$ 2}7 @Y"43РkeHYH -0~‘&̗V[G7 IPK?/a/m5^]@-xrIaV]B8NAJ&~a=n{ك&``Gs*Na}z+3P> ׽.V!~Dn,M$;_' 9En]P(ŵ^kq?a9>S0e/ju?:U")e0nJFja()DjgLz,Q>ic)6GKx+1&?`QPHbs&42^"hߵ*uV_W˻P^jeYMѵp/_-YZ=-h˔]PȊRd}R뚠o}Or L8i-n]}RM6#{p(vX줷 -'&p M*D-t:4n> -stream -T٭f 5XL\ q#DS?5ë=קHֵ?APy; Bg}+0q`b֕gOqSidh|wz'ЯXuc@lZ2by=0 @Ћsr>3Qe,ɦn.frߢxGL! C.TPLaLx?2REx $NSSKfc{q@ -lN1$ {6:/jނ -DwC gl ~G-.kuEQ0aL&uHL3N4(ʴM.Exͫ|՜+t8UAuѦ"ԯvuKZɭc8fQ;͖[=fՋ \2K;pP{LɚkPMɷZbF,ۋ:"0qk8LEY(ߠRFYze1[ͣ" xdh,!}l-7tzoZcg7viw_{USd-EB*Qe[>8r$vggp  ~E -ӝM7ya6؅y9,t/_vZJ[ ;0: fVӪ*X ȚM#G92B7T|dxy<9 $:R2cl;dVͬS$q@Q[kȵ9$NT.F9M|ϝ h̯YyIuoKP!Q&TJz9xZZr0h|5,2Uܗ='so -F0k9Kuj]j!%ME<|"N@rm(`\j&R{J;^oˁRCF%E4 <.%|E;s4>lf5w"=7f m0~ˈ)i(Oʿ - zB <57j1IPwԩntrRR"Eb&L{Bn::RNZV#!r5yj_ -_w{s-'g4+fͰ1@#9VI)۲LSAP2ހ Cಝ4ɻ:ADW]haΨY>.p|Bpj߭j6$0v-\s1z@(}Q*}V/"Jd[odI#xZ}CK|@T+[[§olS*y^~v#h|`7߭JQ:vEtW\+ /@ 7V$sZ9!V!D NOCr'&j'cy-KQy .)"CY#Ƿ_M> -stream -4ž"mTSŐ:ֽ0|A -3"\^"`yeMCwlSTGImq췄LӳѡNAxdYĈV˃n~E^MSnAq:*E{L]sgXzKcvmo j@etzҮ(*RYbCzKs%rC[3>q*$y}ۊ oǭob[gց滇fzyľLMR5;?ɪH.="'u2%6„jy?VHs}?7듾g$XiШ-{2e3c$j@ImǑU7U'H岆jt2H2פF 3 'Wa-^\ȖB_DX/0zCKc#'s1mݕ+rf![Aw )ZoakMiQ[o~*h2W QuK\kg~EĒμ7%Vqf-l(|Dq y2)J> %>N^* 3p191(w=HZ:MqtV2+Js[k K2ZzBǹz+Bˎ6:Z~=6>gn{I.!itw{>Dk!@.zn`'%Xbbs6V8G/`A *muZ2M:=9,6DJP9&']! LT0wv Ҧj4f2C#L#|`˚#`{ #\=ObMiEK -n͈+$ae\l!M3z硱Vy+CÀ.<4o w}- l̬zpPhj{Һ\كNPԒWX XÖٸN_b2z{*Yrqvn*?sˎsJ0^ĻS&PWz:pӐw8iBfBʀyPks⽌6HRP DqU4G[EpԿ"=5.~'R4D98<ۋjk$=(( 'AIEHށlw50Jd# LQ(BxMOܡ@m9AxJl+ÈG"Rأf``yc]M׍UMRa+;E&5DCbOJyGA:,4 #&l')B7:ߥO>dDŽGsij@|STu&՗u*yeY/@0NQh'jcB>DUwT1_y$l2#7*W2XPK1Qw.7|R[ [L.)}Vn<-؛?a@W ÿ3=4sXhgjLW_v`l=tV~1M9?f8ͳS!ި-l^4"'~E3MDGo=>V(B.}I8MA:]?dJ<. d^Y$^CW Qi(l=7ݓsXj &mkme=3X2^,Ldendstream -endobj -3964 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -ֱ֙m*UA|Lk̞o6ׇ|rPWTC"7dAv!|Z*ku| 2PCcVPlVO.^xSVUW;;weez3~kS9"ZB. !~-g0^RxX -HTor.@V?6ʒV@W]&߃vMw3XtQ_"r>ʮU(³=4?O&ъ> ex+? Eu<:(]4o?K⢠%;r byׅ(2~/6_. !SPTkSTDOݬh%F}-95Im$_9#Rb05U`=iMQ M MA=>j.5\NN`Na -LZSі{:;zZ.׌՞d4lo=}B^'ё)Rvh=MVtaV,zqBњy|{pƜIPӌbeTa؏ A?_X59zԐ}O`H㵮W,i& "si$i`eF7ْPyBk"PÖ :LLjR`n;uRUPHx;kQb-Fx3BV^~|j`pb*Z -@~j2gEMS4| Ta2{LGo\rcXKg?OIQr#'#\䆄[t182JN[>UrtDZwIeɾ,)AIs[h<0[J!aP)wݘWNvѼ9s,̸HhtZϜ뜡^3×NBM<;ѻ .orU -mUDR?O"P!,kE&uX-|%Vn%so -}&T 1濿XZd9e,k\\(_'t"Pk9mxs:H[Kwʊj<9@G^͙A֤ed|6n=Vc`it.OGY -ܟ>yQ+)j-)+cl:5mu"H-4Hq9x|$A!9;,c|lE"hPa+Leendstream -endobj -3965 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -Y1 8"՗O\(MdY\{eMأ 5Q@Q8\Km_tx\L=%fE ݔ|]`k?"1אF - >{2+(d]t "xGs+qZӈ!#]4A,k&::/a IOmcOi0#43mRf`j)8qO \j弣!sh^NS֓İF;$('zv lq4l܌+X> "DA3Msd{j2ȱӞ, PG.@p+:mCP=;/LVHPȽm,Wtbz\m_vzPWtL*,£'L@'~fS\lwiFshESE°9e?l妧:fi<1תsĮ1b9""$f=IUm YǗ(gPKA_;`5G&,W +@bqFّ`5@D`T yGuon2廌 {xz, -@agafsk ff5IPdWF(ML}D|.1;5]I)"z8 }ch 3꫘H,I/ )O݉9EZx=9ئ@ :G/G 1K*fHv9WMۏ_|dOσ\ Bu3Z{[)z@ Skɧ#Rjf!)n4;ăgeUv׹*CaOHeb;CV\{D\XS$H峔%IiGÞ ػn\Y NTS(i;a;lGB. Z*KA,Bk_ڋGS^(#owj G})͝Rz[4áڐ8[2L4"Wbσ8ꢚ$YFj Z_9\.}ԊItkōw~==r<逴"aC#ػҨ.1$EB qpu~P_Z#( !erZơB'ޚ4zW cj,je}qSJt&3m֋$f:Nt3w#`~5 -|9p=8)|Bu۠AJT;.I12ټ>xsuIퟹ"lK*A⚺{#kmB'^6ah9r85$3`4n@ {9v_ƆO~l:%.nS,9Yc{zo!°Q/k~p%jnendstream -endobj -3966 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -q41_t -v)Fbsվ!*B=g! jMHpBRpt^)!|4̾8/FD[T4`.X9@d|X<TݶK cxI_BdIm=ΡumggDmu/x#a`qsmtaGQC8D~pݔ`4sTӁ[{aaSVeV tQ*&`yMݾp^K2دY*la+q7sS4R ;Γr&C.QgpUܽPpRΆIhmQߟoZ E"-M+JxeCx -#!U4W[iO4lwJT|9tml0[ERRgNJJ_Y[NQfq(A%'#&Bv@!qƯA.مQ{aEڮxBw# ĩ`s8~:*Iؑrr|O -P,=CdEAN Om\B_ ՓhJtbwH@_?,*ыuЃp_ _S2K 9^7Z-BuAF}be}KhZ)qcmp0tHtbW/tTEȥWtUat}րҪbSuz…b:O4uBo(/>$Bvq)ٵQ}a? 0L%]"ݏ;e.)!dHIwnQ1iLK?l_l㔉bN }>8&5Jdb<.H{Z‘l5([SȚFaf]+o\%ѡJ'kUJtk) ->'A1yd}ƖN' \A BJmows \e5{܉ 3ʈѴdE+(I %IfE`V Q.hV"~BYeF-8dEJcefv(0:SArom?xnPs#1>ՂPYF=mk*G~<`O`x,AcJ & K u(Yt(cbLŌ -zš>ӌ۸Njw|_D5 Ã[}![=V (q7Q3cV wDRH#i$=G}"xF+#6XgE盍hFgN]a<ƵU>>szΗh:gghθdiQeeb!N4 5~X3SדD`(s5Ά_LHEŏOBkR8ܾ)ce(:EKE '^ j`3ȭ)q7 w3c{b\Hendstream -endobj -3967 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -u-c0+D@`R eFպ+,$Z\BRG6ޱbԴIZB%XfViWB&G^x]>&]6[ƒE dIpvMTRx^\*ޅh4uy'eL;vKp{Fo['l)[y_gE얧\;5`aFKXMNh2U:q7 -zx GDL/h ヶ7ިum/1 VnPslv,Kgmw.Bt*\&a\8KyX^z[̋#p^k'l)LDc^\zNJ>{7ijCDvs815&?N@0ǧ_ӟNg*"$u7KYIk@c2^ [ sگqAdekWӊV}5apg!ױS[ͬ -K,Y|xt@Jjuw4&/kC:qńk<5 Y51^K%"]k\w7\)AY V;Uȃ-71hd'p2j(@l{̟Ilc56UQJb)@IWKjX{-}vO΋nm@jhƅs|CTfHuQ -O4[c> $;qK7]Xk*$`S)_nvIܥRsqT-~z8rv"!p*hPu1H T򗻧v5*o~.$>fCʤo8"kS*z:#F<*Je.:@& ~1P禬 1u`Pz`$>{{%a{ߌ307sLe##`ƒOC$\ik/v>&Jcz3sendstream -endobj -3968 0 obj -<< /Filter /FlateDecode /Length 352 >> -stream -Of5?aedmM<M^&l'ř& -{2xAiY=M40Q10x=@KTK`kHOyzE):jW -tr^;s̵QTO<U\N-]Lf4@tӚ ּhtxFJ Q ;^FhL> -stream -._\c Tmp3oWv ypw'ڤQ`, Jqzuv.m ;&bUec7f]}U2ןM_/Dc2y8wK&ޛ]mb>,\/.UT =`̣x)` t{"nbi'D/4 ({^^mN>_E..1|y3 [dZ2gWA7G_:tN0冭er$Ѫ[@WQJTүh[>C. _XSǛLQu]z@5ƕ:qee35? 1'HJ@΀diqdd2fѿP^r6B/+Kͧx^xR3A5Tn >J`1 -4E1tBgݫ8aF#DZ]P]YJ;ܮ}}is׳QpzUZu> {"h|FE sәS=;[ׅP_4bxz{1;+ +_|nWWP}.<+S䬃נaw( hIUbd >JfϖySszȨ'<=VW7av,"\hY!gFV"?I;; "F})S|,eG*H=mtZsfʝ :4Di$[oI`;ݽ!%!iTX6VyVl@lg,&(FuC㝻F=2 xlIKbO`X@ڕ3J$%aFNֲCHƷTz+\-znN"}|@~}NUښ<4?KSq {9!$c%@(&](fN-R%~T @qy5ou{oVs,ݎ.jt4$( Pa7 Y -)PQ#QLTdssYq.z)ahbtj{gBL ײ fA=&'%؂odM&Eendstream -endobj -3970 0 obj -<< /Filter /FlateDecode /Length 1488 >> -stream -g]Gi'GvfL4]b'׍C50o -Fbt{9B&r4~#A5딆+K~تР'~ԔֽiJ+l"cp^f;1BO ɬl\e8? h&D -?/51@wŐX*:9)pr+ ɕ5MaySlaY#!ӶcuQ\"DB(>Skn>HY=v],ҷ2B8(q xV{3HQzS㞕).N5h6|p 3tFsC>Tggf(y+{XRƜ1p_4JelAZ/jx5W.v~ts- I}e;Óbܿ  g¼%إH޹?{'2EEa?"m|ں]Z2(Nӕl* -gS|uwߙNUz$7IUF Ej?|:I9~Hp{Y -p2=)G[[L#5yrʞ F:ˌ!,D*EWɱ[:!7I4eiFϴFh: V2{G(<[k֑]***[hc2Ac.$ӫjP˜~ ͖.rDpKvY_|eendstream -endobj -3971 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -KV&F;|3 -хf"0)|6\Hlj#ՕI7f`;B2ݞ ^[@xd[N&0 k~[ Fsp<3Dfu[o.7۷ʰ[vRK} _] BqD }pN -VM|u)WnS 3!] NcT`+B~N_j&ŒS.Z+Z⏲enV1~o 0Jhoթ J><3bV03ɓ8-ڈifRȟJ4((b?}N N<3́^iFjZ|Tz=zN d~.OVQM/KhX!d,rpXh@\GuK,r |Gm3__ ^}ZG'&o*'84 0J.>.vǚz ˛),.&-N3Q!ˊ!-ɲb `l^z$Co֣k!ܥD4< E U YvhttābJROtcf!Wi+zITp&#TTaޘIlߖui\)")|'"UvN,ЈLemmj0 b葓60IIItkN䌮$ -U 'Pp⊷Dѿڐ9sE"&Ei[v~/j=@AϕmR/5,\*n `81w– -eALNN 26M@?endstream -endobj -3972 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -rl,t pmt43by@EbZ.DJ3XxN~[T()bVU.gWƍؘK$nyADBFEnq+Ϧ%HF-4H}ؠ0ݔ!#R4ѫZ|X~Mß {KV_8@"޺_hP:F ,<26 tUto7R1£̝Eu1CYAUd%$|5w_FRF\ৠBܭ|rgŬ}iR@DdBnKw㳅"BOK\B3YeX iLRlH-.?̬&ȴٹxh$1=& kn7||zْ.,,?1Nt!DW6ޯw -"qO+x(Tc>3gId>8ROO8l8] X٫+Ԟj*'eOYU:3879mCT7;g]L+{6rmOp r$*&f9 j ú˖_gg QPrŷ䔌fo3,KR'rd9@M+@8}m/O9̢8ڏnHy>M1]}F2#'ͭ+\3O?)I%81f[ݵM djڶeVţ O$ux-#-`~vY{Y\j|PD3t#\JMk,뾿(14ڧr^n߆Wh`k*GԸH_ބODjW<`|M1eɨB֥kqds|؃r(N4\Dx8!WwTwمMX!"JCV 2TP"aVS;g6*q2ˀw$uӼRP("rA{sd˚%kp`\ɫ<[kZ )gm>=l:0 "qGgm8Etk9*2|p1;/3֭l42NHC։jCǂg*@yzŸ 4\7zչaveyFLR5X)EħXaf@KsK@tWendstream -endobj -3973 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -Cɚ5Nb_RdtgKExy7%O++yy]>hs|/3%2>ךʌBv KCSd - ІMBZ*KuCOh󎹉UAe-Y+U[ c4apN(D0lmlCs5. -Zwn;OtFvhkH Sizx{8g4}ˠj?W)*RAʧ17e2Wf5 _&_!X}ĬW4vlk T[7]4S.Ztpٱ4 bq(_\zNPz`8rڧ|2T΃cyn a?q:P>xU KSv _'sadCz`0w\]T0%VhdW..@ %䊺) (QBBu5J# -o{`EƲ!O JD2e x@]p3ғt܁g9=QҰM3gT2JK -v lcƄ#%ٙ8]%eNz+/;q05*+#͖R%:Y} 3cgD%뜍 ;"F蚺.&7kEnyxhn,(B&G5K-QScMݜZ9!FU!s1\hwk^FC+7nM~ s'S -Մ0ߙ`XT  + ]xݓF)3{+:4endstream -endobj -3974 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -"6 &2 #1 /r̿ )}q"|ջ<*a=bg^(~j~*tU -hM6tKf f8[ X7K`IuezV )( q1MK{k4 QT4P&63XawS+_ }֢P"9Ez,i,M)rKT>ҖcI`tND!C4 򂩄=q*``XpS/ɐ@Ȑ<';W[C2Y-.9/G^teJLtiLeQY>o`P.F< 5,x=NQi4ۺ)vcG! - րgw9,"S5CFSz+`Q g&IuKu$iRf W6!bSZk3\/ǽ:G;u IFO>ݧ ޹K i KdM/سh"ֽ>2w7ޱp&+v:p2e~+?0F![_u侕9s "_A<,3g9W;>fYvetzX2\?g1Ӱ'nm>z,7`aӉA/Ѻ'2G)ڱ=-X&~'s;(AǠR=I`=A( R'U6AVML0d]Psb9xC1\:~2av)ˁE_:$LЃl>$;h8:8EzcbhG@ V%VjjvwO}羭1$679{۷AW'DFHVx # z={Ws5C2ԗ1% "v[Y 8ey?*ס ƒ$ $PUqvMfr7y9rRgSFKM$7iT QouXP[K.MT)6Xn4~7X#۪PS8p"h - }hx oaŽB&#Q00('_+C:H q6" )2řCM Mt&fPPI(.L?svahU_i(X.³J VØ%+2x-*Yf-F{#H 6˝%mO 0 -Z=PZ*\rӓ2[ll2Yy}gA10W80cZ,foCKeҾ3ilOS5?J5,8|iH) {v2b[ddŊcMǪ~ `\GK*4qE*\t=ٔr036ɪ> ڽ-bRoLxQvv30& ;n%mQ/nb&RPf/eBiˏDLOck2sڣendstream -endobj -3975 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -`á4OW:cb1s(32-|WC#%KT2, jzwedlT!Qe*w:gf=4`4 p$~j]FZ f~eF|2qs@MW&O=T3Sٷ5sɛ?iRygG5[E E { N4z睦55%Z ,L_!t*aK XhI㎓]l3o0fq}("O冭yyz_4aN@@Kah$*VW8*AeC?D|k$~ Gz2v{ݖ/ҵ%pJduhb4B=o&20f `84'%,jޗ"ytwHZ%.[68ǩ=Vմw*O{.v$lQb71m{UtހrM}4Ys !.$pؚ.Vh\ڤ+oQCd(EJ"KyUWvh.,;<2)&YqJ7~xj$t7/$G<1pZ`] >3 -=e >Q鎸_>PolvR䂋BY ->uKdP,i눜m!}LRg蹕@[yl:YFA[X"F%<@%ե`ZϙqXs-{)ʺxgIНJRe^ wjjQZVA |LacIZx0{(C3$}^8%٠G"*x1 k!WscthCL{Q܀h%T4FM[1jON)J0 -ʏqJKEi8,Ǔ|^' ًNSR*FʍJdž j8BrIjHS) DbTȫ>AJۧup{P-Q&4L`"B$FFt\u!vp;ʟL.*ΜP޼fG -LjJdxi@B>GO(O}˘Wl ~p,.-y}?9=Dv4hj> p f"x8#?7U~֥`>3q`s&"/D]> -stream -@k RX?j|'V:f}J9Gh_m}sOLփih!92yARGHp{w EN16SRzK IPl\LMc&U֛pO_B뜷{-1Ɣwt\-!3éB+z`ɚx|*vh_:$guU04y%!Y`=c7}F@" !"Zy --"jԧdD޻8ꃍI/6|(^MSfɃFUfJya F)!c9Jħchh߁d.Jk77( -xTk=,kݲ Ç5z(UiH,q,R.@!.mc۞a5ܢaĩ`g3:"og:y>[8ۙYgGɽ PڃT.ޫDnJ~x >D 1q  9zGq』QCuA }D{ |aghιb#33d^8mo- ^$vv'r~}aՐNǛNF4w3\(endstream -endobj -3977 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -f204Wsҏ( H1dȽYO?D!5N917L2gB6g+]voHQfQl?;:ZV$pK"VAa%;}<~S2Ü75pngiUU7M&|9߆cmB9/^U-iaUr -ua1g~8A_ؠ3]h`(GKC ĘO",[$,_H]bυr16RtNTv!-%XKryeAi'Ўl{ #Eш%dԳ=mea4eThǒ,+^K1C;L}pos_6p|uaU6Ud̊c )7O@0vb;m 2< $M l$f[2ދv]eڅ؟T"/%0+A[yx'G8k#|)lq O Cx%V -/&eJVXB#$mD9{nsU8S'}^Ӈ^I%G\\K]e"!LnnJD+E[dځDSvxk^dʬwOj>f`t0&3+SCͻiy< `N(TvTps#ëpV|s]$6qmָ,EE;5xkAxu̫Q$s/S'EѻS򄆘* 1u7]~!B? =GuGc KdDHzFY+YL@~PL{P}uZ`~v Xcu.[%E'4>AXzˈ[-3ʦpR8TEN˄ΐC7O=s:uo*7l+*C/G{#(5=gK5vP\|.̉4޸qu;Y7_*y4.p[鿂g.!XA}v-qƬlWy™$R~Z9ȧpD77'Fi8qf0:g5&C2{[^JVd(ӄBKZWCD3-_39O+X(oԨ^9_ju`:7<&s7-Q*ԓ endstream -endobj -3978 0 obj -<< /Filter /FlateDecode /Length 1504 >> -stream -LXٕtM/_5ȐCZ^4$o شTe5lx&+C) W}pR./h-kU":;a C+v=Z[3uyoۘ&!Ynw=~gQd 'sI'xݡb >E]dDzzď%mвt<; 32:fB ͛}[XI.6:0JUOhv޼:hWn~ Ft(pr Z*WyK߮i]0nG(VFi3"-T,[ g> _Y6ܰ2H<eAe83 i'"`8], -޴'yIuЊޏJtl*/wv„ҪU3qXWǶ0Cl䘲V+ף,M/Gs\D.[fZ 6 wsGUzpVЕ*Enk h| ёuxo46H1zZKwɭJ 0}e 0 Yq!ۡx)qY -+6<.l6˲Pt~XW)}#!+^^o*F>бw&\a-T->0|߷nq۪r -E {~nm֒YeJ`yJZJ9JN'MjRoddàr;_ֈM HQsbQ˜ȧ/5^dWˆhp(P!ۋ(CP9[z"{!0ѥ"^ds?[YHƄ뇖HW<\#3h ׯ?~{F/v P7'^nfCŭE'PAsTҰupr#oĽECL(jF Eb-|FK> -stream -I_<@djL"n/ _#VI#hƚ7ሷq?Ƀ[Q=nv83|t4j ~ qZ|6bDWA};S-F\1,x|eog!P__umY6@mk{2QfB2yp)mx5!] .`k.kJ cx?QuV!ws\tu-漼 3N:uMqf̱ _#}b=[֚-wҴ}MýLObjTaƕ|[=5XpհACIpc -|{]f]$w͊QdכX%68Cq^hܰO\6!ڈq<9m_YHX\J1 -D~wso;!˝e^+4-HJRaOJEq:ũb[ƌwn~yNV[d<4( kaϜ{DF ~S*x(ð$;Tz"!H\7P;҃UBg$>A@-b Vo6AE[<sx@Or ?! - r(yp),E*G(=#ĕ+,ETܼ+!߉> -”`S= ljI=uMJ91q;:4v̕R.n̥bGߏ1.a s7$޽M|ŽG%Sqj_Ό@Ok(q8a+bVu]?ͤM$i>9u.8"yg2\/^!.3hd[ch2ɻ 4ZljWW -r{rK]~8_l{ L~!Hi>۹ڂpqڔ1uΗh<b:Z ٚgK.džDx.Zc:\fbby=Vg ]OWbg XO5>?ȍ=ñ$KC8 ՚OKd7(O5jXrP`g'ʛv?sUs!A֛9eҵdܖb)Y4) w'7d5ώ\ z&o^fl.bkn -)ӟ6Bk}z uׇ\omIb|("CJ=) -v|%$J @U=VmQ~ 7,Z{f7}*e ,Īv{+ZH|2v6]p $75酧%UF p8'FL)RE5&-ˀ$>VW"=,xCG WLF߃٪~ `N.\%+9$Wv ߌ';N,4N K[%DABfi+}lȴ a{ +Ք(W>MZSmz"UgG}1ˋN}AfՖNc -_Ԟ -bo aJau^ɘˤZ9JA0W1H( ,Uq\melOzI%jBendstream -endobj -3980 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -cLQג KL.7`S3D*E7c,Jf5#eGLKڐՅIK,Tg߈Ido>uޯIDtIY"[q WV^o,;פ}b(\ݙ{,rm}ql jݝ ; ^ Bnmپс~fDۙ|YS Uy1R6Zgg- bR,}zi[áwd}LmJLd̈IHh2&2NŽ5k20k)5ÒӔx}ʰk}ZRbJ1%_*˧X]CBx\U[/Gu|{21 i8b?,MOsW޻̳grbkrXks=rl}$9YȺ#N*מ;|bf#BMy7&?eV;M-cUOp60po'o^*' uΥZe^pPcöl;Z<sHMR/H' -"147[(H& |Jn1l_K Bg/|gSNW#ӶHz1MPtRgPkd3ShI_/ jMز|bݺVFC? "x^X ^Qg ۼJX<A# ey$ml̫p98"eU2@ vp{C (Mm:aXesJ0YZ&+{szsSM@ḌK>EH(C6Pln";524#4Lsfɗ#n?%jN*]$ŷL49(VҲ3ZDZo ?NRLmPx G=;Foc,ˏY22!xFsZ|Nn.%bLendstream -endobj -3981 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -R6TcɌO }l?iUenoD' 7ÉOpL=:v^GXg:kЊAhz6eıTHh=@] Ѻ^Rb(_nݞ yᬣ`//)^%9v.DqnV)݂d8 -"ZѫprO\BEA"1πҜon6,iEu f9 V<لa/+[!@-U`kz/3aT*65@ļЈgZ)pu=kMIti`6ez\.KE o::6lrT& -auv4| OYXYyb{h -0&C03ĴW2yrvpB6 2bg+ d ZCUfEvm4i.i$V+BC5]MXIIZMp%~72 c6}[Ui'Ss~,,dv^'zȒwz;T(* -s@U|ؓKZDV^ؓO2I&4S)LŲQiN=j^N}ϫER3EUCr_C;\;&ݪ۰+p1Yza:M>#6Γ\eUmMԀ~ԟHIMaB4% m5^Fr)9Z]݉٥ozeB/u>Nq{b+"<Ow#c4endstream -endobj -3982 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -It4)= F`dn0+B6 8}3GdeTtPҖ3'lP"|JLwQ&2Q|N]Dhj$zfeͫoOI)le>*XbTo+?}Bv5 ĕ~ߘW1'`[1N,6&[@ݠ@0[s6DWzp ʫr6kQ՚4{&h KI'nIWg_ߔIӐvuHuDGVоt|BPpʊT[%*%_ 24)FąRus ipZKusb.k;k*qb7 5]u85sU9 tԅئ*ƍ:|t`. (i$޶5Ҁb5!%6"DzV3w **1Hp=j1-$ `P7$X/(}/ZEr947Ȗu y?}C΄IrM J\Rqv۬] J:z9tes"Wk; 4Ěw~ ^;V,3>Bg]zCC#י!J)%Ei HN;Phor"6Mi K:enq5UMȝ;'F!RY-oo]5Q!-IqGv/}kH7j<ʚ":; -&Zɷj,xP8<)8O -ERH?RDT0A.N`Wx_mZc-&tNAه9#"7jm"7S$A~] #lĿ&?N=yW1-B,OPfLOQÍ V -%ShouQLk!cO ;t,E&ZQ -0XEz} :R-"reRB8mmOӕށ|1g8Q?TL;ATKFCN ?gL-'T?($2_-qƳ ݷ?i4ltKV)wJ>tXDϮlM4G?iKq2Q.%Op -;ed5UP?U ]8/]mVUrBrk3N8EmTirt"_hMN pqŗO}څiG"*&Ǡ]H9fe!cFh -¬nzHᴀ` ~it`N`A(tޱP_|4h@vW4(NNT> -stream -2Ao<.KL' ( b" PA鴔Hݜmw+~Ӽ/)`0_]Cw7tԿNjq%d.^*í,WtVXصy@Wͅa[m/> t#-Q6`j]?m:Ukp(woal6/x7:[$'$/JȧxG][8i懖pXJ=51Gs0pW?u3B.8Nkۊ 臌yy>n\}C|3xUn`Z^Wrl6;Od7tݑ˥L^? -\.᙭z!ⶈIGJw -؏{6hxNTl#>3K1MZ3 j4A=RO_ Tɷ;JKɪCp 7я?v 1`+"D|ry(U&VD0L5}kokS`I>H檕 n$Sϳ~2BI |IO3!گJ$wiX> 5]ȵ㻨6=#<ڹD9dH[_k>woE6 +O|IݡJepУ 2Yܥ?"?Ή* 8SI&ώz! }V1[R=@(c>|,V$7+maJ6Јe}"74 W)EEsT̗LjJ>6NGZ#]NLj76#x협heW@N} -I֋pNBnjD6 }: K -,@ZNZV?EzaĥhbpA[SqF($ԒhBBб'9 d?0m -I įGϚ6v)@ؠ{eB!陬5$T5HY%oۻ|cfW1z8,_raS.T&"p7DfwWy͢:G8R g3g0ESO*$0urG5Zf Wl\ŶQ@QJL -c:Dendstream -endobj -3984 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -fXM+$en 3&hQ/ iPj -zvJ?Mto1/EeW[q3'\٬3 񼢰>W% -0jf٩YI!{~uXan'ڊ8;R|` fOC$*AVA88NV"dMk]!R.I.A0~ C eB,T8wzYUG_ߔfWzY[1ч8(qʎ.)sC4~#bAC <U՗珑eHzs;d!6,iO8ՙCI*Q3< E*p&YI@i2{|E"rL_و^&-gkۇ:EtLKСVk//X$Z'q/*T|JI8dZX%ȼyzJQ9YPH%f5"o, D# - "j"`ǯLx[Z2)M֑RdhEހg]&se\$B ϱ.!= ]2A]0qlwA-24@"";U }8go|[$ç#V3mg<L2,iɠY.mˣhcyљ` vb{HZkT~is)On.dFYZ&20bvHviY^BR9d-B\҇?Iي"(ve揑uC _$N4:M{rN#p#!Ѝ&ὬkKz!OL՘fMo@XD}H&<(y9qJ{ [\nB) [ui2|}PH@~`Z!pNQOkO掄+&`_ j_<.J욎XofjybrJE m#3Bؑx0c>eh݇:4[89y>."T4Vs~s#Ȭ~J:бrۭ[$pzpeO-4a֊K~ X7-HY5 -zn7Sj%H QcEɡ^> -stream - 'W0 +wG= -_r B* #0F$Ve54 pi&̤%Mֈxny#dXB)&5}ڃN ;?5ï7/88N2tmM*ɱuן6 F)F%?]D| 䆔x~1/ZS%Gh~q?oPn&)OxS_M2W&]<\ 5І/naVdB<}.zdC6 -Q`VGC24ub\;٨/1gXs=*IŚd.D"-9o rWN=hEmux-Ɩsh8n{P=0gШqrG-2|E'1f_9C:[H&^h@:<*3'[> -stream -??jBMɰ7_ ^z,y3U_穓B!e -]&b-o~T-,0ޛKrt0k*IL~MWj4 ->ɁkT&;/2ƺ5 Ts>PA{L[&51iPpժhy;_ #؆rx=Ag2!dh;Y*aȝZ[cN7W_x,^BnJ߻[=W[w2Ӏ;85VBCZ@fC֐<dz. E]'cjp.zR>s^+ot!orT27L7:%󗲡Jf8mlm:͢X!NJcC$D@3f`7,UUڼ_ -zjϖy4n}Ind=4F%cǡؕƉ/;}QP*+ZDj-HzO?^ukS<Z?~4Xԑm -U?Roq=4CسV^HX$^5Fs/W%=ic#tYO/?>8qs@Qyͩ.;@QD / ?`3DTB0Pn*y(kɮŵV1/SաLu- VgO - %lb(ϓb·j>7DEҞ6ɜmb~sWj?vҘ>g_=גt0gP4AeA㾺CH.|ksT;G%] -Q1#lR0}ނڪsSG܉Z \Td m:Hԧiȭ5H1[ $'Դ)xtV $|\WKsٚXd}iJ<M*+ivO -%N(:#䌘$"l\[hz-ýP}Σȫ]x/A -[^(3y7=I_p3K02>k \Y8}&Dd9234ބ6w&՟ VcyB3#]-Buw}w$= yR?hXuf9.Dv`-Wl[̧Ĩ="9s\ `od5FJ"8 d;DBWC_lFJ .cP[fL?mO@XX_+Eg>,xAaϰ~VB訥qGaG(d;OhBh&&dnOmyBfmtO:7+"эON `;쒎nT?{i @xeVMOe Оĝϻs~I1.afǎ6AgDNQK64r=O%d*x$5R0yf%ޱ \M\SF:J*B!4Aбߌ|d\ӧe@uK6!Lbendstream -endobj -3987 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -AWQIQ -l 4uO7m }U$3Th鹕H/(SQr˅V%AKa -0=MpCҒ)+.:w )NtI QWR-P@wbnؗ -O‚0Fۓ[>:6lIywgZ8A/Vq'j%zZ&;'^`LapYc B"Lc0G@lNxN+IҔJxXr4vs6Zƫ E -Aj4o۶:P`djO};T<@(|3et&B? Bzv -{e?WVxH8Zv_(^Pdz+r'az+&;oKc񩨦"ЭL~}k]j$h0}Ʀ:,p/d:H@q -F5¸hKJ"&0o+Uu'2|PUʶɴ6D V DXXDMo<"a,Yf]^:7p (e`.4;%їN}b˟޽5G6L?_oC:2M u>_go缡diqtTm+HhW݉e`(ZApl&"A jN`K{E󉵤eEo=v$Qf16ZiJg,5_Ŵr)^Ag0!6[ܧ$ vuKd$7Qp!NO*@W]Fpy{ކjOCS_TbHOa7X&\**+\F@J s-:Herq IV4[6ڰ Zi#omO bFaG 9>b]YWX =kBg8 __+=i [¦!܅ACPI^I-b= /e>cX*pM] -; y`Oaa=_ԼO**>u8`cXi(\ -QZ*h@zcuioGTӤPbxUB9$F)v -A + -_Fbc ?/y3C,?n ^ߘYt[G|ayPS2!:Bv~p L|ط}/+@hXXII]\/slcE)<úB_N_`N=RVЗw*;sWQ -D/8(ˋb,/*^[`rE{ݜΐ˯UǗ@{ك1*XʥcbjY2m9笪mx~qxMsf T>QL@3N%d֎$T_b>R^iY29G \OPyvEj^8 Nγn)L+ (>ZX= :|EI7J6TJ7gdFfZ:eiA_´u.ś{aO!o=8+9C⸛ -i>Dkv%'Q5pendstream -endobj -3988 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -N>VaȌB PͰwZ^ pgqo=t34al@ s -B_^̜+V_C2^͖3(ɠ)j:&vrj/?~'-.’"=[A_y V:*ȓ]钍}΅H" lU{Mڎz;LÿF [2CTQm[(J,*kLV>$#p=Ă(kE7EH4^[6k4E&B3pċec;l50e}cCbLΤn%ssjSG ,F _tyE K fHƠY~KA2Fd8"0(&I$劾9o=Z2)&U9ᾆEc&t E6]BOJh ]k2/р! - m;VOM=Y`_o֞<ԙAdev#(|3z~PWMAXKӻZADŽݐpk;N.>&ҟQ"MYS-#I_)RAYx5#?<.DBv2ȿ8i\mB~pYF}b[w7QՂPDNq8$4. T(.};,@&*:خx*>RAތ6@@D0h /(0cU'7qB" -DO]ylOSrl4= dUfrb,Fq&o},G.xڭUC; qήb~XFZy9YO#panʶ!Y? CֆHG ř2zNkhyhRo(aԅXQU'd~m ZDih /C1y/#]$-q4h8q4'fի išN:>Z?1dPO6'Hh|@ Bm'4AOfvZߋ+ &|7fP4t3(rmj3{;ql WWԆ2(α@-@Ș%)Ej#* 1Ps?:x>.0ć.6b;wpcly䫠Y=妲֎v}y- ӍJ.]5%YjJHru?Yd@SJV1KB)hI:":Whh,őh -r;,0pitOp3E"D^ 7C.qbp AVdQDSP{]r)wFX QzJ؟,‚6 ly`fD6W@xb WS(M$l)cRU+$w4 h9_\%_8)G̐=BQzM?&Dd.f֑q  nꨪCl|dpyl83!s:"Rp]MXԈ ͶbbA;xMC)ټV}3y?ëpyrG6% Bxx\-ͬe`8FC'{pu%8;h:*SLvE7r6+ǹ)|T8vON*g e* C:L0yon_桋0C>{0q_⤽:7Ѷ-f!RoQ5vwBz EJ{3WOrudaq:^%㘛QAш!xŠލ*K:w4zݽeU G)9r ll>4<$JDs4x.w!v;;¤ <] -d:93"1I81endstream -endobj -3989 0 obj -<< /Filter /FlateDecode /Length 1520 >> -stream -"S{I/k-2(YL<FfW*jg h_†N>UDASfؽ5|thzt/XqÁ,rg Y׎ܚ8/QA<.H߄RPv8YQw![H/⡈*['=55mz传&#jغLK}O\I"p%2(ͤoDTA[)p~إ0.r63:̋",6p:weM9gƣuˉVZi>X2$ŭ:)GC?K!aW -5N, 5RJL7`>qhM ÷YZ2Ȇ,0<؆)wFTʡ+j izGz7cf$u}*ׄyNo~53PFbd&Qo7@5\:1 ؝BfWk%qc~(Ƭ='d4021C6(M@=&ΛX#nt.oLpfNGmAf^B0zP_&#ب k0_|d5xMб==&XOѣlGf-y09/?-"@.H+&Cf1Tʕž<*Otj N?OmQc#*+z.Ċe) $͛=j<c1Ng脋Ka2`$vIg)x-U0aǰngBޡ}qB(|Z[ --FRip`ž.bEGFP:]ٺxwY ź?hQU-t6k8b? `9*DF+ˢ)O endstream -endobj -3990 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -f͔Z8K7Ҡ|d8q,%}8yJebþ~U$1%wmF=K׋, :%w? TbOUb]ft؀Ji -єÔkM_B_o-cc\ސoy,A3<"Xmxf}KJg3>PI-&xBaj!E#vbέ:FL6\Ju+H$FHWc<0JL |RͰOfaG\i@~pvOtaI Tɸ43VLI %}2-<OptK'M*Vrd>d Ӭte ۞?/j_1͐*ZCӠ%*PG?DeJx4CAse)1R?@&ejLU&dJs#j7]fс9ژ0b?t\dYŊ6ɞ \dOxt.891ARK#~YƑ70Zd N.s 9K,Np÷Q_J&n$ PcJllԛ4|%F79)IWy 0h.$;@jȿ|~IǑb{Uo`{x؟J`{FR=|9D#t`6aAP{dL ]q/k<3yX6FM6cx\YN}K`t\1PEDMw,Yݰ&:qd]KUwum/Jг}= #1bZIVј䐸R'{=4Yf_OyxenK FG=X#μF@֫{"BO`L ";A7pJ~H9WYǵ/;SaUc`4j65t4c܄y9# %4VG{23 54zҫ=s 7Z%T2leBH]#`1vj -oS]РBt-ؕVO܄īpG2` |EX?b~HbhFB#ϴxɘQ;=wkY:d }Dȉ UEtBL%SnIg` -t>a`Krhn|f#= ;j ->:M=Mލc0\2jhSȪ;J̱seC)t -~LL:x{qϜ䚎n{бTr2?R-5P?BêjfU' ܖWRf /Mt LvN30R0E/ iM M+:0(@(aL!E5Ocj:%,&۔&qő70_nhpЀ6`xJ[@T}ҳ2 $ȇ$=qfw9 `."Yjp+K?!2_ -S@3˧RIJ9RNy$mS7Dhd?b6HD?v:dH}s -foAA킟4o$~uM$?[ ^H3/u#bR>;endstream -endobj -3991 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -"%ܑ/67)1CCBqz2[A|v^p@KE!zni1krm9 j授`nU_s>D/Q-D+ *Ɓr{O|{Q&ꘔ6)h{0"u,6:Z#d<-4%)nL -O> f['OЪ3w6nڤ%ݠ>:~W]`Ӿ=$&w֕8w:JN[-)0n"nIs6|o9$$bwRމYN77,}" m Vk8id[EҎ1 Vx={=&V$$&6ǽDH礏akM6~;ig Z$kog# ޘh` 4 bqzyŻM Ϭշ3tj!2^H| {̏"euScŇD@Uᅆ|*<zF(_tXrQw1xf -Nu@.*/4 *= ȉ+,Xw^œv>V!'^W *pP -%qPRV= ~;8$+҂]0V^kJ[-wͤI -._ `ѼUmNwUpmȪCZs=/tIR -f_tkHmg4 k vr}8+%..ygfm)_*\OʶT`tP{IYE75+WI{.`[IAN7i6⯇őܑ;=ny -vi,ְ[%@hcˢ`w/ȋhrc%Cϧc] *rPW3[ga3k腋jw3`ᄗ+S{lC@~ũ)d-# @h8K[[J\FpƜ]?m -temY4]|݅$aOt4:QL4ӡ6: *]RPQ%o&o/\QEz:Mr*WŒ>;VUCn NgzCB0i#dz#{b:t=ɛPN4]3aeJpDcV6ʭ y$@cV2k\Һjsl/-UpxϹ5#_[y% `V`VϹFʆtj@rϢ:sњ岌Ar_ppwt%GwXY𐢑ču.Ш(oל9Ye$b"؂V:s+O TMRZqFCiŃSHk35=~y5移oZm3fqLTB>A\4m) w'[:謫 3j.OL* g%cM&l'V'S(\fF6lLlQϲZ ϋachI?a3GUO?Xj > -stream -2]Z⩰23DI(kC0BG}@[ڥtϓ6 _}$rA&4;0$E)B$rvQG=.L+ݳ؇\w/iAaB;J̈alM}ZUSU,kn>h_峛I]F:pX9Nvע&}+kw却Ӽ\+i~e6ƈ1#9ճ$?8Er(|=A騸c)lg?J/[Nu@w{(aQ"ѝ H+ ]AܭcѠ'K৲_ҏo,V;c*R{ FЊt)0{L ;p}- A(0Y]ȆjDvJLZVͬΊȏ &dM[|TX'\7z4" 8V%U]Zz4Jrke,82gZJ} +># -0`2-_7tXIS}V8C@&\Yp-$ۣ.: ) BdшLOQ v\DrhB6ԍRYlնȤ+ưHEk?Xr֜XQA_cT͌nluy'q߿tΊ&ZvZ.MZA۴Nb0< RR%(V-&[ /6v!H5ʬjlv#,H_e.'º E)f)r%["=- X\[P} -YBQ=:P,q7P Q֫9VNAѡQ]Ɉ28`,8T';t =#)HulPح3v6<=R+`Liܱ,e k7Ouƙ -qBi"K!bZlFޕpq8KnIknKXGLED"S&/@MO.7W5w"߭m> -stream -*VFPDgLp[$%p2534n\3i O7)w;VA@OH:KėfۼG@x)7X4P0l9f;I|? ~*@&/wcX 0nT/ \~WRtB8:K-p^ )bL5NzOH1{*g9.7+Bu2kd]GOEA9r)8!%V`71+'"`^ww(g79ɆZvd(Jv:Nt^8Q*Zq_'0=)OFC, <.SǾk/_037Iލʜ~wqm^gdo#j;?Z^x}Dkݣa 0S[h~] -Af -Q.!oNS>n|W8G-~pr)g7M%oޖNJ_<b"؇.ͷKש;W,"aIml^ -~Hd6G3r[Eܬ Ft ]` }|Z׫zTVL \R3( StP#xz&{AtU+J7mF_8@ DgN->53sz=cPg袒 ̕n۸jj8WcztA֭gt4m ݡkL_yuo#7}.~u_ Yve*Κ`Q%`%?\6c.z`e@@S<H>XTh)*{ɿ}9f8t"{KߤO)4>;Vt }joNf ; W798\r L7Ĕ䱃Bע?/}* ڰqc֗/:Nؠ,$ʿ"Q0%y_XıC?I]Q{V>Ir.F+W8\? )>hۊǥRÄy#/woǖm_]{tTܼ{]3h  ;G+u"*iBo/eˉ(斯cRռtuPE!MgB-\y$>9BԘ`I->wLe%l"S&Y:T$֔>`@|Sa{s:NhRM?3e-%t&(ſ/Fz -L[K*{̟5f*<u؃*488u]!WVd%&qvz?rMw}$ZgP޵M՝ q$h4JKxK!itl鑔Km/zcYyrxX-rV9D!DmHp vhrdž59fHS_j{);Ǭ&T|sd4ݫ~4QD_J%.Lagh?\VXgw -alfMG5p+̝OhbS:Y;'Lu)2? &@VLendstream -endobj -3994 0 obj -<< /Filter /FlateDecode /Length 976 >> -stream -2!u? :D'[zV4lrc,a^Q-+7ئqz@ n[mh}2 OQlEI5~8!#8 =˄W~qtyX~[B̊Yz% yFj躙a"7ȾOHW>B=CXx%Ÿnjl~iquOy6 )Eŕ߿@gMYTcI1u D$sivx2<T/9cL+ep21܏8nz p9sA/; =й 1uu$KsZ8ށd°(V̙D߽ӎkCZ`u0ne Cɺ"O=*ћ9<SɸVY6_KZKL'w#jfb^,yE'~#>1?)LҏXdHo cp?󷟭ce+9{?6p',^xf^bC2lYC@^6!j.0HRk*9avzؗ]B;ifUn:˄ -|Xx>i:`-=LJ\Co'[k%!X'5绐 O5¦\dE=2X 2z.nt{}m-_t^׺%ߣQǢ#\Y!fmnN!R7-6hSKګrɤ?] Ӽ#xJUPlףԔ"M\Dq yY WZdtdcKGƒ5'O@z ny8% ;`(endstream -endobj -3995 0 obj -<< /Filter /FlateDecode /Length 1472 >> -stream -`Ri9FӀ?c. CW7v;1=U͜t͚'!G1Wi/|\~.@mIfiŇ)O" QMcF u7> ̈CN| #vtnl P슌wqEpHU.GDJFXl"y">chrfI H N)%+%eM2b[KX@vR(~ffnW"Dk=ˤׅ42cSiy0q}fӷDP0+f>C -8u+ǦHO]Xq4^Ӿ/8&iYIWaIȚO(`s鍢ླ@i`)?L]E,s='"WzlpqnX|׽m"M8d>H+lBU+T+۷QVsr*w$Ze|5O Ѻl&[c)P};=k@ZOWtTC5 -8::*U bS."J=;di*|=9v p,kD?4sF6d}Dz* o7iwE-fEq tXH3^n>`ɾ|5/3ڷ/б{;|}X2LxM)iȋl^ݟa^}S‘DtD Ɏj^#ǽjI'%8 ф`ضYQ6́F3$쇁B~vyrēnlֿ,(a|Fy-/vp`##@Oc -,2/`B4gRCp^bkI@ d (~d\?9uYm.*̢*SJ VoC gdV$אxVy vUT߹ckߺ,`|/׶%swAd '53tHn;)#XNCu0A hPr^!b N c*ޖd<Et^:EQxD>+lHCP+,l&$0zxEgUp1;+IIUB2lן#\ybj:gȲKb߬}.)"Zz1oB=jD'e^^4*x ["y8Lc(PWWo5eƨÛQ;$P>[*uiid3-ѷQ+/p3Sx5SIǢ0$}mqkoh694,{? m4zxG*sKGpzJa*+h+Ouvk&PAnOHj-lEoߠek^$P3endstream -endobj -3996 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -[OT}rWVcfId Fыi`.Ԣ~dU6;&kX9w(865aMRoHȎNcxQm܋8ӏ%:&-쬳:/%2SRk\P]Bh9OFG#ag-]0m|RQ~x~u!2.Jtx].tp%Ge 4څYƅhCiY:VҦgtCHK ޚj- & BN!<Ԫ*­&}SsL|YF* 1"^VS7Def-\T!<(-,lVW6Xb^׎IRqQndK`'"AM -eOeo74dewk}OV[o9 .+C#fQк™Zfc -MЁzmFq"͉pϿ~h$GAZ{Aoӫ$XoeEo0]ŐsGsUHӁi9w(X|W62f|"22cǠv,6tm 0\ uDm ZxMd[CD+ίIw΀YՍ;jv7ü0ˢ*>z~FtAVRG+}eƥ\K9^G?D\P Z0^qY-|/(>Jm@:? o̢DX:޸/'.fF=?Bk̢`@Am,$w -ۓ2j,:mVTS yXH,`}سi -$-ImNo0 iGZ/=ɰyx&."&DZ -9v8IA`nsRD#GPmDR4`P'^q~ӯiPO]=s~Hendstream -endobj -3997 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -k_Iav\1ibv3g/vg -"RݠxaɱkPYhRɥg(9thf~_:b;H{2Ӡ9(e{װ f,bupQRIʿIL :Ob[~~'`WG̶k]-m[-̡{߉_GMΡ8]Q'3߫P)lpLX5Z]QX3Tz pk0_fFWZQh=G2n=6 ;84'-^(]!._Bu2Z1ؙ@N۶P[x({{ZЗ,, t:o-fa -\!`ЌӮR^/RH*ԏBM~,25\h1>duk(GHSG.d -K!l N6"Euݼ45L.# . YW/ }ֹ -[Yq@*iVܨxU3gDmUuvH?׏9íjkx<\۝hqI|1 QTg9+8Ɏ< Ѕ%g^v2IVj v_ڭgpI&૮`^uZE>4c]9.b1W> -stream -n]$ᖎum}"0dztfV@DQ^\Ra_:?!v,~d_J,S5_ҀifY܂'vuAe}u)Nx&1j5alzu eIG% YY_gK4hP\'q([o\YMF$qKF -$%BO7zXB,mK PZ: -}5:ysϿ -=xՙ[GIDpЀd@v/I Wd}QodҒMq&>t薑VO\ {Xt5$vC3]ڟN"wI62#Џykb՞}sP9c۰RDf3UzTLҒ®ԮN`!]W\Jog._ruQiT12qQ>ZG+qqO0eKp -G:C\SQ;Q{jm;&s#\I6H-܃|\RDԘ2Cu(&G\ boRR-ew=0ezx_iN<y#u~(^o WgCfk k[&P3NƪwGf$l'(HS͂Ne>Wi){^A̪Sڀ^-/l&Hwx4+7X+[Y=ptdw} RUwut7&@lNLv7eӔ= -<AR9'}jP׃_|)۩'id̓];Ȋ@׍@NϿ+4zxmA9>%MPB|<^pY4n -#|zBX\MZ^2Fю۸7тA8RdMUtDP- i}qp@Tը}hl^QD{٘XTHjv=;iQgʒ6dE6QM -`t`q=ua?KMС%QF')SS>9כlـ2y_ծeOk ^w}߰ cst=# -#'K"B,Jf@BMR3TCAqcQl,(׋˚y -ԟ}mL;Uo]L'P -{.œp~f}-qJ8yP_u4`jHmӽ{NV\ȨZe?7Px]U[궅MkN 4M>wυ,7/vl=He|$Tʼᎊ}xt.K%t8L'VL('XdZ ~A6')4 M1Ա{a.FrװP}fC&sOnr`FRnzp|\ؔrlܲ+[u}D8䀘l90ӝ@G<%ɰ%GxX6h\n 'Jn=@@M%0%j" rP|_np[Fѡk}գ3B@z!d54E80;W{Xf FGɪdLvhEkl'0O&BX>1^8:Br)a4p<:`\_ jEm;}U*-ORx1nY+~[. b@BuRxs~m&҈|aRмᛖSOWUĎ9 -7?") c9xendstream -endobj -3999 0 obj -<< /Filter /FlateDecode /Length 1968 >> -stream -q`b~xg$$% 5B*7p] Dqe[Q O}/N4F@F..e؜ݽDY>Ĕ[,g`u.еAD)T:zy"ìwؿE-yH4q!&(@T6Ŵ[Rn5V L6*C Wf>qnrNy߫Lp[ED@6]= 2*aAm=ָj>_PN繅^LIjCٝ,2ĐY/LJ{c&]R\2fcMMn+ɾ}4&m~yTUy !G1t>~'',`Rcg>MʗɕRAmi`)8Cy13ƥrkVXG:̲A~^'DZdH )֠_:6:1c? -7 0ߡ# {yPJp;lֈo@)I|cU z Uu UYZJ}9۷.JdzYQN<͞N/fS:sIͶ҂p0yCrF7ڎ>HHhՓ%0ș1՛E=Fͻ/+4GM>gd`JV.!!"eLvBLQtt|O4ZSV6MlhRiWw 1,힄 tZLJwXh._N>D-W-[p,]a%̉saz|KCaWh<1ti5/)ꍸ鄱^d78 4n /BKԆz`ӽ"'i4B6dHyL +ro޲ -$hRZz'(z4Mn]G;ktIz Bp4hߔ9cjk1@?Q)ڀyW塽!?c(VQ$=1R?Uk=N7.'k,bwjsXzcDi$:ξwlͽ@%> -*&5xbAXL覂/3gϘQҜ#=,#bfnab*E|*Rvw2ߧh@n}ǵbb1#dsmsomg2zŠǠ #afG~x]v^Z΅U]RT ;]~^MnBxYWFU82*7lGi/iJ~50'G„O"؅U3ߝv 4fw`| &N;R5gWfJ0;'K=7#.vk!˺ H,9endstream -endobj -4000 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -æG#H:>ae+Wj J9ـR:}ݴOg=C"^ӕ&ӥҳ5Gg^twNR/U$ T֭o; C &bμS ϧ$J] Q-k^JAZݶV9ٻNJ<_!A&\F*뛨-/P).S瀃Ye .iTlň=M-J w`!Ľ9Fk\Nz=d(osALUڜ*]9%}YHA+HASOMX -ܥ1{M"V y9kՖ#d8|@ H{N,02G1|۵v!?ҡmNnۥɌNHTE|WS ۦƍ$+XO4t s2ԬE?n3;ۈ[5u)yr}@:RrB1LMBib?,8Ez377ҥ^Ÿ! S]oe[Q+vr&Y ؈q4E[%G4 -bCCo.Dg4:CɊy+u)+ɣ`?tM^l硻%%ߢF:l1w :(P7vsi6Jai{:1?$O &ȱ:vnwyI;Gz;DDgYs$'|Aٖ8TripoVC ꂝE<zholSq @c1sr" î{VƸU猪8+> -stream -:[P 밀[0=nޏ6P&|ǶVU%!J[^;Ύ,m!5}4Re7 2uC..on7-$OX.i4d>JJE 3ͤs  w"͓+XQ/54BM唽`so ű"g7yˇMq<l$uŀ7Tv;ܫu-) ft dX~!I~ڈ|#u狪QnX[tkMj  xAdd &e(XIy]JtQ?qc8[5aW ]ҥVn!ԵZp0Dn7 mQ5A9,GʐGP2ru5)[}hڇc%W#$ӳ]H׊K Nt&p#w~$Ic2Ea^AׂŶR;I ҡfZ'1v7 *~ĸ҇ͧ \'G)A]@ Սԥ&,ʶ^$ g lٞm3H@smޔVj;x185F/\ \CY8Էt0lդ%MƤSHqkc)>r`HfƗ_i"l'X~0Gj}rfsV1rmw8>*MbF/Ui'YіZug<&_[yE:|y*g5 qLk7̭mfۅ(Ie\M@[-/{COZB`u'&_,^FŖ&d+z,S0Pz7n'皘˦n4.mEs;^0+fc==F2E}^NL٠-&,x`#X|u.On#W7( 6oOH\O]y$̞͑y -r6s9u@bBeĥ> -stream -޾ -dZIRMngBJSW_}Z\A#B*T_v7e3̑ͩϤ *<|%-:79czo&W&av_\FJFaEe`Nكb *F@=LT)S>xAE4Hua=6o#+"VWZDEZfA1Hzv*;3D'tocJ>VlR ;m7A}*]y^SތgSOio 9I:&CY6Y$0y+QϚb7:ы}QʑRU}&pXG0 /B6iQH:bq .Ԯm  C a<> $endstream -endobj -4003 0 obj -<< /Filter /FlateDecode /Length 2192 >> -stream -[x*auN';?"̕jAI? 5GO1Cxp>^oŭULd)n²ZY™gFJ \h?1S n h v= -dfm&IXC:}ӌ]z$dHԥW飼RE{̠1"PҀzkHPE>=;nKP3 m 7&2‚?P? bI^9'9"V^ݷ5f,v gĵUrxqJ638k C֖3`C2 AbSc,VN{!H䷰(Vş SS6z Oz0&LA|ej{66rSC 5 }1ʔd)W^X= ~xp$mAL֟TjJR^yؘ z0G?L݁*U7M*|15V9j$8KC 'EpINۜ,?+77*m_ R?`hlΌ#Voqn =|.Obn߱PxgZ=B@@#y^xuID^e؊8+|R -9]'3Ahdܣ^iwƛɔK4sAWm~w cÐ;e&;=?o/#iJpB?\Pfbk%r(FPK-I4 5},ϖ 1]l(!}|Y-endstream -endobj -4004 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -O8#*v?Q;)J+mpF`dR7}ky ŭld^, 'ȋ&gζ[x{ǹ#eYve 1KepEsGM`y!ӺXØflAPX> lzrP5lѶ T-s8_q ” (j-%·hXEl -/?-j_"rDo3^-VvET-,ޓw-ы|l+R Z w1"/_5n.1 Ahe[],d #H2SJF9@{>LI>弯36a}$iӆQw ˇw\j\IzK̹ -ǚ z}Eg@F:%ޡ!HY<剓( 9eOuhb|:.69Qn_gfC0ڍ-W;a1g̢ $-5m~:aj C!H-c$ 8cC9ˎ[4eB#94> -stream -TW+3{fh7m<,"_q -Ľa\ t1^̵,B_csSWwLq[Wމ;DCc PGs\pER@0ULQ+$z_;(~72Jxm  -oA e6fHF_m -ET7/OSx(ra_aQ[͢n6ɖ[_fYM[+vWhYFZ6F;8;5ݣMĈI.W7k{1S./"[|%T䋲alƯre\6R#Ō)`io -`mFC'麸Iyj -deL"*tIiZʋ"ێcwBaD-ɺ 8>tB52` r9Wp*g!k/plαRCcR1¼'ݤN88Gŝ5nT¨Bqvwpnn6(pLDΏ[ nq,_^Fש7G=冾?`3 iʋ40 +؃F@̆8r1IFˤ΁J[q҃tjCeniY&*3sK53y:z 1's6 |{UGZ|pi Ah-MWԩz0cݱ46[> -a<~Xyg4Oiu^,TxD{Rjr}Bvxp -!VH-9Xqc]yxvEN=@Ӑ2t\0p},[;6u=UfqHE 33'̔9=Pb܊`*{@TM_/Ty.9C -U~W&i[:nm<&"21xV_܎$VAƖ)W{+Q -5NJ~e%9Vo*^7JyB<#򼀆'U4~k =d7.)4J_:mbl oVf1w"f5/> B a?,5P6]1Ef^ؖ%^רHGЛ%C"ygu1 5:5`Gΐ; U*\? J,H].6}O(bу`{ -/>: -U-x L7bK8v^|N@Goo(Bk=9_j1YW9~1u&NJh?'yCYK$#**'9"jt6r޾K`/y=hA&?H^4:(pGn{9> -stream -(σUo5}#M3$1rB7ԿNPt,2#-@r#+aLO?TvA>"LLHu/uNgcX|7]9\$-ioI#T+1fWL5L{XkZ ӌ"f*q,w qaiX-45 'h9HFUjz`'=Uן2;=*wqTI5Cx\^ӱd1CLmV5"r3]Oy zQS m:1c?gq׶Bwu+W>jQ#ClXMĭ;yZg6١,v*|{O0v_57^gQ=*eK)yS)hA%{c(lPxV{n6ZCDFTENʬ_by:dED͡z` -W(`/@R&5A܆Wс&׋/c+?U^Ԋ/]b]^6uYD Rt&)Tb>ঁw8g_^CІOWxp#0h}400K_Kp_HfpL?DK* f.bT\#1aW^$^Lr -nE37dhHMb5Z3vPOFluzH ZFCSF 9nFD 36[(cƋ|gt'+Qѝk+#{5P#H0_ʩgB͕VdL'(|dBJO>UxCsHK[<&Losl(3n>zdGt+rCp.RwN)%Ow"Yֲm:;GVq;1gFN K3< ?/uTQ{F UQH¤4##+Zb pM)JO!p8(-#1&1E<@oz;91#$ s:.`d3zXW' >z! L Q,%O)f+K򛯰Վ1CYH#UuKXg\93ES9U#<5Ĭ5i0m=m=#חAJ'%8%.[hR(uj#Ռ?vس}LYtvo`d%~%Dd A8R@G[4?$/wr)ϬZf7nݺgx -F!jg c"tD 12@Hǧk73R_(g J Hc[Nendstream -endobj -4007 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -Qi+h$9\2&lg͈SsgEjuXƑ -`=SY kտl0FrM1?e`pgɣtů;0EAQ//h3 `H6+vZPx6#`|%q0թjl$h,%u_",6YnAD%:Z]rPWNb#sҟfL?ͳ!,d[A+Y6T`n)0{-{N10l٫V!l4V?Z$?H ir4zl(jlXAeSpذԵF$wFx쵁M5ZO y®i#a -#98#00I0iW!Bc61ô ߵ3q$*`^B-eX -IlQr8MacMC^06@b%`BbqɅ|SAik. -iAZҐaWw`D}v݊Du*tKF1/ +j2]̾뜯U lBO{/wj1S4 )ӛkdŀYk[8 6;K> -stream -P$1 bU4[8ύ٠Ҿ]dH0_)glyW&9 ,#rVzJ;ݒ2'iwAwHw\ .͡x7<i!w -Y,`GnCq{*`بzKGN6ٌ0t1ukGx+wv?vewc$A,գ}R1R»?4@>Z\mYUE8aRVOӜb 'j(]5qPJ ;?uB.FedmSƈU}]s/|:K@Xc]9Q +r 2 XcQѬ:9}ED@ZCbz yv5<)d"T'H}>4ՁW{%<lK@$axuOvcαyZ#ޣ/ڿưX sJ-ƿ]˕OV -C@W/'y.鼹V"muOSTb -T/:gj -ccVGݾx֙Qr$ׯ9iN`ܓ6â|+#* gcM0dD>oU\dÒh.`BrJܝ(\ #8He&֩Qq.lwDKDtΏ=1G& -<QZ 󼽺 GCA#_{"lĎe9nBFE*m4 U%D//RIP"Y%zfqivOf2X~ލܯ*m=R6W228#7LwlS j 85Wx=EWdzF<gہKטXu14 > -stream -[껛ҵ5'YQgƧk6l6#ؽݨ!P>)p -8^bD,DɨTT?DQp9)eFdIz_8,9C u +HY~Е>uP1x?vˏ;hmZH?ƝGG2!hRGYt+Q;F\ jX#Ӟ}>q1.:G㲻S c).VkjQ$1f{/RåI؝2O*NE m,sSJ5`~SH3 %l![LRD³E 0zN+j-R,j%eՖ;#٪߇n<ɀ)'8 /`jhpm),^z/+ˬelCk@ X_vk9L eycFlBŊwTk ζ4~^f1h+̃gxi oL5q6ZmY4"GKt 兖jMid? -(%uC0ܲ`v0婇,q(M64 `d9jT YDTti'}# W*"0>;m4QZ2p -G{~ue<~t۫h(g1 CojR-%rSls68naVp%!02̪nn.D=2vD w[%K-IأN;1THn4o0;$~:U=Qʴ+J 5qĊ:*h?'XvxL&=vendstream -endobj -4010 0 obj -<< /Filter /FlateDecode /Length 2288 >> -stream - Bu\U;v]eLfZϽAzC1Vaq&1y̶ A ɖM&v!O-' FH$ !RE-Eednt5{Tm)]?Cc^ ?3vEyl?ۯHB'eG'}ĩ&&E/dV69>ėQ9ĔHȃl ΚkBszXJR[2J˶4z)InCxp|m]txrRĨ5f\|i y9|:#>b[;:cV8y>,[:N?9^ÌyB668 MD~Ks }M4|-ˉS)x;B%RTĎĥ(CdTTn`>!xz /t:v]էN&1dtw/p/ 9uc&ig(/!H+dL) LQ?P8OdrlfG \=5o~ P"ѧضHDޭcKN;^ ҅0EuTs. Mo$?*;[Ih:F^:@ ͷKH5sڙK{Cq!ne'aD}ڊ9 ʔky"V/z"JoeX{Sm/xO_Wn{>m*$$A^v֩KOt +""OM +U"!hvE K x+3NEvU&~k&f4oJ+h ء*s{e^Gɉ&5{,5A-yaSj:g3ww5o6iOմrNͨlyTҝl5č|?-mNMOnAa)ďsU*`Kckoxyie -M(^&7ٔey12@Ȍ53g ň^?Љ@d fsyQ$WhD rJbǚϜ}|ξF!RɪPBF< +}ˌŢfwS0uĖvڏߡEư Għ E @iy9r^ɖ.cV|KHXk(>&jhQ*: ?XgM:hOֲ[$꬯@H13Z^DQh9a%? 8yI=y ,0 YBQћg;"ӓ0v>vr 0γ= - ƅiko/WB yk0Im"w} -R 3x6hڽ}N|2u.WU< Ɓ`ʭz}(2~(WwݨPX? P'bp@) V22iD/f\$ia O9x[^0ӤKA ᠟'2rۧC}&B`I{ Vu'5_8kVEL&蛏G546[6jz(âa4&|%;/Y8 -Ts9H*)~>d8 - 4n:ٳC6{gDbXUP#DH9߳O<2zzɹ gگf18ؕ'1C /`I6W%E.U5>>nd t[> -stream -Uo{x4FmnKT<2Kq=)@O W]mg|Mz6u~)Uo6g [B'%T> { UH#2d?n]$Cϙ`GEGj/q_> Avȃ8ZCB'з9,唵U$+XqwJq~$QQyXKfMC߃#Y,|ѭ1O<`f$>Y,.g+S oc e}u<0YJ.Vs(QsoYY6D6)]oO"yi2#fmzX7qM5 -o@bbw~vt?d*⛫\{ƆW7wϐϥ%r x0\z4QcB/fqOk>"0X~=vԖ-߃^+DE8øe,[tNt=Y9hZ>ڂT`%񫧾{f(`Yodž+peM;gцmٖ(Wb85BV.n\4X TںCKh#)ev/%ӧ98iN +Ig\ Xs&0&L,/(pm!Bmo$d\<2_endstream -endobj -4012 0 obj -<< /Filter /FlateDecode /Length 2432 >> -stream -sW՗:d MghĻĤ Ei+EDp:,4'g/?N.oF -I"PlxWIPqzZ_Wܠ],೵Xʓib5_fhCOA|0.76?^DSE{φ@K -u"*Wݮb@ XλnўsAa.v1 ~C96T3dAaZ#byH)K(HACD,WF0 (G(Oa~*c?×ycCtM{U5_,lÇ7hFUӊ+dνGPPw WEQAfd񡅉O=X5M,~$.#\+ LbEbԼ=X0{'M xbUU:޻N iIT|mYVv]f FB8xr]b!O3HE_??kOJ˲-)ݬ{,lE嬽â#7X.1t>7޼{D5-D6,Uy%lʯDSMn*uT?'>p{; SxPdn^1혿?z3dc,_Om_=)YeY vyg딒4ˀ+W4*튢P⥤%iiZrȕ=x_S@Tc6 -}+v&lY׶v -u :0#$NqW7DSUKF(y9A""+EE,)fڶRP5_л( qd̄x>'[diu\ ;̪a2vv]Y*,&7 >ߺ,FqV7{cҹveGZq_멍s0\NNr]M78U]ţ(7(mTFqоgb(^?@?}A,|8$e4„ǘ$kek7CJ>7 -Jo_ \l9: /;KPEo29/[ӯV7 |ۖ޽N\"+qk ðN~KJ~6ľPitΔ.؞!)&haQǘ>@,^E"U#`T"Ƽ%b΢# Y61Hk%sAOj(-BL%0%ޗBnA]\s*z2l!q|ud ʛ]XS\oog|x ?Mi}\ULӧJTX^X5 Dz| f 9j霿"vi e._=N`?oV w @V ##3ba*6#*sف30E6.pUGhJu30WBbќu TC vbLRkV*[q9}Zφlӛ,5;x‡1-a\aF;4D -늻̝"*ڐwh،I}K΃kb$e?/c]{2hʔB fnI.Jl$yHCFm{VQ^k}B M},@c͝7a{gXx2,{G6ӄUƓ`4=Kv 8xd+2@tMk>!U3ԳFt O,Av% Ǥ)K;ˁ&4(Dǂ6rh")|~RK4>J%Tcb4WN&.Y=k`XpkuAf;Xά氤s{D04OWO11ʼ#1/%-"h3#p[4SINbobߤ=-sx.dho%$O8S۳F StVmCP<z)#+W0?A1Y9v.3u=T]Vg"jCY"D&A'T9|pJiNkH>S`'}=JJ^ϩ]p2:LCendstream -endobj -4013 0 obj -<< /Filter /FlateDecode /Length 2176 >> -stream -4[W`(􈀊ZxQ1~V1c 8.{4<$fkWZ qoXw5cS={Keaz6ƒaJF8(qt v?yI89)0E3-t"&ӏ0YO 0"ϳm*v2rӿ0y TOs0sGײ - r < w(}~|~ErҽGnO5'?!TjҵoFJ؍r(΄dHDf;H74Bl>A]7پptnAr'U|be|G6d_Z̧}-$Z)LGpZ*U8$[u[xSNypz -'%oaDz7gLݓXKJ֞N+C:vєbUVo,*zCɫeEJ'tҬCğQ4&l@dڶN΄tXx~C^y]0׼Rc/lV;s -6h|5w]X׷EZɘ0}K [Rۍ? Fi# -ϑN23`+!L!iq#5"m5xv-0X%uT`Er^;\ҜC`8/4L!bׂ׹߶PrXt~tu6_J&R.KnfXܟ<,S8u"6qh*#SEjNUJ$<>`Dm&٧e]<g)~ |)zpȋ&r$ɳr Gn͙GCW6A.CaԒoNnQHkydFV <L$w"j@ltT"Lj2+N}$6wЁ=`wp%6"rDctw|]2MJ쯶?Ƙ(ǐA8joe/RBi| ""ĈHcA6j- 3oPl$4hayq,ERX{"SmQo+Sz#QS'U$|L v@Fl_ gˑ $ YFQGI(>A5}&/_("*^2DrV!W_Wc/v5Cnĕ@c DGlÒeqLA~.95{!x3OigšE<㜾r*qߨ$Qahf\v#TQ0j;(;qJU d&,*;Lgn7Z $ [Qb '|Z@B۵veo5Ɂά+ca{óA&,Ab=ӞE~' / .D=*YzrВO|z@o\(W-LK,y0ao#չ%9bށj7|hR)BۻRlYЦ[eJQ\9Dz(.۬zB A(ƖN jBƠ ȮzZ/|qy[ik(/'?rrZ4[QZwj1?Cf3)شq_ t{?H{dm;o_ ͂d:`^sd,_=^R*E;j(RtCv$ץHSl4Nlݳ;(Z+XE(=a8МahT̾nC0N @cXiz%/8w|ÈDTZ> -stream -vjE؜yo%П (H64IQ礗Cv= klgq,Š -;Nbd:azÊuN<6?vaՏnE}פ|BMp -lQ5mϠ0 d%$jt3&n~/dcغyMBg~\R]%oc|ۍ؃݉2Ԕ5NM }qO^ߐN[HAU_XG;jyb/ VO/:uN,*{?d6f - q PĐ6N1K H 0z71R=\Kx\X<8gBm)3l~f$VxoӎMf!:04@Cx&}}`[k{5`|=!\s0?dD?KuGSˣkx 1['EK =b ˔4musOmZG#}Tm; $x`{߭p!0b 2ي/ )*%3%'b _\_ѢIdJ~|( FŽZRr'Y~~Sg-*4.;q"՜AHUŐ۸t!h#sF%Mg‘rzeAo*3Km7l5v#ZUaqi6h%$d,[ݼ-7U]'3,҇6TԳL6"GtQYJ wgy2KeIxkceBYoPVQI -UQƬ8"H"@YRS5o=}Zsxdtb%S$CԫP}$jې.P+s,F˽m|?Bͦ6J;"2;qh8q8y.FGDo!~͠M@1UthEupO #TVPX}ɚ=hԒw:b4nvɁ:«@ՠ񽺿'͝${~1V.*\_l?do{BX ̪Ž -Oeš)1i VЅZu ot`Ѩendstream -endobj -4015 0 obj -<< /Filter /FlateDecode /Length 2128 >> -stream -Uz;c7u|::7t ;ފJp 1X!X$UrxɠTyT{h`!v(qd~kxJ*Q=wxia[#;+ U8 %<;46:8gNZ00BC,I$ ܲ7gq[yėxΑ -&-8bs,a_*ʰR~!a֏F;1v)VﴏSijލ#G2*ΝcBi?\f7lc㉲ݻtR@/ya7YCy7AS":vn!#_QKM%Ŷsdy-Rhq ^'<_O+~y(("(ʝ9fd6DmpGkH-xu[>ʋ?<~)+^9TaboI,<Y.\K2K;aӖ{`'oT[_I3( Hbk`R>|-@A0W@T_Uz]>QR_O9( X%%U\{WQ0CrTi:8Hq,U@B#SLo_19~uҀkhXka&{oĮ Mh6t0tXoj]$)G"^ux*vbwØB!L~LmXMLm\g/ N]&򓯽j5,`ЗQ^1s)VLm~rvUAϰ[S`4f -QK1gp6G);'rN7%ܡa=s9 \9m׽rфA-n^AgL/%ݼ48sP~$'gx#; -\mn VU-[:H$>M+]@X"#wm2vsG.ᒣOE #67>zO)L`aʂv::^Ĝ'%p裸cdCs-ǩGU詺C ![g:D*qWhI~5%YngTXբN'KC !^ ruu 6#MC?)vJI|je"g'dۗjCz" Ìgɘ3 Pl?Ȧy~Qo}ՓKPU!Jgxg|s*ҖA9 鋛NSz -X+EV>HdW3ڥ~w.rKJ4ٴ2z^YZ$KXZgJ'Kt8؁Hm4XV~٩ͧzAe'&!ߴ\65PI2-SqkC&uKycGO:n$H WEiǪHÅRm5O`(aTjvW)b!ed2[t:Ĩ'pNv7[[3]m7br0^JXӄx&:!?Och -'My{2Aӓu<1f6>;;y׉Hk StgnٷqO_0@spiqNH>KTJ*㙰e{bp[^GBFC}{b#.&Q -<'ׁ1r0cـ`YE9Ȝd/ͅT ʾl_ -q.#.O&DyG!bKC||P -R;q d|%YK-a*&#$rXp m }Ж$ќ%/si+U -^IՍ%sK\n}z Yk> -stream -`rA~Ub2~vgvh"]>grK%տI핥ZxIQ4a%o?R( 7t*/C Trd ~_[ac4C/‘Iv+iӠު@}K!Vm[2ZeY%:Hf^l$&VeFtŃi񩱰D<xa'%3xJ…|,+ )c(: -ep+QYhWq4V=0f>Bהlnl"DegN"Jfg)kl. ^ܥ^ ȊVY}NG13&SІZD6m$5F wBx-N!yʳn)i-?9ʜ:r~UB@/V[ET@ w"`:5y!FHq'-.w`MR+XX ?A]IA>Mʯ75]qU G@!#~=0"Ao:۾-L?~*cV+m" d,ȸ@r "XfNMy =E̫o_ j7Y0z5j}:-uTH60{d+8YCav?TL7/ZIJ `uB+-fzStO0uuulGB1H|O= -ZS53! ّL*Y<2pTKc3-3|ۼĒ^R SW[?O|X`Yneh*yM:& fS"  $za:8DD#CC˰Md•`p٦EIo4w>qKdvfU Wub"$8SȦW'wHV*P!N& i2dLB48\^f0 c\knI /XH_P[cQ1?F(X V?zXOJB?6nS.8Uu51\#,lC*)z xc".Q Q-RTmzK$յ$ߚbsaN2· *+mYD63eW9-k sQ@ǯELh jݴGǷ> -|U/3EJX%( 7%o^ ~2rO6N.v>8χZe2s).r=YǜjfE&"J -+he&umOyg ;j>,'>RH J~~/Sy1p K _D=^e*sNrx}<7)U;x'VWg-#F2Ab+E~6; (UJ=-+ `{< ]N&bEu|Opcc8"S.^N?V -µ>vIJ:ƕypNH31B~<ƧԇKI>W}݄6)-Gr$_bGk޿?膑ri;ϞԼQ:HDM// ]R *O:F|loFgqQ)s -Uy"Gp?XkݠGBIY *ܺqGPfq,I=%$W+΄!_U8!N]#B3q'PkʣٱEHڰhN]WvM\ -+FO=z8w9LJ)CB M(bZyW > {t)FJر!ϞHR2}|]Pm鋳d¥FmkWPcuOp:&}p$/sV%F:=e  :.;H,9.x쓡etpdzM,\hX*FavO|dN#W\!O -2_+1"O&|-5 -gKN&E~W,zoH%[4-)Ng{z.VV/ `t%_Uh)1esě6!o<u|@鉑'!A]Ic>s&`=nZ9_bh@9"5̠m Da!0c錋ڊ֞Ofs,D^U+ۿ[jLbƃK|kM\RsZm -;a#TD:V 0XDYiM@$f T;rj0@A)d|i|nh `QLKϛzlY[eͣW辩endstream -endobj -4017 0 obj -<< /Filter /FlateDecode /Length 1040 >> -stream -ڮK3<$-4ǁc[ohvSxӑҘlU,vͷU*ε; Cya1 =DrR<(zH"H4b0`6එw$i'k0/UĜk] 3XxШ@O'T.R5x1c_yߖ+@gS(g96 8=b.}n;cR7y1g27?tao{w[H0m=o},<Qd&ݯ|؋yS!w`,K9*] 8JKzj{Ǧ,qBkv8X{SR)vr/4djX?Ϥo2ā!C]"Ǚ:+Q›Q>jxtJ?yЏ!xd熭evᇁNԁN СSУ3ոθQ2HවA, z -kUET09E4{ODd|w$jH[2B<ťUH ݦvҚ.Q> .^-`lc!`)K8.()CU8bfБq!໑ '0vctJ=t.nʔM,2nUh~:{9;6B) m[l$;S"05'p$Km)m'tZS8`|`iA{]j閽<6d1A7R`@IJԩHn)2L)gǵiSCn&`sl}!_91{)4fd1* ̸ -c<1{Jϖh SDg Mݫz[Z 1 V Y.ܠOsϲ.i))GF;̂KoJL+p!٠hmHg -~e'@f$F? n¢cendstream -endobj -4018 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -o\ ԡV`̢g M!Prvؗ p?),(DӖְzn dM0A=CG{l=`yCW@Q|QEP+0~\1Rzt`u3(2 AT_NlTC3zکAy(yE=Y|H2zV&mN OH=-M,Rd [9$=[ jO`U%ڍeCM5.S`FS n2z, - (7ҫewe?}H'1lܙ3hLtI,U9ȪAyHȈllYM<&)^LTL8I]Ƈk[܎QPL ]/*7Qwzo ^CWR" '"z&Jcvd{,51h,x8ѓ+ג@y 8PeBS-Jt9i$0NoBYʡg:H,R:Ko&Pi2?WGIo/9uǓѣi䵒`c z%_Ŕe.H.fpLQgR -Q aLFvN^-"Nь6}6vq&?XiU ufқ]t[aTciHPrbxsvi\n/endstream -endobj -4019 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -ճ"4OlZmlDʺک|=5 <#շOq{px[hGDHB:(\M* N1.4[ts"Ikҫ|.!NjSMMPpg4ݟ> -5?['ILhuBO۰0kv-m:  ޫe -T]tS I㋪qV|2 6t:Z3Rh0\o*'@0,a ne4ۮwr!Ÿ.O SIYޤ>Фj:uV TL~:B%C CGS¡2}WS:[nwB5*Uk -W S*4BE3!aP?3m%Tlq]V˼P:+VPxzf+vږC.+^{K;W52j[cFtTi'@1{%]_>+,Ddǿq0{X8m6V{Wۙ;N.XёM'rlPG#:hݝa1yȟ:JHYlqy=xY<IN~l'txNX7Q ~Z|/^K{j|4e:_b '~u FgUցy`w]}墻-3}ӌJ?_qRT4> -stream -`LO6 d{V,ɦ?@3y'`qZLxtL?y -z~47Z/BĈk[4N Sㆨu=Ef"BmxUʀu뤉ƭY$=q>15SԽH(f -ri'Ya)ɫ賐)gRiV gr Aݧ)EewŌh2 3Ha_z1oKU?b<(9* -Աe$Bl#0H"h|'ߺw_}x"]󕽘$;,,KJLBY+G5n*"\'@ixG1-Q3vC&s .dHZv Sj@,=֪jb%)I[XTiZNT^Y5p9.v)nӠW3^mcOxW}:Kjh -eo jF<=4i؎\YecB,P[ݮyB2X&(>gEtUʴ<êe3A s -c=z}*tcR -%eSh?%ďK7sG|?v}F=FfVH^E!Cl⯐shI 6RN× 9KBKE: '9JL`H< fm)~$ WZdxT,~N/&x[{ Zw9WoR1]|ՁO4q»ClOP2㮜|#W47Ѵ5k𖴟r9xy"NqObiDBfPH^>c tsk>ēm -[Cʫʺ/=(%CO2Q'vp%3'lB )ǣ9Ν'ZF+vldT *+(0J49ku6腌Jݡt(Q?˭1P׆'Q:gAd~GK)h6endstream -endobj -4021 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -6%+%X.Tq?tp=W8,?`DJw뭺+3;g u&Jc;LY362g%!v,~LBbBZ^ب.|٨W;u߳(S5qCMzj6p fxW3I@Qb=H8V;+mt%Ҙ;W-!h @1o4fFjRlevL^ &_hTYkR@?j IW Xm"#@e \ME!l3JEiv' aA3OE;"D[$7;rOpSC%*~ܼ KOɩUL"\ecdd -[/2HĕCLG4F[yY&el]İ~yX<>d(H i.g_[=)Ħw Z0.۞C£`pD_jQ hdƜfA//.LNcIa|o -h "jı#zq?ÁB^]'KS9In 6(kIsfәD*Ns[</AܴX!z@Z[\6Bg*X1L5l;@vfKlL!E8W5EGVIfԋV, ܫ]R囧[O`Vp~E`5IJ4ktJvW#zM vz4%; d`w$D/k$:k@YjrG%2E<'}QÈbR"{)j-A[|"#}6錽$R@=Tjm6Ik)y{Y}][;0m&0׈UBt^} r8' d"ېГendstream -endobj -4022 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -TV$4ڢ60 -xPtoE2p6p( VЃ^Yts̆&^_ݑnOԑ'6g%{mLjUʋڑ俋n]Z,[x.g7T} W pjʘ`!]Fgg #ITB~, %n%Eៅzb7fȳ(V㠱n ű&h`'NĢ>] ySWNN]N==:($G'~0w9 ?PŴkR.P`C-\L|+8%7qTOd[\mJtCo=¢L. Q@փ<1 cd#u~ >6SIL8> ɑσ\* OĄjn@ :V`vamֹ2m7] * JZ*`41+=oe^O)Z^rM -5'oOG>s[ -LAw@f7~Y ^#&ve\BbxKbCE뻽w#ь< ;,3_Cu:̚^dQ #n>ڡ_6jrOfwnE #s6U(Wo-,WaOg@Z#.ժECzD*%F>l=caZ<% -PԾֆ3Z;޸lvV(b='`d4_\[BggH._ K!E7ʊk75*? Xw`feN_';>B!15'_4~7ueb٨"*@t -رKTN72BcH\ałvI׺ ꍒF~_4]1l/էzCg~-Xڼz.ڔvm/|55ivd= -_!r~E@ةT}4"(q+B<%B/ -D|5Qkߘ >kuWwtJTG1'Mxz;0p$sr(DׂPc{F*Dt:!w@޵& 0:c:BgxRڝFG[G3@;640]w @e^޸Ls~DlUCQJ#SS=#2Y|LyTMkI-6]tR"k9e,ƚ|7Zj |yPRZFcm.]. -]?tQ>Zܗm|q7 =uܖ 63Hѷ>H:j\%@[> םWCXT!ӓ#~ -ݸǦk2M޸Q;'RJ-AV]^|0Kۗ9=tV7Fm(OVcܸL:> -stream -4E2ܱgvx=z8@kw#*S\h׶5Ÿ#jQ_yOQ?g6D{7,[ev Kt۝Qn?ı#Z^NT!Qq5f߉jHG5M:NxYq4mWBQS~G9uJ3r+q -dD< ;mkQܿK_ E:ɭ\j>V~pbRz-45K. 'buFt3YH6*Qא5YO9:}h$IsKN`%6_S,tkȇWn\880+PcDAc h[TLk7{Č},RH9qv9r|p>=ҙx}h廮"-\ -gKOe6`>Ak4 -q2t * h%"x?r1 iWˊkg.`ԡs2ΦIuTWQ566sAĚcc[Ђ&c3bQ;W}~ RJzpH* +&7$0vH=NIӖ̡qjYU:SL}uQ}.Zֹ@Z5 *rO h]org5\Cwޅzy:bjz1QyNr jKW˥[LߐLЁA3=kʸ vtMeHg:UEeu(uO;~o3dWx`4!d}}n #8՝oM[x|A+-_$4F1'xKN{tFU pBed!q W#azTR^0&u -cz:.;IOisלw mz[H V %H*YI`r_\ۉ)k{;E03t$ -[IbGb.C/H@u)m|ȩ! fŸ;'̇#d΢WHVU,o? B;uF g2:"6̐#')1 -}HMŪzZd>nߝZܽR v)4[ -YQu&x?z]Za+2dԀ ɥ-G"AsWU.7~Xaxp+PUs+YhS-N0hYe^|vY?tendstream -endobj -4024 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -P]f>4!ͤ\|T3RL0izB0lƀDw#єG%åGݰP`5/f@~~qOث"\gY7cj3j9uwM+iݏLid:V,eLZ9ݲgJvȆEjS f XOl磳T-̼1D&Uhb ΦFeU)@13U(!{9q"Oꮲ(2=iFuh+(/;8NYgb9+Jr_HbK1P(MO)@oA!tUcξ.P 8"[Wt ,&EIpnskWTR1 utяĤJlbE'S3޺pksH2NE+mq{8],~_nWaD{29^4IAxug$SXDĴI!sz9!c^SN=}n1u{ig˼_4a޳⚲̰h"bMP7W% ?<ǐՖV]´ ɠYI ]Mf9Өx -:xhq6_-\Rzs{ ۆsHxj׃xDU=,/Cmx㈘ʐRPM7a7=2-~K1W&}%xv֓ߘ4g-u=$?NMΉCnR9<:MC?5<#%.{m1TW -77v]K^Jÿxg->}&tx6ٖٵ$1G$,[GgC5/4L|wA=q%)hI!XD |c7piwѪЏ~ ]J\RXDw-ETkp a Poң3_:Ϳ8WɂXk\d=sO )!bhhXWL3с_*Ïf('UoU(%--9(Rݠ>VK {>l G"A6^-@$Av7#3VC10S$p5H<4f -1~5x /endstream -endobj -4025 0 obj -<< /Filter /FlateDecode /Length 1520 >> -stream -Muar&2|ɀFK ڭ?l觉Q (CMj,-eðTJ+<u/.p5,9cho"&m궩vrfj%.=:qOb?_w 1m%k"u}Xй/!wݎdI:.!SL^(mX'EVPw-xDo4DL 9!g,ɣ=i.T;5g@x_o0i3%G(H8Q.͐h<"e\R1uq[yfkC_-0Z._ 0䔈{Y$U<tNT~;ohWr"S1TM u{ù#H(c J0zv;6؁jw"$ժd&X >Bf cK|:ơ--ϪA 5%~ƅ53JT(Pzbd߲lPQB44*pWᆢHGD}GGuXlw9(q~gW9C\o l.AV7svQUE,0cHELY& k!D֖r{5,V};$fO-V=*v.sQԓ} e.&"/Tpxm -A['ve"@33&+ ;6$.%=1@v_2ZUNQ:`%4&NжRXo&wގRP^yB!mzʠ?{~eҨp`͌VXELY zGhAc' -ցmpendstream -endobj -4026 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -+㺴M}R=yYc5;A<!Œf+vS)Pܳϖ_b0H']QK HSvJe5;~u(0`FljڡN?D AMpT{9_NPQco2nk_!|O\5_ -{+nVdې o}\~}J q ~G+C=Uaѝ)fTrX(8:-&ut53%(V+9ݔMMaaD( -[!^}X4̏<'EHxH=S˿b.x@W[S h'+x|gtz1I8nm*7"uz#>.4)ϒDy П&ZZ{.`p؏[!%X0)A% }LxƢfy{}䀓ҕ(II_+L*}kkxqW%?2zuq9fQ?6svH[[n"Z\8%b)g/M}3LQlQ3,xK-}[g --XT)0~"{fŷbZ -Mig"sȗ[nR$O9eV{>XWϰ_APlKc2Ăs ti[/=2vg%I)<+Lm|l$oWBVpB_7@֡ó㢖s"@샃>1n,|C,K+ -u?um!,hѯ-|ӹ/;.X=:pw C֙R౔dA<ܯ˯49U\9zGnd6$<^!IJ* M#*U #[1{ U޲Eo^X$C;uv2I0vbSA)hh IAVq,X -}:bJ-6'M&ĸ_JY+oS83`3^@}`P1AI(TAXU.dD-w+ ֏'J0TWOr2scBcQv exVչިypKdkޤS Q= z?DٿP2}4\}gp&[s;nS"gz9B -?ŰLl3fQB>7H#Z:a E4;C󥌳-:/^ʮ?yr=s/3,0ldwe~ah%jz2\LrqC?6km73/ErhϏ'_"{8GWa"\P3+?}IA%&{UI  tZ pK=pU)zۖO7tϸNXA:T!C")Y=:AVd7XpT.)9Kendstream -endobj -4027 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -z\{,7AL.0d-@<9Q,H*~X)~ͤdԢ@'[ǼрF^-A ҥ*sx 0cieDJ{4o/>&T:LX֩2\<斳;\SGEY7D.| nu E ,C,W` -גLr{ Và7J1rⰕ xpDX9ؗoY%}mnԝaB1 *񠤻s9=eE9d-=X*wP*#{_\"څz}KY,qP.9mW?U9zڽ<`ƊNFl$YXaڵ<"-#̟bevb t -M'V!3Z }r>%MѸxvtڌsVFǸnuDT j/14l9h D{=eKUJ09{GNbh'*)O\ḳ}4]itU'dɈUA EB2'XWvb1C 8_?Z^bMބjaL5 ڵj^v"aQ16,3 핚YOT4-fUD<޾E#ƭ#u,qVuA1<ȫ։xk'Ja"R G.99^fQ5&[D:=ǚxlYSf/IU|vgs)V<h1DBSHv) -fX!wX`mMk ;!w -R󧪒 -\)TH622IFqË͍ F\;T//.3k4 -5)švDJY<ăàT9^Mp% 0)xaadåV9梓*X;;ᚧBW;B%Z<?_fE[)'Β9U6q#Lm+<``D2". -Fp6A{d4\Qzi-bI!;>mј7捕YWlt9ʐ}3F#&;/ 7E; -X7x+@֩řR'7O?9N"uLXbJ?5C(á1_!~uYQL୲֏B#㲯Xg^."3Oaf2Dk‰Pq:^=|=6äɒ%cZz9m:g(59SYa KYBJ:F̻t>z1 OiONxXHͶXpxd7I伦<"zOL5ܺ eR;9T_rIMEPvFmZ~>*R5endstream -endobj -4028 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream - LaL^kai_xgў*.5 ܴ˭\ yQIߜJGIl u,4AWڋu,&Lst JM]zIĿt6ǔ3_ɦ">Vh.t.G PaW =-yT{lJ^&#xo BF,BVhT2M!O} 3[ߪqِI(b!1g<2|cW# -s[1Ht'AcEq {^Ņ 9AN,jOSmK7}_R?u D/sTNg)-&B4fП'/q'&eZTErY#VwBy(V2sT9pA |ICʆI2,-bTljIswu7LD_V#Nh=.`%4Tdc!#_"1>b`d=("x'^VX5Z3[QJ-`ntGNG8a2Dt羕Z&I -Nz)*NW= $g3286i*EňJ>mE|{0Ip̙2{=]Joca+_`NiN#RCf2R|r_GyKhGI:,O~hhH!{TYi֟ Yb:OQ8zEB,RF72OhO+F|R\֏$ȸT+7EhrF:ޤ`l/qҲa? ͘rHn4 nL .v%& UAB_r 0[Ř`$ Y ФW$إ{Mqd -)eԆJO)8#5Es<$6UEn3(~L-q#G˔~˨f()l*`=+N<X)(Nefw\ZnzO9ֵIkzDrQ"o< -(Lao2t9|BⷒhE/o?(-.GHB֊. LXJHrwX<O(}h3OsoXv:_GU*ozendstream -endobj -4029 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -CWF: p9E*MGc$#n:/?&&-n"h!27/ӊh#4Ec"npY"a}r -}YPgYH@qoS5-zw7'cIadv_9UgD\mQ/Y%K @*1$aZ(tkK/6 O驢rY -^@Mqw!vN[ 0{;[f(5 - ul{?2twp]QD5ľZǸ+3EER--3n,zM0$ԉY&5a:dTL{<+pC\=JGӘZcD?F'(ͣi`>)*m@D^or+C{{#rotKgQ-u PHxf`2)n4]. ~%ZTh!qn;v$ Ż.VH't]9@s\RY$ʽZ3+q]\ےe'$w6h$J -9R%7Uo-#|=f_ZkbF {a̋siXi'p)Ȇeq"_ P| -C:(T˄d\:( \G{JS{&z;m7u(u/|R.=dS*bu yBV!s(:g | |=0Ymp -7 %x- u04JuPHpi+ܣda/خr9[RKmS97MR\ubwBI㲪\oh=0٨Tlvy2r(SR*.i@3ƅqmBj(~"/TEyM~]{0|M*?lݴMXcKw͒I.d%Y҄FNT $E/\azՖAXL|.h£aC6h?b>^T|KS6Y\Y t*vx.r04. Rub\I?Fm`y}]tTßȫ!@+*AZ47WeQx4\)7Y~H$92@OS` MHS&/s12"yG8J^츂~ ^BprÐ# DI~WG%ӽ4ΣdRv`oonj;ɴ^46(A`4dwPv#ZK]XʅF،ʆlP#MhsbwUj#up<2.yW,YMTGU'{p B'L*֖ -(3* ų JR'j4]w`R 2*Ēa롗;41xn9wڬV8X_јpK5 -g ƒ b0{3!%C9]SEpp҅~}4ww v5'E^0DXo\<.ZFgp`f !Nd0)^o:YQOendstream -endobj -4030 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -" %24Kz*+w'd Sz4"0|8@$`=)9 ZOoc0ɶ4GwWS%(j MUa>qOGY`V1j?Št@1ԟUO8<5Sz*Mb Qs\>h ~Df%[ >Otd_j$-_p.>9]qSwO;TPAH/92Szrm3\A36c 06W0ؑ7+Rm,àirM4} -Cr=ܕS]%lcUJ86n`?j_kJݽL4b5a7Fs 2˖?Ujxˤ?_)*$`]6B h(ѥE=*O5ÑG Uާ¥e\g+~dz8hT̗] !L - J>wǠXѥթ6F'G޺!ĪA|iU۬N[R =(~H}ԏ#ՒA iGhsT֜n2ZjzñfK/k}&^c -ĹF1a `:IŐE{^7pSw%*oI]sŌ RϯOc1s(\ӰZH8&{#{#P/_?hbu?:ozyjAޡXx}.N[I]ٜqxl(LJ3~I&&bVRjth *T\'9;k=^q-%IAIX=!-_ή%';RF}/;hK8H+~sob,;BN"ӮU]#Րf].ݼfՖӼ+Xq}W(QtXn؝ϗqx" ~q=ȼx iIkM1 [ z4.={w9=[Ge:vLv!aA0L$5Y_So֣BW1a3B<u:F]Uyx()i2#&rIYNā*hDD:_ى=y J'߽r}` -< v\_e!45\=U|./˙THNxLPb"ef6Z*40"'^|ةvMz@\faVCƞrOLA(U'Ehlf[} ieJrBUK)e ؊F$hƝMVrygv^6>5Ӫ[TyNp+e%x#Jz9Q)  )lwZNo܈l,!RUɅWa fxJƲ̦)k/煷1v:Y`uy J9g^HCn# +AO2;X!P<~zU+HߵyWAGՠ$o**z͓\xKh=ټ-(zwHJ&V1@1WճhWwͥtTq+6,E>Gס2P$!{6 f,& E/s=endstream -endobj -4031 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -۝|l(鼹@zLv-˞x}PqNOASs,Gp<6tws!qėݎw<R(?Ng{oUOp+ 8=w{`@LSF\ɳ$+z)\r# B(]DbQg 2[_`U ۩}2|l6 Fi^٬!I5yHyԴ;ρU]#M]w;^jPnsgt+e)̆{om R -)൉T%d:&f!t1o9Wj?Ϙ@ϵ4Py;H|Ȼ_w}5-~rAp`D.WV_̓,Vk2ݲx}Z xU?RE#Y sxpب V? -xmݼ*UaTјk}hYDq z.WylfDlLSgy݈dRRFVzE|xc0< t$ -5LT:ҍl7Ge L.JA0.wF{Dsq?VAA|\X,ڮeb*ko}JKLdaD["3W#ZQb)'M4@"T+` ΍Ukdpi~y3jQ[q懗o& ffˌ#7{uU xBՓ*݆@H/{N];bxֽt`+Ilay*1۪\N_ f®IT2rϻp;`LZq:b9UO/Q-?&y{ޡ}BƓ`S^C`VX[(%ؚhCai;[߄5kcnο b, 㸀MؽHQk[ 3qĀ'l"cѸeOM4tfM{> -stream -[P>XbND<7 .X@AMp%UWyt뼟/T2J?dtXܳ}k`B֜G4=b*ҟ@#W fJ8p :qxX1TmD %kZ@t9c]8M8I$-4j{3Sw{zeQjAlIusHtB7`նO5pgF0T750dFI8Sc겊[[1 Yi^<*?'_~ -1ǣ"UFrb[չSZ@qj0M2,/~37GEYm -.^hU?Lݓ/&*e2SX6JI}wZs:}#dgqIP;N2kW:Ab/c#vo8T0hc$.?>AiRgY* -j|2ըLeK G̳ qqCi%dF].׋O#f;=o[ .}"9')l"'sIޯ`9mFŒ~fG9Q,jo&b 3F9Џ]Uc]qw> {bInURS0A! -JuN2C#y+X>A:2!P]"*4s >s]{x)p]5k?-DBU}ۇ5K٢ANP(gسU ^\GepQ焋BYs3pScHut bU~˪ݓ,qeu 6ek VE^뭑E{ͬ >ֵ/H? [ 1Nft~05<%T,l?b]jlQӽ&5+ F4d -2 Gx^pE( zPMaV^CݲTdZ%>N -,NEV^:!.{}H'N5׳ )qTn; AF/bcwLbt ^Gx]0 P6wĠm{]t6t,6i^Kky)yghU-;Ya[[DŽ L=xU%c+^>P5w Ņ(|`oN/tϊo/A1jz{~w|%[C?sB+sC&jz?[Ԝendstream -endobj -4033 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -B Me)+ X¨"Ӧ߳^|H!:̍FĨ&_(,gwz}5j^t1z=1;5/ T]Rgѳ_ҙVWFVpJSx)`:B%wp Ӷd|}n8vz.^k<]S -UX;#Μ 915bKtz]ʶ^Vt>x?Zq1` Fb?inmFCd2:Y| -F53+GBlߨT2F;w[N M]A)"p9jl-_4O?վ6X\N]Y]t7͑`]@F\`اφ*oHxXj͏^ =% JF]BWͯɒуʽZC_\ٯd6x]Jf(SBu9QbC9Kjd7vdv]{rh8[|LQDŽ0FoNd݂;Es -u~?=YTr$]0 NcqrtWO(GDG$q +. 7ZPXX])3%4hT9vmGh0Ћˍ6f%PXe)`Q"d -oژϛ j]!4hI 8hOOިLӃ#էϜh޴5_Zq2 9XgD-;wԯ AoY A1Ba=``nWWI! WaOʑYbC8x`ՄWÀjd,9kb֥Dgs3r\k@e7U'Cꨵf_ۅJ+N@;F-\ebz07#B/\6_⾢49AԈ:خtnisUdfVkcK\ i ȩ}bʢj/xX9N -peTt PDfendstream -endobj -4034 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -hMBɞTjD F6;m, N]|:5or-\2xt5ªZ,?cGYɿsOd ΍뾳وr -a'](Cx(>FEeFJB>a-z *0JAd{iG0ZIk>DѾnN*X?K& F:JH-s FJn%j ;I1n(qZX&^4ȭSVthaJɭ]ŧO7D9S0hSɛtZXaVsc:ؚs~a3WꚏoL n1;ކa!`Ӳemj~ -,2oJ$&)2\ޱd9kZ!idNE/4KC*W@P[\6toP7`RSg"@If$rVO!/Bcn -J_ eָ v@Is-{* - Ѻ: ׽qDW8@SNŠA֤&hv2~T6ET-uu+Zrc9bqeoOmQC"fdsc,.vJޠwݖB"\6|A2̛Σkh3UÙ|zPFIMD/UIF X?!DB[Dgw轹 O@Ҳ `nzCGPG)``>av h( qX}3z0bb}R{a$՝(ݧvf< iFtj"z5zp >x Z6:Wbl+jwf{ߡ5XZ3\hJT{!1@Rj/Kuc)Jd(Q+QM@Խ}|P -:DuX =!W<$W?*q t㱚>xkl2 ^nEVխ@ggF**|BI߅e D5⊬9 %~곦w9lKk x_*ŜܱF$ ]Rb*_#!z0{FΔR.f=ʇ% չZ3Ыg`^g?Fo 3h^G%j]m::J?`߾N/4?㞶6 Phanʎ:@Ku/`(fw̨vsA?#5+ \ݹ-Wt.;xb~e,_sWfS/9dELn[endstream -endobj -4035 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -)08`$ZuUAPI'2"b226Ty'CL#H8\\ׂ}V.I\d Ahw$Z\@̛ biT/#B}ůW|/*^i&@L: [B"2&^A<&ΠiiR¨Y5)Q0a8>UPmФ:'ŠBpuyۣu2SC?yEe:M̿QL$ Cga (C}2=@2FK$>λ"W 8.[yر[K\ϯC6߇S/ԯk"s{>+%d>$_KV8^Ƒv~ucBۅARwh g]5'4{鬁{Am`zHo46lfQ͔}sEVo<-a_PGE' REϣs`[H6HN&+JZ(tEX{ZJVyRϸ'>!M>BQhrB=m&|Rk٘~t}LcIH"fS]|r(/. %b.0bͻVR!4maBqXDZ Oyw (9ҿٰoܯLT!#1d |/UY5ߣ ƒ#֨#Pd5ak!T(8}Zj2lNckK0\bgm87'ֈ@#}!ɤ51Ck3TKvr7;w߂6=mjZk:puأ^Oȸg|z+a#ZDlkՕt~0 'K W1{#7 ^z+tngZaO՜u1 m[?c##sM5~V3Ri^5n~'-Hy͚8Nr!AM싗i%·t bҰrh02AdOz@ 1ע -aף\SfPro}- qd] eqMS)\hRZ_O旡{GJEsphr雧$SQqT[#h%$*|N{"DYc:LREzEA \>k?&ݖ`t0lʙxSIRQA7&L徐K0yV5&0wyv@*Dcw*0Xۗ08Գ;YIP!\8F邈EMjW>=~T>SU]22kWqlAu m}D.}ᤂ@c.k\X!Gk+=6}W sSwqO*p]f5I -V\&tiwaD٧v>@/d"9ĬȫܓS[q/7ICES}ؙ([*pgL9F#SK9uԢ/ۺq<6 -ҏb<̽Р~\endstream -endobj -4036 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -sj7\/lB߄+A xhϬmZXmc߰ǂ'1Χ QP@!8*Y5~X{/lsB4_o% ȉn ;h37f/OCYe @ '"9@&j~FSSDy̔b6@;+'1u(1 >tr9& 18.ĒX;0U\>f|29ˍꥳQ5պR25ly&Oየ5<#tVC@bv9[3{1Ƶn*h~Nfh`>KN\ZW6Y6~w⋵/OMPH/{vkA>ڪK,aS)S[< 'z;޷DyrߥV?"frW>x pMd$Xe[F|4 tc h!å'>CZ1G9Y{N&t/[f r`̖ !L6]몛H'N8 Z宆)g+MI6S23tusPDQЮ}Q )we~IP-ӿ(mB8Dcg_(\pIc{~#nN.:- -?v/:cP)k[4h"R ٝL׶sE5 r%o -J 4 (u\[p9;E>W@c.+(d+p +hhmtaT :>PS[dcq6/Ɂ; 1RE!MW؟@`#}6 筧+An$doPd Lj/Toendstream -endobj -4037 0 obj -<< /Filter /FlateDecode /Length 1312 >> -stream -Dtw'OP{ҼQ ok5e [zq<.kC=wxb*hiaӕF.JmZlMoW. )oG3bf*Iy|RV6FaK56uxE]uBMRS:҉`=?Pg׺"Q{hȳYm븴I><(ϾkHSβ*&Çf\|fP:HHE@./'xL -U(Yh5<}.J\ Lr͞@6Zo5#|kb+ :d^@)u,*/${8h ; m eh쯔HXkHAנZ ­wC-y'̍q]R:Viaԡ, ٓvڜ?[~)σ_9Α/(,RQw+ g`P[24ɂ>_tsYMn~ -;M\v?~+?_S(-eOmg;h~uוI hQF`',4I_c *)@xuDog`US?N X4[/p;,!0Adi7fS -LɶUa"g1^X>&UR'0Uj!*֎(sw5/B(=;_5m_E!V;ѩ ̚"d:56s ͱn.KIJ[aOԣ'gendstream -endobj -4038 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -+yr9kL ='B^3ÅTEAj^ M6]!iIela֫ ptRɩb)s+fM{ -+#" k"n1\hƇʏr:k{Xl1OZl+^P/xIŰjpGg-XmW:lflj,-oeZ_P6V&w"qBԚ_9K_dd4dA&( feҦna ] -H6&@0棤͓3Q $%^bT1M$GcX1'I J%یP:+'^{46&Ӟ?C Ŵܚ,mty$]DO N0+0rR3r">hMSɀhb78go8// Qi1lXU1a=&T=v3Av:Zw.R8$:.?$~8?3sC3 -$CCEͣ6TQŹ 5-]>N6+6pN6[ _T"υqa<\?;=C4+U!$R.y?7` ; 9P\B~ m޵iL#nmP&CMe8ks ʓ -3fZL{sy2s\"x!O@lgstBxlrȁe05aX;/_V8/0_,hsI$x.^J1B12T{|Jt({ƝSzkF>a`aҴ4s"ِ~RQX>Dfm₤hhh//8MZwQ致=`@"Vjē?ZPHyJ(&> ćlĂNzCyM'nr{ȤͰ(D)#ԅ*p| Aт8;%1z/fFq2zxm- Kf;5R 燠JvD#N -8sy"X77iP=z*RT>m6i>, -iPj#ăLU3q%8zZ 5lqN[}XAZN T[>u~04 -$l|sv\A'acNlJE#1-0h}F։߸OG^+ 4 -ɮǠh:"rZFݘtܖ:s"k{6HhkbM2-r&}&|xI5x#ph _=u3*2e |g]f(}mA,txahh+#oIu>Į0oxM7]K .> -stream -0}e{P9|) `8=䌪j7ۤ1QYWXFa%qjlr=E}0o['_Xhi}u_^XF 8t l4306W{3uL<2Xi7n97}iCT({h#34mZ7w6T!hS`CHBP.6 ;9 V?G>2$ZwKMպ_% <~^҉L'QcYg|3Ow `:N1z0ԅ>mrZ+@jr1#`(1ZQ,4"3s:mkο_r#hf҈WD^yYOxr޷|1fA\`Z3^_Ⴊ "%g`Mr9E)++~7+Ž8A1]QCQ9R %'0O.yP|*O2ei@SJTC] 3_HM7)S ֦) -q\ciG:e#GBZ{G pjM0GE5mX7ɲfSp&?} Ѥ<# * JYai{~L$Iam``UP;:r >9?4s*n<2xIvS GU~10cn/(L[ã "FPwһ+!qba~kpS>Fqg M36sߎ=xل7؋vq@K0tw 2]ՉjA0(4 ء u5\ӘOȕ\xxSèOx35Nt -o(Z@b'rQ FrmtlF5{_ǛhiᡏH1atQ,|D̋KOJNqHu3jN(+vpae:L0Ql ¥D6bd\#/x7Vޗa$ z|/^n~2eKZF+{XU1a*M~EQ-Iy5^5/7'!b/OhDv}l״{] Wxt%Y>OLO'su+,kV 5cRubS#\Q)4܋CquItI\8˦[L wtendstream -endobj -4040 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -pzoxNM>;{2\g:ϴw&hq\*5sSwpҿU֍vTI@XUzwDOAZvW rtyi_(st|YG[}B(gn"pS*oeb ;c,6BӛAJ.^M,K\rs(qGc&5_l)|37b)sϨqDDБ0RxxQ&;+\PaDlҘIV6ӿ7DP("U ]0YnTCn*fb|.兀ۋG5C93yZ93KYpsl0a~/(T`8 5N2oxLI*g^WyŲu('sy1ڑ\12aPMBwaqXx`1>Vxyj#8>=Kr{C*¯Q3g/`KrV4F,xRkՍF9$P ,Jo0v(\CHfa/^{UދN|czaR3^[L"ZB4?CZ2T~緲Ē08);9r ۚ%~W"DI~3-G~}GhFvb\^bsrtNt3:ZdGN+,E`Ѷf=A<`@b)Uwq<,  -ʁk^4fMrM"TfBa9⩧/lش0ϡI3)w16> = ]{-سt*楕qipǝkM]m6-D$O9[&ƪѡ_5H<# R^Ɯc7o3NJ)GH'vZ шRPCwmOtZLMK}*JEBQcvKZ ckn4Rx `&D8r}7ꃾ(*UshoDgT0->m +]7 -N&u/NMw3uS10C6zw='&Ha0ĒWBʍJsL,SҺmk6;6GG$ 䢤M!_8c#qKdwP4[&OJWfW0 EjP),(> -stream -LON0ljK<^jZsm+ N捑:؇T;r/4Bӿg2 -ԚfK -+s*|!;Ayvy7hAޖA@u}N@Qv)w jfo|tmHl5`H7¿^UtF́#߅-W]SZ -.E --Du1v9/rvnvG^zj=l3& pH֪Q,q;v_p6vCއǏᢋ+H,@F:p0@QQy򟣇] '{'M,EipBJ&uݑUaS?o7i^r ?(kgR2x6@3>qKsDNƠ)50 y*6w yOvS3M(ݕ3mY"6شf}Qݡw~{d>Vdzqm1"tW8$p@f@HUBM(Y $+ -J<˖ -Rf0bB/_t4&GR]u҇x9Y&V<&+'4#QAs+*j'\me@0 -f%ݖ>G2=R4Y|!Gí{ML9; AfzH-HiAؿW.%&>`H"/ f[nJ9٠oFBrJeGS00Y`Pƺm뜞S6M{1'4Q`s6ߪk0-7ZG`C0cf3qP@bكd>iu:ZwzN&(Μk װԶ+z XNxX66,ʼnej\s~EUy2y2XGE\D)^Ff7s-8K5G8mp(>݈NNB$2UÅ>fK4twu h~6.$$b肿>Z[JOt/2)CE L.S,I)%`K;.]`qDlZ -%@!z -(Xrq4kb0Ep1e' ]W5mnojI^S{-UQ Pz6\5q }~=޳`RuF>d ^8SqN!GC[W+AsgQš)lٸހv`G/vjoʾ}\ -sQ2h*gЦcRbm"Ajn=~hY~Uׯ՚>]FQFZ)+k6֎K+Y<]yϝrNk"U{Q8+S~MҜ%就?ysY6!DZTOMێ{zԆ4lojI3SuFt@X$'4ձ`Ӷv&>8=ṽ=XU);~=]2"ѫxNBT3v)RvI ݃?\iШIG 7X{𑽃eV VB[KS%ة]JzMLAdy hsz.)ګ1 -_K\a$4_endstream -endobj -4042 0 obj -<< /Filter /FlateDecode /Length 1520 >> -stream -t PNOsF -z! ό KZHmJަ*oHMHP[ ׀P]c?dcpenR#lWkj'=J|(HΖ Ӳ_̕H -8|F?էN\o5O{chdm"qK˝#>C)ctI G3F4 ӬWnq»1ӒJ {;>_]тTXeי0 SLQt2HGrC{#~j"HcEZI\Q$[21$|%;x(ޮԯ&|lT|]hS|QG-pç=H}~o ^4:p{2:#8%ApUL{y`xSԗk64EBgIvvb# x+Z왇2MFp\PCvw5z)_{TeS#xckG'E Yr&=(T0۩3!aY^`4cS <"k[ p)u ._,hm䶵ҿVBMkBwpGtӯO}2)`8ֿS-9R>zo<3FR$DAkK6 Cp5RLapJJ n4rA)T^#@ ^T_2m}Qe -q01zhd{ж*pO,mZwg{z5t=OCBךY}2 9Z - -F4 -^۳ -8j,"m1r(JޔL,C//Y=0? $2餧}mPe+Ѕ*1rz[zM7EW{^.5?y%Ѓ=O?R&k$:OH\> -stream -x8)wr}wQ=`VG(SG/涛uyZ:C=VnN -dE(xKmOb~J,͙ڼ&cKª*Z(x6e:"cE jЅ;`wQDR]358x`}qgTh6k9F:Pu bQf%lr  ǚ嗤NABdoʓڎt-!c**-dhgeώ@֧B0%9>)?Sfuz6]jrJVXۖmq˄2J';͌ıv HPI~8/ҳ"tU$d@jͶj dM6s[B#T\*qtML nPvu5څVe"|ni(0eAcpfv:F7_2OYo)_jD m)-pC=c$Ψ" .H4#1ԑ֎b<$iC81\-,v[RfNP|H=t2DOƤWL(YL$귌 T\)Dtk$VW?KITkxSe>cgqЈϸs![Y8MXJFG [Z- Rңd+Ӡ0)EDpl0OqMThk2nLpwhsPެock Ku0ӽ. %0 1[~Q bT;h -?X-BXc|嗗 {VfwҺUk$~ gL-=uYJۦ(sQto = >k VN l7T:k[Dj_@>X 6;q6l4 -ɇ8d-4*[TU[JqbKc]&LKc\Pgw;E0@UN>L?iq5ֱ6g,3#&B tr*]{)B1 - &S!xT݇R8'jO=*6u*u;[SRr,'>?ZLDȁ术vaknyk֡|G27.9g/۷!cKû#}H%S"/`"MTQ%+]]-Oe9ls"X-,P}5Q?C'b)谴E Ǧendstream -endobj -4044 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -\ 6|ߥ=J4JhP w=&V*7^q.6QI)/w?ܠh"GPb{܆ˌîti{N^F$?aˀ{BKF |nNQ?9>:0Q.ܑ/BGNǫѕmЪ8V3 .M@vt&֤$`(HFbLd̘I@n{:.Y`<3̎6Z{c`<l`bJz*:&SnuCː-8]TY5ٺR{n`5m-Tk$0mx`s*MrM0dwB) լGem/єMӡэl~mUޮj KCۓ?,@/lPfe+/>qz rVIOw+;\z'xlqoLNI|x;pd P==? רKnP# Aw9 -!mf\zLO :nm 7;a(ǹ?2ꓬ2m*z@&b=y#p96Z,SrI&FHX4S=4WQ/V>+v&fL+)"Av7 v Eۢ`^ D@.U~[?c-ƿ}uصǶjEK`oMVIFE.EKaQ**uL_>m \1Nn%)a0qendstream -endobj -4045 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -sƗf]ݑh~WnEmeU G B:tr}bYgX"ffҞKk %<䃬pșU)!Ѐ&x~#@oPF4`T<ץlKk<QN/3[ُN&9T]. eBϧ|݈v8FHܮvP{|3%mS40eID0V ,dQ}JJ-[5H4?~{v$VT]pa(I?ާ#wTbeܬ#wgS!5V -D\/*,}q9,ۤȷ/xӑm- s|16;ͦnѦI^7W0uICU!cᩲHV-c~@vG gC1"wdh芻,67 -vR&;DS}x?>ʼ.O[3V -V5oZUIդݏ\L WW=]1$p}ċJ'wBu+}&_(-] {Lw.#M~LųQc -mdYN\̢!f#7>uX{5|-c{G`;kYޫA8]#j^,hv=TwuמѪq\s7γOqCtF8g TdƤ^&oPtw8YA%IhGkX=7?溚/v(7N/~!]"S7:5g ]/9:Xx0.c$6c``\LWMm띒 J*RvR.7wrM%X>QN@'8vLU/7j~!o^k@%hz21\zb҂X_ޚ|[T!E E|0# _w!y=L"醠aH\-.BF50sVW32QTtDy 9opdKϣ -ġLcYYѥq9!Hj-T-H©7ü-a=G_!J! - #SAT˂J!w!oNߖڃ]ŭv1FoP[^CEDZ^^Ya$*b|="D=Dz],^Hɐ|t` ChCd"ؚK:#G^=PBQ7zʛ$f84¹u9bRcq$-RNpnӌz،daXjRx9Qe(e* f7?]/Mݷ.%TρPNV@Sj:B~LsCw{>TL{,PV?IڃL\d3W} [û:;TȌ 6+sX endstream -endobj -4046 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -WS}\9d(VSyyrCA1bfo6GAKmMP̤j ^=~+Xc2AN6'6E ~WK!L;(ϮV+ cʀhuƗ -Gx.rTJҹ{t@ec$LІ\֒Zi{*vOtQS_k;9Vԍ>/=Ƨ%yOZ݌6FFFR4"H;Qc+EJu6gOYmr-nIgB }k)H{'g8-|!$:kbUY *pI-l}=ʣt>EVo j>ncj[ZA -UkfXMVL_崘I2 HvrFp..1؊k/'qdY9s#zkqQ`ٯn CP7a%,Ldt)HD)! 5OwGKflxp >l%Q؎s R%:;eNijV9"0fO0^.1wO^,{@i羇gAB1G-A`<8:D*"kD69Ohlk;[݄Hs/7?W؆^CR+% -`7i Md fz2G5'x7S[/ݤ^uy5-}'IS|%[l@0w~d0.cf%iPD[}̵?zCbC閑f.audx/pgB]SNV.adu$`龘m{I'P%:3fԿ5DvgfQaJendstream -endobj -4047 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -b19`/ HeU`+Q{ -!nbhmhJ'kq[)MKӉYOjoxӈ%BR׍FDJAEyT8o h0Z7jYR (X8MU4|ջ`=v"{c!jΊ1_#LNE)`L(\_TB!I=ٻM1Σ ܳതp)l z+,y#zG0H.JAj~vxtAE嘯ϪvTʽ1DsC*a3v0M끧H#n:[f6Yy55hǞm1t]Pk˗Ie -1rP"|"A»ŮN\- e8^ɬGi nHT )h>p6UaK`dj ?FR=Pe5~G,f Ve=n3:80/;[NhUk#f&E$5Bt>";y8#v§C?҈mE^A(7mO^BTf j|cPY2Cj•P %Q *wZA/w4읭pZ6.lY3{ L~)NYbZ5Y$AӄTE̵ L*I<t -Dd8as}b5P;|ICe"ikV)&m`Zqm"|5wPR5::R8xݍk9b':J@Q22_@$uXYZ(AGwt#!r>$_BY{;&h r@ i_X Pj.aziJ%WBm sArm(O䳗9.Ό]PseڍmPƘޒ|=o4ٿ7;}Suw9G0լ~hJ&V)+jn}D9 \!ʯ>|YW+~xrݹ}v;L}4t[sx5)TjfS!Zׁ8ɥ^s:yTRX8{K9B!Nd>\uQ!6q=59iz4@L.OO@8^ -|۩z~ EDPnM?s6MvshEk܏#Iendstream -endobj -4048 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -`մ,$)-1#rV *5h$Kv6i'{K@. v>m|WDJl rcb͗h˹_h/L{]_V$w(΢fϜ+>io-tqo*%sD[/a])^r10#-ډox]I3#@B^CͰ{0r?XF۹2bh>fH=V ,Hr.rq"mLJUFE=x/#MѰ"{]mǥ8xMa5yA*NqՙI$h\qB3c=,U{ң!?EZ;WNc7R*R; PD6 *Y.a0NӧxSfVY  -Ka.F -#IX-fN29vЌ -+c}Cd_ -gUOܢZ~g;rN]&jfax64M~'I)u'=o|Md0uHjcfeBjNS'k9Sm$ЃƝhmHD,XsܔYiٚ֏h[ᳬ6Z3;V< RJu$AQ^ɍ-Nvv8 UG2+Y`DSZ J7 7-f"a0-Ѻ|;sIibC8īp0? 7 呹=A;Zi7GV3wɰ:wN u?2@=LT1tE<էיzBi&A`~Ykc::; )VH5BE -64^;Ip5Dn3yFMaPxgS ME(HODB2MF6pݵ>yݦFՆendstream -endobj -4049 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -Jokh VZ7? 7P35>LІ -7p.LZJNnBZHXQ6w P0YB F+z$ś,Ч9{IUv,w1K2b2Cl>r̜Α5[^C%[u[o0p -vW]/@ *LqnΌ -ܤ]|kPWcuIꟙWFʢ6Tpz+mI3 n_E`~=,-wk$,(3Far$zia -)t.1CJ[4{TzCNT7L>`5qo&!\9\x_BgJ?liʾ] ^+<iכgs1D'?),o0o7ڻZ ͇ -f~B cZ!vg~>C[{9aDA%*Ge+Zw?@$ 5DyЏK7:p,~x[sߤ#ErY+` cպcj^lJ꾙 L-hO\dSB C% -x-q0BAg¨JtyTX ΄ײx#™]Yui|1j|x7zRhhshiT9Qrt!TfR|2""Yv&s@ cw&1s]Osz³ QXUnQ'P=v\X43 k֩_{@g$ٻA*;ɹ"(/d{Xr&6}Mˁyz!蒡1|C1aUUˋЛ7zaE@\ia 7l0WKLQo0*&cfDS4^ ]@϶ -=Dͫ4=RQbdJ;s -QWU(јtm> -stream --xn78dwtFf9ҵW5|v.-){~Uo6rNB|s{>sgl=,0? %L$9څo}!?_Ěm쓚yI :=yZؙƼh=svQtkӟTboY#0k%aip":xЦi$}2(&ZSG-H*iOg?>;&7I&yR}GPF'B&- -Yʩ0=YWC浅I{NbE O^Yj4vp[E@) -nT?QP6M\R^+xOy3y|sea%hVTN (vυeoJ0]ZUuWJ=n%L#k_Wvp |1E#,;o -%Ļ[ rhj{QbrcΛ|&nA̺vh͏g®h:Y}e4Q"9 A) -|]_# }xj fݷWJ!iMa -sW0 c{v?5@@T/Z4`-4n]ET!Ҫ8l4p3H?gZ$oNc$ݣ \ u@. -,MtA$|V_%?W,K\.)gt:R}󫔦>í@E ˗R)F5ۉ ~=/k~šXv]0nw){ }|-$/{TD` C8U"Dendstream -endobj -4051 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream - yV]5DΑhҰ -s`$Z GxE&@izcǹ#G=Zc+|H qԘ^؉3jF)4΂Tjyn=d,"7-oI1*VK XV`߄VJ ^z"$kDV-:%C޽8"CmjwA#Ge+EGnS4[uͿ zo(T,r=HmҤ:aUoeb6{-I%?Jk'm9\`0rS'tw$K\hK&owah?t)2$|q10$6f?T9!(} -M #?]!dE0f޳eә_kevQPz0,sˏh=:)B˖*rY#k;#jcZd- Q\j3Ʉ?9MZHQDs$ B1=Z۷@tD1nq, l)vT 6 bqu܂N]+EE*KU:.EĆ`{\I>o2@V@@Fa Wup>ӎI`uDFFXthˊ9eo!,`V{ć t\,3Dq{  df&hU8I{?e7y;Jf8vV Ҭ\pp_ZY!ErU^0-Y [MbmX!'_U,>u %AE]FVPש2lx"Ac!$ +xk'R&ÂO (̌QU,UBS!zOݫ~TW }/^[6?믇f:yͲ Z8. 'n!(ljz0c>:v|7 i5@U.,e_Ӫmy sOdm ;Kw2E2Ag+sQ-2 rPSi'K - -awv77 ۵j -"t>KI]C0sN/qTR?ted2r{ƪrȣ)ǀN"aed*W%)ornXWy R%J{9 o~4+0<ӬY 9d@>2gst]Lj33n alliؿAp3j2 ٲūRHYy_N-O' پ-{߁#*iӥ/;^jIS-rʉQNU[ Cq^GGԣ -i>$(B5)Jmm `K#jI?F>Mgsd_N#Aeԥendstream -endobj -4052 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream - qrͫh*G./sEX6rt T5SKbZvgZ \V9 (}g)~+Ĩ-fUo7<.5=641G -ʅJ+8!P;)ě|Rڴřrxh%rmxqvذ4}aoGI⭆=;oJ[/xy0۽l-Eڼ\'Âzl'cy _E=V &'q;O $I>vׁԟ>~w:uqZ&~B-OM8AIU й?.>&A].k*ɢ&KkXy"';Ʊ:\?D}Ps3?PީI9)Lƾ d)/w'=d@t\GH{~·Ⱈw?|= -Q];,vc_g~W[JB ILnhrTJ>)&YɆ40N%x'',\> -stream -XA.- )B|]̊+e˓~i)fKӷ,?U#ɀ\kǛe:M"K4t&#dr*y/2CYA(Z]1%ƚ-cy$A'v 27{虤xGzL`oWd0U]Aǡ䕓4+ -=N.)%qf4}tr!%߈ryU#X~lI<1DgaL |B!9&ߑբp%qJ+xw1À:wx`U9d(n1hqW Nk]VC< fSsӘ85C~ -௿A; b e))YN=4JNP53%scp0P15kC xayD3)đ⁡CIpѺ5A)rKѻ/\adZ 0'NEw~UXo6:v\ySrslm?`BdQӏӒNֈަymؠv<\]G!2#mMxsN=wdŘ(֛E) -sS>p[̰^Aendstream -endobj -4054 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -&;n' I I5z]nbJaS!/14"k8/ mG6T~fD\J0؊ym1JYŐ5] aQod";[ -70-|ƷUyQzbxw#rV${nl7YObo>HOk 7s}YpdH"jNj"hX1rϧ2R5Nߺ@ O}:/ܔfbKxw*\PEC5߫{*K1ZX%$fqL362pY"y{x/" ?>"Ǚ4OD(mrX}DܰD񟳴jjF ܖ#hIJ8iDo'9^ޚ;8URKPԕ5=BLG,Oin\?X<'(/(fw)1|ML̝|pR ]ɽm42EqCpLj -Z>OY2A_- rFTi0;ߵ -.n-: a&*oa6QwZ"&Yph_$UJc1V"p75op2OBV1sd8n@iCrL( WRxmk^f鵝\7,;]/%Ac@V2J i0 `|-\, -5{&YV\l8 gZ_61='9R _(.{ h!HK tlp]~"j: }na̵bvaVB-3*G -o-9氦_;)7eiYRyVEqq!Z4S3ҾWCvc5eMϰ L_PM\gyXrg[Dj:RX~NDXkjW#KaL&b>5*_U [newf>]CQp#kUv -ȵgE%F-̣'=aYC4)kN  lkV$=ɰ<%3g8fNQ)Jҵ% a2p4%֟ˣ'qiN@eMG9ĝ hz,߂єm܉k?Z];Ic`jס2պ lekXw@`p>NVkK]3R04NFO+&8Òh'wkۧgpB~ )wbZ8:w=R[i9|Ox1g+^ “Ivr[~2iP1c75رM*8} L_=DHcp"Vl'#Ȁj'% (742;endstream -endobj -4055 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -;!@`D/L[h+9r&.0!CjkypW,xxz70:'@L%V{>\\s5HRɨ/zR2Xj?=vQF4ږFܨ ,UIde| 4f\VKs-x E^ )>B@.SŷgIӇ\ KKke]U,y}զ'EO7PmzGcc1xvYV{j=Y:*ܔ0Lv6feHm7X -xfK.=++'i"9,9e֘68 X񜞮\F=SYB F|bX߹B}!b d&))`PcخJ-*} (?o<0&qs+.r,egQM\ y'黌``ĺЄg#n*}H7: g8JۈC/;t΢eD|% 7 - #Eۙ(4FF<ӥvxD2BC$ۇ5r0JhQO4XK4u̟T躢㒵 k@aF,EwC!t݉44T *td7ima(<2)dea~|R\T{$B/@,O %x AqNJ)v+G$֋5s/GCta0sV'*O5sce `hMA&{=~S F1dܒn}Zۣ%4$wMjܜTQ)e3RxToqTsDU7%B,T-l?n<ĪrnN LO}iTAߧ^Gu> yPKܓUK f8roe1?e&!UB#ŰMLtzaǒI͊ 7.VHOo! S3!u<jp6~jK8\׉lGQmz n84Íԙr0Pmh/i{ڴ/OlETx;VO22 Cس}=H`p)d U]3 YaI(ϊP6uwc[o ޣ6qs rWԞ_Nz`ayy 2Kk@5@r!xZendstream -endobj -4056 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -ք#-I}{mM=U>T7wn~~[Hk|ID6~ icuESXnYrmy}H%U7!P V{r&>4? ha^'>kRVHP#]@$IsPnDw?҆T";[8gr,˝):c?q er !ODœ+GZ߫3ToFuGe|>D c _˙OEōQ -To.8NeM/_8 kGnMBu#vO(J4jLw|s1c,!e">B^lL-<Ӽ&ьTZk,z?Xq[jk/Ly]Vr#?gu`x]D1ct3jiYuIj#T;'~`z7)p N-m6!%mpdspNl<̠:>ln#K?*Gt '~3JU{;5>zSሳn tg+D}0*y|xq^JYuLw<[1ę5q-L0=Im ;An<I1L/7PuA.P&c5j MmmΗ1LC!ݡU嘵tr;n燅cX -9VA $n)Z[f=rU: N;E#`RT{CJ9ߣ]*w-yYXUx.5@{w[:n٬$%+Y^2/X YT|*aT<( K2y3յ(d s2N`*z8\ endstream -endobj -4057 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -k 9یe5}f+{r`ɚjѱ -24G#(i4YJéϩB)8+`Òd\t& nMO$ZKxwo.Qۀ${],KGz{Tw}7鋬3pJrv3#LhLWPo 0Q!_IER q;5A|,k3)# ,dd֎74؁^b=K}ބ쵮.-(R9@{db - ALxfQGf.ȩN5dPDriU.n<G(Ye pLTk(PŃ:|'muq/L0tdJ{-`g {#n|aDFO"q/V:KKG#Oo?.=U|D^[|ǁpSy*$ .dNuDjE9P`[)?q4Z fލIzLQPJ7ltũm=ΪhL(1Y|$}D pD$k(?,{F2@`_(n#,'"ݺ/TSoɋ*UA= ;"~BdJ1 ¾kG3$|A.W#Z A!g&\>~t&ksJոSblW ɒG$E글9H OVul|HE;IW7b\ڊpN }' ;š^wRo`\X1,tEE ;מZCp}{wb_G$ृx;'ACk"/A~@W ԔC%NEX4_$ q)xEZ)( }UcKJi;:Q wQa /gu3eM\o/K{~в` 6@7fd\ʢ]q{dc;Sɟ۸b ۯ<KvqkJ-rW>Dc(#v> -stream -8zIon8T/oSu-OsDNsZ,ZNMWF7mwU {[,CObSUAʶFCk^iވcIZ7*ļ[=:u.1q"֚Dzbɞ=>ToFP`Fvhz]y" -*S2:h'A.Ȅ) i0W{gtBz׈`!m^ϋųݱDxzlf3*ߤJf06¼KeyWox^îc_/nfdl @Kaq A1%|>eUPWE5NP2c6~yme#pp _вoCXyO|ɢV@FwJhd 7evZ8Q^!" `ճ3ŘJ#1b~4*( -[/y+Qp`` xw.x_1ԱAKx#sяFmZ0=4>u2Ec=.38 3ϧO}6&!8kWa/ -x7iF{xY6XS;k҅]r 90-4k[yʪGQ2(v1#lysvc fL=8lSٌh%}CY8OT(WUWU¦`N;~=|.!u:3mq&5O5jWL>I:_39.\ٱKՇ+[>WIJxr\ZTUƩhd8kbJi{d$@vNt]@ ? -0#>L|Wl#D6.iP K ULQN Y6_X2~.1D2}]:,q^'Ȥa\Mnk9~{⍊R7qVY xDW&y0T.|> -stream -NJ`aVaGƒ[d { 9)!021fUU;ZC}yIըБ#!G)fN\ƹh#s#M"˲dŜCRգt7t o 8R\NIx+rUt#4<[3s(+d;Mlflߑ), h8/Y3"GO&.*L?|or_ȟ v{N7%[N qt`K =7%bNi0^^2V̮Ʒާ7RW*I(c+@޿5b,ԨGb BjԵGW%CPWfXHpQ[[S´gɨ}s5 mM kkNfVo8w5vfu?`ZPGhegoէi&eI澏tެPN2-[x\܊{Y4Bތp2fѦԌ6S>3j>G˥\ڊ#h@on4tQ=#z_"ՕLg݈GnirÄcHN“_Qrt~W%fw 2T9TVboj./j\Θ߯''*9XԔ8TbSʛѪ_ juG%}n2T ,LbɈfOy0" e905t%V+{cw HEle fn8E8>8jUYtH*`2v BdxWSA 73cB?NEߩ>è~嬟?݄9 -³T\KI?0(l!d΃Ӂ_eyi)SW,EH=9)dXqP)8>;G 94.MB}䛞i!-v2&=?u0ӹY|ew1:$v |yNuR̽÷W,bM/k\^S -K }մ( ȡE;ʩyI#-x䄹љEZZ!6(WGoJ^E @f;tبewmf A ?I _Z[ aH tSۅ IYa&,endstream -endobj -4060 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -GQ]Ɏq`Su}h8r `!d=;s]ޛʰ΁ Wt2=3M΋ZDPՅ'AddɁFA -aEde׭-bvAnQ4Hi@ HPl\>UgL6"tM0=&PH]~',Ch9ohuzA=tAv?DYq¥ùUp~=21`S /%8oL2R6"#sy~kqL -{RYF*Bw~0_IA->)4E{{ k.H&֪Ȓ '0=^em7dcl^# t<ʺti2䔨߼A~zo -'af{AQ@wއoJzz.]SK \Qn۞%;jmrhWÂe_~%Ake)Z1eI)xM.ڿ~/GrwTEH'_5:.T<BA Co"l.ֺA 9Tqto,8IowpW!FY,Y⹑mtu c*n4R WdKCÏJ`hyYSC1z:@ox欯ȻWKvkAaKC$_}UHvZhw2CD>`VEnZb1yr-}0XB1r*k n5vޡ["<_m=EI -#r -\]c{䞉_-X@-@2(]gג3;WMU!0Q4J#Ů0!C -m 3܋-eμ?^_! uJP.O MZ&x"Iq D:xplá+$y :$(kW9 |Qc,܇]J́I =Ǵ07F91g.#yȊl+<s^<:~ʷ -bg+O=%,%s(e$5!uҏ=*#2+ir\9sd=H S#ֹ޶zz*sH b=#0nZ h 7 `yu.z1J`" (21`~S9l3q 8 ~شsUCSs m?G[zY=ID`֎v_\&KIe9_;mlH_7p9;Ɉȇ:"臋KuN^~ŵnXe-`GaJ젏eku1l -rvSL&u%yrPqwc Jߩ=t2tdM;H45Z7dh :/,> -stream -.-#(CĵȮ{ ˷-?@l992J98׶ӀL3oyNgnAYp$4䃸,\Rڍ6lw>n1e& }gn:J?{c//)f. 0woJ"Oetccɣ!"6<2Hc/ɤ9u?Շ)8F˜ĔL(# bn7lTD _b'$Yf4vq 3C% B 0BS]C ,]ؙOׂe{JMu;>ΝcVǧIڰ_Tڄ|XIEޯC;5!z1L<K$K+2봚xt&F3;:χ;􀦘wqZi $ 2dpQVӿe[1 xCԄy3ڋ}ažL ?QiNy*(&W T)5m˄侅eUddD;!f]?ϜĠ 2=3l2ZzqG0`Cpr~-M[k#g>vŘUa{_/t}&MY h5^jt}maTLm j7O!A0ie-y~ wȿ︕+9mMq-/-LwT)ˈLw sp MY d51 ovf_m"WP Wn"bصfetA&@_e EFxSTgT9͔tF ʈ;q $dHpkPqla8`GYTF&+Wo6hMjI]kZx4(R'sGJ^Xt휍}SlZK t>vAW˰^)Y'7[MAc*EISSfA\ (FCgl7z$j=⤎yS͞vHpCw;PJ XtVT՘{J叡;G~Jpa]ðL?x:l\Bܐ鉲}^?gȤcezD," #V}uE1yGPk5Z辣p,eҷ/ h-Cۖԕ1?`i}leT9b!K*"2Z8j[m~]:zuJק -/jm68_N~E_40lH0M ژC[(D!~6*&ϔ8R(aG%N7ZLC08;ff<żŊx3OG"LU(PϬJYCG<`߅#cˎK~Eh2zY97ר\MmR+sjF0<~선& 5FMNendstream -endobj -4062 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -\BBO '&ܖ.٥0 !fX#[E4f|eWί#R -Cɶʎ:;BX -`!iXя|\G s D{W[ v -#u}Y:[*ulzeU5A:/Yi:..[l2< 1FZ"{L$n}U}ܖSb}Qf %ehPfapҨ]iM ]!fiW5LuưSe6X%Gw 8j4aI"?38jaժL7q~T)%aGi]0kX$Rlw(- o ( -7>"%`{^S!sةQ}R{S=~LEh&_jRKn(R*m-rջ=_/rؐbaJEQuэߋЦ!軄3@ޒ"m7'Ѩo5 Y=s*a0t>+MoN(?kY%AASvTj.Hu缛fz&qseS_ܿ'O#OGR_9xO޺J]. -1/z6E*R$\xj>vhB`K}oĤz#% k=NƼәhJ\Y{l!R w0$B=m}zA^bU5A4&̊L/G[N^ 23+{S]HJJ꞉଄Fv)$X[67QRvQuGNOh/V%!P@H{i^"qfc=p=DZIVC~ MO5d.̐O?FZdEF<zUyAK쥦rŘbxuP"ϛؽO 3Bd|e_'تV _HQT|c;(wE_PQ@} QٓChISލuCɱ{FmPZ$i+Qv~81!JK>j-4/˜nB6'Xx]B6>T]Sk~ZƟ6l&zfv0+j-Zd>ir'>do/V ϧǪIηn++r÷pr|uә.(h 8Øb݋R+8ak_:g=zSG3׹)]R5Sr*A<5rȢIC ].$6" -s˓qRxJ4Ef|>+f?*[fty'9)]f4<(ߨ|,ReRVeȿ\>VG{JΟVRwux /VLsdćh[bs>|V?]o/ň-~'m hw;gP\7zdS;Dt JV&6Dž} [lqq8ӵ}5KIqa8Qm/gɿUP(:bN&*5`ݥ9iA9ΧYgQlpbn\`hd=)MԣEfpendstream -endobj -4063 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -A]b8+(l4aB3ьҰG>Mf}'$\ٌ33P/%Uyjc۱ȅPas$Ubp;f\+n Ѷ\9ŵ"ujBi+4v;UNcfɘg6@}$ =c2İtIy&O8 *WGEm77^^X9Y?öw{@T@k8EzAڜR*9;~rӴ6 ;/ N(4+>Ћf(<tzsг>|0*D l5*X8 )#ƑtS__SUzB1Z߻֚OF!.4,- RtDEN(+_+{)H|F#oPUIҒ5"9٣L;ګ5tH;ʓʁ9NBs˅U(!Dܘ?:/"%Ȼwc1~0o_ pfl 殟!"E])"qxqfT?'R"5J TϪ`𳀉m9nOC7n&wc۔X7~9]"z~VFo#MVqs#:Kc:Y- {#ǻuqA|V5.Ev_ -6pd -=f_[P -3ҿC+g ]4g3O%t4O\r@NYd2(CB-k -`7TϬTUBcC`S1vguCwVQ,:uJbOy23~MyA'S< -[%*Lzb蜫܏y~lD ff#|%ǓT`T'(Jhstb}ͩ+߼PR.*`Hq`DfweKog1HSHd88rۉp-l?B: LMݴWSr(B xMNϻ j1ֳT.~`mf6.>(s| -ZSS}_ >gT2 4INjV\FɡT.KP~K_gy8oI|K0&F H$ ~e 跻v+~QH| -.7ȂQAwU6B^+EH˒4z VXf&J٧DP0F֓l*[]!E,l-t2o%XBD|Tn0\kdi Rqɷn -؃htBvbr52} j{УC{'ǽ(Ҍ}c =8a ߑez4-eVv€ƁC~}՟]Gyendstream -endobj -4064 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -Le@[`0@vrW 3}f`\TZȧ ᒨ"ݽwR@;W@ ck]J8Fu'2E|'~(9y%~ˎG ᄈaaovnhÛQ}ٞ+f~6dQx( ߌ bSƲ=Vy{=%RqpN -v7Y~gwBFb֣ņFdՂXs:TB&-Av͆01af˱5 CEs(a/1/Jм|~=@tq&ߍc[7k%E9R񆚐ۨGb\.5]thZr$*JYج7d`@&ÿ|J -mX5oT:TuggNsЋɟ;68דfAȡR_ҙ5Ƚ% -f;CK3AL61>쐳(sn7d} ñ . uLrZ0 Z߅ -I]~ X! Bj5009sR m$Mn|si4>kfFL|u.70nY1+dn0qo>-Y2w/9k -WL}@#a ?9dWqsivyi:Bᕗ16d7%1Mΰщ;)2lhZDI3 ,} նГ'˶yfТ -(__ >;K7$ekAڞXӏ)eUYF_9UYcMu؁\z8[g$݇7b(q!Np;A$/S]+\!ENEe9#8ti?pn7!̝]lpŹX蔬ND^da<<.>y]˼&dD7nI(tu5gI2z1ÕhpAVQ >/!â\Gi4ci%LN(t6D$#6}tgQfpj+ G5"p3!k}3Pf>oY?t)$eC7a? -Ö']F;pqieQ -rWٙO}m;[>V$ZmTe59o/n*|nƔJGybhGj5; t&gѰP]]nūט选?F PK2CHS״L~voju_z%%2C'\:2QiV& - -M.yR͊WnE -tGMJMr8mCȈ沼*1ogl:$#z)H9?= ~K@Fb we))E/mA2ɵK>Y𼳨 &;ڽ+䯚HgtS#Ȗ(,vnU=L2W_mq3>^~nK Oytjk@ם5óZ[]W"1с8M:Gd_Ю-x|%]*hҜѡ'~пQcX+MkjБ -ܚ?Gxendstream -endobj -4065 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream - -xݡZQjW2J!1AkuyGHHr2C>!gOsK3:143gD"McrsHCGmS;4~MJ`k} -D$rnm7F\s𻈃Tyk"Vvܚx04 2}L Gq־D,Y>r(j{%;$Q#MSD}֧4Lhouhäm:4mQ}W/Y n|>"U} -=$;TpSQU_,_ᦕ4%үW,u]nS8HKGEz^9_boq]:ūH{H<˱xlL@cx*zoC\*1V:=6M,'7JO۷6*hY27KML5:L]ao8 -YMKRXO:YVΈfX 6ig9_ @#a5c}yf`f>&1t7c%\]@y0AF"^\L{O} ge3z :u EUmØOaHKokV3V~-f4eৱOuu*>ځW5iY5TVhNGf 珝p,dRc-"0"}woGm4 yR:}AD6Fљa |J\MѠRMKCh=)˽X9_X,lWF|UkFJo_N12 ʈJ\J}ۦF,,G$]2m:!z8!^`og'eTl&Ȥ9 ; .Е MGUV:'05'U6A|4rlIw Am -;> -stream -(.5RF5߶2rj3 ș+7/THy8' -$yyx`ci{?)9%Ss^W9a6=Zߒ!<= f%ie!I Sl&[ -hw&v4Y+IJV qz)x^T6=Ywѿ\d71#:ѽS@ ~+t7w헀5UW_1Ta 8 -@:\?&ojPbbz4zeY MʸQN m6,:'ԙ4XycH5_BZ8fG.:YCQ~M^$DQWT ٠2^Tϰ4ߵ&\ߙ٧V$=\8ZW0l2| 5}}i6>"K6$ H2NvdV:;-d/[i(jM6 X(Sbl~P.v{./ZoFqfɖ˔ T*g}HV` 9C!j(Qw`l o\󷮲svUT[16 Qs,v=גWd X$n<ubhlh1%}e֓Cu,:5}[aMKp3>yaJ*Š>I܂ ?qXvq[MSFF^v4p"@-IۀĻ(2Nm!1S{۰ epuG\ȠQB~ trqGendstream -endobj -4067 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -暨 ; J:V:m5)+`bmgj3cl #<1JOb -FItE1,w=/;c6\9|uk_ -n+Gp3)T Z($x.'ï1|pA:*&֢K;J/>Ӻ/t$P׫銹ٜ\ޣQ%K 6j0F? 4{tif(.B O(ipBD')ԈW"s1[DD\Q)ie#œ5*%W>L!ɜiD@̳b@Ap Y TK,'<3ðzAU6I?tGUgI:*U bJ:B?ܓr+ umJGs\4 #b yxe P:^dǀI/z,Zp@ҫe&*=PSqe,4O ==v[8.(Ƅ2w\`XAZg4-"Gs#2{uX`tLQL; 98,+eg3# nmA"̛c<'}ND2m;HO@a$N#b~O\ɈF['wa4N 7#fka 3 Z@wjax-`OijX+Q+OMnWg] qkΆ;}eNVw£>=k8˫!CT'N;v>6{LrcXp*)^vڈA'z'#:g`vWg4_64O|g Aпl٥@3nԨ~q(8pK- o]QV(-ZN/gyrpZŸ倔[!1QFHxt cQlk<=; /Z4W"_D+{vq?~NY"e9W02fiFsUsDrF.u35.|! ȍ(g{ $(x+my . -Cز.1`)YV2?nRlq4y+n*U {b ?~ #Xdlg=C^M302 ؁kٷ1TQ1 -n^svݗhN`kfIB!?lqdxܬl%I0Ax>ھyH -_#_!0@T,V@THw?efg|nq.-[61PorbϾBe#5N?bc;=B>׵"txa~+XSTE6о 9J*XpW(KV j%Nhi'd;@qJgavGW6 N¢ZqI.oxyc7D;IZʧC+}oZf]'In!xAZ0aRCֿH(u -ޮ.{ZB<endstream -endobj -4068 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream - ci:eև&Mon/=HIb 2X,Up*#t}7MOLz9V0vhP=N|MZeiq~Ipeԣ]K]&Wcx̐'qzaO&OڵHɼ׶RrH6`f>hP)=R7C Y櫌)skpэ0y6A+ gbj;Xy+yz yX#T@K3CGZh UB C64WzOh_?mę[XC6Gxǖs(vNr(jl9lF^Nޟ`>P4j'R!8NY ~o&kD䪧B$Cy7Κ-.1'^!!Hg\.1rBf !6.^n;QTǖ>1x( yןjUCֲfE3=4O~%Kg-Nw 2]cP%ԱhWџl_5`nLEJJ(RBߜK2dyݎ۲cTxkXW(uhGhy[:8G7DrWhK5O3f)%d$psgot޲ٚv#0>0AV?FchkţMbH*P^V[XN)g K}.♿Ma/m5Z5T-㘚[6'Af7־o[/ ȿ󓠝92R5 U/b&' .;ܭznIVq*9er- aI9ԻnE@o(S¾7-˝;SsAj "bho:yVPՆz`W z:Z>q'dlV>2u"}I>%hfhHXz˰>|49 -&T͡lcjX@cxX`]< SN!ot5A5^&n -IVD]c -OW6HsҊHK&Q!%%j?bY6G )4 )q-+BLqLi`,YҀVDW'K,2+S4Āhqj%[K(a0[Ύ=yT8/xI]g3[񑔒-C6n@T NR=-'J, - r[qػ*4^+mЗ3i/ˠO&aoc#nAB*fvi?Hеуes,֊-`+ԃdڶ hQ|wawu?d_~Gi'%B<<򷎏f\ğ;Vo䨉hEUY9x^hi@~L^ӄr*dMqNendstream -endobj -4069 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -ɭx@rDgN"W@("N9?Cڜ#E;*>]mTaImRd({5vΞ:=K}R}} v~/HtSلr<夅@:ؕltZ~Ht܈9+X׊5K<59P("J ~^k`@O Վ|8U= IZ)au@M)0BJ4{;I -dZy]0|@ǃ1_;1K1KXD!ʦxa#n(i _eνbca4-8Ҩ'q,d@MQhJlbe nUZ"e*RReg!,t|#2ٷ1$%L Ut<ȣ u7sQ4 -wm#ԗ šrt;ho@7Qj}V #K˱y`Y^}wNPHK:bn^5oT;ިGH5.@Ƚ5t8ݘ9I-2BރKwKqEU'S+ai6봣8OPEM~N"D!uI|boUT6fpV{K)=i($!t d2rnbj88gȡZ넩kjP?s pn(YXƺ .аJ%HlVEz-`Χz*+MU;K_.o!DhGR{JnF`xFz]^Ի *qFXxdGqQgy CpjAY遘kP؛goX/YHk8’yGS6 V#Ba;il-ڐbQxYOU-WrWaof/'qԈGdoyetLPs˯~eܪQ~iIy|VMW>ƣ)=!t.kq9lF>Zo[5S`% 7ջA@۸em*㝌˾ ܼE1-(+M>]H;a> -stream -yo8{t2*m`uD[cR~DϤjR'C籫o4 mm47" -#3m`502e8֐;d|8 Uq>3 _ #cPjbZsx1SdsѼUš.wwxuMD2+;lAz_jE5٨*ayK ;T'@^ MNe/)o3ìGqǦ43 .6|V+v`Xy ea!tt. R%ϡ&vi\hbS&6~G_+i w nǏYG[Oo)lȘ<:'y=хf[N|PTL 2ޢ<Is%`g9(wJCZ{ܹԈݦC!IoK3|Ugb0mq5S{EUʨ0@`,hNxKs@ʋ<ESr0 ߬7ڙ䙿i خBg9GpƕӼ䈫$^@wawpZB5t^8*rПg?C΋^6sܲ*ޯ`Qץ˻r)vPl^ -5k -UK( 2cR*>Gc"6: Xcf еߚɟnbzx/JjD W'{UJՑI{VQ&;2k:W^tk}J,, $F!i]l/Э1UEHҾIprs;C֑l[ .F6/y_=LPH,e}Pp^S&z,E@ڂ^A>NE{ -h.W]q1ϸf zNns:XZ"BtoEPS3a7`A`ZF~& -5;[`-ώ8٩hlCCNs|Z@X6I̻,~VXCp%}<jZ޶N7PgyfdžF](* bq8Φb(u($߿7ur,)6Qb*H 69ݍQGm}-[3n2 -ӧ0=@Ǔ=8hM'e^,xS;CRN0.]BQMqFy]1Qendstream -endobj -4071 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -|Aݪv{c]0v@xH($O7?gbpᮥt{u-4>6_kGg6:X||3*'Sf)mgUO ?P8׎dnh>DCD2O*~%iA[bw.ԅ+L[ω@;0 QHyKe$dэʣy0 fߡx ?ܽ,~.Z -Jπ)EQaox?*W_s|>3vX_MeGDǠΜb 7+ct_f|`ڔo>Ul͙^eu>O_{X5nXN_;}.mRժWv1\2 cp:'z9"ljH'|++8"}Oo%VNA"T.:t`u? L|FEWDgIdq-#:QP_S1-;o2"@ - tD$;GW >7s\My90-`G‹k @77>\M`9F}Ot+5KaK*q*5kP+t/۶nM"[}d$<ӏ$AXJ _݂ՈHfcݹz.ly{=PcI7P3u3$`*kfVH.U8-C**U1 bj;̷ ŻI -ZQ|DOz虀5C26U$L^"XR[aO<gw2H6'ʮ#pI /&K,B f<0mpp #őaVUB}߭:||-@ !ؘ-YQuf&> M!zs5R1D?2_o6bUş;9^%W[(+M:Jjm`~F X\E-A%`FU;:FO8G4 C?\ V4@961uW"WNlEO9}2aKοb_ızkM:ZM((E^-[eerRʸ -HiAD:"mn#\=kdS?UˁTp\u$Kɵ!D >)&>Q@?GQkWjgj1T2 %kg矴/8/ã]zWN]q^}JzwY[wVgendstream -endobj -4072 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -=֒4PxKq,s,R">vyFr?:$.ᵬ:vY()"A}BpM\x} f)\yZTH,\!SW 4?w !$gs oj d)he^AYYtjAo)8C;Y ]/LOV\ߎ%j\D5>ŔiR޺2+2; ]󓷽\^+Fi Qa8.cePb4a'=T6q3Ağ}*ftqh')Uj -6,s`Dw lxm8ʛ* cD"rZ/B*rWCJDBw|U٤ɑo:Ç/xm7񳭡#\w}m)Zdu:d_ gy~;J m 𨾝sE|i#hS4@x}NمUiՏ/D?!լ8+ \M#+7VQ>>U(mT %E HDg,RZ;2L7BpsZPVg9w4P#Q9ڸJc4T?̐+PN@2 QCB'BF BMexc  1F>F%77ޏX 5 k!#f,??J\GoAmaL+%B6vSqm8Fy,0b5Y~>Itc/e΢;'$L[|S^DpesA7i4lJ6;Pâ^fphCpՆvOĆ V{F ~Dx2 -r> -q3 -<;;JW¹&cfA yDL4u)1;gE"K*Hd)Z-+ h *†(@.l6Z$o?cg%_ tÔ+aqzOzŎwƬ.y~rZGO'K,7 o ;xPUhf+wCba޼E 6l -;I#4jz%Я#{eo_Z V푅wl[]2(ub>}i IkVfV^4F4į v*}ٗ0:)TT6~WIC{44ͳpS/q9)32c RuO@Vk"vktMpOf~pKDlmҙg(D^_@pMʸliSYfS]ΑV:xI"܁+([{Z=V2čx B'y7%'/F|9aD`띩l-PRʪ/ÿ̸[Y8#ZnX -"ځ]w}ABM_§x+F+(rխ ot[ǩq_^S iC1%M/aJ M, -7Q}%?I0zV3N<5SIL,<џ|o) vG=SIx2ª$} >e=E5Hܴ=Z zTD[gp;gb 18y5ILuX7{1!ڐsJ̎# 9KxuTwE,y %Tm2FZ -Z|]vYDSendstream -endobj -4073 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -sP.T@߲(pY-{)FS~O fjMk -FqIx뱳IF'noYPoX)0RkGVQ:`Bm$+1<#_x6Cy_݃'U]㊼ V;1\~qmL 6,)^eӜs&U'h0?=֏@60o;9|h61-;!eȔe[<)2,#v,e`1 =߳0qeѷ?מ%&:ӋWZxψ^C\``3_iɖkصR*8% - ǔHēRX9q3$ʘh柇d!~Өlz.ꋪ|wUj"!ރi/:8z'*Ÿfh63CvQb"OH\MLpҙɥ[/0l:@J}< _g,X䓒У$s,+djJ#81Yaژ^HnvSRV%';^V1}{K-4a )N#ʷ;)u34nZ>똖c(lr1`}i”jPWpZrYM9eң/nM>#ti-I5G='ęjpu7k\7B:xhL^.NBagQ]qAu/|0'{ko+DzY?ֽ\Jl2Er{$l >٭q8=Pd9Q?6_> -stream - DgHR7DN -겕Ú6i^<ٜDsQ \ UUnk7T]z9(^.Mqds0s*C$ |OÎJVs[Ax_k";D2z͋aNTg|OW\L~5}vkiMh8JǗfe0Aq`Y@ dy|RXV3>&X[wSN{ 67 |Q@0nn(=u2AҊ$bb;#_ԑ6z -xDAb12QfT5j032z]3 ŰtiLI;ԏE+jy.eudkjo<5I j2kٌzTD?WXgayxIj#j4`#jQ|48>Mq^$e|_ -;nϊ=Vv.5Yc4B%*/aAj =Z>]]]7gE]UnO $|A 7pEGSuUǡ -"y%U zO[ATeW߼@G =r)ʳt 1p%,;pLSUZ8=79|ҋ* @#YC@c;/t?CDʂ> JNV*s|ۈS7 hSHO+Qh؅c>t1Ѷb|uOaMdt3*jc__$khA5[=PTrKs>䂦 vyl,ae{?BqNKP:-E>WuA F#)i SOhmF<ɟO.ۛNy- oKO'N hGΫw>Ya|P.'ӲGQk1 -8tqcv+CzowMrjIp}KB>1( 9\ gƇzs9"R+]8S'IFĝ ǯLaF_U{z#NƇe<d2md%Jm>RGg]Ԫ\'-hy$! -ow).v0r_-xΏf/ho^WVn}^A~5 BD% a]i.ɵ^>Zwz )|Wr~ G4w3-ʪ@)乣%~E@u]6 -`O yŋh;˞,ȚstG\ds=ވOZ3`'\#q7!9/Iٿ1(ӎ.jmu V>&ntBEJ8IY f{Z{endstream -endobj -4075 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -VbP3zyN9:?Rz1+oσ-i -0t4A;ET/-"ݝ&r5~-ߗ5,v=Λ2< 'E[Qa_h -2 '`uāE3kp̏/ZHW<)k>YkҾ=]Y*,?,:jk߬8Ws}USKK |owmw^o$ ͹eIcipJ]ifb3^ dܴwPЁ~LB{I_`aY-nv5UP9P=^F7[՘ @iԷޙDV"Nΰ9TY~V~ɆEL]jkp'Ov?-$D!vPefY`6>FPj+'m !p֓zT^6 |9Y<zAbHy]Iӑ~KIY)*~1m5'[ *倬/}nd‘mc+e{"U9Yi8;"+4;f¦=> [q=ê<b)j'(z!܉_ST>bkig )prYZ\{ul`v.Ba6A^a'a͇.j]2tAEW}}T ll-U78&^ >R^)PB,%+jʹtJ}s|ŲkSˁ[Re$:*K\=0;tIaʡP' -0Zz2 ,bَ%"L *Dd%_BD"q<#ڝD3J}Xys~ά -aAUúN:xgr봳cgIS(^ȃ-]ebz=TׅryV m1lY*MRsG!Gcmq"G= {#huO)7K Js'/pXO =؛g/P+aZ|AͨW_֢ndCҖ@$ -`$}endstream -endobj -4076 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -X+~)pUGe5[S^%˄i%i7E2#{ЕFF}h?t;&Xv1!+dy+EU0LtCGHp?L6, '9b"3W7Q\ Ą$NΎF,+p_x4Ck/Υ@95`ΦI%̈' g,WB~;&:/̉{ nCgT0 -8(&DgYdk"\=0m}Rڪ/ sv,?ЋmD4.Ll~zyGEi -dm@QN1frXBʿ9wPX;J?bpLoް%Zk6BFuO3Hlk1ɭCX_B_/}wsw3F2C;FsGLgHp% a<O2靆5͸$fY^VVDۜqd@v4zTO #W\cמgR{; e:6+^pP. @{:G*@xm1K~@x0a!p;,:Jr}U]soJ{I E~3^bDe* 1(] ֜TP&E&׼qkDiƁ[e?&V'ܣC4򖠇bs9sӷ,],BX WAӘ<=]5[&,K[wu/8jN"l4g ~'AB t/,tΏS' -p1S8mQ>4?th睒b^sמҖJh{k@d'ާd`#$/SZw.6 -O)7: 4cŔWn*^#v.TKƠ3N60nMʈz @LkQ! -BV ӟ/6\_tqw\^(]%W&w05* -?=}N/!/> -g#Ѡ>ܯgو嚪b=] ^ei NƠ@ x@8 }lsyd9o$}·t\ChBʠ/c(ҁz ( -!kF<fG>"mM>H8ʐYMΤ]#϶Y -UasR*c澔_WSo_urd -2ڪtLR/gg2Ocize5hdx]@¿XFϋq3{ivajF>.19/endstream -endobj -4077 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -_?RAK_&ur1p7b~ :"&? GRFvkbjy;f 셕5[Sa4ΘG]m5B =\0m+;/q5'arQmT*:iK$-Α{7lHN,[Zy1Ȫ, *tN΁"3ReB^.gx~JI(e.vǚـ{%w%9!g 1ywp~zVJREZ>M%p9z>UEh#mӺ#[*m< W~/J.1^X%z*hLuLbCFiF_6ޥj0 P %*힇^2*2\Ny {SU^]q=\HB\7#p)rOOM'piu;-JM/1;O<%,fĈm{;of*AM -XOi{:J^<![+ -qXEh8ź.WnD;۬݉$!鸝z^endstream -endobj -4078 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -ϑq6ً4.̸P%ڣ>Ĉ.YIj|Toj:z}"azg S_d'SE)bnzO{Gjc IE n=\P4IN)r2.=X a*uoS3qƭ'9bIuB[U! -Kx3ZXQn7HGpfv;@x&/'Ϡ)N NkIZ9x E{3l((>l$ynF?ZĮ9m G߶CBfGd'0FZN,'U!unrym"<]YA  + AgL- lfĉ$ܰ@YV |$ ]7]'× *M@ř:k00w|uN^ٻi# 2y֌SE WWخc,O͈HIݦz - +B"2!ٲws0l`00j$ -s -2j=!?MNZjXUeLjNBT8\}Tz7l)ր8dݪQ.Vn1:h!6a[S Wt9[M:-dm浍8ԸgFԳ@A<:!ǹU0z㉒srܞb?M:wʝ/{G^WueX m`\WdqsU2%((Kuݡt;}(k1ʕ̅)g Wl9;zвԶBI86C(n)4M4t [ UfLLP4{&5إ5!R r(llhǴAdž]:QwP:!7W1y̰k4Ay5xYJDYɻapu|_A/Q3tQr!_Fu;N ~x9[>Z09E9(O#/½ 5KUT؉KaC]C6$Xڝ/û5Ň%rendstream -endobj -4079 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -}lۨ iql@#1 -NM>.rbH /M~|:S05zz#˘"S7j9x>q@cЍDBQrYAkbz7rp֎}^?lDג/zO͙~Wޓ?t^Ȳ`5lnU -& -劕0ʵ$5-n*&TQG4gP*[_yh\Hjbo@&%(o]/Zt\znkȀlTmq&D1bʥ`j`#YUXZ2(1& -epiCU,n1NKeI(lwar3,S'~wy!03p&0J⢗TLiXa{7`7`0^sW<% pBܪ -Eomu2.'=H [ {3@ц!o n~B;2h؈wlPღT,Tݱ'⭁kOAW?!`aSTDy0~ep8,tK{ LL=mA H5:x$CnEnu r{1/ŕaa@8}s l 3Vm5=>QiH#-6E{/5Pxw·k^?ɦ@A `VSx6w&~BFϝE"yt g+G57WV86aw*Nҡ$|Ri?M-t1@-Td18 |>4'a; Ze(ӆhe|EW *όz+j#K]F(+,G{{M^`Lչbq-T:_BtB*$/++BL*ή#='$Jhj34f׼ - t$#SGM#(' tz Yvᜈ#ъi3x>k-A+ -;VEv&Ɵ3>̈́jҚH8&7{JRsp}LfaVق񅚘]~y 2u{మO$)}f8u;.jpkJ>ʻ5G -;@Q=)9Ŏmb۶*| 6&2fkU/%Yo9G? iE=~fWdd1aD:$_J&SJQIczw)2&oAvcv@̠[G[&DOF큮/j8,aJ'`n41~m-bɿXw;'֝ksZYs )endstream -endobj -4080 0 obj -<< /Filter /FlateDecode /Length 1968 >> -stream -;;j1cfBcߔP-=̃ 6& -}EQ A~Ky;?4@~ާ@Ḱ=&Rs2i }PeU(GJk5'7X; a:d:^xŘjS|O-g8:K]˃ uQ],%"\b^oHs'mߚU.M*b<$>VޟĖP/ⲧCb fZ"\A^8i-m;V-,%+MRi{WˏO ܂+(f -T4gnPޯO2n48 5g+4HyBm]/S/,4+T\1d5@Re)e#cZ 8¶rL"!CnNgߋ)`\IpnZSmʕJ\a3uL^wgeU{:GLy~uEwLql?G`}?Rhk߇rz E *KޙPscjaw,ϓCs|lRھFacc ;ylQ.@=^+Xo -W/*zpNVm4X\zw7Y M&Jꨝ1 _<Ґ^m݊#$uc8x|?"=m7PX4Y(;&?rR]@I.-j#mlnw𭉯0rxY[IEPJXk,*=0V "h>OzN6&ր -Yx -O"@ _/.&8c -荙`1U2% B2@[ B,lj.]ΣHP2B?>ɪ PAI{= )>mY>ʥ8ߪ{BOhɓ:]ϴ$_k^mW7ha因-/4aN:LZy?'p;B@Q*~=_R8_)x?907.x!b<t\94Dd2Hn[<SS,ĀU쵏!\ϼ>Ybp706+ٰ.vp_P-Nò= \i zv'5w7'kˤ${U(- ZK &a#6;X@@o/cqyZz}0|p_ ?{c> k}LִM Y[f ;Cjc5ln +XJLЬG?!Yl_E@,+CaLd@^?Y=wyv|aE4FZ4]gSZ(\h/ETZ5X;'Tsb(N: -L:$4rwAb\^‚`CZ -2p$zz`E)&C v2W|X1`< -ydG-ORB@x$ jKIٲ6;dPtu<sӈ%=E"M[ZK7o4*e%[@a> -stream -#4THNƱ^‚S@@2eDaɜEgMpPĘɺ;*0@1ǡ<_L*բUx>=a8}F -fK3q:|ח;ވNH@7NюF9]?%#͓[rWÕkDZ^#!鈆2,Jz $D@ݼ/' -`{>*|+`2Uzm?biQ~o8x^mdAP'IF(~ˎdC%ҐTb&I(N!E\ *Jg<K)ΜZd?Re{~hq8Od|Y *ZDwk' eP=nq+P盧lhИTAClTC[NKO^J4 gkhW@%7NLϧ\ \dj~E5'XfݝjU";vG=C~x9- %s qr+mkɻ<2wvC#" -X:JkugqRdM|ڦ[wϺ$lnv3߯{Nw+kFĕ=TP'E t^p(( -ɂs}ГEE%Dp>U~$ aZ{-s%7[vR]?VAE̠P?rKQuv:7u C8غN^^hG8(kk-]?KP3e:y@pBS]Uؠ*@XVm1j3Ĭ:Uu% -|reAܘbAA.1ƒ|5IW8ez(`OR'RLA9`I΁^.%Ȋo8gW7MvV}fBWt%~qnñuֈ ut%kk{Jm`E ~.3OIB!!DX͏_~G5ԛ* K[Ec8Yxϓ';fl-M6ڰi^4i%9["[4 -48% -8n\ -k?IJ. ԣ k5\*I? :e'IEn_@>$endstream -endobj -4082 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -o=˗{E\`˝?TU  鉜4bJVװ=]O9[`]a ).)ZeMWaXC;n#wވT F*T\0U,70-8\Ye1QJ_dcP?.|`K4*!1Z]"cIR?==uD?vQs/T좍ѯnu2<$f$Z'Է 3r؎MC|.ޯ)z(ّ{+x0j%52 2,W90p!*OJ?q Ka~Zԙ@/{y 焌0 -оn()s[("W"MO$\lIyqb^[OZV}?f<}75Rw0n=x[@Ҍֵs'Hk]/iߥGh~g<6T /o3¤\+?O B G,Y W - E5i~ r~]A8>ɫ gdNNx=-b䙓/AIqQΝYuaf -P(ۢAt:ܩ?;wp٠ت̐/anX.81j7:bnj6n?GGPN*ŐSLS;E'6P? q, x&`Rl͠f):3N#RD52䵠rdLS6'=Faq#ډ\{߆Csl=uݳEPmtغrGXmprwi{y6:4OVNgp`%O&._DF8*Cjs5m!_3`g#ɫq0:u9E -w”tİ<ߪY 6 >gRRئVz#8z_ &-q5B4_['N2de;{ov]m(<_N}m"TQt1c)vS% \UwMgpet =Zc^~JCLcʞac);2gݼVQՓʣvj:,( -=*"t{+(tPD#{+SBGBmy\f +jo xvGA+gќ/Ije\w.U]TŷagžZ:[L m2 zR' ֶTAN^$eXj||&}jr,$%0P3lPG@Tw4.= r )cUo=?d]bVRLI/ɮhɄ_L1e}rNԕzJ"I.2M~2{Jxܩ=vW_YRi|x cdҐCRP!yHx3G ­+<[o6gj]=Si~xPh <;ο#.3D_x -䴓Lo0r> EzEV?+/;1H?GLȭQ-.{~n< D Cͤ1I太?٪}?]oCendstream -endobj -4083 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -0*rn샻heJƪg2%O{sZe,/8zd0FlgƷAiBcJ8W1.p#թ0 -)ADTj}u+ n\ i0fč5(ݠiBBLG4LGB܂EٳO+a-l,J&O^EAJBL߉_x.Jn9񘥎G}Y qv7U&2&j Mӂ묟%ˇhjxG'G5Y,iM0 Ch%.RqvW$P'B.?\rKm^P yrQn+jFH.фWi֐}iEYN~^$Hν*[y1G}w43N~M/BZ5wg[D-е:ӡoԨJ1:-ҊpTXe>+X1l2Ÿ؞Is 3JZ&+@pW,_N.X(|clPI^***cgxʊZ亞RTx4N<}Vg:I&^{n^F̪[ .n۴j -d#Yi~Re/ѩ@LGk LJ?;ұ ̜= ,.Cڸ p#t^^ -Ё$ԤN 8_c[!(֪TdE>]1IG=?Suf<Ӏ O))Ɣ5M7NS@*{+˸Z/F? ei)R"CnR0Txչגu{Ҷd]*#([;6׳>ǧ&H&82G׽<<39~Q:MިK2(.J{%sR} -=I#?vh'z G!CIJC•JV~k/ X5[ʹ10i".yn n܋Ԁś9n>LX-ܛZɤEyl `wQB/DH.1:}wn/h"͙=ZUO'y%xޡ G61tzHʇRj+|ޱC\,4Gxɼ,OlO<0E`;_)[53N>eೡ9i'.?Xk0K-kR'p!:[5wKzs¤N҈K._vA#wΈ gMDBLV(n LMᓬc^gawDhG#nk.;>hE*0_V}?֑?sd}!J_d\e:ܩ2'&lFrZu'v6s}Z峰#TP`jE O_Z!EՒMlCp>@&Q]ZdZI\J"Z?A+D]<_C1&df;iwydHmpt^,49_SѴ'`[ёEGD{wIgKoZ;̌gָδW]E#h^Bu:B_b ( {i|-%S.LX&amORfv/0I@*5Kk\MEN] sKYendstream -endobj -4084 0 obj -<< /Filter /FlateDecode /Length 2096 >> -stream - /`W[.VTWIHwɌk>d\p.@N5bW+8rx/)C,>7Se<[Bm)Q:8+xW% 7LOeFؚ"e(s_ړkpA aeP{[{U:#IAYenz-8N$0i^Rq_Pd<=.|.)xGئ ="!- d&@BN3`oЩ'u%_O'u} qYO(V0p1af4?: :\bhc\T5owÄ ʌck+kq!hiAmgą p{鿟G˻/ds9͓ mq}-cy껢ncww]pp K#$d_l.9Q#gLҍ{-[TJjiaW%Uw>pfUy;x?"lklxvOnE -J%Sd> -vXw6fݺZ˧{f-[M%;JNU%T=jU2[)`L6;֗p.o`;FgʑbD= "M>tKf3խ@L5WQؾsT}te@[frS. ֞C2?8c`|78 -GyT)!+25 Q Mfs MY~W3Jش:.yEIxP#Ӥeg1VC$''0+HH(?4?!2)i,p)dSf7=%%/b;KEd<ܿ!K0"A vxnx7<ԭAbG/"<(mU,[ AcM5z0f}XMYI=l#?M~x.l4T16k_YWC ^kK#B)[0:1zauEgzѡ>h۲1RgRV?8 WFKL81aw"Z(7M.ڵS롻Ŷc/ءaU VئBE0;cЌo@xHƐ1o .zZax(p /l|͇Cȶރd쬛Fvƞ"0kɤ|(XO/k ?RDa!Q&"1/<%+Soz@dE-A{ "tBW?ݱݎOu -ciؓp:]ǚ{t`80}O9> -stream - %"^uX 7K>US4BYHVbo֠uv zȕ|||0ȩ(6ׅ o]U 'y9-ckQw!Ⱥ2F ȄF3WvAԁP`؄nazpi\4*"CMItwl3w4hThG ઞ'Ow"G7a#6F[ɛ)8}D(\<=DLyِCZpNDžb{+a[ yRRϷ™ wwշ<pZ| -( O䒕RsÔoҺӸ\0*Zjbt҇N6,$lKywe_FX?k薽́}Ť?oCR5(.Ю|$F ᴍϮM]cDuG jq&pQM;mDrP-L%mw -TI,B]nG)Ԛ@< A'kTni-T9["oϊ|ihh'f NRTYh?jUml 5|Dvp ˵ۙqld(c Y4uɗƄ'K]INU2> |RgqŰiPa̴t}R -=jҸ,m=&=W;xcObꦢT2Ś hKaށ)br+!´ܫ)>LV܌ -{`e.QysY,k%a4q24pLơ>?] ;a8?-6[U^aÕhjW ..(Aü|ʞ+| :Wf/1h!$HZ[vihAGD)+t30VaeZ{md2Ɍxb9TqyM܆'_~Lx; G6-Jqe>=XNW_yh"&$ÌSrDyW)~̿&A=Wߓ2'L@VR&<KΠTشR,_ -mPZ M=M2 cDr21,C%(7 k$7z'Vv&&J]}Qm3)[Zl0EX.$]$ -Zs'5`V'Z8_ٙJVnzXTJ~֨ZEsZ\iNÕn"n´w!k.˺J}5kyQ4*;^F@mV+ƒVpÑ{|oWE\i;c¢ë߹v!YwT58^0(h*e4]k|Ss(NTP%Et\V#M ;n>җ_TW*uvfd: lB{ce dI,=HV^B!MsKtkIR8 ўz ي"RPBk" vl l!1~>Lz+($3:DFf-cEQ6򿶹kv)k1_ƊYċ{+endstream -endobj -4086 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -GDTdi.9 Agt9"MCVkE7E-Oi(1H7T9O/"7I/da@ߞ8/Dùs-jUC5(@YhzВqgq -!f lU!GS`uf*kY3}?frO![@rDfW._`fpe -O_#:-> !ch{HjD݄E&*Ê$< k9Ov -u#)x? 0.n{=F8B]WK'G?ԤL7CLPZÝi -k ,U) Ga.).5~f"lh/YoTzPB2iE taBU/.5B[֫a#SiAf57Ml&O%h;ٛGmµly_;''C1ݧywyfqЊWZ6"Rb,(ѱV)O^Pbb&Dm['࢐`$(@(] chl<*cw#jdb&]SПb$'7M00 ƖxBH6}y~0M4/Q>Ks;RMzwH2dQch ǮFYV9mfP?j0GBْBWd]B9,6Vd=Db0gJ?P p(j耥>1>\$ka <RFE@_l~^e{ '+jnbMd! `/ -+{y5Jb9 E.DŽ ),Ip0 -=kwa.%˸ 1]7ѰivFΩ[;V -c8B <CrӱP>2wEH%='X0ҴT|Lcs-6}H7$=ږӭn3*) u)H+|3"޶BTsbn A%u=ؕ!Q9`4g?C NOѢhYLA5ZH/{1LLs+H\5Vh^M-q2;ߐ&qӐ-cE\R^ uf#O[ '] gX'7j> -stream -6"r-'S#b>+L[R:[ϹAO5^UY`tImյ`#l&Io^~ML9.o -ʱhTbsSJ3$Gm;&OQ^cIӪWSrKvY2Oi]|NTTz6 r2p- |3"^&q<>BdapV*95fHKP4,liF= .]/+bd"XM3PF&rD_,b51yKJ{NwC-MRɳgԁlR%eg 27@-揽\YO jqfPϸ%UUxe\!홬'KOwȮ񯊢Qy -#1w'iq5 Ofcڈ8d5㠹WТ6R)E''9Ɵ\kVHW;c1$cp l ~1tXt&ܡސEPtxVq> f"5L4-SmF-A?LH6͏>)SܬqP<_ -bfq|SGZѵr<~ze0?etqJ#y-Ԩp(4'ӭ5-L]ϟFhԖTVNg$~k SRv7 u5&@+qfu~ 2%N1ZqTg>`)vj|!&,ꚑ- 4O=V ;c?cb.ה](PU-K.DHҍi AJo-2lL): -޾*[q]I٠{Lp-3JCA/Zeųi!SJ_pJcǫ|Ap^mɥƆe{潻3}YI#UT iSӔ[ nߙ+ dكK8yBۃ0=my!|9{7~_9' ];ԻvksզփzzLHnKotUOe(rD/irLWWz[yA -#> -IEǵ&9=<܇zMm YrT[ܯ9[/hXgC#L = E7MXCFx_S\Ŭ0\QX(Ėݭ6AH(WXɝdzWendstream -endobj -4088 0 obj -<< /Filter /FlateDecode /Length 2160 >> -stream -Zr:ekg@ \q!`f έUd79 -,X]rXDIiq昩Bixa -O9E=2́ܜNu(?28zȪQ5$Yp[U$_gU-zL&!9 /i ch^aZZE=SnD6SƱJ+Od W:M\J2Xo"wtׄu0^ lqW g XYauc!(ߝ]c$Džt/QԝXls*:2MϸhA@/`Z2fwO2&v!T㴴WN%vϕ/ԈoY<44:2=;};T\蠓h@ߡƠR|h5j^#HoiDpcu+vgw~T(-?t| +\˰wzmk[|(HOp]pmm#_2gZoC;{2Tpᇩ:qpls~C4Hm*n+~R}e$-y<"ga% -,&";m0Xv4K#OSlv$| F -Ͷ0f<*JWDos9{kz  6NPoûy/q !v uPƒ8 J.ZH@лR܌0j"P֠;Oi{qҒZP`=pw?Pԯ|=ҷy[{ϼ -x(ڔp$rTsB A:0wf7 A% 8VFȷBƆ, +Z^YeS$sXF]Ebyz%]UOIΚ6y5Y=,$V-{dop[gHCcȞTZZjj7:&%:rQMgq.+6 x&k[f'C<[;.@X W=Yg$x -t;ۥG2k|L`*IO@ <@]@g6.^ 0D˴RgoRta'c*nnaN_e%M[Ie@z0Ֆ(BY>>1=Р-8J6ty4YhYaQ]!]%K(3RǠhӘa`.O$ᧃXdrՄ'dͶ/zhPr{z?v;6J4yk<1I89ZOv5ּ |u%d0;㍅_:w_s  $J?Hb;R%b --GXZ+8ʘW8yE>7%:ucFOg =&]U!> -stream -kL}$knYK ;v}C'"_eBf5Hdt`Z.ssTD'¸"|ƺpy&SON#&zn*NEfDktdMX}$F -WUM9r*1'"cƬ_pxX$7mpr!l~FTЀz燑Sjlh[#;΅FG:̉8< "ݪ#YM`'naAxe;Րwq_#*$X/J"# |4"K Wޮ?ŵ6Cc*>FUe\CWP3c*z'wrwݵ^}:ÀǹբϚL3->1 LcJ>nmx[U!PWknygL'Ag-A󴉋$e=K^ЌG $%Œ)$~!GN y6=mc 4C~6 Npo:r?6x75 -C)  XW27/qlܗ㧂X:0ocGz&#DT^m;a#Uɚ;8?` υO%̴d -]ܫ]TI oi|R|?uOaH@ӡX[&=Tc:wjˡڒkjQx(z#IE$5^ZݹGk9 -/rvE;3`P.n IW3:\,{h'J6ZJBf7X2&BHj jߌRM]Qh֝'!y{O. 2h3| - KM6Z8>ndKe15PJMP( CaF.woC" $(8upş]Wq,k -)(dBzO|4+ԠDXirm'mNnl MgvC߉#_!<7*VXnens{;S>ÖmA6Ί j+f=ŁPggb춐_HvqPcr|B0n,8f$JQ 5ʑhGw䢡TE&2M9;iz΍KKIcZJp!r_ьX'k7__~Vl=~T2a_,pcP%&Յ -t^J` MZOO'B:=nGQ*AlMWOV+ | s&oQpwBU{zևgeBr|a[S*Bo|*_c'f9pB<}t_(Z׽};^*%endstream -endobj -4090 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -{g$ beA6AqpU t(樟?da73ƹ͍8/}p/X6<`-a(-mz[hl5K-CRo(۟uRbJˤ {M…|ӭ7Tԣ`#roe"CA-E]oM"PY,p M2. 5<?B^|v8em<(ՠO?xP -T\N_)Ȍe -/RٶYJ#<3UM+)RX7@Pn5t.Hk6QӜfo`9J}欎hC*8jcC%G1V$;V3lozrmо ./t;ES,A؀4*^ .K#FP)⢡PNeGձ(&Pxs@mn$hUuh,5o"F,_8I5Fȉ3 0Pn8|̦p@*[R[a=,L~WKLA7p#zYeU{nPL\}O5Y)b hM"*0kM-V_:P_X o=)8M~(4FOĄ&ٽqmEʟv4HP뚥7iw<*Bm^!'AV,lf0ðGܝ_Ćd΍Q<f~^&1$,s6@"-&ALIY{[w=L,Ap{KwJ:ӃZF@0` "U!%^[Z{LMK*Vy($PI<31yx^i%0bpOd䏒eVR)UOIqڑ)cMzԘ\b7'1~nd -6gpD ç@g̜ siE`׿QCjTnf>T\+U(m+9U%ܭl3×Ŝc+"i3r">upyV.))>p$}ntеW2Z\tN&o -ExHR[~,.|&]IsZ}Є -?rU9@jNtN}L\cV Yb^k"ޙw!I=sBa%[)Ϧ'v*22'iH>lpwEh,VjS~h_HRoNIKid|m|&ҢþՇ|@ނ[e W 5dendstream -endobj -4091 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -Hk֞FzK:͔r_J o]jL =ڹ8FÞ+ --{R"ܘjLG2]4ѵ*+ۏ|$`K)8#+,?Nw$d -!&^n򊾡H4,S"<' gAJȽ-Lz2;ߐ:%&$O>^o@ܼwu :k -J߷F!ӛGQ/0FX0"A[w|jǧT)E2sϹqٱV^+-Q+%gL| {cM2p ߶A_t$zvFYjJHͰ%T޿uBk cBMajX |,j5g/ө ;8|VpZi4RGp5Çvo~)rH@F80!c6iN;޳5_57mЏ]Ix5Z@/Y9--MilI`PW4a&꺜Ru-n -?=RЬl,Qw_ְyKI|єޅpi&>2;W/XsH9Jp;UN"ֱ9o^Ih@ Y CR8w#tqKsQp1f+{)l+ۛqO\Z;ΐF͵LiAQ TVYRwkw 6F7> -3R l:Xf!,VF.JoԔwUˍGx>=WY$uL eͭ֔J7N RB֑]řUDŽ!υL@~ܖNO >Aύ'rÅ:GCE=gЩ].Z鄥"{P8oRJ1'[>sD kk [9$w%}*ޛo`AwsIO ei ' -*ʽ-n mz0m1R$"|>'[Ӎ-ywEN# ~bSE2Ĩd-sc{5$X<-36+}U-U<=;[qbzvtمa`S[w,u}`u1.dʥ:4ʛy@B#SI"- ;QֲrH%(Dó70OX -e1!ARI/K,Dܺ%5FWt' ,Ab*A?4~gaND_b&ZqTb(`2X֥PmKJJUPO,`ʌ{bAxּ:ɛeEXkzhhJn-!r`ospB&.&U5\Vݧ跉x88j+WJy'n+68yr8kI^x6$z0j4-8QL$@a -jgr6,1 p{K~f3y1Gk̖ yhob`ScmvLs5\[rY{SOvPWގX6/ 8$…endstream -endobj -4092 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -ϝ{G[D&ؠ:◝ud}$4;r޳ `)fD1=r0(/INĖ&ucZ@:hUkg;Otm0JddR`X2 /qdئ.S$}jb"0Ln7~/H>^͓3t"^E=Jf [Fs`}MQl]gF٥;;c_-%i=͖a7;UU+4a]!$[|;I)amzp/'HOě՚Ӆm.^u8PZ <#`4Ոt}1zWkQ4nȦȭ셔OoafA3CP,Ohy1p(=Qwu$})7m!uϲ=@ZR(i(iU͋u52a2WC3J,0]д\n`~m?'$܌lmbzLExu4OSsQ2?Wa|_-^ BO6ەm+/$/,Jý\S%:S8z!'L՞tj# nn,d'hoVE3y 7_DG3˹KTujQ12(cDQ;N$Fno[o?eE]26qbۘ --x V dV %z4~ -` r"-WsҎ(qLWƻ..IP袹w}.(tYOFP'? mmZwFt=78&\y}GuD0~8&!_,l+@Zب 7Śޚcqo8~QJ<e%mb^4ZJ/r!4JsQJN>Z=VXVU02Nݔ&'z -@}siXYW4ң/HPC w& (Bw+g+!B(0:18͔߱`Z Ջh7M^"^I3Yb p ULmW=p+S>9Zʆ4ɻEaYU,r5g2ȁҺh@d](ӚsZ\yWxGLu5ZG1R̫&<5rͪ(d}ɣJqc|,>/XզQVJNA# f;*2ߢʶ]52}K5㤽|Wzx@J_]! M2G7D?R. r'-`&6@Dum5W'GqmK?w1!9^{R;7k*bhnɬMEKـ+Bzwn@bBY1zq>b>kPb$5GclV,O tm -9` DpwYXC6z]L> -stream -2)2wQj܋d>PxõR~Ȁ\]ZGd sEZțV/0,N=u3oH(8)ni@^6b=X 9ipm 9eUR7H\0{elON YOn|ԪGwPPruV *7b -YOx(uUZDlg\*w/rc}YH2 a'4Q"P#81^Tp?2_Bs){*.!ӽp}]bH -0P(uUba _}f/IC̚R GҔw)b"e/s~8;NI9QF+v!^9q!tc7C戻샙g+tygB}x sRli[4fq3HWMV'zkZɗc_A$ݯ1'A(hKuc>_Qea_Pl},p  WH'X]t4ߞ-9z'!1}G+nI礂O%X -e(b vqi$u=V FW6Xd]ULSho`YB,BV]rFIq}_Ոm}&m "(yKfjaV&iEG>=Po0CKz7u^c@70%T*Vvh@4Tjk3#{ -&>Qed:n]TiBVU4^LwMON"FMV+I9}arE#~1Q#ϮR%oț)s6*Kur:/ŇRYDU+zk,bh |V/IT/"cJeH%dyl+(J*TYR 7%rki0֓g!J͙Р`jD69׬Ǿ@ȍ1ΘmOǎHNz{! aH7MBSZ 䍭v:`n4#ڔմ)8AC_ͿM=L+zchiSسG%&S8WjPMΘ AWE׀cY dra7z ۑ]]ۺӡYtV$F$`  Έv? 2bJuD;ҳEЙo$K^aBŶO43}q2 ۉ=W=aՙ)t3I Ꟃa|(+2G-:P=I<BaBAdVx^2A8O7r`Ϊ4le^CGôIB*X*_AY䃜 c-al4 MoJUz7#v==0flw`%4Ӊ.J k2xgњ^ ^+Á5,0uj؂{0k@ٺHfpa7<1|,ə$sv~(K1ۓyq@P.a'2 Fs\8uP~V%l,Dt>SaV^ȯQ%zY:ۺ9Ԉ_p|p]HR$4͞;aYFg*bMħnzQmuBendstream -endobj -4094 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -:Ґ]3|ҥ Y>ESmp>Aqfh6BE2vh;ظA h#0 Y-u|.jZy284fqđf /rD-\@nG_c1$S\l!L5MH]W -i}~v}EA)&!56nÉfhSG;Z:rG*P={z;hl]JF4 doy>ىUֱn}i$KhLt!HغbV00+ר}Fk#ܪDLl䉮pcu2X\0y9!]h_enXkM4]42gmcĥQ9V |A)a͚Pgv:sv$5րF"3dRL}~sfzd$`(*hB2 Ơp,0~<jBn\#9:'Q%\+H`V8 ޱU"T˶2f%(@:PIYH8o[¡d#X:,]^گ7d)f+_сCY&[9q,a@eTx"OTUYuA7%::č'W+|@sRe\줸0^Mز6h%?$Z?`^ zzC?ʛI0τO&{koqCʯ1Lթ'L:/9߷s#<hGOVE=?&Y!*$$@H7R-wr`#*pV]>RV{8%AEѻ4HzzK#! 1걣iqHNMKM].Cki54P/Uᑪ+4b(. w'4vBJod`"(yd5 zʋU|EwWQ-buf^k%Hs˹kqg_d9p궩Tk3zS>`pOqJO$s J] ̬ƈ -v[MVfTG !56CZ%k+iҀ m ӱYRR>p3v@U2.>X(Hxz^> -stream -$"Eb`ï\_nT(e:L\J猏9Z 0‘+fL[S -醂PC`9J ܥVLfoVoGTHM[fku`8b[{B TTA}K  [9hHOOLl Jx+qe=|zV`)9egyg:AR))mLϣ!>ϮKDuNUDSgBبW^J0:[QX|CYrgSpL}'MYfw -b7Jzn!qfYؠI% `*`W~SC9 Scn Enokmy ΄a-ͨ,^ự-w-zYOf.ͳQToJ]ڃMejmqѴ:% IE|+?`6tTsdKHAZPz]D聢b{!9R#GoV]tplNMQM@\Y5n,gsk}<QQUϮ;i3c PT޻Si1P ,]RDXC$~ -:D[$R? %ֹpSx[;Kȉd`X+$ S! ,\RvZ mbyVB=D#d#{6CDIHZo]F )͠zRNqF—w&ZK&A&-!]a=s/ !BP<:>i?e4;H[-ǾTGOY7-5?5}o&7B^*_YUc+i hXwUu 4t &qY<+]†a zm3tZۑr 3aK> -stream -hf'S}fW0l?K- P>t>;ײզ8RΫ'Ud_ELVa41y&"LYB /(8>;^Ȏ+Z Vxb kD~WTj`y>NJ#цQAO#0>Ϗ# 0Ѱk Yb `s[[Ʉn;ySj]f_Bd4aw8*Lbu>єB|ID%8Y\ g7 -sa"r\mΊνq.Xd -1A4n3ƷItx] BֵtX xlL -9#*_YdRKcuEbyL&W]jCFydN*W.A|դ Exr`Svz;˩r8(q!Dc#HoK>$&;~x~RN8픁^',#;/68'Gu 0a7FEZBZxޤ=ɶǎʄ'T=S> -stream -Bޑy- --w\glةWtv4\P j (d]Ikx>=Ē_RVriM zҫ^rgC\d bE,hk~.XTҿNѷȬo~3R•WjPe f8 &:cwhu8Dykz@`}a-)#49oVoQz!\]9 u4>eM0XCdzД(Q%ZfbۯnTA =_; "iL7jQhUS*b5%$c)W -\3ksx;H^}^ e3Rq^Lh>e\nnQ̩N"쟆ٍ֘{ [ҷB ;kѠ_mpѹ(/e0 wb֘]Xs=#Y&CEdeSM8Xٮh\T=QC򡬿8$%o˟܀d<9HhRrj/=KtՏniN؅H<zuNq[ -5k+ȆMu:M0AV,e6z>3v٦k8p+DI -iM+psXBqIY䦱M?^=+MG5qRJd=fk}!錺bQԱ̒&0Hy 1<2|2?ZnCP8Img(c_V!~KZjqe5žBxRK&9cYUP`i}3k>v!oG{:9BKFj/fPh6u*kkjw'Xy%3KDg!װK,K7\fHZE9awV[0S)K^)*ոSCH6oendstream -endobj -4098 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream - sK:c联1p A—+jX,LSe$3{Tg6au\W%_L.X fGȃUb3CڱgWgv3K|%XO"qg懻  :لԫ! -D9D׾, +b"OYhDkވY!xTH@;Zк*,`Ȭ,( }sfv R)Ӫ3BzT-d!2׹0 zL]Oٓ(c2?ƛ:C Tݿ÷7h ECGwL.urm1eeYC+#$ vp_rQPVHjD$:0e J6R;Dd|<.Š,{%>xne@c$aO` M_'(LJ oh^Oe1@O\JgڞR`kcm0+fr}E ߶Lp= [|},S3(h}HkG*G:4΍apPA WLzI? eMFk5@'=>$1q 7\כ/ 3c>B R 2-! ze6jmG\9k^ m}RrHʮm6.F~ܹ"C32Dqb2JaN9 -π̓ڰKGظk_w6\GwxC0fC|7_UYO%ߩG&u ]g ص *FDS׊T*/ֿ2rs [^7AkZ$Qɺd#_|agP4Tp\7wu+g%+ZXC=PDŬ9&'mi -r!t Bp4b_ Q}Aݪa; ӎ:Q15Jo0!xtvbY8A>^*q'[jE,=޹di@)q)_XSz&C"s9lw7ŷRC٨qC2e5BTշW֎ -qZaP4RJQ^-AFֽ+wQ+X_H4ڑߝPgU' UUpZ?x;Xy 0WoY?T:mwv" ,&4ORA;]MFeKzwǠ ʾQ*^pˑQ 4>='q=;H״W0ER#@nXT'1Un;DVOBWEyIAC<ۃJ*i\!GSlg%~x?m(+Z:3퀐$/!DΝnr"aŭ2%+XNʅ0y$Cuq֪{HIA'&dԮGAL.&DqY> -stream -U^KFBzAq2KեI3%.,= UBʀ:<?F:/~@#I F)K?˖B$2lϖ x]'8`ߍzgsiK ܁]hNArbꦋfyޜh H@ -ܪt m[M=H^q ?@4UaQN(|mN_L %**7pLDqfX <S4_/Qg#t} ̵[c덀L-K>PC :iC@qLy -o&y[DV^2gC/](a6N\NZ\$v!AS{#)&%N LⳫSY#kN!ﺑ DK#;o`i~sSZgH,WbC+DhW)1go (K[*TC-s@#|+AMlD@d9[2߱rfc^ҙi#f$utT};tt죊/mDN-3-1P]@y[Q{ƅ.&=~f" -o L !H{` 3(mAXKf!TTDesb*2\7Dni-"b#tQh JNRugN;Я  5Xim?GՄ~Λ^hIx^c8äe`SH+n/o uY@ -cuPH KWS:tY3GcN;QVSIZٝIf<H -ݒywza:vIx헗΅]n:H"8wՕ#gΓG? R<jNL-agwwW|}Q%U]SFuHڵ:#:Ԩm]J6ۭ%:'_({2;;ߺ]%Q=}?87#"7 ^' srC:o+?#FO=۔ 84G樷* -",xz;Ig9c<9$Ma+l8uB%M`|^DZaIA#ގ׉q\x=|_5-O,^ -}}F(w1>}s?v3Ь>Bt+CSOC d؏$PS{Q˒S^,`Г?tê-ih3ݿ;GR]Լ9 MR\B qctU@9~[ x dqQ|bX߅8_z,ӻf(_߰4̞ҮB?JUL}j|q\%/ɒh1dIeaK頴τ`MD:f4kk ўHiaT7X8lYI h4ϭtOݒv|7Sg5.=a${RBxN96X+0ɍP.ړ]e{, .9ɛ -D5ͼ庙dendstream -endobj -4100 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream -KduY{]Z?߭#?4uy(&Yoif5-ۘ<&$V?.Xq8t7ƛ7 %ݬs*-^_XvxeG t¯^+~b~7?dC5 N?S\.,D #gOok\2P Yn2&дťsvo1rXl |WiEZ=--P:L%^&Z$'V{6Gp ,W3zuh _o;AǛw7IEPr?3qff&0\ȮoYyXg>FKƧ0sBuu-*%w;C"-;\ !ey㜋IFK^{ tc"Qd3#fZh)ϓUs/)߾#E6l>^a=zaPBhו$TU S/ KN?r*V$ Dߒ2l0 &w_a]s>9XvG,+}"aEnO P W_ҋyzrͽfab7yCL_vfW5@6#YOo3 Pf?Z?|{ -,ṂJ-| EդuS+\twy p<' ZȐQH)UBtC'-4*~MLt֨ hbti\D9n -rjCkPBd|SB)tJ«GR.Vy1PCxZB -&4|;Z-A l`X1| 縕qT`8xNZmqv-sOOX(,k"f5x44ލT]4w9`n>Aȩ۹e@ϩsrk&ZJi'5 4w:yeQhrRhk.,:B̼*Ϫ -o=Itlv6E[\_+D/1^gc9qXNU0[g ;Tr؋}qdEaˊ!a?݈RŶ9[i Rd[m =b2D`(@!qǵq]jz?9ƝdKѠ,`c+bZe0XAB-R!Qrd~+xJqb@<7`$יֽ$yG? - w&}umij \ f=1m241 V*-K|M7BO1ar%d2MZ5R xjG/z'I -ЉE/ 1BLw2SCXendstream -endobj -4101 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -B݉~N,}9l+Ұ块l+"WFT.dI=2|L8%`%.gcwN2kI/e"QMПg"+D` BRk:H{?5EB69:3cƨvT[FY5 OdC#9) A@s*j5܀14 M ̷*=(c̙Gk8ykDFkݥXl JWXբn|=Xjؗ?j^bdc)Fy<+ș< -# VCMY"= I$計e'tPXwkj]6?x*c۲JH,b~|EO!0 Ym ~K|]uc}$ t0^Xh d,$1YUb%uܜj,@m`k -΀,2C՞ PL41}p Iit - uYj,E, -~}@=mc7Ac:rb,nF]1R )3?̑Xo9 -EU;ʣ)Ug6˔JO9 loZ{;c\ g6wv&+yzԽe5.O<Y+kz.5>.lٔltű:#OWڤ۩zjj~YҼmK8$wQ|%^eVqÎ*uL~#@+}f)|!qz~Ftk1 p]#}3+jFL.Do%2Ok^hD@_T~lGC O@k3q-NNt|!r }. H>M@H  -u ={'*h(d.^l.K?E5;)bօ -kŃ^gw -bLjEt%[Gtܧakyt gw^=_ZF0P!P|螠+i7FT5Yc;i]I$?Ƶ&_;۾Ц(Phk*G$͐eRL8WHY7蚚|Ǖ4>枮[}ϟMD$)+,BZM݆G 'jUO-H6N0ZN1QEH#nj@@H*3h Л huH7I@?)'2xl5#Zs{2]0%\; -Z ؝ˬ@Yya8.*4,MHb% 1YX~dk>Mӊx(Rv$` -%^go4e֦N@(rP7.چ2 J.0XjUOo٨0EJ4O|=rU5uپq;79sS'Y,M4rcXg&ҟsI8PFIHgpmW8"TFW6U ;٪@*n3ȇitӈ?hkʟ][)Rܝf͍<<A_ӭ]l\F),|/ԏ,)p [rsE'>.r4֡+.`bL)h]Z^twfzN&Qc4n=]|\u61ϴBy%6pk ZS *EHށѶ'"+pZ SOJ%UZ0et[la> -stream -RVw1p:6ǹSMep9ÓCQMKa#W|k4'XImͭ)3 (p[7}Q=R$׏\L\8K >j!55SDɡXFW쐲WU)< ]t_&Tp]ޓ7tKKHc -67z|٦MLZD:j*Po&fg t}3!Q82`xIONUm[ؙ(q4΋`'{?<3w2ii`Mn+9`R6VcgәZJL,km -s`1J,!:s8e>fcm5.8{g=(V ؏p˟quD*>%ʿ%0uU԰)dC"pTW  ZmyLHtf\AN%VUٷVvl< Sk79aF ) ؜8(j!~IY?MF^~$=Do5YK?+cdNDMvZQ٦5ѱc ]]Yy +V@w{C;Ra"x̰ΫzpV:TV9IŌ>$eaȹįBnRTE ?c\"8@9lw5͞pXId|N -LP^Aං$Kq1ķD{,\\?z+b9;q*$ON ш/ib_N=q S^Hrx;oʂ|&1GOz:e:§[  ANkgp=C#a:r - @ry~U:5^φL7]ӃYY|. m VBW u{j5`CWtvˎKeEB-$xC1(RZGP~LRV,K|~ƒG@e×8l6.KӐaoZNtQx67V5!wp6뗓ywQE:32lo6C?ް퓏F 듏 `B/{}0М? -cТڗtipQBq^]3j?.Э`vR pV,l*aͨ=%:[Ej -Xs7H,qF(Iu_彮0K÷XKM.Uˣ.H`FXD%d;ya f0LNBC% ݟR,#'˲@3c7Y_Xrշəc!,Q%0 g+I]q O?'24fb2-|T"8pL;fgYB4!')^j* -}YHqJ [HSi{Thyŏ2#V]eBе1RQXͶaiZK\ Gcb&F<6aޫP=oT2/d6M[m "ܷ-p#"ֽI=:ت@l2E z2HW|uߺ JSiS>@b 񐂕RZf!u`aq8> -stream -10L\ ~3gbjL 5)~)c-NJ)Қ҈27ЧκL -Vw>88ȹImoIfō pN8L)h5k p%DyT9p7/fCAViC)p,-Snb%\ls0[Gt]2چC"5qYޭkw0>o*,ɤfz--ILGKDuMm!@Θ2_1TW 5MWd V~N)# _7ITUMa^Q[sז# mF`0 !X8 Bx{Xݮ}Y+LxJ;Ku>JTA(r'7-Q`*Z9a|=ye&^,dβŰN$${[yuG`iFtWJ4D_N&0յԊJXٽz SmOR/dlEaAU-Nne }j7.<3ӽba;H9t)|; -~~?_mOiVD~l.f:2/.jX1KΥƽፅL#ȳ fZpLnYj,Ӣ| `R4:C3O++o\uɝJPƓ|Hluo+P_' -/3 -K$;Al~ `>]yƎ -qPz" i> -stream -1z`[M~sg0 :gu=LY |i/l^@,AcГz.8鈔n*TL p;8u4#rJK©2N7@l/a]/Io ƤM?Qc S\d}`w,PzE%H*wh>H1 _u{'e]^9 -ʿw-Nvy -HEh6&6P[6 q7L^FzZlwaЭmF ,F1(~Z3#E뱉aGRLX_hF!ByT)(Gu݁UUR͍&9 m"D <ջXFԟ+y#ly|B"9X4䏎9.SqybQG+VywX)99aRE(-2!J?Yl] o{aQ+xߨtSDy{i -vVk=o2![KࠦZ;_"[K4}T[s73{QKbK(\tGdߒT؛kT`)`]d9FF~W -^Q[1*Ko7FޒHR~6rlgFe/du"HdOy{cDMmj -gՅ0ʸ3'#9>|:PM~ kn6%P:kICfaP1%~͞Aj(6p /]7F99مe=endstream -endobj -4105 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -{}7؊b'cH4BF V\xJ`AIv úh9K-#DV#|4Q{[#0mm1U50r)$p|`C(F*ј<΀ -UJ(v2DE 5T ĩf0O$_RǖPgxĹzx+x^J,R,_GqKڊ@zQQy.BYgv36A~c1Fa.9޽ tc(۹Ij5kzc|SmW^}o&*ҁaxj,GRcl%߅er Oaxq^k/3w  -0Q!_ǣ`­<<1i6zIO|,x23wƋ*w( ' ZPd|8[Zxf0÷jT$Vqvg`@%e92\a9f4XHVz -:+Z=0fTm 0/6eIc_RV\$r$C4EQ \V"} M?X,M<6GbO :VlEp\낫9G\M,JwOAD]ZjlQg[qf]Pޙ?;9cz -0Xr|!T[:V"GI״*AG"1 -I!&Zw) 4G=ؚJ3kF:@^|\%tFv,%IV Vw*B`_Q1}]՟דp$p SQ~;CP$x`[+ (Y!RHzwNy[/,TP VÝ͢6SY!m|IFf6` ۝"vvYzlv}d4~jiᙤ?W|ˑ>eʶWQ{)L>8Oe[[E~ѕx7hq4Qy}ʤI:.'.9НP:-|3vM"qJ>&hemj;QPJ)W갍deQzl82Cv=HXyVƲ6Mᾶ $$\nJϢ+b&"a4;up d! Lx9t/MGld`+Vi&sn6g򭓃]^!6u߬uee}!U68~ꪟc}h]kii`( [fwß^0װGC[G٦ tڲp3;v1?nklBGZ_g^B.?]>ʙB4P%iF? -ZAM`ᱻendstream -endobj -4106 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream - omؼKA;Y̱mMx'K{慄\wS"+o(cW+_Ba睪>xz7h ":XC0QnȵzjX;}9~3F@ht6?<IXw ַ}׃K+ݫc'W $ %k^B{id,WoQdX_(j:ahnDZ3_n9b/V".q7 -T/K<Ղ9؁q\nֳG65MqG(Tvĥ:vMe6ʚ|K0YIK=ԻEBꊝ3N =>'?]*'h+t"MQ Úz}xupL۔tHћc&a77\랍%8:h5 {&fM,ݺ_zf]p٦2)+ -)]7a -Ba dU'+ ew6x lUQpFKByqˌ~`(7>Guu7 pPjtV3r·뤚{u:@aV4q,B$Nr 1)/a*PK2ҎYꐭ ia~/=ȣ` # Ɛrwϳ(*Z_$Ѽ5YB*v ~+Gg6\bJh4h_Zb}FB۫$7=Wȥ<}a-GE#8ު{ CxM@\е2?vBil09n]K~¼ #hpk?9!Db~'D[G~HkBE=׬L4qEyL۲gQ`!\{FC]qpӜHT]Px~u+?oFO[@%S^ -]CApA=ܶJ}k%tljdG7!&xτ ǥٳiD:ARFRTCbfkKwb`ͭ`püJe=A΃Ž4;.׏rML dvQʈv!˳[YF(G{@UXI s`';왈͸'1se,\0Z O[t1! 32犜麝Yc(W/YM*lw*ry<$_yU溻qPyWN?>Ad -! ;+]> E&!;+n/K:ލ'[`O£i)ON@jm|Ft @2]2Kfp]T}:vi_A.w$\`GitmVw._$Z|U9>'9Hʃ8ѼM1 *BpǞ-E#2;3uϘ~%::ʑ[}h'/9 -wu oL:覹j{@ZUG[t덳FDuiGY7<1PTuHV EiB\iq3TT$ȸ¦zÔBWK6?=ǡ@Q6wy*P:MBNjguET`>"Yf$q99w#\<3^RDŽݦj({thy"p#fYetlCuL)-&o()6'3DZRaSgTUxd}w%f/ 1٘q>c*s{UlE75gt =ڒԿH(E=BtDģ51i 9RBk<{ЙҞ3鉷tch水X1{^IZ8VPNӹendstream -endobj -4107 0 obj -<< /Filter /FlateDecode /Length 2464 >> -stream -G]w9ΟYחTBvdZ5Dbj 槸ѵs쎬gNv19DS|q{k@ .ʿFvoWY/d5j`GP@!ߑx7=R!$SC+V舊E >|_>oso89ƐGHͭ]*_\([!w|4x%&3\*,$A}, r㊐{lp|;hGC%o-8ȼEђ2Mr vX j4w,lb?B.&)3רLEpvj%G7_h Juh+tPtzZ`nނ:~~;sM_KQxRIfI@i_KkpKtE%GsY i'2%MP):)ɝXy^T"pA %E> _<.Ÿż]61qI +3|Yݦ\AхJ+U`Z:%(wxL3eS{_cغ$srXZ2c jf~t24>g>Uy,!gDXw)ҳ!.WUۗWQcv(Věb_&@앰m1AER5I -`;o;c@V蝬'ՄBGԌ%Y_E'-QkAu&ys;s,S9CUPX!-L1c^n#~+ɊλpG -W{vBub-QO\]*b~? -h*YyL z5&T(Wg l@ۘ(n&m T IQS.AFُfq$?ھp)>."kEn]3VU֐$#]\?PҤ!1cTܚ-rKJA)fL&!<4H/MEO\j_ )\!H+ n_`U;/RS@i7q#kX8N5P"ڑ\Hކ");KB10p bC]ZA9oQ{ߙk7u(_uZKUË^W: -ibC .I|,)I/}T1*q5+%TVϷa-b`i7y_g*B 6q-ѪX^3/3kCȊa>mtݛ'~qlNũw Ǧp\b"+m*l)06 UMK,v vSN]D9\M+s fv,}Y%(YieJt혭tx%R?my~䬏c@6;x#')k:4/ P"n?8EQ=wJBhbeI>1};Lq:4(/:T'Pea7D2+YSR 5kPtjXN@=:u{e[|&Bܗ>;.ƹӤ5J qZ[]nŝt#"XbM>rvp ^%O]`*C"uhu@)_s6OZPm[h6>dw -s1PjzȈy hEsANi|_x@ʂ -Wƶ@tFeZXLQ&K{2azs|< T Lxl!?F03O< ny$sendstream -endobj -4108 0 obj -<< /Filter /FlateDecode /Length 2336 >> -stream -4[պFf~t\ZCnn]!=+5{h -ӟE_\tSx[=@ʯSdo|_`.lrйdz(|}GְBjOz-=k6ɱɠO2 mB[?'v7 ,'F 4?q~{XaZε N8E1ڻ%0yZ'H~۳$SmWɿhvW> -7Մ& [Rg1rdJK^q S0thcHv\$'8CdsZQ̻mQ_ G[JQ穢dp_V%f'$vt*-$N"c4",pj>#iw(Txtiuto[q?oj$\ZV<Qӝ#}+fɯ* at{} iբgJH>31TTvc\9)avR׿!jN Cod~( ӺgLXX%UzECF|@{1jFgyڙYԭ.}Go\hWgX{)QQO&:D%siU&BxQL*x808srEs_FaWsV~nbكJ 82)0m D:g> N}l$SDu۳6h{e-0lSyM_ίr,ok۝ 8`p6:izJRt@Wre62F%b0NF43Sy uwza͚#|P^.]k n1!˦LHK`b6R U)j]k*X]엞}V4{ᱨQX^Z w U4PũH%5 rhğ4;Z Zb{8d:OZ/a%:ӣÒsi@ї+ٹP#b*q` - -8 2!=QSkNFix{'6.k&7$2^QXȦOw@FZd-I:P~ZA#Ff#;H[չqHӓA9uw `͙Tan*B D,8brш@R$ZY5uAէv# Ym7.qendstream -endobj -4109 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -'L -o՚n삓"Gϝ5j.k4mlHU9p1 bq N$gF@şn ‰"[ b "e@`_L n!0R -/!^$h klTeTwa>KKjYXC(Ӏ(~_ՙwyuI~х ,G攃@j65#ִKSLP^}UjlU8&r_09lGCk91H[~:fyP&d`c:{Ή) -+529l/fu 5'{lrF֭:PfQBBp)"vӧh(2 FD&6 a#q XSI~xm% =h|,pV_x?Їg͚5Lܜ܋cx̮E_*>2"!> cD^;̤#MvjRT< X ikmzY;hU\԰b֨Yu\LWXiP(v .ұ0dR3qARp1L]dq+$t[P3wb.uG.C}(h?.{3kE?㨃#e?2hs>+Ub&. 6/@e][%9nf̹DI~jP}-Djlʀegjqǚ :W)k"6%VBwhJ?s$_"_"b , @cU#rLMPJOɔulo {vbd`[9ܷ86;q707'uIE[R"^gmN?lל|[ -_vS5(74Z'3.$~iFMIت!OjRg;un7F k3M` 6| -茵/ /9N !ٽg-0\Yb`J~Op#S\aa>{Цz p5T1T DyWQUT1`x*3xtALj$#¬Y7bQŊ/8#.zU(a#uu/`}ΣFzG Qk&gS@jw:endstream -endobj -4110 0 obj -<< /Filter /FlateDecode /Length 2240 >> -stream -])V4@^>[ a[^g|ˑUc~iD$gSDb(%E#$F(ElЅd biCzFl{Yo7զV9`+q!#[SH ΢,ũ]a}ws[Q)j) ,"Y. SBvwpAgɭ{'H%ܿ闞$^7H!^f:lwή6^d'V7%}ծ׻RF^ziR,H3nDסNwX9Q4'hp0WW0g̹|σ ՍaTDŽVa 8_[yKw]ribƳ\MU!9gcPcal)svBG|s}6Y+-$&\9зcy~E`(>1+# RRFHj5BٮdtRoXH/TKx1usD -o&$FEEby% l4 }$>[E!JqK OϥF2|)֩nvi.^hbOe$N߉&~b^zFM^##c?^,9\GޫM(AQнQF׿4:5liS8D͋&ێݟi+0 㷷'`Ύ"u-).pT f8Ԯ|GZ^~w``$3B}f6q]E)bq1ѽ*rCe;Gv 8,APB*Yd0%l=)}p_GH(P+hYP{I{$_|{Bf1;hm {+Гf,.ǓՊ NxIV?lk$M;o I" >I=Komqkc1T926Xw7٬zWK: >ǖ|$wpi_Ff#&d陗>Q2qdo8\tm s\ydFrMV~Gy7뺧d3|JbsxTK&~2m Ja{f5,YCcq*V7po-K0@mH;+`PPf2d=`mOr^LMl|gM\ʙFW)hQ(Is0| F*Cb:a{#9gWmȩܚu܋/%m DZlp:g oapuqjZC4 '0m=p'myIYǶOR(vsR얆M,X ށI3flcy@pv,>#g^cҭIw #F /ٛܡx4$X00Db/'AЮhFAjG\Beb[?Fv_ T)gⴂ< VE4W'Li/C;|Kcss0 ˌ!UU uUzI5 /d8%!Ԇ'PNKïr/{*]Uuq܌q?pJM#^gx,Abf Yn+ciŚu~Rc_ @,xzL4a},5ٓkfŦ?A9أW;ԅQsϩ<{K/^)$ m]~Mol֡tT z؈25k!A-*P7}!n_ra;:8XʿMܾB_(aAlg, lѫJ 3͝6=>V$Ϥ?GW⁵R@š5?ϯ&p1PQ}n (m/Y3s?NeD̀xEV9fS]a$.shR??<\cYxxs]2SQ:5rz [Qw\0]?Bc{zl[:TZxU!(U/ѕZ9a3Zќ(nQԄUk!農s'Bb\endstream -endobj -4111 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -t`9~k'W9 C?cE7j2ẘP%bP6 ԩhWALE{n!cA´ J@'`ت ?ꥼ lSD3M^@C<[\Y:k%!kz-v%صn4j~ÿ>5S\OqDop6;n%z!;6,[6PQ)SRwlmxFGTd/ERX6/ܼ7s[4h*m:4.r׮%kifdgSI@]fYaZvh -"{d> - ae$3; =4x`,$DMHL&i5F*1֛]ORpxNX*Ў"!wDvVr4ѽMMs3(b`enV$]qaȖ6^%*M<fo}9-a]kYS7bNsyeP&+%2i4^Ģ٤8tׅzzFTEו;oϘ+d0e:7?PlmVaG8J/3-[n(ES;6hO+bxۜzDٞ5Le!*4'2K>6RV\[kDC5LQCۖ´mTx7M 2uʃ: 9VHRl˵V{D:xP/t! k!|0/5ˋ -0-ks썅H8/pϤaiq:C"9 lCj&͡:cRƺ6 9/ǀq)lh&]o%w&2H 7Ug7Sz>;@wr<@J-,*kf.O!l'ț(//{~jի˴5x`_ӶV֯%ad,pL`'t׌?5yPTA;VTVR^֗*a~ SưK,GU]ёݼul}XSrXW1$_RΥsn,kfG7N騰G, QXͲ7W#ƍi #ㅢ9Bt<9B;V_Ʀw,y,?4|va3-/0@%EäF܏ ߼vP(%+?|鰁/QJAWm tr/Em!^^&O dSXiΕw79 -HZ!mXiw@1GŰ0NC(*UӮa@̇Qyo_W%}3(ѵAUMendstream -endobj -4112 0 obj -<< /Filter /FlateDecode /Length 2192 >> -stream -"$τ}.OTaN y.!\'h - mteD$Vrl Pfx:йE _bCȄ+^"N~FZbiLM0h,l9yAϑPvA4I -Ә%b[ -䬄拪G5}*?l#FU|2B[e_AJ;cײ༕jEM_@m]7RH!!"=?Y=`t}= - =4܏r@' -PrX + 3zTSР:|VnK(yR^78=c#_LW  w ƨܜ7ʮ.63"ў z)wkT^%<-|g!Tٔe^Q)-.gg ߈TA J\|%mYTmh5-ZM!]m$ c]+v|Y=wd71<#} /d[|5+P2Qwe25=4Siϕ -j-Y,c,yoUzM}0;at֜%ԊvO`, -486+Y -tl7P밳t)csIT?<<7<໣= bɦm:{ua&,V9[4?AZ&NWx^if[yRVCVok1-~5.}\GeDE ~(c(tK,/[P&3+ -ŒK&HB^ZX1ɬ!Gil5Wp#8}DgVށ{{K KQAc<)vFDGCb|Ą(" @t],.V ܔ&qD}f m?-e5_uFJ0wYL0&[nĤsc9U:$aўS Y2~[r]f4H?z ey#]/0;ITe 7>VD M$O93MF~[`e4Lvg\ c`trLɏ,J=R|\z>,Qp^d;&z5s{76^D2)t6Z$],c>9+s0UyZ6㱟-$"!‚ԌIcaQPo,<|} /u]OX!iu gulf|,[djm}JpiĨmˊe/_X6 Ua7$EQV(8͍EwCu"BnUEj5j R";@ūS0UJ'wc;fp_ E 'O U 36|;*vDB --XU~&Co&@Y҃rF݄NE}Vendstream -endobj -4113 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -B_jBe>$Z*"ҡ^S56d3:X%+M"X {B>ҷ^ۖ$R-RӃFD.Jщo>ƺ L& gڢ'xߵx%q=wȥ -V&,3?gqf8Uz'Iael%~R5^݅ @90F9efxxKv]oS==uC(u9@məe@<2)h՘UZ]B'/ü7?F. 8b(`U ovÒ7(I)EQ?bKԈߋ tĢ<% +s4 +.k ?gMuVQM :5n_.vIoz ZMmݬ07=wOI -C b+K7 ,`;Ymw7o-E'E^}s -눣Ȇin|41 'lM2lN6ls&I+gY{c-$ԗQ(v< -ܱWu)IZzZSciv(}ԕE@|浂/Q S@wn1O|3lǙ,@@5p6l=鸗*jCOj9`9,'a{sZd,65MڗlDأPk<E2t* \WU.' &MF;-j]VW0Z4i磫8SCռ^k~@̙OoDj>N=dRީZ^{X)ibL֜;S$w[\!*;GBvLSGG}ALO2HdBKDZib] (a >Jn- -{xĿ : 3Kړe&$͜:Y[ۏS%|w5a<00@^ ԲK-MѨVXB̨ S̭k>ν)~ɃB.9ўlZ9qy{d~"V @| -%VuP'm捡->9a+S]#@oCvE#088CRC~T4WӴg|ۘCcwe4+KD ld 2JM0Ų1LlUFTܯ=A# GۨjLgB^*|-`~ArWeeЙ+X]"52WxѤOan  T RèpJad@ -V^܀bc̴dyQП1|]F,uj lVР 8{Z-!W WPs }Vȳɨ]َ62pXBCf1hGSgTfB#9)ucC<Ҵ](qZa+hb|RB g7ηbX% Zl!n.U϶#5 Q!j*RJk _őjXKq<|VJ&^SV53LٛY*)e'}~!鼇7^h@͝{pd:D]@ -4S4"BA^7(2E\,,v۳:x`ΕGjߜ*mVaƅd3z|@o1s. g@E31Yړg@v=؉wwiy-mȭ`׬.?EUIҮQ.$P -BC\ hendstream -endobj -4114 0 obj -<< /Filter /FlateDecode /Length 1376 >> -stream -m0?z?(TyQNmkEBmu,iz&ȧ%7T-\Q)4Nq.g9<S\I1^D{6SDf5anYm8R\(2>zUgk" $ۄGyM y3VI1H-Xiu 7ĔZԋ#N9:-1?~N PmDoGJBzJ~ٔ54"Ye -~&}bL C??:}A3^dw1Ȇ 1HVe,0|SG"}U'S#RCyeNkdiph_B֎|6v Vc an *J}QU$5Nk5+Es7"xZ!cV7ᡑ-~@9881&Y>} 2M B/L~pv.Hxn,7 E_"GmU ;Mmv -Fx7J6NRFlռRf8oz>vdsq10FH{t.O+&(2Z;je`EY2~ -'GIġyH)u؝w8sT_fj-@;+%o)#~(ٙuh09x5LM3ZK"OO+spǏLbK6(…P0L.Q+d?Otrt6e#> NZ%]z+eđOX17+EJSetA S(h nre>to3G$v*5ѯ/ LVOBI;鴲%@qi7ӠF .xe?b?)o)_LZVf;Mem{_uFū rtewT+%ZAH6]Wټ apA}}X$$ez/0+endstream -endobj -4115 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -.\ .ȅYm -ګWpq[|~[nNGHy[E̞c {hi3x>ldC,uhBY=@;݀ 9S|[Lv[$=g`\u"'(L>9P!$G >yKPRȢʝXY 2{- -i)4aR>|E V|,8a$RF^j" oKs ޲j 6o4b+-bV0ask%}ۢXYQo eڵE!@ȕkY ;:fr 5D'*\Yxv/.y/i hm:~FɬeF8a@#1жűZx2K>PvOpc:u՘lB~:M* -[:2`sI5nMIIV9>[O"Ҳ)J9SzuZOJ`F3S:(vP?s{Yp&)3>o?wgҽ-@?A,ED(=M! mHIhZz ohӤzyW|B - 5YCĤW -%[ tL_>lw6@$.muIJC{ -@Ppoɧ﷢lQy.cu4}C|n -[)$bǛTcoF[eY{;c1X`A{>3qQSt\ rZo"Sy֯#MF| BQTק˵{Iv `az~9d,k$Y|hJFgjƦ;vybVW¶'L @,aSq$y6/5i4NGإݸ7cqI#zU\Zm_cD|ԟA77O˙`*-do ,p 5vuS{ 9oqZ*7IpJ/QKjmRgZ{˯@Bc׆@bgτ_?l/ϯM__w/&]wCeQn -sP|,#wm/><~Lzq)Xs3~%@P~8JS`?2W5VGaT*eSMU. 6tڙ2@0)0Ko/f/ZcbŏZՁ -ΞPvlMP78gI|X (?dU/~1O)_-vR/?5 hIPۡ] FM4<.y>t!XMendstream -endobj -4116 0 obj -<< /Filter /FlateDecode /Length 1488 >> -stream -oTU;C|dB('(7%a me۳̸،"T\2kۍd ɺёt L$T[SZ33~!:)L7 }:Y(O"QC~BT/DM -gF`-'g(.4fZQFvZ]A'W2p;ώrj. $k**uO?o'P8PՅSkV3 }=Ref{m/u #LU;%gCπSo'NY`9T/jء_n|iBEcڢ;DlVtffD%r @FXwE<DzyJ -gUWagHqhX[="և3}{lث۸f')Q,D듕zbvz:EK!N!;aT|v'^r>I]JYēF_f^n۵E:pOqy^owкgL}1xzOlt);` 9/.U'lK+"FwX:F l /!`B̔L9RNZoJ9q]l$4V~,V- >Wf2 ;Cz֮,Ȥ:'iW/%ӽAvK~-۶\9akd:tJbVȃ4; u# -sOFfrw@Ya~5vFnCG4_ }obZNW3Q9)vO: -@&m1ڳXz_RuLiy|) 2Xk4Zv -)rU4Ӈ3l_Tq w2[t57DZdbDAOb4xjDmssód.?^ }c\[b;ª:E~ ->K*fPCf)+!/v1$h$EeI9a>䰺x :C_jtv`"ħ˹P!4qTH)i6U;Y.a Qpvڧm]V;zNGVŎqQvi'Afd2f՝#پBzr M'rCV SꬂfpY' $c-k# -+nWk=J2 e? q xb=781Z,2@:;}T%+wWپ}~p?̶%]}Zf6n4ĴIqC򵼏eQNDu,0+endstream -endobj -4117 0 obj -<< /Filter /FlateDecode /Length 1488 >> -stream -w -QrJomQnzz@|yA -sՏ:"S鴹tGĈFcs|]\PN7rmn-L|}Rgʥe׳Ћe]: 76 m'jI78(~-lֳEh;` b7 E, LpRIca n75d\HQez8%]EČZ`6׎&m*.$P zw( OZqUqY:[XCi~n;aZ/Gj->(ok@XFH{4Jf>)ƟLfH@2^T^߬ʶXZ)5tBy3u:,oMCd.OYD4PZon?!*&GQ8.xM_ft5>t|lܚ q>b)֏./ܧ3R%'K4` #Fԫ?ThB.i<֞$XFgA"܇b=DGvلBlC.X0oİY]I \q1ìʌU3֕^E6|,$Ȅ8lC5/BK|_u QjV7j 4LP4Йǰ>GZ\j\l[E12=ceƭNoAkP3߹Z^mA-g@LHTrV܄EY눣vըDlKe[ZPk!t%Vk%.O5{vFЬ"wR =u6vhMµ`jmxP~4Pgύ`|= " Y--## |* -`CS?w]>VF9[|&/79[RG8{ ihWFWr|Oz-族Ʈ"MIvyl#ADS E҂<v vmAUq8q+vt6MOg6Wy 4u% -j3> R.I2-;aG*>/B> -stream -_O='qd -VV볿oS 1lƼ]ERl ӂ%cлS76d#`jZcL;\w]ekXQt3JŁ683{4Ik;G2 QZm)CVr-+/c&gIY D Bh(h v4̩.M/ !DEpܤz[Abq\9+iD -mI, D+Dz/;TNLW ,lI]e$Eb4UIOH ]@̢9?5`5:(X;q#ۺO>J ])( pAމu.2 CPd0`Oo)v -vs[,DÔ*&wRZW`3@l7r|ψm1P.gmEiI&(VA>]$U-R~ѱZ6-*"X%"@/: )k@`BY3Rxl*@amhQnTؤm}ruKE?+݉¥`:8QŔD՟` _1 -*-Ye-;KM^OG*MDoj0Y Nfuendstream -endobj -4119 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -v~ -p5VGU WWLp$U(_F^Y"%ЍX+&@āR~0N^[{"Ye,.?׭P_ڌ>4Y2r^mى0$>\+| 8`1Rp,IarA p50Qcc紭z1eF?2ܖeF=\gOv)}F+kr{b1Ȍ$kL>bHpyh_b;Z$ ZcY)nũ`*KCu+^kj%Bwm)( &͋JrnM\c!SH]u5ܝpqnng=sMI`c+OY#qUR|$&1Eb_%H:Jε|iɉfM<#qKlܕotQ(_=ns hC[Mchg3T]39$cDZ!yk.a33Y)#p7(1I&%*PP?9hS1 )<;9Qbo"s(zB^H-bjxnTD6Y/›i2'B 9fHkF4n]x_H^72ϗ`:@;LA„,yKkd{йsH'CQ4f3;^ܐ/"VKG,|q@J̾397\;7L1W 0aEX<ObI5bHtMB y0xdh&C3T!Igal&'j>iP3.eDg"%3m |D_& o)%HU>Lluz)#xPqv3*>$1=?Ve4>p{X0 -E9+Sw+ t?u~q4G8hqjJ'Dr:z<:'ꡋU*GQ4De iZ.W#*OɞMc 1;02P0^W )^vA&2k:'X0y.y94endstream -endobj -4120 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -Н{S>C}Y~Xu3)N$wά{Nqmp`ڱeUyL@K4k_/<"8kNtu+?wx&=Gb6ӕ\މxeװ,5hLIgmxB e3"0ԙ,(ج/c6"V?^E{ވcZ N~yy }` y]?JQ9LODc?:S^WIĆ#PE_DӚq%B[ -x1vN= wՎIB x]S nyI- -Ϳ~6LT Rh LcbP[:-^ҽ:9Ffv:&nlcEE )tkС/Q|"{ !Wo<p2;zkAHib IԸѪ,2X"ĜY1ٯԺH&fUـ~{;)5^S=z?a@cxD.Pkjy&̌^钏0 6wB|~~ǂF{g^8MDA"9loehDH3|ݫYnCT֍ÒqgH -|QFx5 -[8D2 @:s[F/|?|^%6:rע9+LJFZdIrU272O`\EIەhi^,TWElyP4yI4F$*J!)Wc@&{JWo-KC&r( (ZnzEƺUϱε :|upC kSGL\OD4辰w\t)P#|>.mi(Sɖ٣8N+=a! >kl|(Lk@p)(L#`|\y۩js)8pۈ& -ZA[.ԗզfAU&&L%O^ dgJJzR }zb5t|<1cI=6(SY-|t5Bc^ޗ)*>&ťe_ޣDbr|~2d(LSqW}LU ~Yĭw(`dܖIKAǰ5tRы%I9` -aox|31N>~#Q[sQ`7>ɴo־$l !C%dA5@N -.]YD^ %4IxE<2"3z0^GŇxf\gضƉL8}OFiY+`B\Cyg5lĽv.6V;6z.W)l$뽙 $^\x|¾4V60mSђߥ~ɓ!E@ -m)@Wf>ލ" 4oXh`/~1$bƴC"\lg'3;2˷@J좯nGUC2@1R?bo_12MZ70.PHL(Gr)>hV{~uH-\C3ϫْVPnI(ԍ7|߾t{UwYt]lB~endstream -endobj -4121 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -W[&Zw / Fnxڰr{gz/fx;hW}dW~oD|/XAk!ם)^֎N>1qg;ѓ~  B+`GߖB.aY%h i8pFp 6Pglo~NKf`"=ѷB!geY0˹F+On)fCu~g8jCk7>deSWE2Cu=ȬF9гAft/\7KF]4^T$ 4 <NnQCvAISh:ꨩ Bt; -ݥd -]𻢏 -U/z/hjC1_$*iZb~F*2dN)P\7$?ilQT -Y;hPW"IDZD5GAwJ%;c:]Fo<6 dx]op+V`%D9Cr&,:B=Ep"c.dWʝ!v|F' -g\5U_w$!˯|ZVu3+}Rř_ÖbƊ͚xY + `!L!xȩE< M* xL޼OҚ5 * - cB +J.rMԢ%Lw -Gl Ux."rjt)+Xt>YH-!ëmrA7ަL.ۮ>G *d8-6 #IݚPm;\zL8J7iЊ7E?TRl2Z^#D -,r[P䦨fy{vE7OR ~엫_ Wx4stsGb(3 u_s4O~@ΨmR+DV",i L$@ܜ ] ,1Tf ]mD-hB='}k| {z@|3Cu=XΕDyJƋPh-*1;Y xϳݢRENBѴuMd(^6=uzΔ,6{4:u>t՚S c.#u˟4I/p2u[@rFհ 'g=1;)g1Qx2$.:͘QNGT*91d@v]C|bQ@01Pma~GB.y!}[G4A?@op :Zb֕_LKӻϻQq% 4^5PQ4YAYzjW+X5jVxL5% D,yWʔ+VDٻGb܆2|Zb[M@Aa!c|8Z=l:v>OygORc`̓gyJÄ}i!;b4^ڵyLi:]oi-X%ռ+jfzU[#K[iV)?O Pp(endstream -endobj -4122 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -`t6~AR3e&s=~2e@ 2Kz CWKA_fnf f1 mTh~o0d ûS)}br&'~kJp7";ݟ!;D?mj -jQN}J%bz>"R=^f<=IN9Sw1m(p7?+$F "7!S~%܅Ӧ'GiU;ӉVf|rp ̙Bj\trXL8EݔiqrCn`Xͷ%H^sA-9)2ˑ+RlubW6<|?>l h`;0@K"Q쇡%J"4۝EUn|I F*&`\Z7|'i\d>77o$q94K 8W*3rP0fwzrtL/K- 872d{|rt̿P|"~$Yz5 -ԙSˀO׍wTb?dnW&X4%KϷV@~Bqٷ0߃@j!\C|\Woik LF~Ta0|M~)ݡA N#Q!@Q7o${8wXUpXqMsQ# 銈,\'쬈x,@茓Lz"LIc&#xC?up8d4]^BYI&|=Qn&ϙa{pQ4I(l@Et3UNSFG)vf ÞSj0e΢\3ct=?(M EQ_O {L T07*˼;TЎ~Oꍉ:5-[:8H*U`P 5*.)<#Ja%e1 xQ8,|yѩsh t%&W# }ÜvQyfS,'@g/־8;czY㙱רjO᳂5wyu|F> ֤`y3K -MB_bT>0B?5CT@,_q'k骿HrLJ&.^-C)p)NgĽ?>^ֺ6 uY;Xb)b?QlgU -,4u,5kz嗂~?I|x)F6gd8a6sHV#/@exL.毿\QI}Z1 -8QPPFj"۲2'iZvi縭_K$ 1 +3G}ܲ0LKHp./:O5b(x $|U3h>7i)zL .˹*Fх:Zsrj8_ _y<*qDftE,UzP}u16v;V7Gmzʼn^U3*f@Btfy,?Kn%zTc"h>`iz. -"eAM 6訳$Y*,uv+Uꨒ(.*Ӓ^6ԣ&:. ǶVRtpkPJf$RE+uz4ؓ6*5h)N3.N{Rß.8Go&mOї">`Bԁ|u3BEbw[Ch#IW1!;<2¢Kwfiq5ɧ֡+^%e[su1" c -[nI*ԡLfјWϝpkN#6!7ڇ٩7 ;Vh 'Q(3haFtM 92Fw@%ƀ +60@CD(UO sI2d fQ<9wp1Lendstream -endobj -4123 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -aBU3O:If^vٳ%Y8df~'>hK[+7Oz؝{åie>c-y?O\@!@:d6l!ȯfn#&*\몌3>[s ~}đ&ڢLI`_x y -PA@c< ^V;<,kp'4x˩NɳQa7RV!8LM/Sb(ޡ٤6x0Zf?X*V+OR+4u9`Y^*^_ix' ȔR.Ћ/t xGUr,:lGdF)q -MV0kKc?ntNxa:{"kD(~SzH'ڜYQ }CH@@ -CWK5Qsk0;a 2'\;ĩeK˂--HSG">%%y#!i3ڣb2+d/!8+M)H◄]o9-4 xr|NFՌv'4CE׋c=0Q=4!!Ͱv8׮\|l=1g)[~!zU(Pj<^F{Q>l d`2cpۀYbg2gUr56|-' #'iEpܨWx+ -8JA@y-F.QԍطfNB &HP `8C-9߽WzJ#w!ش6k:6Voq/BdADZ|˺$nde-z˞m[ET|OX|DͿ>ws -HA(зϳhԫ.6³B$h(ӴN67hV[RYuNah3j|>_Dr+9g6ޠ|fǸ,/wbOEAk^g86=~&Q'[Kʯr`{ 'w^vLO BٯrJ[]uUHwOvelOB5W _KdF=jlhd3_KzŖ'0D42&|Yi9=A3ɟ$XM`T/\p -?l:o;Jȗ+H2NX -*1Di#ɥrϞDa  B&3#Ii˨u߇UT^;WEZGMUC" (MS?8amçW{6BS^wnrU`E*9ed}ekw|dYiQ -mxb,q՝TBoZ6'`(ZZ63{=P*fY[vx?PVni[f:#MB.C iZ A@@aKv\ ]J(8,NȩXV74endstream -endobj -4124 0 obj -<< /Filter /FlateDecode /Length 1360 >> -stream -`O' eou.%9_ WhPNvTz}lqfU B!"f$,noqZ0kS, -W֥'0?JPXMXӮ(NE=-ġ-pi<8.?molmrИgBJMb=?@Ժr$X7T}F@K_2ӮLEW8bY ;7\ R{wj923@!pQ%^U͜ZWtjo͐*JC:#aVpc!&Э%hTYxdĵZ̊UGv+A:"UL6T#/|`S'FS~Z]N+xqUi:_j4eSd!)|vg\{7%`>~\bBC˰X4EϬMrp#amf4gb& Υb5pQx{d@6XM[>'[љ)alsCj՗O>:?mֳG|hl -!nՃdQ56ԁF')y7}N}Mb"3)U3 Rɸ|HA*9 DXߕOZ]h \+Öۢ3FX$09rB zW%z*@VJbMڪ'2k?B?jh,?%Ȇ͊|MPuΛK|T  }0Tœ,ȶY1RW1g̨>3OkUSU5#h-m}$TK  !PJt@LֹtHXi%fLx^淭\(XMKhmI8_3'FgPD Y= -3z^ŏkCf69-I06<-1tmdPLyyoڛ˾^gEΨ '}bR8|xf@3Ew!8endstream -endobj -4125 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -'3~Pp]@ C:AZG T>\EQEP(iL羹q}7*% DހA Ig9PphaL=+XQd~EZz^BvȊpu8EH7Yc a;]?MSLL H~a=_CfG,ьwI f廟iDɛRԇZm43n2OUs|ôbpR -6yVq 5N*rĄs h%EIAOAi+ /B-^*$f~'  -]BDbI06>=]tQݒP?|!Ji[iniAn=C>q:ruG`K>[[k$D]R];5K@DRn '-|8|v#5#M'k@A4yZ Thzff? -lTq!9m}3T~xd|XM@X5P<>%wypT|k잱k)Kܽ3~=5= I21&!ܭ6tdEdPK0>kJ ߙEK)a?m~Xy&~}(lڲWYmDN:MNPLs_ ꠙתrM. \'P$So$w}n_ =RyRkD69LPܣf'%^Bl$4%LORI {\`&6j ģVOOr=!( Cp"adE0W Rf9o8GuegdL[9A=iKK(z -nd!8 ɲT.DL;j=!J:3>豠%=m`ʢ XQfͶ4D*#[( ߎZ Qr9Z~z5LPDXx+aaLΤVɶT Jv#*_l@iͻ > -stream -7*gn6.ly -6!v`Fԯꕐ1zEUB9e6T? M N母ap)iK&yI3I^07/Umb7.|KȍcW:`7fTY4iL3ǣ2\jn,3oí| mM"AU;IBV=$whHEU6+lj>Spoh<@- -O{2sqVP+:N^{viiXƞfa{sF"zψ7/у>>8mJlѿ0HNɥTFSi3<_xJR(sdቤ}#X۩Zw ˬh0,.+ʻo>N6m`4ZNiݍ3ԯ /DMuJvpRxN;鞲#gr ~ࢻuS|Oe[Dh.~o:Ȋ+iJe kPho;&mPj~6(GfSd@Ht -: p(uX152cqQUTG?%'cL/!_M^ +H JpKH~[`7gdGk 7+X& DGIbMzw]J1LC"~7Y[W舢_$MJcLf - &FIy6Z>mnyp 嵫QA>qkRF:Su-q7,؀XX\{}} nτ-DY,>|:;;1 `(Q #[endstream -endobj -4127 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -vMnУ]JLT$x*+sBe$J-ֳ>kҙeMuelb!0M1Kh elh%iRr똏, ^yС҅vi9T9B"4D+%qQ#FY,ץ̾‘F=y_"XGnTs&u+LjA -mk1 uE8,  -iج0XSgOnc(֥:jڟk8Y ?zؾ)8! -J#6e ` %C6ÒM(uFb:/+0Aw["CEtB2v)B 7S|Gv0 -8Otdot,8$)ybVUNVn9(^kZ;!`/Q|ӕ %+7m`ڦY@4{KƓ`nsY-^_u#aF1.)ei 5p;ۑ4},? ' 5OR.[r,`эt?nRfnd{ɩrA!J!6`2h;<>).",%|boE]%s2ӒݻDYՌv]|"6e$ Pd ""j3ȷw,j̢p%b] ;tOȔx-D8鶖aLc -!FZ?%Ԏ=KoO|%5Ur'=7ur,/A%\oR!y%vB4>IaW¾MŽJQsl(iȼJ+W݌Iӗ<zu@9Wꬿ3y|wc06w-tl1:B.cE{(3wA֌ֶyendstream -endobj -4128 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -G`n XUۀ-5@K\M}!+%gXayN1BM[cߓWĢag42h蜷/t,d-5um OaU-+A]LnmNE -xLr>vӝAC>pF'ya8E:-qdk{cD_|U}P'H(L\PnlѰrIFP#47A44AEg#Hw*G]>&܆:X}Gil8;Q]fb,*eBfEbѭ* E\LhD ertbobH zMh깄^^KhyP8S]ܹ{126ZVEAs7A>>A2C]1-:v|{SKGn,gq:BN'ZLw 6W <85OXрЇ摍_74R"ꯢ{I%:`, \*|>LσMKjZRo'w'n _Mie>6cwPs'@'?yju[b>20L}w~(襁%ܨ%#}*i`|3ońʘWi"$;dnXtP_4:5\/A<.ۃendstream -endobj -4129 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -R}DZl[ET4z\k>Hb/F뛘kڻFd PR8&ܤpıwX1"xq7YL2 FCTUCHvbH^0=hf' jjS4ԺtnJU1|Mdžk#`B6㿺Yt4`Ŀ^#QB<ـ 1y1m$CԐL\=eTtܽKh{u]~ajá=$$Ԥx;bmu'}uR]O.X_5n6lj-U`b}I;s~j WL=YX*@ҬU&;ek; HP_i=Qv!%ӏI!}CgW|W^m#w'ыf6TIYßA͇3yF\Fp⑜5֖o-b"9W,27`-9` K -2?'`9=s)$CmpFP|Dq6i8*@tKHtՐSlpt)/uÊ|5,s9Zq&/.pIP]S#)t7!X|eI) pqB^~Zv&)"jS?ްv6~'Ǭ*Et.+7?*i| .O@ӫPޫrxKm9VLؕ<\4Piџ:>uG*l -s<+…M9s`x#yXQ9eUšquD" 3fyQB=tIfxa2Z5Sڣ9UHfW5]Srکgɘot00w4(6A#a^Wȓ¨997gYmas͆# {An܅b=?S< .?ĸ7p|z)-ڂri5Ӏ6G:L8@xr{f

&Z7}:҈PSkI ^H1=2ji(W#il݇rI #q?OCl3qd6-Z]6<df!"D0ዲ\`5dW͜~@Ia -霁n!/j 7I7¬ERCAӔxF%VUc82aX{0_>to_j&[ -b3H!xS <*9kOe5D꠰:neZ8c(HV@.|">jk,JjyxZ~jdLUVff/d23ڒ3RAK&1ӳ:2^?GA>7d0Ӎ>"AYBYU|Jyx("Ǥ -_Fw`& RV3)#endstream -endobj -4130 0 obj -<< /Filter /FlateDecode /Length 2448 >> -stream -l|Z۫3Z7D٧SIk,a-65]0hx93}W߾X?fgYtB&$tHt|ɥ!]F=™$j""tP D{%a{}|UvKƌ_1rj,[E?l3;Ѳt:u#L͙eT,B,k&2MuyWćrw oDZб>NX i }l\&q8E,änK!N'u3ϥA{I \p5z1-`Ul}m!u|kql#lpH'l!Me6Ώ톺rs1A'߮Z#?IX>e@f̛ x xERF*r1..YZmIٌ(C^l!Y,O~;O" McHÉO|4*?*僋jʡ[mw\o뙸pf~<עwK-8Y',1CY6<_=ţ>9FᇮS J'6pҽ<M `VȀyMTdw@]ʃ `5 -S ̀/~X\\ׯ7TG%84kPM W=πMSd`gqY3ͨVY֥fedJֿ7Tg-*>*d(1'1c~Wlg?3y}}h\bg~d^kD>8ָ6SI ǩ/tO옔XH~H_: GbvBH&xܴa-E/ڊ/q00ꬫK CZNfܕ$ASk/ l|bEE $#:w[8gX@ CU<+k:{QYOϪ7k"{ -14B{/z'c.bw Ecԑgìmڇ+Sed-I-WĠHmG&W`.n=#gz}DDܗˏkdN<^Euu6C -'GGm$T3G%zBB-rwL>pK ?u;&!f _:&8^+`B,At` [oKAQB_[$TâެH-,βj`E&nyJJzmczި?S132{tkD]|o+Z,?$js -> -stream -[;d/šM1/ ?>eÌvM5WC:Dwhy2xb#2I&A jv `dJ.<0N,H+~;x fۏB!^3 (tzlUzN>m:Tjn$j m59n0 uL)`-!cjvnD4,m;Dʹ[Cø8}~5@U^d4wɳ:;/[wlT/_}/k7m}WGXk;Gt9oeqA$:񮎏HL{s-LM}9# .3 }kvبdx$CKP2Ca}ݲ).XN}z=ǭ~DD148q:H䋝 E?Zc+zpEw=i,.Օфk<|sC[Qg苾ަ;L?[/(5t'(xY:J%pFn #M|)LSG/Jڂ5xxhv%bna)HWO3:JO, DMhDY7=ʅJ` =*7`UZ<D[Ws\-K|z.Hv*#6469}D ҮYD2'3Ur-rj[gǰQ3cg21d{AVuG(;1ECõ@ݴuK={q`S! v%pn:qK[&c `bUCL)S]GyP4?MruCak~Y*o&m0{*Juk,>+o;I#<'$L"Sᐝy ѹWjlhkx' DxRr=݇ %fMIF0?`GK^r>sx1*%iUƌ. Z6+\=!߿X'] -ip*YP1~J3@'졈ܘC=tA-Lֆʰ P]dD4AVcO{zLPΏg*, >̥=pՂ7gJQ(;T.RAg:MKCX}55-IJ怮YqWd?bBn5@?S\a&*'}Uz#A h'N]BcQ q_lXbG+B<GQ\{2y*`p]"KrPv~>~KXG3ig$) f32PAHDžQ|'0K[5>f}MDwQwb5}0/13&G{|z^D$z>[x"`Ԕ_96Y2 V+^i} -)N&L'W=XH N?_cN=JGDNشKP37S_`)UK2=t[ԻahoESB[||=E&wA~+ɨ͇@:B?h1FH(R{ 6.z p_n|M=Zu g/\tqY#Q&͸׌ ,@$:~^hk - [(-Rg5Xo=GX[]a ¼h>x >]kڪhl>fyWgk`֋"JC?C=7.&9endstream -endobj -4132 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -}TζVNlX7LY ib2E?o-™5 HTt6A2\67lPN_W+ʅ,G#'+ǻHp8bYձYLr)Vi-ژn[!@I{i#=Ӟ'TBjjڳ G0;Ir<25 QXMf<3;4 -Ss%.`Mdf=m[V~c? OpXbEl-,OײE:C;9CBEFߴvņc,ݛxzp  $6ܲM WaC$w8ܔދH4ze Tqv7!FUzi"ُ"M>)c>qh(RZjw)D49xMlvAo=U|dEFcP)3٢ls {A戤L삧cgVEW ]'[f!}talش,o=5i4:ADBӗP-DzNQ - I ~s z#b Q\l}V9x#%`vm_(0=&G1 @E7g\gK%xFFTE)"GQA%& 8vI -U`r @gnC E1~L@T}*+4#a$vS\bDMF2s`"B NjL2ˬzθ7FB.9$NXiߙ2ݙ[SqݶQ7*6% w-h2R}簧GmP-Z":o;,gX,bNEj Ÿ^ʥY " [K^]戏"/XGpFz}VsSat.Ms5P]lhϫY~Aה`C9zx)C*!RETaӴ> -stream -U^ }g N)=A6ՃQciSnV$?q Ť kl57ܢqңe];->I`-ڍG|͢xc Bt6'& -s}$c=LW:@뙵\rձ|J"!;{mǗϷ>|n-Ņs˽_.GjMV[d@  pLjgy^ ySRZ(7x˽D)#lʉ -QfݔsѩPBXKEA|@EAf+(}y"9]G dvxyF5݃coE8lZ)eG0^ -{aS <(Ҽ_ =p"utW@8Φ 8Ip>oBW`7u/)XWhƯF8`80gt$MEGd!VKye w^\ͷQr|32"V}emסp3Ȕ -:qz:\ݣѺ+_q+Z6I?ܛ5Ǐ\`Ik/m;b.P'ݼM }I ȾR68̡Ty蔰 ĮCQ6  pIA4r{@~.#59*hO9?2~ʞQ`)&Q?D\1tOz$v6c+fQr>_PCʣiVAasXC^&ۿ?-:D85ms(eՈ7~drs"H2=.G"oxX)U,94Ʌ'juU Wהy4oL3z*, V̉DxI@f`Kٌ!D@St:s;|p5}Ì0]Y!D(G骅a`3+K9Y9 nByUãB^' F=p6FwQendstream -endobj -4134 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -7u<4.ê@+$kdҌ;0an~QUIPi.K˴Q4 R_e->nuT;QN$_=Hv=\!zK6E^?_aybUg [Dä,WP}eCZy] H5i̡"̇N-bma -&SQ,MM8#7h´qP\NQL ͟vF ]q рRUGmnB[3>u.r߰֏B*~.Epd\XH(:H1 .EM_(ɉOh6)Uŭ5Hz'[+c*a P S\B/R`E++ȡ.~Q6jZ.\}Vu]uյubl3"H7.5:H遇IbƝ&X^W1<1 -U>Jt&>U]ǃUh?nM"oFG#{ܐ$&ބevߜu6}BJF[v $I唞6X;]ƲiDΣV.p(%:ssDŽ7 bt5ptYf>8ٔis~T۩ӧGqE#gV>E͐zG׃Y]17* V#)P{ 7"1f7^g4/*9/xU8ܱ8 ]ch4:YǞ&g~IC"4gQɼjV ljArR{|Ie\1븲l_&X?$B@JVg0FʁDE755LGh,~H5kj6L>b[4b10Ѥ#9{An2LCc~܎M"Npj(n 1!)N%-7|\(IzxBQz10 - G1}N~K(Qn:Џv "DP.S+",})i֓cMzSYM5(ޏ=Hy9%hs&CH183 "9"} iS8sڒZ,(\Cl\3P_n'*e< װ|JYRyNo fSYP ۰c`x|F{ЂP/8糫oK5% $8զ/TBz-!«l`[i;B{ADkR-aA\l;b]5ۖNz>RGp^Ҋtg)=,.=V('{G/?45 S֙v_ - 1|)!b .I$^V00{ ru4~ ʵ̗5@ٳZR7 -e5C"I3m(ys»~}8wEI+cB|==Pj &S# ͸q(02bMk;7;Cԣoendstream -endobj -4135 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -K)@v -Ky/5GyğRo PhD&u b F^F>|# *]1QzʌÍ?Ū4)^ Q0-`ݼu ?fdB -w[Md5i/3?"aߘxB^;ms;+1UHTWNә?Vȶ.~pO5n3Ҡs5CV&GحD,&\>LeSLA oxm4[VPڠq5G+C.`YKPs~Pnؼe7^eBMaz'?r<>sfOi|-i>n6s,^4 -Wi'w$) byP7oox% [xT^܅n2j.?Oh >XX X̿ :ó4rLu-w{L~ Kc4Ywݴ*^$ndjm:Be!pp @Ch? PVgThm`~)(W1I?,4tRF8qi8Z,xּ):G݄7C]zC.0Kln`By. *]>`B,b -_:V`^@,6)tG~8SM#?6EH*@>Y`z^F68a!|Ip7=#j2w ,Y'!bR!,Pki|l5%Ƅ]&> -stream -8)+Yߞ =C$$@ez~rԤD!`{~-k*lu,6go#ƗqD cLTSȫ9B3z:dGH,FŪtΝE77 d߾˅WE*or>ІqlTbcª) 7f:"Tz*_Ŗ}S^MUL]1q~daZkBǖǥ=}п{dA:ţ}c2#{7__}0x|*&g!Z\֗gOv)n}sHS Y8tz/E-R 2xb"^ -u+rR66$ qZ ]%=g1 -_K] -YP^!J-.0n`|%I؜:p@,.J מ_9滏 ̌ZT3OL 둪p9NfQC&\Im\NwIyry g*<=qݺ yGVyԹnr<Js]jS.c:cМ/H혊wd6d~033p> -`>'bO?,> -@.c۹Jm%}e{ -,Sh,%S Z#&04[kj1D6#9ZQ%eOo{GdgYL wBOȧ%`:/-JF\&( < a7ZaVt2[EJ1*.iO]~GA kqyiT: Dn贵_IGD?|#Et(V=ضK -TEPz5fHbېk67=7 MCm͘`迶` ^(_Տ=G"PIBJO03wFˤ+Mr:ߖxG :=0ߡ)rDި\4Jy"U.i4-v3P z,\%)ŝ멈i*A(O5b;ba9gɝ -/ 5dW>7[= PVݽ,W_[ԣ1iE,II[cH ּ9c%DQN:EՕ)>sU'(ʷ$p|":q23»x=FH.wW94ɑB #^}U>/L$G $\7S} -}k=)}BGR+F%T Y,R*q?.ރT3b)=\~#Nio2$`D%oj$+tzozT@ߋ詇E9@,{Ψ"2-E'Q/[7y[WCf}T3 9s~Lگ+ ԍW{o&fb~F,ȀSl?Q\R#o D]qyU$ĭ<+iH&_>NcF$:sQijݧ\c3$C+΀~k⥲K&Q`@γ+lSTaPA-c$.Uj -y@J A6wrES/Z<endstream -endobj -4137 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -F$xr2_"-*@WBʎ$TӵU}jϡ{)4)e]P_BG's$![P99͠k@I !pnn7a?1S%RKb9񣰨Kh(׌xjuF0q\TD`1QSLV $'O+Pl:8D8e!o\q+r~쏺DmY\tRZx3.{|H-یGyEx[Tm˜;_pBF{UxgR ZVuInCZQLI6$vԁ%b'CNQ2H@ ;7tXw҉Ɉi WiMUR"FE[OaDZpƅJ"EH CEahmBcfG JPy邕8؄3@r+ \u-ћ!CaQ7)ߡ7Ґl%r(.!#Wp9КJޥjtq}Wn4H;L)ڨ>Ԍ^什gπ܌)$ŒΨI~ L:/SF-S,]yFJI # 2:AxKt1/nORTiVyq>56lW Zi*k{H\}m{F;lo̵:?gVnp;TmdW4wI6;2 q4 }.[ԁ<9rvtjQe< s p{nBP -: jz4RTT>~B\aR>s>t 욉vy[:W@Қ@ IjNu~x}=8a -!7TAy۟ȿ0`GaCMTEiA6cTR{qZ#j:_áS|ݘջב*9nC>WH*$z &Jrtoo:r¯+CO [Dmd$.|y=M ։:FD5hhξl)eEQ5mLd @{Uǭ]UX=sW/ZޖܴpD&] )WP]hDI$9Q,3delD4+A~?̣?^Зe#vs}Mw5ͼu̺:bpy0v؊_ S$ PlҊ9dkOU]~Lfu7+$zgA yE^a~<'%"ɫՓSPoGs[0j7}0;\݇D \93υK8?+HǏ4mqemf/>#\(u(^L~> -stream -N~\;x>qgi72H[+ڣi|($joOviz!.nJQЕBvLD,P?ǎM;nXʘ ֺ iN>9#PjM~)%PB3۟aUۿFk9kV!i4;E#6U6K -Az3])":=DCS#/Db瓹 X*—xyHαT3gNXCB:q%e5s@knǂU3=f7%{`v5?f`2䍞H :*l3lCX>p*UKrH@4[VGC_OfyٜP9q#@\ɳY|<J\I]zKJw$%ٚ4wm܏”/ }^wrj@3%Y#~w|T7W፯vj-}BQ>Hܓscb9lNbs=Rž~~{!߆'į:I8"*Qq޼*z9i7$ĢX2:MB!!.GCFN,Qu 7 6#k61V_4// -rԑH`_WP0)|5< Pk9]/gQA2v`@wNK_ܠ="t -N}[((OR}/S/!V?q y^ ;`3(˶>>&& ԠBX?h~󅪬SТ E&;.Zgڭdi&Y`nduN䄚Γ2U[w^W=+O&]gͰ($K؎ ge|c W_!6h_?[A@'H[_Py Lɪhe!xVWoʨs~YܠC6eS:V=%Ώ%bwF&ۯ{;F:H*? { K>GOU,Һ|f(By챟ufÁ~D&jt<+4{Ce@;S8+7ccngE -R_fn#}endstream -endobj -4139 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -k.r_=[Xg42C0}QB2n|)ӊ&2 &bWo)nJc`b6=tkN5Ǟ?6=9OI 0kwO-(WͻXEcnt+ϲ题lȪb[*v\)qp? -6hL=Abb)cf3|P=oL.MRb6tI` nP:Kй}S 824d gUxc~ }CoWDۺj3VxZef ϻd -(KX=wʣqh9m{I'BM})OM+>VdPLקgNRMύ$9 w^+%qK:wZN"2k~+SM$_z2l4K -n"73Xe=vrW ,!Yxx1DAKH}=;mp$MuMaƚ xb)fa X7oJ}#,/cOxk f&2kv uRU|T@a-xb\I)zi5Ui/Yw 9(*m`mcpq_}W[ʐ1F]ղ,&1r؈I|"__OGsB2'alKԏ_4ٕˆ1 UiC< pмGL$R6i }bvNu_7xqZiޟc>^&֚tYnBt]oL*FwcϘn⬣t t5Vȫɤ䡯 :5apw(sv11uv'yV8C]cZY[3ؘݬpaS؇kMbŬDz|e4'DnΘ&dC֧]CiAҩąp8:ٚT;*Qt6 ϶ÞBk|66ᢷ+zendstream -endobj -4140 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream ->*>5}fW9y#xs}ӹ-UXE4RbII`ACۧŇfLc}sf~%,k[n; -Q|c5'*e &WwWw1HrOzV +_q!FcHmJ3Ȃ4O*:\8Y'E`FEDe tT?^.X8fLz;Z֍HעgSxiIT(˰-}Wf|/DtyB6-Ele0!6f:==Yrꬎhj=X+mW0 "Ccx'pT&lW!< 즇Ja?{\:urR&8f@%QHݸАWzy'd/+EW -Y҃J9QD_Ǎt?W% \ Wm#ű,aF \P;T޼^J6"h1f xhj}qjQ0Q6>MR.CS}Uo6c! -nӽnCW~TxkmV>4 MA{lvS{?NMc^l}L4V6֭o)}#9&L6F|h%qv7'&8r\_!_9L0gWqTeAv[= -^Lf'MjMzHv\H/H%1P\EYkOGɂ%Ak(FE4Ƶ LZ"Fҩv6ax јc@ElD ȳv)oB#Pc\#!N*t+!$c̙iqiHU]߄o Kt4ݸ1:-3Ϙ0|YU|1 V6x%''-.7wr`~~y?~]^̤"+_ yYv?V1DLzVī7 qWl"풦H -"R_7HpyʺU_䋺 v]TdЍl.˰H--gW[eJ^zL800;7 ͶmjS\TpЖd<[wfdC;IC_a E3{"&Q5qS Rq<`'XohRpWU xNendstream -endobj -4141 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -Lb[oL'# B {ikR^?w *o0l*Ӵz[Kq&(H>uSG̟A:U IۓbXv&6h9P?%KJ773G -wXQ}* p"!B4)KcޞX;X^}FTSJKpo %HmÈ@т>@ցmgӁ Ⱦqz3~9 ~ɧdL֏ 2->\Yw4?1wE>‚"um2Ltdce(AU'ʆAg3) -SuRL'[utߌ+IAfiJV9z2Y35A/"QRs-C[p#Xh2֪Jmd(|x歋/}viȀKV *ڑ|5ܟxaBW?R$IG2$V:YL`S|5 6ҍka5zh[9-ՄSݑl$Cjqe=,tr␢9۽X-Vj0j\+w~:7oWyd"'ds+[Rv4F>$, K{lKIFk07=1歱;iqG۽,D ,pKv_v\6[qkyN(ܠ;[p^b.Je= hsDm1MqQBC>4k@ -Xj9[2ƙ4(0 Q鄅!D9ܸRB1gNOQ;^kVx0l;g\>_I[F,& /Gl@]ۡlX ax~?KvL69?F-b(u\]W\8qq&QMU|7ɓ43T2 t[ G -%4Ϯ_vkU0l4(gs` 1~&FF헩tL9,^r#L({9'%m9p"Y쏬̀ps-PTod6E/%: -N0pn_odF0g G^<B̋O)F-ywg,*1UQco mZ3 -{;%d i{NKOelҵ.cG#%V~cVtgVM ѷE!uGBhJiuz?h'ҽ^lWήkRd%]ҧDҴ|z~$=4q|6Knl|4f:ꔤE,gjӁ'f#2 -EXIo"Ʊc1 p^"E̵N[7dsvNi7['](jhv\ڂS"hئc闉JI*|gtԂZL6+&N[L]#i>BFw[^R>_Օ@DwmYۂ.q''v> 6endstream -endobj -4142 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream --SV'{ld~ m *!ɹ\@Sv λ%My@ :; $"bR'r ~a׳:I)a4SHl‡Z'שM070SjAC^?.XO?<>iThykPޫGGؓfC J(ƥ$j2ǹE[t~5(17ው NT\vxuz^7Ρux =؅o)}>OŪm_`₪_~sIj,tR:&B״S{.#UP_$SB U}h tɼ`_A ?eA!Mr3ٕO9,hٲ5Yl$&sTd/d)z~tЯ3d~M9I4yveIFĻkDEp㜆Ї|>T6Y\KYOw&Ҽ6Zr:~u6GKVqUiLu RӉ`vJ&gGQnm+ƻѭi<],ЌIbn29ݖ; -uJ3aU%rp#o&L>2b'rF:z>nU}Aڌ9WݡQImF7C2h }Y%ñkO37K- 1s;wepx/qeH6C3Ț'݅fH,aNآ\iԫTnOOTɞ_-WP83sYG/`@ZQx-o󁗗* V$ c#$bPLUe}*Ki-Q LORZQ VX8Rj7"rC)Nd"=IN(F>gN~Ӎ Y88%J./iS>4ʖq%mWj5nBt{'q=% 5o?~>B.[&&2lN@7$D?v OP4WZzS i*B,K=颣lxE_z4osA~\{ pㅑ.EaoJ1Ʊ8džP3ierAҁ78 ]?P2זe_?z|bj -gZ(`r >}7 jsulp=aiN4}SQ V=J]Y4U1qJOֈI߳6)x3) bDD⭽d]K 4*Q),85²-WTH v*K\ NcF c*הy{KpPq "gBչ*$V@Fe̬PGbe`p0+J)}Ɯۤ-Ɍym//兼312%V6͞N j_8 :NOp;a >yM3\8\Wd&6|> -stream -+68(0J+}RкY/@5nZ=s;SB;@?b﹧ɸ]idtw$@%4QX&U }#^?U -ʮ  U:5 ̛ \PGUrL ]lЮ~ٝИ^`{KMɽ)>nDjpSxԚ.c]5]=8>9jH;Pɂh ȄpfNsf(C&?ڑv퍜< -[K,W&:k틆9OЧVx*#|>c:) _3<97 F F S䌰7UڊCi+Me~Klf5 F[xwnIqCp&->e(I% } -}l`>ǣ,n#VeUw%T`$п!-L侺oкl8 -,=8N?dm^4( n=yv4O\"J,ae zkpslH(%쮾,^}A22ħ=DQL.EkTr1n\ۍn rTϐ/*ݣ0D< Dp3 O/"z. -9a讐27#4_S=XBT: u4&EQt8Ca(d]Q{ر2z܌tRH[nc>tԡt"vendstream -endobj -4144 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -G/R4a-"! l$.I2\3wOu5ܕm{>Iv񔍲uJ;@0K _dc[3;KElfDt M[ tHmG^:e5SX:,f?5KK&:޽  *kȻP{r2\ x73"Q`\oUyٞ<*޷ a~g(d1]L_^]NjEk}r]Zid1DH<f $Tf˕5Ms܈bΑCR}i7x9L0&#ݟYn0ӴYXCY鍋U}5.0 >$bxޟ#T7 Yu)fKUD<#!q.΁uQa*ҷ9&We-}4j1w3?^X55UW;cZ=:qbu+ ՔXV쵊8i! 4n5a {1ESs]0:7Ι, Af, X -TzK›Qb n[Yht:Qm B#x*D;Lo{,1%۬-PvF܈âuwt]D=XMw̓[^̭NbچC$2Qqr@pGcO0 N0 4/` Q|/!|96mhr!b~։{w[{]W&Nݙ?:D -3!#\ 5o2[tk9A-GSo jU4Ӌ᧸3БƘa98^Er>]nj$O-c/59p|}ĉ(PKiL&ŗ]8\ڊ`S7*BV$:>nW&ҌĆEj[3hK=;|N4 fqټK^= Еi-2ʻ+*js`/Q ICkFre0*cgݑV_Sz8.t$f+M4?kd߹5%xC.5 3, ٌM;B6Pz|jZ>+zx7Eb2hVνwQVNqlT.J>c"/"pUizѼ[ TYSk/ĞFEcMlneRPwBFp+&`@2d#IO1%ud13khCLjBM3;Ƌ#Z :>NQHFH4)QjGaGsmw"BAu8OsHsev[ֿ@A#nup4a*e{>&Qԙ)dQ @B[C:%Kr|sUet_9 -UGt Vb [t7jȳjV&wϛxemѢ8 u+>}R暅p{ș"B)+rAc&xy/A X'wW^ؐVTⰷNendstream -endobj -4145 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -r@.;pԴA7z ϲ娣nv2K9*a<)bV9VzPb:嵭6uBNj'=T\h5dΈu ھ -|Ncf%6M/Gii#TDSicOxVFہt;C4.*K(N.kK ͚$eWҶۮ_@sY/[Mf^ % -oṔ|Γ$k%kt@5ol3ǼB"2wgxD>1s,z-9̙8_5Lȇ)kV+HuO=^@?bBӣ,桖b;Xq/'?,եjl{YHW% ,i$4jM?'OӠOЖ|-,YQf3 *` VTf77 xWsJMӀ7Y󥄈j)[~*[@4d̳ޣrUnHDċ_y$ a𫔆M[*7$ծheHv,h[HD~\ 5ArIzD L -M, "mZt`x'xE\ހ>TGENȓYLgF(1+7ŗa^V)ANq)=qb,"-}TK$-ћԁEs PZnVO xޝ{\Ј/ 0Oމu-J^)m)$fbX"e& ɉMznCԈ/p6$endstream -endobj -4146 0 obj -<< /Filter /FlateDecode /Length 1456 >> -stream -Ų5XN_!'sB-pl)wTZ\L_,]Jb7)l v[p5ԳF_cg[{w/ț4D.G\nVbOt6hߜ HGI,Ψ Q T'_2D k]uW,+Uu(/۠SM1XY&;p{iM= -q/9-h?ac-~{ `(/9^ŎbG5P`ݷyhh;Hh p "ڃ3|bf[Np)JSѣa"$[HxIfm{4!|HԚN gďp˘gY yk,y&[*7_L[^Ybb#Fp kqD:P"/` -J}U y#d]e %C$<^Q#sSvSPb3z8.={or , 6 P2RJ]ڀS/n"sJϿan}Xw.Y/¾Pz5Z㦒 aU!kiuEie}g<4Cz.w% !w[%nPPIS$#|HF?Թԑ<H LjG7xatVnLmX~OYϧJNO b(2jnte}Z| i`9_^dju/a,I|O=c -_ԝ~ ޶Lut˒եfRg%foZjZ3q.M4%4Ώ֝j=Cu햢0;LXӆR%D6&'@G` QvJcMЯo0KEQWz-sשZG|b4 n4aj\fc -yؠc"̧Rɇw8RAN`'pQFeLz8V iYfVܱB{!bJ&A\,s)Rd,&.Pxfq֚\sm_W[Uߧ -(3dM)XS:aGz'>:%'G&Hƫ>O*#18}&ŋ+O/pv׃P֭źubL -j(?5q Dde" -Nk3< wfx,Us+R.K4LguYi 8lZaS[USONNkw!YKw@~$:1k4n~7k$X[7N8h2.޳0*O4M -endstream -endobj -4147 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -lK> -JwRi+~*Bo --R(1,7E[#JU$x0%x>WkMNb9$'*^>֏F yST d,iffKF ZzWNx"[L" m7Qu2м#6_Mp3| "sB*\QmJvIJ67X6NP[ELB2XPQi ?--?=+k^ef*Y68?zx*@jیo-QVcQ_;@KaD"5#^*/GߜH2Qbp6;h){hd U3?;Oe&iVSqGd?>b[ڮ^C_`յLѠ_3ڷӨ"F){SGugVpz 'lkÞN# -hI9j4tlҙ̈́߁f4r#sVQ|k ox:9#vӠ SU< -Hk ǦbXWu6`}hgE4<>"lGuU?(0km)U<ȁ _(u1q~R.1bيS(pOO$o>FHB?]漶Le +n8l/>˻L@kS0,S8J+7}c^x 3FK[~uH3R%tl&wr=SZ*τH~T^\eY:Y+' -ݬ}Yئ?6 w`Yn>j|w:uo97O qj5jzm*Y7@$9/z@;䇂.yc;X`a&fK#PCݾqѬyChˢr@c1k5{ İeQņB!.LƉ 7)&>kQP*:_jQWҝ6+{ZMՔsXL,kL~Db3$9n9ɧ#{׭|nn"endstream -endobj -4148 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -"– ޮM}OY/ʧ flDgT,gWiLWjp=ڃv_D,BXLrZ&[ҨӏP<`0;*a7&ãe RXy#<6?s~,3@&[p٥eKֈ":{ wK[[.-GcU!FFY -nrEi5@s#[-v&%-Ff>A`۽|~SY7'08ȯmc 2I V>2o{ҧzTLȕ28 P9=pl,6zUw?G2n'_.9q?+ y7z3@ տD-rYml˽q7R#s=Zx1%ziD4,&5@3fyHr!l 7Cj3nڸ퇋PPw4TdM`C:6 N} 7g(kh *\dc򐆽p'¼ſ`ɌOK=:z%,jM+Y_3hf8gBz%тD%^^qJOb - TS\t[WfWZL"Kj9 ڲͼ Hjquyɝ/q|߹> M/Vbٮ+ɲ=2׋ߴ<=4'hSYT>Ǟ⓪/h P1}6ɵ.ڤj=T>mdLIHeAQB+\j>A{D5% ,W]R?t1V{'րpӺߺYSc+Yd^^+wu X:NA_G ?X ҩA#1sD,:FԨ1"~ͧ[endstream -endobj -4149 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -XZeb3N)&׌ً.T)\cD?zQeJgz#_ǻciba* 4%?[zhRC:Wynjz*ɬʊ >۷RiW=\3QYƏ -eFVU#n55t\R|uBwEZW-eh)Z3b T:r&*VJاbI%Hk瘨.U*@I0k˪'Pۉ}۾NgYsg7b.!~څ:r,w@e:w[) IEwK]sx -Ux*E"E'laLMgϔ - B_i9CZ1˺=⺓ FU;\Řn^9`%q`[N@!W?'1l&9I$>Q-^鮘i;N.A3+33Vj.H[Sia!Pؙ_\2 )_MQ ٓ f=,ɳ: =gu۔G9ZR#\1/xq^P6ggxq58IPA%mIЧzynGCL?dQ܉`&+,]RUquzSqFG.a0KAIE+kfM_dji3x,bP/;Kv>{^eHDnm1ar#faм~q'4: k -hU5aqJ"vN:)@ -m~f/FRN=ڄ&D݊=i4t?"WoiVxN0:]»I[1B`fJJ3X,`4oݮqϥ<8W-.9f8#AYn(_X~M:wvnK!}%=VZbQ i[r҇Sb|Zr ?Z S:MB`j|SdNiW@4XZp T;Exk)I*PN0=X^5; W)pԔw mUP#c3h$?ġPi|!P$OFqIӨ±~XmQk9CtU9ˉaTE ny#nu~|H~E-M_nڒ {CXȚw|#6^zo=B(¾.8ZCZRS ^tmF~ p" 3.0sXs;?m(X!77Nmpr0ħxm5 JG(0\{j]!W2YdRus`k[qS龮擤o$$)7$Q*d8 RxR؃.v;6B#fqOݻ/F3<i|w釹E0%9Ee@.i$#$A -Nfr֨j_APRFzX86Hs0FS0|T8v56ESz[G 7yWrƃ;#`Xmendstream -endobj -4150 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -@FfEN4ٮ*;#P+zI82Sڠ{`s*Bq۹ -2VA *Fޞd,1yC(I!Z6ȟ%aDVJ5,"Sl<~dse5; _Ḽ/C8LQY$Ba;vڃfEoK|HBm]Zv2??Nl톛ah km!avLcڪ00e˂5VrE4bBę+J_90_0/9~}i4B=1; 4J=NZ- ۽gpXh뱴h:A<NEsKߍ~.}*V۷$ _G(~v} cx S;G0w؜^ -sw@Vuh}"`!ϯ."W!Qsm~Bs :$-y3SAӣ{cvOp&' R*d]+4"Q|nwԡ: 21u%-MDPH?%pȁajE)nN %ofEТ0$,m9)ࡑ/<F [r":BK-uY}E ZA6或?yx%'7W)-awïg/>}2NaPU*մǩ}.! -|U#fѪI,RZ%mwAE sɖ 6Wh> -stream -X!/QEЊ*ɘkcK4ɿ:PfD^"$dJx8̓5& -_G)W;!X]țͪ9zb+Wv`xnR@N=Џx9S_<:s -6tQa%xh (sp} X*`! 9x[g PP sw$3MXvȰs@ZX鞲rlb~a3WOm 6OHG?/ZT]ޯy*.șT;x->-c*fQ ʥ~vŸSF>vBḞ)ҕ"gG8B-l<}wܦ3XH92m߉#IjTb׽94,(Y 3sv`ک>`UPbLML/Mcz?K^u)e%G^BW,ý+)B CRҥwhl"&%бiW{NEW䀴Eh*#Sݯ/mb|zQz@uٵpvʃ񓇏Q x2Qcbx,##zv5lpnjeeK1)N<ӶR#^Zi wT&uFsFfl%,jj5YŸ/ endstream -endobj -4152 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -VM$AwW%fH-ta\G2JZ[Ց!zGmonBU gtHJq^#u "}\0ڡHHmA`hm_"Bbc 0VDYMX*UP ŨVꜲ2$7ʑgXJ*_b4s9@a>JA1ldЬ"dO<Э> ,-6;?К Lۙ`|]Z>b|_c iGC P(7 e):Yw5+k:olD'm'6qZQ}E[]Gdn ./}((KvYkYZ -F-]Q${n. -!@q ~\3n7ɐ*?M=cBPt>Aus}hm5Mrh3( ix])XޝJ9$(Fs](nlvOJv %3_n,'sA@epp⨸ϳ@g"kq1ߢ33 -Y%b U~%dM.ʟ؋ 9Є陚B*+T8 -$0u 2PQ2KyեVn~iAPNI S͇ =P 7YBfxFAđ=OźGh B a{đ"K0FGxœ&d_l0F2ErC#9xg)5M̀kQ50le}S`(|6)ely_/؂vlYT %Mܝ^q+f]z*t^>I#{# ھ>0m -*ȩKw:N ЉU )څ9. gNZs'u+l"QOL9K\2@K >* z?uVm9+X(#o7ПO$'vkІIh/=?e5 Q`Eo42ڽ4х&Krc8?=fXppjk2ei&lϬI Cփ!2L8JJqq#LnhuX%>BY ޲40әypnZW.1s @OZ/Y’o`se.ӷ-D7J,4b& xشj \ -(3mGՐyS=ُk.J ?h~%N+#L [ߧh%,}e>>K -/Go}II~|)iϾ]BLzL5cXՈY*%n~R:Qg5g+Y`]D*,h_Zbp:l} ~DWю954endstream -endobj -4153 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -%$$'¬V NOŗ 7(u묘چ\V_%X  Hg7`DeD.6?@߮FZz/{mS)C5|[Bl0N^:ސt#sP`^O4|H, ~9l]:yBa;l]%?`Wi`]-;]r]4UI%Lhsǒ紻cl0muƢ -dbp{I$Jv,щo>4ġ[V($d6O٢f| &R,PN ]|MENNR x}:0REg!LNC{ۀ8YÊoêX iO 7T]ʞ}(;G-g٬h]?tg+3ݘ"1T!pF軾/2QxWws8Ժοz@Ef+v!NK-'{`{M1иQ?"VD<}r1͙Iјr;܆|]BvNY3h-=sZ :E>H=' j۝*(N -+C{,ry޳=I|,›9&kJDHi=YZhU&+a8<ˉo K{|GQQ=փAwq#LH,ZlV,đ9Ij6] Ʈe1<ȆwJ`+.Wسr'Ck$b{j L,_)-<7EO_ʸFT^IZP?~{÷=$~p8Jvc}JE$(@U;/nPZp|nRAAuh+U҅|Şrn%wg.!ёffM2ơxspzq,{_DR7RqS0SKT]C/=a Ddbn26؊J 2@^ -N+l xrum, \NjĬ/l3Sϛ -2xm*Oq$^+ϱF#hPYC r-}M3cߩ,lY+^8c;ͩUi!G\?<ܲ}pʗWE*7Hvo uH2@sTVBZlDXR;aBV^#:5;vL`kV r*ؑ;u~HhU}ʛjUD(_*"v-0Z[mҀF^?p>WNendstream -endobj -4154 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -9η| sT b(iU20_r o%!Җb -N U ZΖ _AúK(i -\PNjw@CHA'v*Iƍfff8QR(;PKc,CT»:Clj}sL3~,㺔Ճa6 S-tMi[{Vq*9+J׽FAOyS|Pqgi,1s2( U!3&AtA "] h; pG܌\f:Pmw{'mUvdOk5mѬw̥uǒm.Tt/VZ#UhI!]o -&k1'6 w+2-u9HΒ`2rЫ6]LܾcZl!c#@F7`Yv7͐ k^<ɩKҽ[q $jV162U $? ph]B|Jl S4GĴ2w$4ӽNjNfU `k]?ޛd8vJnBq| إ]w7@M ?R>5).]J49LmsU;f[1esH=8F1NCɵ?'u%cgc$s/֌Ѳm>+o(D63R{%߰9̐njخ"]@.Fb=OyqJSqThŝ|JGLn C+H ֪ww[^3)&t"dkiFh&pT׋3U&ƹ֙D:TIvT\kV "c;wبvp|ZJ|ty -q/Ll "D|6X$W >;lN'/籊ЕbF`G7g7MUDw[)"w i^&q\m>~!vT׋+ :U DGyXPpB}nZ(i"FyHŘhjbEQQ\ԝʂwAWE*{]d`d2?^POXw+&fRU~Jendstream -endobj -4155 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -/v.1g|ˤ锽>{s=v.$1ɷՂ`zY0J*pZCK( fSm)ɗ hdo^i$[eym? -CM  dWI.y8t%< #|uL.{T:dt&)qvezUհ$&f+N7SNoy<#mm27;³?wi4e߲7PyvCC7i~ĉE*_LMOEcs"+$OTP``Sv!"0  $]@eIdh`g fc35c6&P" -Q]NJwPI.%Ku`,3z 7Haxbǰ|OK7Q~C;il/AW6taK4r U|ixȠg@v5Y*oCd~j- ori~ƪ蛬m/y0!F^vK[L -+>/gy{5Q6WdLnF?>1=?]iG62Xke^q-д2s\@6 :$h`Շ<9&@{ܝ1YFrH -`èn#*gh׈i}M!0Av)bP® -"t5|?HL77{%~_"QĞއ M?!Z, e"|-݉|`uAVO9U&3ECQ1W!Ysk<Aӭ΄t٦ڡ0#z|~CY6L ݴ陭ڔCrtb%2&rkTٸ7%IP?D/1DN\L6\6pHUK뱌׏hc G\v܅/4p%ݝanNܵAX'kXgɽ1Ќh]&>-3*sf]6.PUVG?|I8if݁O -kJµ-iǯЈFoyù,Gk&iT\ecY\@OoBB±~y7{e5@r/_ATou5;`v' ɍY-g+4A%DDɸE?'3ןg|^OԗqkCKݍ>Wp* li'Bendstream -endobj -4156 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -ǼOT̀=;;)nrX" 8nĕŎZ>|+@el ߓJuaG;vOwU3vO$$H}$Zd$[N-w(翮s=9| -KhBsn'bQ>_?J|\=`ko*݅tA=xsV2- Ҷp2mNHqzs(8s޻g<6*xŭ[l{j_e;k$\~)yLB# Ev\!vnĶs=KG6_^hVON/?4X^b`4mϭ$s_ؐ BTJA1H -)0+GϵmF7j2k ֬#$Y1w*ß]V+m3!ݱ4 Tct}F}uS=M9"\,+wZEbaz7t)ZCc`[-b!.$ꀪXBI# yHJf˔>hP~&Xwf-*  &8!?:ѯ}x\@u6623:[a>d0R84Ք2Vv>/2HKj[< -B+ƁWΘ:%2X+a=0 bʦvs;֐iTgнqP{* MVe*0A{ aalCWS,!!^ ,WSbyU CB$0_p{ 12 wqd}`$#ـR88[FZC -=>R ]=4ՏZ=aOڍO` s9Vعi!r0?%x"5EW]0jM6PNԔq}0mJգJ_ڐlAd8kE6̧bZgXxw'WlBZ?A@Y0ZO?Rfզk qg^*MyF@9񎥾v Q]ar8z9tr@OM -db+p4P }ya)PI`2Ur pL⍪S^Iůa[ dl-$ c%MMG<]ݹFPC/6Y]}sﯠ,r.M< Z9ͪ:xE,ʵ$Ut#6d޳5QxkY BN2$3{ -T >y{rW "u͏A'iSp2?ld;ҤӎSWJ@?T2.XvG8o0VA -Æ k=b%;0$oN=?`:J 36#q۲TId7SW°PTŞWll2gODZc& 46~U'dFI2̦2֞VuPqB~"Ϫ@^;,١[ly0(On가 W}t< -WA? -%⦧z6BU7 JYv϶M$J#`&+_ur6aqbWS\ZZWCf޹_H`0OX.'슙;~oQ|qخ\ߒ1O,`gwBOg˧h&xmh2=U$4Jpc`5iπwrS}bpm'·;O H&{œ UW4& -,;<7 U@fԠ&k{~7[]U#2JIɱ?S@H/\Bk#F<3"; sMXendstream -endobj -4157 0 obj -<< /Filter /FlateDecode /Length 1440 >> -stream -ԅʛK"䰾{ooC:.{KdŦBT3J[fFB' [(-7\b :g %_zx5idބR-^OjLɑ|;8^:]z:hcqM_߁ SbG|.S|ǁHZA`фCMYaY3AGH 9A1XP-g= Ԓ|֢nk 0S[7>f$A);&wK 49x=3-4VZe#|C/Srte*8ƌMHȉV0%mbhilbQFd ddnlJ(61s=%4\l>05*B݊PIc)@) ܩE-~F/-u|lAWF! -] 3/7;x3-`_R1*DW8I- -YkeĢ%L}FBظՒ5íJI|Ǚ_y sy3H :8HSZV6[ߝJ%^VDL*nS,MuPYsSAЌ=յc1$y;Ѵ3ejEJv9$kbѷ׃#zc#n_m]Hl.z*Rp~#(y@5׍N[P=SAވ6B}߻pRNon XvLt232҇ӞEh0o:W i*]4,t.^ڡAnU@/ʋ}C ^i3xiƨgJWUrIY*E:\G*Q`H#rSNl!F<0D/{(!Z>ã -AD-/6ӷWeGGB/_̡zFcmlr3Vv$܇3Qk ]/|LHV@K/>4k}ܔdRrH*d8}?"Fy`ˈr'v}51cԲ.9☀E[՟ObPҢA\:ZW-S4}rl/Ar -oq F1I`@3p%GiѻTmˀ*R(Ac|*&SpfW鲩Y{{KoEHzcy,>f\> -stream -=9q{/d.PBp/S0j3HHvݞNgxQ~%W05# %Qr.F~ë] hV J%%vƠ|s^T?n4;H{K95ٷ+p"vʴm FC)%0\:a^)U99Ck{Y"{+XY|P.0o,2gM(#{>`3p ڇET& Rԧ 2vU_HI u) ZpΙ^dJ!1SUG"r]>95ھ9N_K%+8Oa4 h2E ]d\M.F*p4N /e[HĂruV\a˔ VNk GiL`2Rm,pZnRpe(MdF!Bf;)~(@V"-Y`Z=laNL 1 G\nƨDӈ %}$G·e`kGۙ D{WAӵQpqSKH>"h.j/), }7E -. -ܛޑGJf V)NKi䘒`Tz}77`E9b#+j; YP֟_;+61MxBK+ aQnk)4N@# 5AlDŽuv똾 ui2N:\r M#''nA)7jmh;^dMέŴhڡEJ9 ʎWwgaQޕA!q9y `6žgZV߄F'ыKXϥ<,S]v~aqCoWCkrI MBd@ -R5 -1LB*!PK1Թ\AnOrV$WxM2bg -T: e?sNLltE)08)TsE.z[4zU36JspA/Q%ɱו.a4kX5G7/T?NkeEa5{a=:l$[Q~b1ˢW@ a1[d;nreUY'!5aKaLM #O%%kzцоp^Kj}L I 0waø{5~⺥c Jx&"ű?،G yT]{ݫ{ -5͏Cz(0fGTf幵bL-/gv-!{E,1ȇ)ьS,ZU~T#gz(I馔tdch?+ }zͲC Ij5%H-"KHG0աw=s6~=t[aUhNK4J#;ڢèX`h;OvV 4g6 "@ߑ -yTS׮_Dv03 -_$Kk'[g6x^sk'siQendstream -endobj -4159 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -ׁA`lF~ᯔUSBE>IBSt9 t;; Nxю=y׌Vࢹ6{'sn5{ECliZE)r= !9T,l@v' Moۃ!B佄.$5䢋gK%mI:*9,|8>sb2i`IlT 5] `՞![NNjq XQNHȕa_w6 I]kxr_Է3AH;kSuI@⮟*3~{R|ar{E'n/LE~0KWJb):V[[4O7yJ63=|Aw|$| OOwwƃ}"yiQOy\|W&"9|0BjiGjs΋דGVJp+u 99WR OLQ|#":^(IMmqiO F󏢯ݱ =Ǒ/4ZR^ck*Pkʈ)Q aD#blܐR~=fҗtϫj'zn [)%w -VGOu?C*U{4fwxY -.zͻI^+9C lUԋN?Ts.[!*(C6::P.mhe(S;J -'Ffb+.|:-ۄ?`ؼҿzYK[LXKa{b"nlPF2b*f[ Z[8Ę92ծ,JJg#íaP"A}UE8|p #U]Fo{%#gbr1w0 b^u*2Ϋ+Ti#!ݤwpcvNKogaIғkj>J+m:нJh۫$CK'j<ﲃHFaJS}<`~SP=M fI4"u \d-4n$ZuI7ف.C 31[]<4K}vD-!˼Vyo ak:ݶU}8o(Z WI|W k% iR1$`.+KH>!͈ld b9?ư>+?1t fëWw;FH/E^۹,@K_2OSh-Y7XΙ4c`/ꎤdf'ܚ؊bu7Amw#'wXY.{PĎ5s^Rpx9.dJȤ"i:. _7$=;a~$VmyQBӦo)xgPnfbm[ǡёѫx~0)A7fb-*'gXyL/2^w43 &Ѿbo]nc^OIZ]7b@E.^F/s9'G)Q³ђ5Id1ٰ22_lv7$ޥiy [*ndt&.'AρhYYÐnsz.rG$p!}I]oIj$Y 1"TYFp\B}X| -s°yaN%2#Szվ-<rSX#un}ElD32"Ta~4+ endstream -endobj -4160 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -}b2rw B ލ/}SX\ihE?"G¿BCyn*] !ΰUQ4'x',bZD_<*[Su FvU."'4f{~I4bl$TQw_"3LH9,8xqƠYkB{ …ߌx%.Ze+=~$t1%x1l/e=hs_ӳ_س6?`b2#MtB{ް 7tKDh$X|,k"!'^U0 .o]Yzyk - C'_-` 18$}rg^i1|Q-MgDl'6NZAǦQRr 8X7{]XO;-4:?l̞Z%p~˴J#+AoT'd:Yz;!NxWXSn &lEܿAC,` -PH(j96r@;c}x6/Vr9}bNi $r2{PO'1>,3*M. -~^!PD#vo2LC -.!]'$#8X~L :ŜHmCǁS܉!a?]$lPXf}Uq @Uz$Y%%})I)tۖhGoue.h4yFXb~z*z"":Etd,Mz\VMOGe4GYЩf:boSnLkeendstream -endobj -4161 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream --U-GH4kOtK` }13LuB .=-dLaáV7C\24%6d A%YJA-s'C^{:@tm-&/KOa -kQ32,ՠ,aE|O>vYv$u - /cS0d aG) QU>5^fcj{0 h}7vPWt/(mt]V\]0,g*E&%Iͣ>`Gq%20\Y}2H tVJIO>v&9ZU*zVUǨLmiG7e5=U3-qZuQT]Y0,Rdf?'01z5(:DˎhߛSxK͒CyJS +: EYf73jl-2P^6ALѯat&/{5s$ la3ϐ]uh 2:^Q̮CQfʩWMuG즁%i sma4ܙsّukV^<2YsE#lsK(m/dբVXW0Q":ʼZԚz[SXf8t%O/# celG]`Ϫb*Rg_gmpB$8KF3L\Kxl~iR/1ӝWs/*gٷ\`p4ӹC3|Qݔ)KqլJq^/]Z=X4=ΓD|?ƜߘTE -k❞?kSڱ_ׁ2Wi]5y%д9l囀qt1_?ٕQ x"+>4!XO?SD82. Nۦ>[ptT鋧{T8'QD@b-ώǤ-rⶾOV8s7l(ᩩ:@TҼ\)3a*}l{L3W MT@CwpL-=WOgkFO+8f_(5 G Dendstream -endobj -4162 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -Ъ}.HԈ8!g׾dGMې“fAp *olV֮ujKs9qI.÷Lqg$CnT쮐Gv\(ȻD)c8!ڏJ\CR{-:B*uLB(+7- .r%}Qe"!`7 U4L,a6>X ;5z,PF괆(iX(v; ۿ*j"P~]b?4ߖKyCi*3vq3zO߆|&[ - )~BMD]TiQ]Z9( ?&i1\(b5(T?b2N< EOy!y&x{&hyK*|?D%A%aeX[ ;z %bGN*YMxaaOlsOM<4SN9s'6)ڜ^JU.[u:j8Z{ dN) -hz0=9z_0睿ϝm.a$-O44\I?$8'M@G4)l!mP?QP{27l7,CQJa򋽻}cP~:Z+DC"U8 `\gi?@!y FH"·Բ4s3,?U%- -ƌˋ,ކ_&7⦨dc͟Zdc,zo -Pİ?x8 kTVˎ+ QB'r%--c:vd3fxMP'nU,CHL_HM(ޗn%q٪ l !ΐdn[P]b3|=~g^A7rOxdWw@1R]8:ߋ -Wk7FauW_*4_U@a'2@3W)*c"C,fr]$#d] IکWkpYsF]:ܕU`eئOI 8vf G L2CI1pW4)ѪD>.?< ;^3*AU5 Bn tsEa -Z.g^vKʴ5y)=co7iҏ[UIW1\7>6 ңFPn)O.k#}KqRj ̍X_h6vпVΨ6ڹ^XJ3q<R&TMhfuTɅ0r;E8&u0O36J>S@?M K;/{eTP m;2jp S3CǵK+sb~CV] =ȃhH -*NU CP6 6߆.fOpúNF`hSt/GO,w;y:KTv~19ǀJJP)BzK% }?nH~ё̥0~\o s}6k1gIZ-F3r{!=%x .µ^觏vW̋LM{|Q1}?50!endstream -endobj -4163 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -.^bUw;ByV_-,6PB`=x†&oSw}{d$f>#Cx% TS/A+*$%/xB[s'zrO6m.l s <X {JI{ 4HڊE+J"S~(+ -afogYh ~->>]BqbE1U:r䗨/'#Z5z~51fU2ª" -T 3mcOtCv:Icj!$]:4[ 'e~*X2mh g"([(v~,< BL~n¬8DoX}@}4sogoґts-Xj+،ґ>qEO3MO5cP4VF]73tRD:M>w ,+(PgW|$!K!w%O 酤 ~M G aGru~F#0b_p%<|*bT!.X)x j^YZP_%|ڰ~#:Ycn.mupjE {tk? -ۀ_8CS=|:(^W)D| -"V4z4X%cuFT;EH6NTlmd3YBUuSoA&#wM@; rAJZGI%Adu!U)Xml?\X8-+ˈ(E 1]98U,%!y-^Ydv5[I[h]fΏs2`|.AuQ4Ψ%9|TQFy4?e2RAsCoJ@kI[&CS~3]̞:J"5NY<ъwM}lW3lgqhtH-\ 1C|X]?,oߌiABP(,U2YMYMͪ+Gϐޥs?jC[i| r /flG sďjLܥa8cc:4qC!d%u&ehEv8g kF'MvoKŰ//wU'.zo& K5$nP@q@>- !%{'Yƻl -67Muٱۥ: -G%%`qXik֛I]KBлc^w k5!Og9^>*~zR BסrhcكR^/vj1@uP(jV?3@v<CWxGr$t#Pa`+tJTendstream -endobj -4164 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -{6{Óttz.#LD'2eeq|#12/koN^,VѤ iqBd9߻T>Q74#P YSTגJJmt\dmK=+okɹE}4-e-B}p >y -mhh HZp?+Z$7"5\0kQQU}Us;'lbp5qTAK2 uH\JxwJa:skΔstÄh,Q:D z˧lSuE0J]==1֦6n`iz&d2B C2 L>MtfV[өkbQ^ZJtIj:codB̡\屢;3-)ZcWr""8r+8#ݪH6<|\7 _"d}r YEzsHЦwT XD903=*Ux*?9F=ϰ Dhc˂1~*#u&&wW=3=/1 !_+ٴL5LD8{gPD2XZ<}M4ө16#lZnErsyp$)58)kש7/Ѫ@mj*Ҫ+zթ{$[U;l݄Ӵ ײ!i*ۣ"Bgp:$I-zk%'* 3߯]7obY -%٫񙽢#J8)  -SN -ћ%L{72# B*v'h)>KՎl2cD*uMGaW>8CŸ[&bˉF2 -½G$+KDu ^8($#+9\`gB5X?3RLݨ"2΅ kA**][\grj^rM[veCfD3Zj.ZĀx--%%c#m\ (Qv&oN(+Sis֧VmP742ye;x̷13V1FzZFvVwIN)G_c6Ѐox;\TiyE* Eu_Zɚ+sK)'x@a7&:绯/{!~kXR| y9&YE%VNzu&(Wr.jm2gݼ^[k !O-INϝӀ< sU I2H4B}Kp )F@^\B+{/Iݪn̹fpj̩x{'٦-G*Fl>z `񡽀S&MweV LO306Y♔kkhCEB[(endstream -endobj -4165 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -Z5y -+}6*R#m"{Ĥ>r8߉>Iv^PD0_EIov(F0A^#xYdr}/qzqPV!Vdr.KT`C3Wy^dduȎ`HNZwmnSӚHTQ ʦO#8*s48HFI˵MtEm I-AA`fAAjp=U-@%t,gtzQ<Mz aMt4I+_2hon*UzELuhm͓Cr -c -[_YZѺ9&p!/AXJLMTz[8 -z)a"MI:BN_c*$ң}v ,s*+$)rkJ|-15ZA~/Oێ^QYVUUTZw2}ץӫ|))YZؗA{Iр[u2cObZKhyu}.azau3h4!2PP]L9~$@Cz&uUS'L迁[0:tU%ɖsFepKY5P/C[\+Pe,tw6EmINhE]1lG_>:!Za)܏H\Z Oəq<5@$~Tq#%]-AP%djg˶{6 _m^57aq7b*ǁ:x`FWOaJqLEfwwvaumgouN0< {ď]nMY㞞aΖK Z[;!t7-e8#}.iTnڌJ#6lFΊmR:F,&L%*$3އvmWB_2Q"pƵvek| 봙R5w(*:"dz|;K<dž 'Y@O}VMf83Cjw%d*_JCB Y#}I{j~[ߪNeԅ8Mn ߛ&ꤔ0480r8_9c>bpY -L! *e:uCʃeg\i.XI ]S茶S;W4l:/\9#ˇZ2U_hAy -lZ>`;xK DѓW헝+qF~r",Gzӂ,%-mzaoq (~K.աY5t151$se^ErT7M:1:nHƫbo0ݹG< Z'N rLn]53爐!Kr(LqCf`Ͽh:wl ꨟ -Δ &=VN4-,3ߒrj[19vOcԗhDendstream -endobj -4166 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -U )KHeP2#SÈghӱ\ikAI=1֋ƿm ϋ2u{`\gT 9 )Awȸ"B3d&;QL(%Q{j@"G4@]yᦄ*$ NAFNyQE3%ll8%YZg.HOPiһ((T.g"94kn.^26p]}!b}z(0;6L?Bu--R$LƩ$jh 3ا1q>:a#Ƈ'SiZ{Yvן is?wJ\Z# ezcxJ [Us;n2e4T?Wa$2vgg6߷B\)2휞ba*O( #)bMW1ͣ/lѥ0E.Z!j~4GB&+E 6Y $sJ sÄA醹 s=N!NY,CU ڂZMcdrx&΅3˩$em}5U8D_V)bC Y!/$nZBHqO˽/ -r0t ,VS/EuX%s')3E*NU V )6 t$ٳ{.x?v,6(ӵj>}<+Xh ԢBi~%y!w|I}0c'renJ]7S#AY$ Ǧ -&ucz,>f 9} F#J:+2{LJF}5Loo7vl9qWUL&J4Gxҁ C.%,19$=jx`> n-Ҿn,5uLZBڛ m5W"24;&ɛb:tPi.C7a &tTFwdgx:6Y1q,}ꝼ* +ZvvoܦfP?g·+0KO&jTOd׍R -ǐe}.km Wm +s|k7HpAe0;;!mendstream -endobj -4167 0 obj -<< /Filter /FlateDecode /Length 656 >> -stream -:~;&&PoW-;̣ ,'&WI2I {hN~2@Zhuxh"C5n5X;`S˽K^gaNSbz$hi,lkQiU>%[FzoMTn@QX^QA%$ud4R/=G"ֹPWߣ% 'vƼ9X}ۄ&+U,.zLKw%M(*|sgm )mKҹt}3htD"f,9l"QI&g@*DyvЍٵ;ݫim5;C 4t`EwDgc\F&VvTc%|Gfk4q$,J -;k:3ةVʿIc,NtCgk9AX$endstream -endobj -4168 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -{ш"e8is!_w&$#K3yuv(bC Xc<[C?S36)ڊN.ழQ9f/pЈVZ@.=YͮCKjsxb 8ʤbϏbg9q /{n4E"F]?'_6CYIr&.4MiNٲp|{$Aa ݅\ ըQvdpBP m|nNvd]:LLn PyҤ2Xzaq8}?3@:=9D G.5 - -[N=MckYNNG2==~]Y|`S/f9Fƻ ?Jw߿oX+a}@aRx+I29V9Ni -ԙuyzGҦK -6pix+sYh&*# -9X> -mZ -ZSίhmj߭o!mS&ffK>oWHM4{Я,zLUu \V> 2{fsThg A}_jh:j ,=b,(P09u~Н[ULC'U.HZ֬tz"ڗMS7Vs晢xIKwC#g:v/ScE=_Wnv|C|B*}րK}F(3T~7 :e }%}6ji;и[tHʦΐ=L9.]_# 0Q VlsFY3Ơ ςNNf(wX 2}C3VNfUp#a;ezmOvS"ihNe1]_pnbFXRo-dIa#E5 7 ) -]7d8oqѺ%loʦ$GJVU$[7i?d/m4T0NTb˟LxHL"D:. G=sDC@I 7+3 O[}} -*LXg| ɗBoUT8Hq)ݷJvȫ:`pC>iPnb祧%ГoMyړREe yz.V3[ -V J[up% 6:h_ -RlĬM #Ow#RP ȌX^ӆ VI$#*5=O^XJfҢC t&H _endstream -endobj -4169 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -pL\mɎR>Q\ЃޯlΣeGp?]񔦇Jf4mLf}27TD8c-@ui̬%ޭ$BT?Q,0s  jjA<3[u.o4Wć%i_}/\ԓD\# nlLnOڒc>y_R)2'?n8zkp?4' /oko 5Pog9d%+ITw ư ۝FW_ƹvFu̙)fܻpʌIs'' 8cJj:4hLɷݠWiu 2)41G5EKTx ->T9ĔǦo_Uc!-Qy0 r[6#x7?iDw#"=+\-.Q$t5ᘪ`PVcS$a6h;EdʺLŮNcOlXنܛEH ym94Y8Nj>;CFhȯֈya*3"Ksw)ZL i %+_"h%X朽u7Z3[Uœ04"W qgn LYuA}f\oS2on='{u,^uiJ[#P_9Esz75 -o JoB]]1P@ЄǸ 'N֐hI3CfnCI5vho?CSi hp#!k=%qG,㐽iQT ,W@[e*x/:E+b.vEQ]%*t'LINM;;:v>G\k'"EEw 0HJV> G,Nvc)Gj#|PZ$i9JFTp,#UmH:b6آs⇿3ƅ^:v4Jr>-fåsК5:7EK=ڵh@`wAm)p~7ރ qU-wHą(>G@kN7 liGj܇`s{ޱi %'"Wendstream -endobj -4170 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -/&Ӕkړ {6/f 9ۅ"Œhrb^4>dkK6 tzBIk۝n= ܽӽ(֟ݾ]l;k -}&j"uBm\Àkjtcfn=[IY|vs& (aWKSJ5`]ݳߏ)Klr7bɊJhޖ}̚Ri9yz箹dzEZھ,K+)*O%Sna=XYNa:dBEg >"NK=*ȿF|4o#/?yl|vx?)𳳌T֤~E,I9M  -mkanM?to4Ɲr?P ߡ#z (RX3 ktQ -֖s -b_帖"b!OU,"֝i-mv\|ٙAA wV~!_Śg8zҹQ-@֝0)v+vP0^Sv!I p[5qI1Oc_^pKhx7. vHΎ-Cfuj؉34#*ıuԐtǼ3,"L\D5HAsJ^ -4wǗP{[f5߉ I_ÿ THUXn{Ih=X!W'9S͞ę^v @/Qyc@6AAv/IyWk KXG;3ΠHx ڒ[L\2}fChzr8^=~/" -LP0:VUbHD"G\y(4B 0e ~݊#\ml[Qw>oDikWo?ݧǗ'tZAxY@Bf(2 !Ø5o,0X]E -pO@cHS -0g1_:+SP:C9<rg40Etc":M!*Rmnz#F<UJ*P0#{`ɖT];7lk(KB -\mAJQ - GmS'_ZSP˜"gp60tH(h;XϚl/~[ UD cz Um.&W Ռ;'vE599'4" j''_CCtlű_ɔ[%~U_LJwK?>ߤloN4DڀN@MLߗT0av6ܽ[%endstream -endobj -4171 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -$?wGӺt.O"BΆ$bY E9@<^^<:ݭUC)!p#׵Z6H)bƊci,A/W; g(Uڂ[oK="Åļ5;G#g5!͌39nQF£ZtöQ/0sOHFmY!8;{@2ꖶt%!~^'H}GX`ny|*T$?ۋ -uB YR'Hk 9 B'։o ,Ƞ!&.F -3UƏlI-QA)z#&fƎxҜԌϯFx+pu2눥bԿ5 ̬ 7K8&iuE$IK/$ o.QRHw@cي`*a4n~a)Kxb<%_ԗzVܯnVs>x|QȕǷA@1 YjN6ґs K6o$gSnv2 |3Gav=c H"3࠾(ĥ<Y8Z0-.+F~Ic7Žjf`TD(@Ir̡J6a[=.ձ/8)H -e)%w5jEL&`v' K F<[@Yw|RvG՗:`뚫o‘\w##-B3bn?qGRVHoiNz ytz!,pz:$o}J3% endstream -endobj -4172 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream - wҒɬBZTo,>xvh`/P2^7h9ʒ7e -[fo;n T$(ۊN| ->PʓʏdEQSK{^ v!,sXTcH-O|;¦Q6ZlL\-=[nEordxB߳  _wƹ]՟‚t^inQ*؜%/-ړ[#0@'J%,$B}Zg)!fGOfm lP\ڒ2UR9vqe{C~uF=sfڬR%(fYexc6-[AI <K35^ec+8R]XjO|`sVlKEvUc8da --`$A"TY( ->3չ*%@"V_ΎdLI_po^I%OZœ5*X{źQp\ЇH -8Vn&18orex"[`E6G X50g޷1 )R؛:ӄz5w5a_wM`p 艑4+pWf(C:z!ˇ#FCCY1i2҄& Oc3ԞsibdD]9o`?-քL;u:qJ=4`E\U;5.n/_Ve!l%h#@r2]OY--WvJW7gur^ʺ:洙R`] $R<O+7Ov%2TOrw[;8h `~8zgdi H;DZXX1Hi]Ăf :CXQdbLk -~rrc!MR`GSOA"/ơ&LJFs{$)xǴC΄_mԽ1n:[3?J1H8jU\GhD— -9ppGk|}OB&AjӈVsw lYh Ү(p0%dzy($#  < N cb5}3ic)\WL]fQfVo)[Z -NgiU8?5-A~@a֭GN{2b/s^6>tD*3 #;ڟUeau tb3rcY -fRZIJ-7b08zj?my;1x@H ;#j}vKZg]a{|mS(,}f5ȹ6>Xsyϙyv2c0R}iJfLy& ި^XtG:Ċ }DI>Hw' JKXD) oŏL I@? '#_0e&qma[ )\K)  K+!fFendstream -endobj -4173 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -cYPc+bfX(kYq*LfE -9!'[^7cp P+6 -]nM 3lW(/N<7.(9(4p({8:ʲ+;KC+ ei>evk.M5dK"un L!L -0 ۼ Ӛ0ik:DgԖvQ؄nɱd)>ūw4e|3i?z}'k6 E2p&hcH K_ܦ^Y\craO,A6Nx?q -dI7u vTJ^v<Р1 1ukk[pVi!^jQ,81RjX/"!B8 A_dY(ʬCNXD?٧'] %yuM-ny>T >S"&W.GdxQ˒bg)!-XsmOђheHzKi{!TfXՐ bCMZ3 )3xd;UO]Mz;Ά-.o5U"% -BZa}3͏N۹ZV흦 QZpTɴRY+Ů\=pU;Va'4 _xAår)Aj-y |YIV eTBE"(lG͛9QPX3 J0ojK6 !?!^ti$;FlE_Gd0+jݜB.Vh >{nj*gze>|1!̳~xkC.6h^0@>ܿ[[/|{T$аvAWu#+[ZXvl5kQu'_ =h崪wN74sc'9(U@qD lӤvE{Ce9!`N$q(> -stream -lI$Bǭcp%3nh(t -NŽ^=ZyEѯ6nO| ^&d&qhLiZ55AS JD]$kgT̿P?{(8ࢇSAEIl5&6,>ϐr]ܲn"&VU~zQ%hRx|04P蝺ASD%M1:MK7vhU<צEϱ`,o!!ʚ˞|FƠOXуTKӓX'6[21u/+Q9_oL}m;}3#:>\(䊴.>L[~1b] (̧c qRGKֈ#xR qo^_BPBA3+@K^K.ww,O`O)ֳs:paq,#Ɖ'Tߥdxۗ+ -WpQs.Lhde* Db nTmMʉ7I.LKNzȔ IȸvTUͲ cz=6 ɚKUt}Ei ạ̏\ax$r iXԠh[t#PeRO)ޓ-%y =`.H'4ԣ74Z;ۥΐ6^a &A[]Mjb>qn ¼ os{ߏcv !R¡q)tu@NѼڊL$ؠ!\I?*Qh{ ^,%ylv4S-4dw_5h {X~ighП;s8hqDb_3XnY -4 !}*pױň=\+ycPQ˃& q/QHn|CV&sR1]PC\VWNE,48Ϥc#|vp ȰRj',xȔM^x{k%⨎ה;^:@(шk -֩Z:SڑA16e0-ئֽɷVpwzRpAvĕ:N@&N؄.65m`}Os0i;;:m+힍c伃|CX6wwDrC>}jxӨbىvt,vpsp3uk/m]ʈ): {-grjɴymU /{ gP[fx(~w5mhg \ V8]ڨpD&hD|$?֒aNWg9J ?ž%I|kn<@;),B>Zn$ t5Ivֳ΀Q!ˮ{j\5>K6Ctg\eTk/mQoLI"NRvo0p>B(.Yi|Z)n"~W01m$ƴڭJ |GU+Y}\i s endstream -endobj -4175 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -k8B -yY~4`j0Ĥ/Tf< ?H։m&]|SaAL žnHd!pubkج,'0 ܖ5‡ƪDlO95~F9HݬCT1#࢕é^<'7T܀,HSY3|̙s1xmg S'WzLne#)*V37CdG,r=|``(BN?&Y---J6,`l.WpN+IB -IUxzLr«S2U*N Z -4L|}FԎ6>X2AϽf$`k'LTF~h;{Vë3V7쪛P0s"BO$l -^t1ʟ,'q?A۱M]!Jg} #?e ؿ<@0w.WȯM 61mw5VD!jƤ.|paz&_F&& {~gDJ=vsġ%#m MlA Xi>SvD0-C7f.S"JʐV3&/0"/U&cV\Y/ʩ)QJ ڋ޴ ibG9c5ZS9@<#dy*A6߫aSIt9XP|ShA:A:4*C A0}]{(F!UXF@~~2kWC*ssn0Dop焂!.5 k⽱ X&ny8~)mx*| +? ;d}ӆ "x5\2RZ=Ϫ-kaH,,TA׼1l^ ӊ٠eF:xr߹t`/)~|?̃8mA \I_DgmQcJ;54ḿJ{;s[t[u_=n9mաZ2 %-%w$:i+$˸a/'n}]ؠ*2h\18w*]hqTA3akU7S ̀5-8=AF Tc1Ρىymbbrc~o\AnjhGR 9u7xΫKd!U[3hVBQm-ZfMs7@0w-*•P+H`AecZxeo61N;o*+BAkT|,j̫o 0`w@t.D2dFlěH,UԂh6 2ۅ;RҌJ9r]endstream -endobj -4176 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -홴(V@suFꋧW٠7c,MlsnsnRzxc('OD9}GA#AXLMSC D9. ^;_ñ";bG9_!?U[/_ >Б"G_jGBvu-.~TDrK,QQ -d2+9AC+ j}^O#$ -yz%m~RLn¢ÀWQ5~E8"m(mSe3LlLeʤ)AH 3:P31\y'JWQBˌQy#EN&K4r -y6\) i;ŬEk͎HUlM+AY(K"Y qiC~cӔ.a`ېKh/~[5-IMbp-@0`Wtrj1&W)@$).kZ@4`*v|d.3m1ydzcK4o9DۺkO9 {4x@2:6:,B#@ha$?@xt24sFx~UO']3벧uW% p~[+}bâ>hnͶn\0ux\4LY8\jXK&HᢞfKmQjg<( -32Y.nPl4nFwg-q&0)0),v' -MNh3a[r& fټZc Q')`pqzyUW2zl_X*xcu+Q;&}eE&g7Ng`o(^cxi>8%˼:gÒze7=XOXB$Q8A@5@ӷIFO U^v&jI^>4 c.A-SO皑16sR,EkH&g+ i)6y7T_(GŃRTVG8RfM,?',0'V$c8UP*xFY~{QMJJƋ6O$;<~"*&sqscp}ʩ*ZIȵSV$Ƣ^/^S,6ʕ6$ -`N[q6aEc4QnC_Ɣm^,b3ߧp3~$|jOb9+t0i/7Fd򠪁VrJ%Z̆_ٴOSLWJ_so K|s6V튠WOOH*z:ؐEX-UAuHP>Pө;1endstream -endobj -4177 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -j zb.š!j@LU^̕9-sM3N.=Nj?okq\cqR4^\6~)b"G.vݗHd00DIYPҢ-M:IZ͛4?p+?vt~_=5['#YU_kVPH0PT젎Rwl |QZ[x0ӥړU|(SLSt楳ZAM+L8+T0/ -Mtmc2ukQKhN;~ EfpGpv=:U4NE&2oHf^nxŸ| Mn-܏Kc b02͡'oOf[?YH ˼;"]ѤS. Qg4"m(5%a){Q^F%.hS&C,0!Fokٰ),+MAwȵ jX.v/hkoT"4<╭6.2>ܻZ;BmAc3-[),mi9uy>y,G/H-Im_ Tv_+njBhY.*)BTҨP_銍µ:}Ȁ1Z_)厨3CzN?+2*D,*BKx̽p=e>ۏEս!'8¬|hH&rJ&f\c(˧2dE f[r=}WZ,Zr >Hhnod/-S7PXTsCRJm'Rye7}MihX%sζK\@' U"X&ut4&j%4_gsaϭ>O}QWV;0"qy5vx5'!̶{YmVJhtVn]dL|Шw*ثʃ]ֽ̄Á#-n>Rh#Eg>BAfCK+?IJгTw]E_ izfWA\?|{&wݸ6!"d?ʨ -.p WWbp&;R"qaɍGE8{m9lT5@N13.m u)m %wM 0+9S;$T|2#2Lc -mTg_h+B Y6/VTz}~Zendstream -endobj -4178 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -ǰ-JC/ٔ"B$):vtZyCt -+ڈq=oٗ!wju L(ۣdzn6?R~US0BZ{RϗPӞث{ >k. B_jhBg^>0KXH购hpJ!6@ic|cx$e'5?O"AEt.u,<$ `wLƧ j@ZВFHm=.4I9gIoDpĒml))OQ:\cbS G<˃}G~}:7Zr"4Ϥ^[ROK LR8[}*ʰ:V}WonUاKuT`g!Jf0gg{z -}HwK1pム~y̺KsbI4YbtE?VwǏ@<˞+ak&ăzQ7AbWكT|r {Z#EcpYcC| &iM_|M;!,n\ YT'> j -{t :5:OJf[&֯4ht (Q6,v'[~<ہ]J[_2gqǥ}5DUgjC fY|#@0<ڍ'^2͐tvvcB*7F%U9R&FľǐwQ4fЇԮ ;x4Lw[8lcDP ۿ w{`یFj]}8Z/TM֟㝃9%$Dս҈E,5qrXQ~2#I g+$?*ߟ/r%whYjڸWyЏUr;vX,*342]D{>0ʆ`ݒ_ /| Lϻ 7:Q<-OYIn*ާ j߬Ζ?,;SgFa;TeVtUȎ<r[Y787Q$įiLvKa#B52أY.sű*DrsM *ҝV;L1_bvbp1p_97VLzt0X!BxM^dN,aOǑ\xZQĞSͽgX*_N3#`oJ+r 0l¤xq~gjwDUX2XxcQfGgsI(Wti2h9:<Ҥ-~]Gr$ -rJֿuQc|ԋ>k<ٰ~p!/5~p$א.mt%endstream -endobj -4179 0 obj -<< /Filter /FlateDecode /Length 864 >> -stream -IKDCQ-9-ueT=p*oS}sֲ‡urhl,:Φ7 -/35Dqgzh8#ɮMHf_Ր9G.lr?98u[tS%ס#FE*`HȽ{ax4|K:1Г_pCș4^HM NNN M}u=~Nک aS8SnVwQ LdW+~ YgVRt;P3k'?G06ܣ޾.6`RG㌖<+sBUM4PlcAun{BVf2c538Mnx$L'h1I%])=_RBRq qv֘ICCD@cmOUDEaԉ "&WdHEiZS}D{\)\6AS!$ խ4>EE l^ۗaΑj}pKRg/t7s)5V?Ãa -S;4Wi5=Ieuokb4I' -ߵ0@ h6Ɗ( ?܋Hn5fJj}<:QPwFGtcjDiR[^QLMN;~i.;_23x[,o> -stream -/I֭(ka$0P\ -Ywt `Xz%oU -x{v1C'!`6t ְXו-bjN?uۈH?9T([k4V^ֲJzeG)e60QH_e?{ic0fXϩ !?q45`<и-b rQОo@!@5 s`_)$W l;huɯ(Z7k-f_9vGWx UFQӽFŚ -*Z49! eF@גoRIøvgO=Uau :@<(g:UDZ˳נ2X)9%ϞKK&QN!(-ҡ4d+VggM0Qu(b=ٶPOYX_;./LH&D (^KbW+n9~lcN0tV:_W -3thOh#=>:cb 5n`"W~ QAXNٹP%:7,fW"lE~ݝ=h΀.`ǡK Gk<8dU>C){`wǁK_6t/fVLYdQxD4$o^{2kǴ,oF7mw:x kS~&S|wT8ĕkc//S}l҆[LJ;?A NQyez `~WA(_'Лu3 oh#&3 D`.̸ukOk3)F2Ck7JF4mpU AZH* u3 8ϵpNA`fSP5{Yr5B5)S -韘a,"uugp0[ZOmH b;2##\OC|OC_ڇ*+jOt -f}橑EG:SԛLxlW2^i$UT6mmhf 4 Yx Jzc۪"fx8Ԍ;'3~#JAAvQs0}zIU|endstream -endobj -4181 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -$R[G/m@Գ͸4~2A -`jG*=#oJ -{m1&HkS!lB8y1 Q0ރ7?|f쵉P2yLS|~zT{E]<ُf[m?_p0-vYwjک\ñVFtܐAj+P4FfnX(6̣Љ>=>TL4J0oy^8ZQhsGضͽ@N$"z~0kխzoqaB6iv$#Lu**O(dO{T6Ze!zT!i.,ek`'qQ"5d-`A*H#J*fcr%{UO{IFX|B2&P5{+ׅL7BEIHJ^=FQ+JdDVq3Q·sQ"=Z+JPn`#,,X![pKN[ Ub1yzGOd--t ܮ4 Cͯ;d8$A ﹱ>Lgڂ 0qts39dJmЦIR^K3l< a߳| }ܟ/i{ܒӔ_rc,h&1G#P1 )vD8%th鲙ȧsS}ޚxdA])o,AzMʤk!3opÇ`@Q-WƋT5->+ƅ(BKәOBKïGA)>CemћTijZh7qO{YiGv{JF$BZzo{<ТѾR:@{D[a dՌdrr#۪^UGWT88f(f P&pN̺`]ŗv> -stream -lLK 7o[Adl -ꪉƆ S psx/I~ౢ׉ep)`Mۍ'UF$t[P\*gGQOќeUU_0f!b,^t9KO-C͔:SW3.(+U0ήAz'/GkT?ڹۚ8MMGWٓܟ%wȺ4,@Gi$3K;@N_BdrS-Kh=rA=mg"r:L2l Gn&1<~gMs4:rj+-i9 e$66L]ш?B> Q1)EVseTuyphu{gbB$iֆSl\Z9 PPVQ8CD*+.euCw8[U`N|.SUM42#T!N0W6deNW)&QpOG "<~Ȝ'o؍_D -]|>9e]jÿ? -_"2I`yTwܨBqh(Z&m'srIDaQwLCh#Kx>k˝K pۋ < -ܡc' 9eEnC$!W"k(+{ż&"^ܿAb߭}vUYK*Ĭ/YvynXJgDc_ pGđjLaJz>bC7u@ϦsՐ#6P ZD&"˜Y-1'hуD׼+MZ卞=8ejL<[\ yMұ+4~OyJA UM9iL3y(#eL}.O4BD憥*Jc -U<SW_l)b}v~Ё}h0S'S˄ŀh}:_:bভ,wH{sy }g4#1^t|NXdWi.ˢ%0߫3Sk0ok;3YGUTNViy37e¨W(ѱp/ gA -,F?IlPoá-c޴!>JPfjQ#Q͙':` hKfjӋkPƳ-٤(]P5x| -I#Ҭf{%&ִT:*5N1Az*a-]nsc (J\i <|+3VKg`G*٩KxlUendstream -endobj -4183 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -`a='['`f7"$QSB};@-ңS]Qxbs (`|Nexi.ٓxI:U @~zٖSZ4ԐlƜ*ݹH >NH .? V8EHA9[U),qe[m7A/F]x>FJlݭ3ahT9INl~e4p}u6 xȨHvune8ٙ5N%LUtotI텡pNd7_DAkا;OoH粰֎~v,أc#LHA9$o:-]W#*t!V[ >xUX`KmfJ^Af{~!}CRPcl؋Rl+Pi3;;A4uɮ5wh۹1r17f#?МХ;, 6=}?x[Fc8lN1.Э563!!ŒP=,EV@KRl -Ϸ ?:*R>+Bqun[A) G*EUR# -Үi>](/ -x!)uc`gxOCK{Rдg.Щ k$x6}=l"[Q@=R9snUR9zR4UL]Ͼu]`ݕˢGn={BUEeqmIʞ4 <֜MCj;L=b;d1Ӛ.v-A1UTi^"9Y4}BcN|KzpD%{ YyuèB1 F%c ! -ʼJWmׯ\*-sż4 V;whʠ0FAXx 3 rʶb1l7&kuQK,?l唂-vۚ;tʸ4/Nmpus4_idT]zq xF@-W9N3-kɟ&f橛|,FjL\فGV=p9.)ahԢL|m6*v xQ;Z'tq,roG2jyC']nTiO@U.bUj?;Y> -uѬK$%(5ϛʝZ+$< x1ׯirO;(D:y#T=´P;x_B>Sm/®#oDD ߴ] ҄-9J3~D%Y:9XXqmYǶ3B'ǯd:=#TQKԉ$vO&*mW/7I&]h3 '>PDc4a+àwsLVLgy=.Uf}#OǰV{,S۞wW@A_(2T*W_5*( -nUuMx?>Ł0#: 6ɦ^D`,"&G`piK7 clE0J!ϨƳ-АX -zt<&S@q]сr<.VH.UK[n ۊ2W޲z0.Mendstream -endobj -4184 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream - r pKk.>hƢb;@JJue.,#4g6.}>2:E's#l)O$r)5ҁDG"j@zQ0MErDl[6^0KyAqU1uЯ8F'fu'ѫ^+*&JZ0m HnW9P ehoآL`XK3PKF.Zr6Uj{Dr`~"ZuIu-L(A70 '>GS{Jʍq[D|^Cg&#6#ulm+@afr䜝+\|>8_i't {P+V*x7^p-L=Y ZXrw}! K/5\XҞDٴp}Qp>"Øs‘% q;71,r` -*X 'kûu?Kutd;^x6O c%_6խz->q{#+GbuQp^;f|̥P O:g| -wrPjIt>4dѸq-cJXA įNebOeK.4[)Z (&Tu`ux~|EfX >ZìY2(RGx{\f\%*0Zݹ5lM,>yZ5Su (t^/eܟN+Epcިa`AM[+1_@4,j%6 0|wA~s$K }-u2,UGJbZ'%'K IM (<endstream -endobj -4185 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -{h0!᭦\,FV,k牡Ztxo| fs3A]ǔ% &{I*/ٹg"M -%Q>XwJMm]GY -$Ǧec&"k{ I9Vp'nuwTUƛ~Ed ,Z]ܲ&[9!t&8Ѹ C'QY/!Ht([cuL/V [iLaQL?x.)qTjIU\{4|5fJť}'r,G0xAp0Bo(}2q47D̕óe3Ǘt^ȓbO ޚg]Ovw 5J7%HXtDo`ۗ%G$%&yhY#SSg򹑣7zϜWZuz@ƨdH"n3fXUaͳɑKo@?s0,k1㝝hKoi~ -9&KtOu8Z?'ybdէD_mQon(?è1k?ɶLɤ4LB:[u.&^S-OsTufX4bl[Y I'k=0֪-~&#,K?uv"`b[%A}[3d4Eya؃H5,@8ɀ.10k0 yw45?/=\rhR,*UsA+=Z'Δ_`qNmF| -P_]acI0ֻ b}_L>|2)2/>}i6<^wZ؅֦hQPh+mnhob!YRvs?8 6*IM ̇^ 8xgޤendstream -endobj -4186 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -D[KLHCF%g+Ըeȧ ehM-[6%~N.6V-൐ _1E K9Vbrn0!NM R~gT^kK7kwxC>nBK[BH ?t-GS>.;V9hkҘoՇHn΁N >AUO:7Z>Эeʻ+e0"`8F;{-\`Vѻ4 yuP|>Fj:,YX|cADFjWW][e9F}7BȌb".[y Л~&>$ḃӗŞTP) -+8Pb4gBV ib˖Ep-(-(FS$6 F7IY@̤8Mj!"@1ze|)% -K#U -/ ì\qg[i! P[$燕 )TBկN=p6ʩws,!a\} zbA`-JF&9Ugv -~@o尳ˑcNx/Y.E=Wpikr|}P@N׋$Zҟ4P.I87t8|En A;B - -‡`jHN {'l6"?鞖Ěkdܔe`Qr]/|w -_-cWThmGդT i!]ˣPI i˵* Q&|Wt@ԦNY*Z&N\C\E)ycu?mX;{m# ]s%,f~lY8Od]rԐhϖO?o@1Z!Q'm>8k+`^k_"#*zUXBV4'eZu AƭO YdKv?gC]=K\69loǐCn:S79TzਘU-o_̂py"h(c4ፁǒG}>t~A&JD-߮j%wߌ0 v_g1H+Ց6yBC Z`4Tźg8J@Lh,ǤIW&տ^[sGm΁8tpτO <G`y뇽jS|7݇OYt][R;쥖 1`R-13$g|-ǽlߍXCǭ(FFXDX#`d%Skw_-ƌdMC5k_؄M`0?3D4*C.쥠dJendstream -endobj -4188 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -jAzmld)Ե6I -1!i!.eqy94tk݋e;K?J~MaAAMa}K 5qslR3t'q'1O:nwu%6 tV&DG 1Ə'Ո0tb'@XXS Zp}@qxHϒ'UκS&[ -y;F3":IRtC)T;wSi 7Xv}.xiOoYXք́. K:jPc -k84 D$xZua%̉G)Imua#)qW%[qܚ죾ij -9Bc:R.ya -M^)gGgbqu Jss!rje9`Z"ܬZsy>Y,{̈%.6Qj֘ -GPLɱ~ʛ9OryP2wg@/sAs::F;-hTIVu=܄XN1t:gqg#JPuA+Jv;LɳF_‘o&Ej6B&eҗ`( {/Ѿ=\X@YPQ{mtG)[ l$u˩;Y/SI^5d7#&+5,rooHj&κO[r5"ME&V^M!aYʹ|)fR'cM!rhs.&oQo;A.e7'~O,(9U[IJFsT]RƀGq\Ao`w* eZ 8XC|'d!//S˙-7/:yx%V0ȤJC: >>r*[:3H-F2Jg&IEn=D#o, k:\4' -Una.%Xm0' -Jx.tAгKz TvLkΟ>jfmyu{p}u{ %jK@px"!Y։֘l2֩SSE4>&R%KPZB4~Q) sAe Z|)A5OثA վAnobξqsSq0vW!)H"̤mӕ{buȦPB hX0-1k|0K`{),&[֑$ lR.Nve#Pjf[16tv-͸84L6x Qk[X|zg8FWs-GFkophl1 JLvwԤ1D'#Plս'9QŎ}P{Ph WE7b#&> -stream -(Uc]{OhZhlW\+&P"j 0=&"OUMK#>`rwc.DIXUpg{Kf3K,$ ؘ"S1z:>v6Hc9$"Bual6;ŗwkw 28L8x$vJ|73D+,e*+]5e|&FߪQ)( g )c𣏕E_f%,qyϫYxhǻ$澥ij,rp9WNRtHV jI\yLѬ PZo`z%nߖ-jhj*CO VJ}AE@;:ߗH'0YxtW~;ȶ (Ԟ\[1_oŀkr~2HTL}ܔp}Wn"BPqB`6-i;d6OK-S5:J&["GgsUu8 , .YH+=!9+6,VXX5+uȠAyY2Gcwڷڐ'Rn-w(EP$b{_Fvs9%jK>Gj:U U:vb %aI{y(iyFH}?~A}&(ae3^Ty K\^><ѳq8GQOX AYuƃ -B$;So/I֖Ҿ-:q~;Nƒ3&mxJS(^B5l%@޷pCUjgma""JRӲ3,hzR#]l^r@'C `i-K`Uْ[hGJ/IS [XkVs=;rHtIyHlʮD|e6z_WclMolY:0@V{_,KmΆ}- i]])}út`\51)8 ܁ܧqGWs߾[ *5ƙxJ;\ZO A>'IG۴GL[sXOGsStSQt}L3y4槁sbR(L 4GZ!5i]q5#r3O&NiXK"&aT_z!WDCF{(LVr%O^ M%~1Z -䄳6WYtM\?׳U|jҞ[X7不ʲt`ZQ_v%ҫxGK)EJmΛ^bar7T*Gł &nd(BvD2ǁRbGtHP4> -stream -T^yl޵BpkU"7%AJWʫ@,3P2M\eS H;>L9\E[c^NY QDC]a?ĩI.XE8ݸDLlxUy7 hiAގrF'_X7S ):at U5#/dԝelm -?Xπ,g&MBN%M.j_ƏfO}teVkq\^ao}r[˙CFz~}iVoq_sQ;^z` U Qmc6c6a|!U7)]6@-׎: -)sS.43XݒP(7U L'K-OKx%g"[/{96bdFhc+RR׉y t*'owk[A<ӵpYNdp0X8P1h^VYL{k E0\ _쬙)z_yYȳ vh`) }85Niq> Jͺ4+0t$,jIXa("/͝'1`DƙM\&%PKQz `/[;'Ѣ9k;jD˺ -H3߱Y8}N@sB;-NybGW\/1ȜLgq^3SH7A$(~CrK;D-qU'⥪rB-04[ vr/FFq9u }G! Tcta0BaFGKuU|?5P8oo -/Sv4&y1%X,Cu| aw-4-)$/^{Q?c?-HDZ<ܳP1X;qΖb'}k=fdzɊ@ ^,rhheНgeԔyWhl Cݓ^KJ)aQv5m"xډԷ^v2ҔHR=-$kM^m]ef 66uMU8Ly۹,ͲF leFnYl`8CCk6ZqRkW|Үﱉ(` Zb-J߽`AҁLfci(KY E߁<%M1-OCVm!|}Q.Ĕ# o,zfRsE"ns?0Dх()/9:& 2q:Rg#;tʰ%t–+.Ԟ@iœWB_ -ι x'6y@su,Mn>k1[7 f ۪Fendstream -endobj -4191 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -_诖lRq\l+}F&CDp>dA.ja"pIߐCg=h|QpÃgsپ jUo>; -ԟW7EU۩&@MuŶ5|cpPaArB@x4D'4e1pM>+i`Gg!PE]\[Ul5ѩkɶgٝu+Td«B7yl0yu% -=>y8ǹ87qCK2sQ:A*lOm?}!L'kQ#@n# QҠk "^] ~02?ɠL\D҈ghQ5I*ǫeĮ%+|l,; q>}cE~Xع ?r^d,KBrGM-™9..=^zC&` -j⠔I -]c3ށ&Z2 YËތ җ";R!Lu)!Hev4yGi/fN['q)4yuqs#t( =T_`j}@߳9M2T;āfWpg%ݼ2Pր7(pEԳ@#ܲ&W ~ ;{8Ts]X!Y k{$c հpPd@cذ,oG9 -\CRSWx Xa#ttHl,D):Nm6uĺ0,M} -̀Wpɝ}b -FQ݆kdF#Y/7^ 2 ?j^ȑڼ{TCwؗ#fESj4+AWtAp^$)& Zʝݍ:\ب8ֆp%)Gdt\:(P}t\f,Z̕6Doi},$ #h H2[҄p$'18XoES5憲{!U '-tB=T, L '|ɣijym}ѹܠ)cǏgh3f~JOV0f)xXyT\IsepB<#gT9hȿ'yJ(,֓eDܩSkȓFڌ؏u/? XK'3%)5}Y hy8Vtߜ&,7^h9'55 @vۊ)1OWKJ_2Ħ5lxp~?ӍE7OvSL#Dw]Thi\s> -stream -3ElSciˍOczҪǣF [`C n"2<5[ݙBHP4&V~̪s"\(W@􃝠zI D"0b:3Ѧ0ή+ӡrϫXI[M@T>Ÿ D#IݵAslbbP37u} 㚹~c=2*>Hv9]5u&d N^kϪI5u Ik]ŭkGKDz"Łl]_uW/U=k¸:R,v?o{;Cn0Fbw<}:l )$-rᢗLH03e@6&>)iDK\+<2mکvuMo.vp'r0"Z%2;;CTTGgoU)8~YGbE7;* `X ?i4m#L\ܸ/iMk;ZnC`ee%/Q‘ܓ:i5akU!w滧P &J+M5Lw 5&9qsW1jQܰ-r6RIjkg" n/^Fɽ[ -+"6r&xFoM_ - þ9o | -[%4_wC|qAY>3ӲT UdnͿadR;3=8']bp4n{ECݤ1ܒm8ZzVEYwGЧ5 % ߎF#ߛU6Sq܉L]:y'8w`HB4# [-&T`:Lip%5w>h>,K"RT9*8ǘZc/pO2Ȟ -wQZRY%aP7bl.+IvynRC#<-,rZZfӹ0=\1|h`#kC\j0;Cߖ9 -z:UBU$ͨGŮ2]ALHWD@&g!d¨)'Dyy UJԊoS^4Ir]x:| d~ɾHU3HP젒RpBEv/>HHy?q?oQ{~+fV3Íey̖Cnؔ^V4敏*@BAS)d_WSOɵʟ5d& b9sv #{)#~e=0(H0=RKͧ*ӑJ.`MqYYB=WA642˃9]򴔓 @toaNV"pOVg5+k6Qkh+,Avsz ρ%;{|wv 3/ Q U7NP`mBՁ|U#TqAWyPp8wVcJ٭ECGZafقAVYýј5zT*|^M$2B9r,]2"gKRj}d˟<8eF)R ׸IGs&D( Y,L&DP[\ -KwQBc1ã$VIV1,fQ(X0kc-BYO8go$kT{p?lnfKr$VvE&&[Vdjendstream -endobj -4193 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -iyGfNwY,zl-~)9{,H"ZqrE?* \}SDews:2OS򠙇2(9}NL],rY>"yH4+)޻C,Nb&UݢbicWOY^yC1@#.02o;,Y2?d,R|,lZ@"skT>oQzꟿz ʰ/,Ts{h`-*U h7?~P aRͱD|?azƜX rNh=cL*4pNG_ٟjppg6^b%#8A-0N_]gհW雀۞"5QmT5HT^tfLoI!8O"-|n >~"7UIR8e㉬,TӀ57Dvj͸j"Jm0Kv4I pe) ݏQ>勗gtj]DCEQU‹V5gӜx1~LqPXʓmGq -2-V(M Lϼ*8_\xk6Xj#ԆrKXl6_'#a`186S|2UXh!,l͘l| uJr`k##KO: $rjQEM5QaP5ϴ=82:8c: -º'sU$Iv2 -dnb (/SH:ݒ{!5 ]6Gs.K tC#'tsjH qc{6ėAÙ<3_ i 2cS%GSEOquEUB9|)ؓdoȡ仙;]crǵl^:XlhÛVLB}D4:wV*0wyj}=T'Zw<kY::\\*0c8 I RtS:y"8c{g9vwWQ+4>?D_F?sO =s}S "М{lŴXlWWF?&)_jrP;W#(pBo8L/fWqV$ކä`x.&ׁʦ4 9T̞bXIgllHX]#r9H3я.3 +2wPlaC3ͮ -fQf;]cۣ=U.[l=ihO3t։hQ?P`CwL?OSvX6}Fj^6>ї@1IT5'YrxXҨ`-v÷=|B 5N!ʭ;RV3 ő3jncsN'j\ -s=֤xn'zkER7~?ciӭ0w*1YD xsA%}~Z s%n^3 dp)Ob -B>ًR;ռ㢸/JqiSXaؾHV8eo!Lgƶ>>E㗂l"h}o2(>R^l偅̀8.&i0j6zbjiUM͚ujův4ȨgVmƢ7FS |45̄u5/i] fedY=5#nQq %PQ(U[4ZTL"b`4d÷qXlNlGB=iڶ+,ЂEmsphh+"5 -ۅ`sFKg8waE㭷Q)ƍ.Gȏ)x%+ۯendstream -endobj -4194 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -nO{Iz2 ?$z8b#ߙ $I7-Z7M(>Q .e42*_D>[|5I햄ڌ&Al g[DYWkF>fJ|_ M3AuW:$ES2Z:OȝClYbdca,,$bmJGĮ5i <&F o՗'+sF8N.y^\e`7+=L~:Hf7eudAd1КJEn{N4 -|\IDTT%4"{])U!z&.3!vK%jķ6xjIo0T'ПďG[, ?E#rU9q .(tr_cmWgX0:PEبQ ϻСw4p5m d]yP1Cؾɸo>w](Qa&sV(I6FV#lNL ukSσ8aV TJRY}G=M~>eDxvRR蝳?. PN <1[.cbO$?s*L 瞵vGJkkp,BўEE?LuSy)5BOw #R;瑚O 3ԂN닾P5⇯U4ѡ#.i5 -;Uʇu+S,߰DVc0CpO}e՝r+j@Ls@B22q0IGMV{^b#"BogRE%e[rRx a,PކUtv6!imj=␁%^sxjkۄ0s7(u_:VtMf|>`lGX!%t(EcJK7="rɦ)I g|e8\6/hA eZJФ|f|^Ks7 -|xp2&zW219 ->]( 72G\dwLx9 cyJbEP*U">@eJG":  {@MS-9-*%Cr̀||&ݽ Exml4un -ٴ/~;oSXLl`_lUĵ fXLw3,vw.ꀔ9Q+T\z sZ|05+.d 4$K'I 4K9_eT+!Aؔm OaJ|1zX_LORKTFAgG`/r^[f9uh2q(}}{{.ҳ|NP^N(#zw2jߝ.AxGpn)0G"5jpĒ+@KVE3IT6iOB넇M?Wc`K0g2,)Ղ=)c~_x#' 4_0eẜ6ǾNa!B@8p [,{|4wkEű*R Hl*v&CKp6M<$b{qCﯗajB;rp1~[L!u Q;-\8ua2n;5B[51ZMo@$GY˺G sok h˙@dAKt껤p Y\E` Ust@LU.<$Ԑ= &>z!\qD$8wJא=x#Alp0oa*Ch$*YհzVzA|Q5Ʒm!2%ekav{c[ߝ8hߕ{MYKoBQKg邯iendstream -endobj -4195 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -bg2Q[Viu^[NeU&Gvb7a$Qh&:5RP[0 -؇P)Hr3bs<3NdeR`/G=-o;=?Tߺ˧`%Zli?e1sv#a+4 OTGm"GІLYJx]̧.l3F - AGFz8spb߆3^+h28}fj;T\L@MG Ȝ >E[ݽxKUpNo"Y]pCر I||ʱ?g6q^[KJ x۔̋jE(P {71Jv Dž~ n5kwu0"G-L!Vt^0Nμ} 9:dvÐ48>$Ϫˁ!Ւm|A\Zt<ݔa-)=נZ sS^6XkF'ϫփjXUC:M*ba'#K5lzOA+iQ>Xӻ/sSNʻoۚdF%D gPH5Wjʅ_@VmkOI)_Ys} Ε"6x6oVH/9Dħ;КPM ԶVR 5#%@ Ekpa SANz)f7O|4;ܭƿ9:c.ʦDZ|r?+aO$qMMԦ1=~|v84Ik E:/շ#>T\ěwcdx|hgCOVτ> -stream -nul1!uR-p3N7΄9nvbheN4/,DBNJywqSx;5V -/Erl٫ Kו'fhЪo/D*<#?ơ,Rڨp4s ^2mޝumFF9'q x^ C>st){G%XV3ic;x0}TV0cyI\s쟃DX>u.4lIRdPFh^ <)h"8pRI*$霨DG;0jUЌKp2۞PdIB>KH5m؉1AE&[icx+"޳A(0%4HKʌ3ztM')J(a?9" -_AL г.Kc]ezs qiCI= n DZ:u ψ"pl휢KmA;ٛM ? X/f*A pY*eePnSCP⹌}FE9Hw$ɗ̭@6::`4 lu,<&tx>oP1< T~\0Γ e-%Z#ރm=ӭ>}[׫3;V̄g-= CG:)NQ[W We3$W0݊u96S"3{61R(ZG_ 2~\0MW$Gáߤ5\̨w6)Ҵ5@Fh`j; p-Q3Ijof=:l7Ϟ422%8*[ +z6iZמZ5j&;S)Dpg_+`M0&Sh\{ -/eI[;Z\N̟ŵkp/"}!61jmKg\`a 2lDSS7m/ob%I~:W cPXd"]6̀/.=hڠ) 4WQռltP2|$MU6(N˸ "Bf}ڵ[C!B qtnoBcQoxvvE!kq!x[7?* -,_a\ShHiVU=Zlc!Z8_KLJREJwۣEF%RAtKɈNU.x{\okg St$6LZGՁnJbT,{;  HN&bmUM9? ?,zt% >Os499S/%30px1J+c 22`nǟendstream -endobj -4197 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -b)줤 Nqqq>ax[Dox~fGay+]n$eO]a;[0CXqy:B|Ԣ6KOӈEb@ Wt.o Hay[?;[I|x1"/4$RVZq~= -F9/9Қt#ER˱ ˴gW>*'E,_ڑ -fn0Ô RVX~Yrɻ~dm Kf -Y1SyK$녛Y`C#\#NQ1Drp=dbRx,(cä';uꉭs|\k$m(D݅ YJ<)jًy,[JSj LBÉJLT+DA^KK**@,eX)VFJG3ѕ'1H]7<{aʰ]hg5 &?*|YOyGa̎u~$N -%Wl MLЌEh[M;d[Ku.K4qMFV?*>Ƒ B]\?24MеAڞ^6wE$ɝ.}@݈llAXrlFbi-M(iٵ= -MzW :B27Y'"~Y3+4NFFP$JS 08鱻N=Udm0vָfbK_m d}!Ř -0P/:v -5(;;dE+P8'5X][*0oXbw#FEatKg>F&HAZQvyYlR 7U`U)؛Xh6Mme8jNSK9ûoAa߲l 7v c+?csݡz,oDh& @_/tV1[o >e;R}uĄtA,o3i~okس?gg*Gy&Y)8:} -bCN3cV@&ub_ҥUS(.6)~tAɢ aw#,` '&w5^p;_7>C CAszݘuֵ@|C -B@0L$yC%75𘪖sV"{eeļCv{-7`hKO=h bc_詈p*""c7v I/$ gu_R\fi>.@yűQL!| |ra -)[e3` -Y[bZh!Z/] r'yߠ%SJ{ ,<Ђ '!ݏû3Ɓ]IRf7x8z6PYEvlPH|iNE(1dC6C -KdZpFWY}f4AD( LQVaĉV}-K֎%:+4$AHzuI*ll \NL4FCeu<}?tL}_[/{5:ٟC2;(mcbzT%Yendstream -endobj -4198 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -uU¹AZdj+nECyft_o -R,Y ] -C:zz)M, IGOP5#;:qd1HQy~@*> iž~@©k8O<Jq*iH`J['o:tbХLa+瘨GJ%7a£P~(Qj\n YV}"/ -_nRd -:`mLR>ƒ)k9z\-LL\pyv ii*\U3zZH&vHJg#^V=U1@eWq(;2ʸ -h8ct8 _!M8 w* ;$&DJ7s{9?11QzK6;JJђ}B"zfqS! y9DGsQ%xYw@BC[T6@|L,tЍ3@",>Y-@r./SYa87fۯ΋ao;e-1.E)%H/?K ֘(sI˾ϗՒ6LpQ -iaƷ](&nYSJ23-f NaBm"Ɨ..+vӘoٮ}߈X"֡1Yw+iX:y^WUyeٟ5xغ;{A{c7H]>3WEr,2sGN6Uԣ.dRr&JixsRQowti$O7{h6,Uarx{̡YE#{qɭYXeBqRL3ӍZ`357=h4K@r-j4X$5絡7w2*&bô&Sc-ϋ@?揾? -Rk=*RƻRw86;tyGHI${9T\cM|~_^;2RyQ :[6~FjkxPZ% 7޳ocնd [s  ADL#=VO0OۼD|`)vE߁h¿My17z2.Q?^@ɔVh?8/J1}-~;v>KO\˃k=wWZ 1` ٔ,z -m۹4Pï4adͩo>D9^p;{i'>"T# 0)wAUg<&W(/I~dw&5t&ADг -3lG>ѵ5 -3.|q$F?bPkag)TDOd08BgQx'8 ߅"hi؃ ӏ ͦt+6;m sjw΅?$}d%WTHXyɪ ?='ηMYh )0,F*J9PGtR]38?/ד֯ -* -46uendstream -endobj -4199 0 obj -<< /Filter /FlateDecode /Length 2672 >> -stream -L㎷M*' MEj8 kmĝn)M΀QTV=ihaR^[Ub:>w`ʧ&\zx\ap/h ={Y'y~L7m7,M' >k&1oD%Jsi|oQ k P6SJ}hp(j0Y;P!ʇ+mS\)v}pCzae"\{SBb% onqh -t'[!p"OcNYt6؅H( c_oT#D$p0Uvb6͖Jt!SF 'Όff/SE -`ٌjL_NRƕGlK=[ܘ AߧYx쩒9YN5}8h'P|8`$N_ד 4as 6h{ O˱PZc r:o"2+#f*&+h)Ui :Nw$!gK|zKmpu -wDVRש]|p}(/'A+92iΚ#8tO$)>+yX!x@,wIV0%*'}79+z TqvIO'q{C]f~P^U |i7h.89uHSrmPC gR@mmt+Q&2Oi.r#)EYr =w⠗!q.@5{m ,2t2QI=P#qzXig5(Pmy}! ǾJY -p#y¦s~Q(흳;SpڵJ߃ dC^-sj[|Ed[Z=M- k[i8ϣ(/ D?ÿWbԳp1t_.&inkS3I wFl|@= >v.&-4:Cra;ֿ*_I)'Dt{ʀ԰HYS"Ղ;Ubu St} )Żqu2wS[&\ FJEP*x_+..jk(߬4?2yh74~ =||*)wKCi-s ǁC]\Mw -xMuk -T^QD`߱ʽWcMRŅ|랢`9uɤ䁗1.HtTmTj%ɆqʝH98UJzXSJk@$paA:;Cׅn\9:Lc} R* c5ǭ)OXKR'IyHE8endstream -endobj -4200 0 obj -<< /Filter /FlateDecode /Length 1184 >> -stream -<&NdbPZF^-@˯VZ{&׌).Eg9O7An\Bl=&v5$-9ేOƙ_{" x+Ff.ltGt힩bcGW m3q}b 2:<5z CZ8ڡj9C} 6cr:g:=!)Yo%YG9Q mP9lxtNcDT<Ց1Ocd3dcWP/ji5~7#jchJN<ٍOUrTR t6 (I 0}y=ZrK}X5 X4:exN RϨ۫Oة)Gۋ%`$)]- >۷0JvAHqx}VxrT1 sO F9W$;r nZVe8RN%Zٯ8ߍ{q(+ӯ1˖.dtrk -婔*fm Sҵ4'#uWW֒<DQI._.Wfxu]yWF=T.٪/TS").=@"`J:y0;,˳Q Fͳ<>F - (W%+K蓠| Oe6K9shM|!@MUZe&nr.ehl!E~8 :i+';q }L]]ȧ '(AOvj`Vp5x&aXW -0# -l5UlXK]9 s՗DY3n|a!΢%Vvrnqy31̃;29S9,"6[vjĽtlme#ǹJAPɱ??ZZ |S(My٣-)|?Ds 6uE{FN3H<sޮ.9+pHn$!7)?$]KyMG#Ӥ`WņyJ@JaU9G)b,Rj2cg`:#Y[3V12HZQvendstream -endobj -4201 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -wMɲJX9X7pivUꞛAϹ#G0kXdObI_P\eyƲc`+vUe`eUP:N xLn Rުʺ~1^ǔs~ڸ+۵d*|ԌF Q:@FVoMY,⥦AkX T Wp5Pӓ9Kyn^VgV}f/iR];'\FD5-:l;HgCH/6TSB+#}WNG,'?~0D2k6*ʓi$0kP~eop(M!ȖȺ p퐁XA1s&nfy`pOge0=2eaGH9b9>q`K|nqn 1C^r-9P0&{zu8 -`9}.)_voupVJ8%)FgSʐOgNǍذR8 -TmJp7EQ[OOh8y}ʜ쳧$kϳ-I0C_ JئU@QM+L=HFӲR.lݷ*r5P5)L!obb]ɜYb!S~O1"> -stream -g^3, CUo94f+eSA'o去+,ۛٴf>y)b@ 3Qq(0+Q~~57ͯ 3U|96+D^+Z1;*yn~/nE:G+N6X`G*ZX b?xrëD#n$(Gպf0HA$(YYFI5$ cR&&WPF$ӄ߲SMj^s5&\@HD$$^(?rG>H5׬Z*0ws;HhŢʲJҳmL_F/HzJgI;Gm}B=BQaUHRXBM Xt@aka~/DgWsRA.|/$z?4X}]+)a=5 PiL *0zV 6R^R1.'vsdf̙φş#iV\Zv~G}^Quǣ](Cm)WӻuY- y>am: $':BwOvVsz -5D Z.7X|6dbCOB;t]5=nP,y@3*P bm o6J*4d$bSť֞.Jw`*0# -(4͡r-v%2uҳçNAĊGd.KYgމXݚRA2ExDj7MMDU Xi:LSBYPAQeVZc}rKqqBI$KޅraqyIv_S[cz+VF@'Xw徻?<)]n7pe_X 0n>)Br\XdFR_>sr1`=FWܝAR^JmrTNRbT䅉_7Qm6֧b2 #S5͔/vtGc$]:064{ 65D|fgx4N 6e7nU_I9zR(St^M;+T^9H9IFsooИ<ʃ <]k7ҍꟷ) QTJ<΍\dG$BVTNB"H7i YDŠS<30(яk RUgPh0/`sAf)kutЋ+gc[:O)dlܙIvjʼLi>"ϻ -.t!ub>Hjdŝ17  -PzZЭu dY?P6 $[3SK;%THŨԊ"4 IVJƩ1S%Ѻ=jS yO^yԶ35|Xkc|A|.o\=锡#®ϑ`[I٪+y.tV@Hendstream -endobj -4203 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -dR6!C'4Ю`g2c>2A&mXPVdr/ܼD{QXoؠ ]Ln; :6& -WWf[1LkiLn@Ui?BlBXs 9 7p27O vx!>,;: X2Ek/^Ot}JܖiℰgǠso+Ǥ`: ɖ\^,vˊ4t75l`f3>|iJ= sb+u|l<")@P+$f# /q -=4ƾDmNwY{QXO̻+=uεG&J0[ZLYbA;:"ْ Úg:=[L@}-]/85B hnF -`)BV^ܵm2Xn&"( 2 MuC9Q9;ʓg7%/>=A͟-qZsA22oXl)B>ѧ2Ҍr*x-Δ"?)eulbK6LD@O #|h-5vƶV;8P,P \gPp7ꎗ3[cِ~Y;miޡegvXWNհ l-+ Q+~#Ö~HH8HYK,AƂ`BE)͵5*ӷQw @A9/)(K -9@ Lsf텅1 Dc"i!N D=F`Z؁=wQ>ưDO'V UKmryVk͆[;]Mʢ2npA@(w("PG -txRd|:SBM,a"[FdBX}?#F7Wb;c)`?~+"K6Wd<)xQP mfgfD4HNP˟Grr}@endstream -endobj -4204 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -tXFNV2b }6]nv/WO#6PW'L:!,K?,M(%W ۇDovl+%di@ȊGӣtX98Λ*M/zEFCSjX,T(;T73;XU4$iTI8܅dcBdz(\&Z9X{8ay%Ғ\} Nd#`<7^3c< -?MawR$͠y6jpiܑ6^GJ@fdz'""F45w?M"&lUG@#E)(RP?֔'ףG7G piwl+3Ʋ{} -.=:lI֢cT#I"pH6N߂ -qÛr3as ٽD=2gPwc+Ol$yyo jI tԕ̡F#{Qe&ar?ԓhV?}齬NMͷ9k*>YWt̬_:G! cԘaRλ⼴#򝽜ăcU9_t:TV!,NG0RI޴ hGD(8'w+IzQhP3HVC QeRfbFa8Ŏ/*uކEFޗɭȰqk]oY|k{1o騄Yߠ kAŨ9qšhɟY#>#>QY ’L܁аckBLzث]8Pe m T|挕"s W±K(Jno(X눃b\W&Y-qCUZ׃$Z_5 -j+:~3Ⱦ{F9MPN,#P>OlZ*!e!JlꖪU6Y ;O-ߡ{Gyynz\klM&hȌb1m >lݜxPaW-CxWHe(BhJ*88̕^AZk^Bzo`i?]PRnf̡*rރ;׮kJ'壹W8X"Y7 ry׭xkS)^!ANlV]J-{Ŧ- -\=jEOz‘cѮ=g07p?Q:TOaSHς8)uT79w&O,n=2t^ -/cUendstream -endobj -4205 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -·/ ا3 -Rtn=2OWAloHV !\ q1x#PQC"wzk4k4zj0=F~9ͳ[{:Erpf*fR^M!V( -52LMRp"? Lв~V#ݮ -hT&E豩>-xٯ9Q*:ex{Ԡ-+RxۏYɷ@5U ˾G,u|ڐk}uiQkIpî'Uv!r3-O.HJhsfOs5t\:/ֱ;m4f~bGFK_҈3^SB xhVYV?#HD'XwoE>[++Rg:"7 ~ad4qo-ǝ#q= pgtr}[?6pyj$dYo)*9^Ix-N0%()f̦]ʾfF>k|?.v*lOMױ -+09z> -R1L2hIrӼBy~_PShY~FZ+kN_ݜ]5w@n=iN"}bs]޲*h`}Ƙ罹QMrܒ 7)@ĂנMWN>*&XC{Bz|8=3՘k)lOHy|^sJ HUFj0endstream -endobj -4206 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -N7uT0%i:DuBW|!ڢ fmƵQn(i?D߂g!vBeR1p_ a}PG{\?ӂpYFULÛ{+cv1dk"}YLozw'h׮ꗢ>ؒvq ?4R4Z7ދ 麭.|pOy& -\z~&pzܙ *!6S:YKwBy_O;pZGngYT~sS3oW ˆ _sYz: N!]8eZ!-K'DHb3ʨ w -CS0Ȇ{ 40vut0- +A kW*ƹk) ->jłO np&3iM=cuT9`q :3\ zZĒtrJmL]3ȵ?yjhAO64f&ёCZ] reGXsdŧ ie&B d>tn*I6<֨#u5SPf)$E0dUњcѦfb+DFjU֨]YoME 8U +M2cJּZdf%L(G:Ll!~0.霢JȽҨv"8-H);Ptge:.<6$"v^I'Pefi(U_hY&D{-6JI(!;`Lue3Ct(D &x.@RmRL,V[:/$Ypq_hBe{/'?q#׍8@F5y—i }uBwĥ\v -->;}f=K(67eZT5V64(T-JPG41UldΌvd巀W5P}lW9n^(>~;b  KV6$jAW.H{XJpI:J1緳X1dt !8rO4(ʭwZ/")ke"Wʿ,q6o3>yzRS3L:\֑LO~au_~C~g멗@r.wH@YWIbeAZum;ܓ -5endstream -endobj -4207 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -iLt]2y%% EmB1!-F(XaBɴP\;>*ÐE D@IjDNLIFGCrym ըq"L2Ce\JOJ?ƫgTـ!\KU-DjX5}F6 Y=AKG7 ]C\!1 [Vt}Ǟ:ZK5cIH>ZvrRi^ܕC^ @F8熎r$ZU Fň;wm4rzjq5Wє'iY X XM,&W{.Ju"PًVΤ>G1e33o2Rfʹ%+71>%P I,K{bja2jF)\j` AwVziNqStjMwư#f*IR&epZHܓWYi.?/^ a챨(иDD ̘rV as''YVؕ>@]\<$ٞIgƗ0WEeI:XF -2FDq68̭;4R|i1SMH1wPm0]Ic˷k1#*Tz(wS4U9Fe6ƷM+]df[Nov4!b0‡ ѷLOre,&EK͹Gퟭ:2vaCϱVb%MS٥SG>L5g2frFP VGbLaVw.b8(^O#DnsX/ - -@\`p'̳!j &~<ݦ[i42k!،̫#arXk#uni -,c^ncq5# I(N*aOQ=qhG7ט huLc:qW+?hY U -ovmcAjC԰ȡ:+3NK0Czu1j.sLs_ y'}b{`(H7KR(R Ӝ(&ʶ6Y^'i'^}>s:[T$shu 鍴(JK.v/qBKFkBR:ڨŶ; Z -ہdxpvQé%{b~Mhj(L)Y"O%z;Lp&SC6U XB~b$IHLD8NhœRۇ#|ֶO͋{Ի Zj- jfYu6sVC|x{,/5 ֐{eendstream -endobj -4208 0 obj -<< /Filter /FlateDecode /Length 1504 >> -stream -F7<[u=%xn%fDB~ZBqK~Ϻ̢}-GlBS0,_FF>(Ƈ,-)x͸& -4<­7'Iɒl{V7e^kXHwHMu18˵ '%R?MHff-vŲ-yZC/XȶV^eƥIG8¹wS(!{<&*։iT]/#}ѷGhMocЖDI®2 #C aX,$iig^t8yc;onho> -stream -Ya1Aqc u{}&o &}I 2Swzq,p iO߼՜o(#V֭H_p -?u&s -&AySMVjpG٦S_"|\󜄲dM$B ̔$06]"<^SfF .!? 2e^82#z ձr ,M<ŦXศx]#]DEaٽͽ{mXlWx {3xSxS^x<"F:YKfDp4RrP{b$^ "%Tb -2._W1kFC32]DedI\wOSCW zqh˙6Iy_zr-3cL;׸b([kQT굃RU=[ (lN/}%A d9{(?/vOmcfr,F{o#FLjP.BU 3 D:S&@ٳGh`b0Rsدq!$SoʯxLB!}>=yR~~{]:^(1DT;hWɈCP@eR>̿~ȽciEWO>zcRtڪ"E_K){%-ZJi%P-0 )ΘcO9'.{`kbt[E#*;nwT~lum+~=>O`$ ^ X1`J7vr[jj.`0v(\H 0= IhӇNj>ʉOvI?I c'jS@J?oRƥS\#;B {36yQaʧ۪(.HxlA -L2Ιp"{e\ ug@8(_.PQ*<Pa"K lV 7pUg3tg1x][/XH%b-ZoE4XDWdR܃GXcG.vkk ͐7&MMj>?AIڊ1U6uA'[h.6ݓ<^u_m=;S3w&1'ҝ͈yU+V3REb7^_ӢuZ^4O<lW?jזTXP12ԋkpֻNY&}*.XBf6H/+ÂFD&-t5-\s`m:\.?g8rQhYH.1W\{3riGsS4 ^|4^Hi#vN~5 8 Pr+^0~-šuq͓Ʃo.6ChRrv"s   -Bu©zalz/OVǁߥ$K,d˧*_ޝ),gQrmѤ17HBKg|"k:~X\lbjgԟendstream -endobj -4210 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -;2w1UJ&txǘ0~s5{>'EF0 Nd|5g8]/uglW|s -i833I&Mۃt!7 .;0 -d)Oym6S G$PںFpŽ{u'UBqT8\e]xG1T;=W]'nS''ebτ -pgu Ʀsz>qDJj"<抔_YWc ~cuH0dݬt -"SdIH26ZwTdyJ;A.D"pm'{A GY)) +Tޮ8>mԮֳ&2,Z]ssbTGiNq.Np.+ߝFl謭:YuQ'"G,@vgvpƌ;5_%D@ %zOMN_(3 d萆 + jyI Ag؅Qڠ}6f3FVӣ;Jw*xHyJtUzh,In02Apk~BoCᨠ@*K氶>2fʁ(T$M v[{<Sz.s/X[y=qf;D 2!]ﰉOzRւ`-῝~7Kr'ȔH3 5ckit |\Q$!ϣlRA.MuUd>DcP-osH֞jLO?9Pym&DGQ*p߷tzW"u (T7feD 9xG9% TdKG -JscC'yOW~prajjCIM(ږ~7lCdP ~Tu:RFt!'Mg)ycC.{,ߥ,[cswL|a+ -M6K䩥gPǕc&S-CerӔV9La"a&ɶ}..WViəw{҆&s|3$ oVܳ9eSm1ej--]g-ȚjD/F#XN%z,E->u=r&h"BQKچwrEOOg:]> -stream -)ڐ ~ާ) uvt lO?-PJ Le7^Cup`bRne(PO_jy֏ԀKX!\b_ľ=rGj8j/c䖗%B $U,Ζ y>~2AP/!lh-qSw@;ŎYQG6q:fMӄZ-5&ZԶ%4vмSa9L^+G",ݖ , ,#^b&iF1>W aVP>SvrJ5˾D3AqbvR ov50P*U«:ʩ%H^Ψy"T~lϳݩe9?Yǂ}ǟ9):Z$-' cUvbOARJjRPdCd|i^ ~KLZy<ha/:Tx7;6or\,R"Pk/^TtӎJ"3KHc#o xٷYE{A84W/<$aq-;@ ϖMhjq;=:߶ݴ)6OeN8 O{>"xuaNI_sfU;*7~ C W?\'Z -CSB6V86,8@/UK*  y˸mn%kkLJYIߔ\QE- Xòfš>NcW9xVBY%1[X'pAqZ:Pf*Xϖ0[::EW$ -Z%>Kâ[W:'\!'B*Ym,0" ;ϴ~CzhX͑(*Ѿ y1D Sp< S>܆ob -qBD|~.y3OCO@V:c7zd:R*qҎ1lv%|;(KvfxCvxHaVSS*!6$5^(g*{fgǴ"[&sv[ "Ri2_v /[ mxgUc+FLϾFZ 2]n7m<Xk.1; jf}i{EmhoRUf2,\ LWW_fZ﵉FB`V3WNU^䪗GM8|$5zšw瞩N"IAǤ̪UhrBX_Un&^\㸈%o6[MBK;VV\yC ZS4?%s6:Fip-aL4^/mTAA|tFjF*. Kg: ]ޏv|.1endstream -endobj -4212 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -iAH5Isa;O5c1BF*S5nT;.7ƓRi;Ld5;,]8J&pO 'Vc}o8ə@oVeT+^m](@C*RfC9'=D~UB4T.OuM gOބ%qgވL\lzBd:+[P4+%oˤ۸e^TC-VJQ_y_y*$*tPt @_R"{2`!McȞ= ZIpg?@IQ'˞Gae[ߊi;.Ӓu}71*q,TeI)~sTyqC2?@v m;@L`}Yj/@Yj8T2gj"=}s+2(WR\ :wiPoBP/=;QSVaI! 1դuq8JvCN`A-erkfuH[/t-gB+F%UWp/jlMר߆u]&ioDPj- s?ntit@Zfi%Ƶn 84szENvJ`I7i1рEYWG1\צmӈb:( -eMj۷b14--Ȗ2+d07X^fP~4`kybI)`n[Į8lڃbmi8tEjzl0yN_ /zN6KKRIGk7zQӅç QFȅZ</>W(}:HRgGN0艱%&@8?V.ƩFh -3qܙ3CRqՃ~4XC6ج;*2a~xˋZF'pY -8z_f ~UjI Kal6Zf``{V{es݄Z#@SXV60쑙եlOl'jR`_X* yOׅƿmS>/DĎ`K)q -UBS=Bd6TRkI]׭IT7n<Euz<3Y퓉y$}$LI79zW}1M)7G?^-!//Pq<-1$fmu]\ď)8c^i2Ƒ\ʅ>O:iYMOm~GzXq1 Τ7!j"։uy}*oSeMrH,Ur S4n$b"ΧEq&[fW}(f<Ȓk[ނH&83 [77T=~νmfHB?+ WWr\R=8qpry+>I%2KWǛV)5\oءPugAlWF9vciBœ`}A¡:者g]+qun DB.\hD8K!a`4{g{|$Eؕ[8[1r5uendstream -endobj -4213 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -QJH0 UM(h!%VCR>c|H-ٔKԅcrO1gO \rOĪM  m6,={K% جk//{ig[Y*&lʴ28X+81=WcA#7lUc%OFXn/x2II&)w lqN?`(Gew$$KBHFItD+|5XV< #b؝A~t!؂$;zf8T8,[6ꋶMZ`HJt 'ZDG@SAv?\;6UPG{gY<¶&Ouz.̮)xg.4'ClY _TA-a7JQuL/.-BބUJ3 a1vGU/jbL_"9 :eۊ ѿ{j=_{ljN:u@ -WWEGH6qY^*4iZ3Tn!ϒx[̐ -VhWmsH՘` -+u1cƹ6G5H@k= QEX3nN`6GIdgMaJ]S8-V*/a޸0cNG}TЈ0.`'k,!ɢ^d8"Vg6)aܗk'4$H$Tؿ~ck[4$ųzHWuȈ=%^Iq,؎'բP n1Gǭht:s-)wNtSZ /ĵ(lj:2;G&|…O@Wb2ܠܖ?+Pp(_-$^2"A'O|K8PA{f9xX&`8\gT Z7uC<$KA9G@ڪ[ƣk+ mB.;*oSi~=O[ &NTJƶC/+/ -Z"͵nQޥf.\D$B;HI X2i緐%% NLt';2-0+yW#I;`\IͯJ %%ֲTU=C6h19LWYT?h0 -@Lʾ,yBG9Lum#ʔD?q *w]޷ިt_;XM2{~>aVnߵC}J 藿 c>$i.$[ت}0j[UUw28hDHXڐ7k HԺ'kŪuu\Kg,E䞈LAQ 1vjn{ֺhp~`qkcQS1P߯w!|^ИFfvշb%ÿQjWdĞ/x.c)X#i&u]e'i(G&A4Er;MI]Rd** -OyP=s疀'YSjνYC~ -b`?H }O >2 δendstream -endobj -4214 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -—`J(pW0y]* !ӭ֏^i["*>,fMw7J@5ŠEfg=ٻݖ+GZg2ד2]k{"O#y?nT2<:mЎLnTr&( }9nOޣ`R9mԯΒjѼ{:ÎqR{ߝ>9?♱arIcїWX1Hmd- &i({ڴy s6#|HFSJ[ZcbrX$ HN= 2iT_elt`|q7y2Wo̠B@+~V=B,C:)P@sD9ܣ-9 QqzĖ$x]6.4p~7r!pKrm6S0:upR+vw(nɨ<;ցR.έYW懛n33J Iy#VLmj.|sm(P BYڑj#կvU= H8pҎ(,g:ul[y_h.[TŸS%)B~YQatA dOa}B)(ë fv-W.b[BkՀ'&@iJDYd^Qr<}sqnߋqQB!e}{d'j0?hAdX;`ugP(+ƚKFh\gndNWOݕ0Di^g\qzҨxJ]:vCXK'LҶ#fONŊM5o$TOzc3<_\6j)'=fhQݩyz!g+pG<)X]6|8w 9Iv -^;3ϑeJI3GyK@.U7R (job_g6wxQ@P qǬ)Dgwk .y*gaG\Il9–j !/KQ OM2De*I׌mz^PrԒY:qdڅ6 -_JȎä:} [WzFEIѕn0ӆ^L](XhYYo-@\%,(zfT,U$ۀϒ<BPְb? ,i"'b!,^ֆ.#KX5?qꨴY1ߨb']1(6i}Pc#g^`!DWT1ʷ,Hc?D{\.K^aeIEIN؄ 7T-5CSWר:y; C`32!ΫItTՇeI0*^^@'/Hng~bN.>˂uO|U"b6Ԋ:KC0h#u'U -_I9&fmendstream -endobj -4215 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -5{Y&9N'al)^>YG>FDZYbvL$ۡǂ 4??P Q(R6S|\[Bn¼q\33p?9I*- u R r.I9+ij%JO}g״`-CTjs" 56MvqS:)}ǀrVC"1ʗ$Tx^63^aJl#?>y^b1( wĄcVGU}؂LV)ϋ,uͶ;3Ig~ejdČj}x9kZ{NDhd`IyN^V{Ȭ|֩.( =bSpMs-3.auHe3^5xe!@萱J=C'sxVT8m& ~iwAe[i]z3ڔeb톚(KMK(UN0cfD,PIvA4* }4blOV(??7Gj1sWin]1=GLvUhdM-Y]AV7R1-cNt Q#J#UÏ -sB?8<[vDlw_#y βeȾh!g&Ŋ,\5R41|AaAB40񞚳Ie0W~QZtu50oEKsHvVc5#jy9mg8n5$pḥ zFU)ۮpwYB$ -+g|mE e'-K~:ƚ4j% -B;yj\KMK)),;DB߉6IXl^Yo3;NY=iU7Qz;#ր;|W٦c׉ȁ)S00͘Zv{~p?M.я e T - 9q -*#>6؜,;D lBަ&"e$e Ya9hP (<nP^CGK[{4rB8veu ^9lUlFn~I)P~?78ۛ<#l@[+ s:[ܝԔ2 _ܑ I&?B`0oўy0i?SS_^82W0seB #6*,,K -endstream -endobj -4216 0 obj -<< /Filter /FlateDecode /Length 2448 >> -stream -8 ~On}'[FͼiAX\Ӯ ېvK)H"£EpμtNm"<5GǼFn^3`JH2!Yiu SX%綔Pih=Dwc= -fqdDd[uϯQHғ#:h -3mOMNIPΆsc\4e/%72v3sgE|X~uCcE;L/Ww1В ѕ -\ 4!6Tz7vcaB5ºrfI[DVN0ί3etRF{U.s2“Zy`lNj͉Y4ħ \B?/udܔ lEgaaf)$wkSa#1r Q>EZJ$cVԲw.Zkȋ  -z7*k !Z 4v^Qq*q!zҰ>ྖձ%a|;k<,툓 !"~YyUD]\I107WlvUR` AvBfPnd% "]4Zfк:^HټD:ۜȊK><|I w_[`1Aա=g-D>M6Yǎ~.I?q\ÕmG;*Ja'OVE`3yETS:Tts.#P(֒DM輥hs:A':1cQ5ɧ( ̻qGxճI+3&vdEViUɋzH=qXlױ *[Ԝo2~}W>8~WRqSy(Op[@%WV)֊U{ibq7(Po|n4y+<7~fĚbl؀Zf`0GA >ُO o&Ϙi2'3aچ -Jlᡙr R׬[}8yw-]|m$\Sٮ- wl5w4G"4y `ieStݭWU%ʶ.u/ -7gO@wPY`1o'q\Eh zp|$YnGZ$Tmف r`dF4! &-lwF޻o3=.KBn:(5~QNYrAGY̒ڟswMl//#jd޳lх8i}% 55l'W/.EyE-$8:O5,]+¹$"Qrߗ*s< r2L!'W$~^* F[d#$S̺_+R>i=zv?xذF?3^f2H -+aXk|,x!+o-ap& u- DAOѯѲAqL0EZcs$ghڼ!~k@u؋0ܯO."w?ux3޾ -&~J#b˵53,d^Hyκ, {ĦmN&&E*)F@D4Rl_+8Tg2o"MYw՟F\^o>j ->ރ(!}0l@m*=$\:E[ĝ8C㪜E:ؿ[H~(a p@e3{ ;x!Y[8P @B"@7Ï"tv`30|"l4+;K{> -stream -U:s)]Aw*%NoǸa!( KS?C)%D[\f kp֎nWV⛛!(xjK=Uvs䦢2O4u5*586fȘh́`?dP;o`?';-xuƨ75n5w[P]M 4j;lwj͹ ոyQL-trŽVEe_+?g -`cm0xPX%Mky,kT1g^yOm?KaGѾ桝M0;9_::D?)G~[۞1L]%,cJ9ٶlOQ؞X>8>)>RQ1dt7ޑxd|eB]?&^7jk1PMuq 3v+WU sc ;Ify?2Z(f5&ۻ*sݞ?Rp9H"wdl9٘Jyh;7mt:cwpw!i >2Cq3~y&^޴R9M ũ__h0uVϕbO  ]sC4+&:j%0蒲L'/g܎ p aIzi#0T-|gYxMlm߁Tgz>$QW~m0lr89Ws'1s&fCr55~4,]hg:+*y֬NtMeؓԖbRFuo_[:Q1Q#GҰCが׸V毉UWY~*^t0 vñn( a ?(6dt]g-!b΢:99a 2辢0|{c~^tx<7,թzo2^$[\ kJ-3 4^"c'~E1dPp3i)fv̂+)&‰$i+La-[K_0uVQ;Wޑ!cg>?>y(BxXJCquJ8S/ CTcZsh endstream -endobj -4218 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -E!65hT\Duz-w_Q[-117S76:(['(Po4ֳi:jz0Rzf&WJ$NO|U_aU撛G#.ڶ Hi2v,o2%<>F- -DRnD$L:bq7ӬY6gH)Xxb 4 [T ¡rG]D-o8H["&gQsh@YX v?YN"@(Ac"l֎'xZZ1yb%v){z>e^ 8^YBDRv<$ljG^a@aȞ靈 I+,ؚ QVSa@ۃ$/'Yͽ;$u!evhLΥI%I -1y(Oail@>&-@T6v޲ӘV2yB57g_ˆ&ZD#Gt%23h|{nP:ч2;ALp e HvB{g1b^f^4x0=[Sʼidg=>wcyv{gg!ޑ5[@mF;M&*#q; -vzDBj\{xc5q0LQm01ckf:B!n!))M-8`1iSHiEI |ǎm P.̿Ehr5 )i֧hy`p 8[nuKŔ"`\ !w}T+J g*N E-ho(hֻcL>CQ$$~g2 ِOkx%hɈ12L *Sv-.ԩ큰jۏ -+䡅г(d5_'\MhK"w ]{RMӓ]Cs P1Ͳ'SYY,R5Ml&hɅ:p$A$鋉RoHALeG4phhnv֜Bv%FELT]}endstream -endobj -4219 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -.Tؾ"ز+[z7Ta^gID6+E/P/ɵ{f*+X=Bk,UN3`V1܎\p٠LZ<Ӭs2);p1B -0 +jصa9=zާO5-y Dl{尿/t@]]Z†X7zJ;401=] @2t2sx&7 gΧ+<i:K^s:Xv;7>*@^ ^4O%."MHá.(UcPʚbɵ;sU7YK{QiO zg*%)%?dg-: ͒|8KDZS"c02M!PŏfhÕI&f@ ؃~LqZ5-ۚpޭ4ihML{Pñ7*-喅gM3VD-åZ8)Ϫa!ng@I\#Ə+)E9pG9BEoVg T/ȧ@GI;uf SgâL0 ZA ȯt}~E5ge3d'wxgfrSr8]}sD}0H] 捵M-Rd7|LY#搶ҷÝmtͬθMt#jdBo5$߰WLA?@˻/ҡgnj<{'oVI.,k*XX[Z5\]Y[G<^9@ qkzZRp1aow`t5'XHh. ZPoDAqpnNm@b53 ±M(li-}ὮL ?*VN_֕%_QH/ˤHkQW@K@:Ü7cIv -йG:+mmQ\=q!C頊D뿍6d1+⶞dD-bNu5vQ\J\,tJ$G"k&temp.;#.yE9P -ebP{` NfH~J0Ar Q+wdi-[I΍D6ʄ]ƛ&#(}نoAj  UZIZ)уOe*S|G߷A>al_! N`:w^FD2`8~ ӒCYsza-)T[fub0W`)Ѭ4뾖>0* -?#w{1q|xע|Aa2 "/ۃ+N\5&a0Q0i^3kڛ?xJXIEPrT=7x ˶.Ap!1g0+\Uo AGjOSۦ$qR '$.$s]mm -E$%[ |xv(hn%G>VYH$r&dendstream -endobj -4220 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -z5`.}#[f, %5rІ7vPQ!^eP>Tmˡv΃Q4G[H-)ɥ$ibsi[a2ax.~}jPI!ş6nHs{&Ox[ƭDuH FS{89"7/N [PX^*8!/ CT;ÉpJH_0Ϻ}֜P -ތ2Ր\u| W7 -WlBy -KJ}K E!?d!>>3sz$aiXs8l cA1B /=a.F -9`x˗2:s[P,+X.A^hq,sL,Tx՚$RvBҀ2 r@Vt8 -YJS|s)stztLKrg?=*nD }@0j,?RiI%^bbwA$$$APcIQ0 KGdMQèKՠsn7P} G#zbqoP\·Kzj渗B$m&Re+.Oŏ_ZC) \NHT({9^ض8qRӡ3~ 8B# ]A1b;l_ddZjJU$mJ֍8]tFj{9v/>/29_O!Kn$20%^Yz^q;5߯t.~Y#I$~N6:j;A%rJxgһ>ӄ1LeKIꙻgCSWC;4J"c6?35+ȗ#}qrOX8pE3fq`%_)i?^R+Do>ha_{Yed&7vH3_92/۹M׷] -C/ii֝+Z#bMd5:Y1'y׍q-®#5Va0CAӄ~Gϛզ^> -xR!&f{ }v.x|啖Yq+endstream -endobj -4221 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -xa򌵣c1N歠Sy^mp lKABfUaV0?wWc|ñW=nf~Gϝu7t{[b-z#&Ym{|o'" |ӐtQ/3r$}*'Hڂ=݉#|CL;꣋uL=ѡ}#MIv(I"h:&߄d=nQ92gc`?h;i4^+Y'k[sAp {dSP"ΧyU:x,CZw.K5i2r. (- b$}P{_"ov.}Mr׎TmHe0|ȗl+u%R3A3t 6ʇ6_@5|9_MO/.Q8 3(1J0zF:SN [2Nʬ +ȳr3 [Iǚ;PÎmttQW[ jp}+ѱF (l#)evHwE=?n`(w7|d"1 @fx 1TXET_R5~52\%Y]Ƴy EqG*f2( JHzIzFlMDpJge֟qWd0g"-ɞ}XԩRˌmaJ׫ E\<;Earߵ^xa$yхI; 8L<Ո&7ZlW C=~nG]%YytaP*"&91kI8j oe b{֢a+3H_ˣP`rdޟ3yMGCCԜۥt$ 戻)(uf֒䤭ѽ'z D~J(_2]V_~sro*Dv5d,yNϝ8<9'oSb+?25CMbuXvWB7`9 -M~8tDX \W1ve'@endstream -endobj -4222 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -\ol@题ĽUס\e8a?F+58-"yCrUx ŮZj{`"\dHxj5AH# [z gcE(zLz]A5?=MSX"ی>ɼ{ސ5? 36aUG8 iQо1sx U3Xf<1s$#b,",gEEt&s y\tXh X_xQt8$FxHdѭ- #2Hܚ?g^ -i1ϾZh4ꋧ]˿zalzO23GeM%E/j 8kX~?j=&*9#`Tћ.&HbԭsN;VI-U*;(t%6,?‘̲?@7[<碙Ava iakN?.P2.]7XRwM;~}UÈ"Rd҄HmuCRκIn~ c'ӂxw}緆f3/;#tG` Ÿ;l[qN.h{䚭f@g2e?~FlhU!1d9LJT es 5F" v5f_S Ű -W%9 ):0/P-.6w^we#.:ZvMf0LF N?FBLh,B%N<Zj!gM1GrkAd 7.6EԦ$*!nwz~` <4=XwO7[&ee M=#k{'8{Y\kyejJ}u4se5]LPۚ l%vN6C|W;Ʊ{$d6uM38_5jh1 lj=uU)B0+G3&l| io-AjK"Nfmi8>~F㎝Yp -oE$es˿zt>;_xb)0гOMwнJo"ۤXW=>3|z@ʓ=}@hç+8gŬ*@ˀ¡B32 z &І^}t= Tշ<^XFIT@OJ3\dfd\XVt/!YPS#.Wɯ֘*_nU ^'iD`zcG#!4ќ'|qzTL g{NeH!Im񅋭IW֧`=mX uw{0 ;_K$$?:sUJOe>kz4"tb|Ȃ(7/|lk q9[VPy{F -FQhLzȔoƯtJ@IBa<0" *2-˯:Q pu#!rs*uc 76(Q^QyGiQa!>J%A@%xZόdTZSy]_,E~N,G*epIN|v'3Z1;$E[GH@GL_.#@ Exmvj1o5^-pjX-JVRSK> -stream -!wfqpհ<\4l@2y;w7;72\]q !ks/x@D&_UYn!Ͼ+% r>u܋`#Y,urBLńހZL 2je+% -׀w=*95 Їve n _$K~dKt o ы/ I@AITQE,\PJF&.t3FKh%$X2 )cBf-=X^|98CJ}Co;W$ྑf炗OpuF]okTu -80k)^Q5hf\Ndu ?}oyiXiޕSw$;pڈ.R&3"\yN]w%[^'w܇ O6ݜ2#7/G[ -ֺBvmM?>mWиb1!6Q7ЀSQO֋~8d@Za/wI71+blt4bAM BAh.n=̐,WEWfi -Rg=Ye=9жjlhz@z k[lI&2 -4t P漏ƙЏXN7@ F%ۉXT9|mO(T@!ѳMmkŎjX[;zwpa= -*Ú=48߶DNe]jeM0.!QAG%#R#2O@݀*88+a3z~ .;bp;͓eK{ bY'(M),ir;L?z7n$~KFtDC $^phta -'NKdߵs*I@HnRc.gzEm`V^g,X⡑ս)Ev l ` +iA.VHW tH>$0kB؝n(x$?2j!x s/9# ZLܽ^Yv^ip!TKٺ{^]hfNrML 8sԥ|L+;ۖQHT*[fںIlHYl(jo4dTq' -# - .^$gl =z3r? 4#T!\&a8'k*yљ4R8x:LdWߛ6gQff@MGm\K&iqq% ۨn,gB1ya$( XNxcwD2 8Px =p-) ZWFk. G\%.I1A1I2?Q#ݷIoRO!Z0CUBo-`"=}9'Qo"\䣀VA6цOQ1:Cn:+8=8R O4MC-54$9|V[ͨ3b6Iq 60 BkXҕ 8,y챖n'@lb-i[/O3$ :ɠS}H:db"!,/~ R+:kp}F y/XS`Ή+\^@-guO$0endstream -endobj -4224 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -Sئu1'#UMlzVS~Ό>muWIyoO\[gg&Vƿ$]. 5Zo -rT{Wb>!DB4;=+ѿ@ܙf&VYr4͑@n_ ޸+Ū;S3!~vmN{؁Ca - v.;*~9oU)=i}I<15%qDfߒR@^]O˽ͺ/2a`cy@S!u²ܼҾ 6vw&^7t砷9#fWlMu1Їdu?szޑ }, V |  a1F b(n?T@e2w4rM6%Y-j#hn%WiLK]90 Rν&!ƨQV?]V8tdSQ?04 V V7Vuyًij`_'/i[]~كrs#D h0 Vk] -}Op46^#W-hTrP]"~ŇBv<f6+6v\v."=Ӥj)\A5vlZmP"C%Bv\ O2vo[н)m Q8tme0/I.yDtys[hVi2rb78mIP^֬-1C{i -Ӣ .?%?R2gSxmM V*Β -jgD*?1Fvf\$s!h+H! 3Sb8/Dn5,@qC׌U=(%;79!'ċHxXk`(sr1#P(.P`j[mly,5δ9 -W|k ,kt\,~CnksWe!6fi|0@|v),M;l{? [XFyr[FGr鸆qL:߀y /Fϖ4? 4:OpB^y Fx4&.i+IH_>ݼz/Z˭2u`-yƸykV-G¢z>+ShV%/♆9Dy:~ݒJ RJWBHtTb<&,JYGC^:Je=!.EcCg(~CrvDF8.MV]{Q0Ǚ -?*ZE@N)x Fn"#!&Ol{W]IJ/qH%mTjRMBl=7_cA*p|C0IW\SO -Hዔy.PO᜸#ٷF6*j@9T%dtYe8fSL4yi``n;լiq$K:+6&BUk?{TTIP#{B/|V7iY{|%ysSPuPj]DvBĄ+4ærJ4\)fӱe\hލq1 `6DJqCExmPpK%r0[F"ub 75jB+%bsg0SE}]zIԤ{0ZYzd <v"åAendstream -endobj -4225 0 obj -<< /Filter /FlateDecode /Length 2624 >> -stream -l84(q\6u,v/ --Scs[=5$Y Ifz s%qp/ep 7UG(w:[>f\U&͵~ˏ]e??P;:wAPDH*%94 }yud0E$G95D /]8 -B;XsMlKŅ:<f8MF%og?bضv{AKh4Jk3kCo -R%qcaiuFБL)( 8BWupT^ɾ Ttٞg]z!G˜?$X|xmىFEx鋑/Pz墇;}֤ـ*E߂G%*./6 ,cx2Y@};=%X{*287Mq`'9rN8`Y{ J#C&}0QBWK3:fqKwBb*`Cq'D9r 7#MQr5d񽓥g&HE!I2D -WcԖ:bY?g3p7^_Xڒƨ'Ί9ٞVǾhkʴ"6]TI֋mU0 2jolw #cf$ ys1Q]t56^ -)&22Rr7E职G'"ޘ_Q|J٤Rtf6vzk+B= -S_+LzgnGIݺ^qk^p%H㡧d|M%SA% 7xvK]{Z}ˋ3Stp&o~a'V} +;u̸ߧÅ`E,8:V~ed$ ~Xjb3XA4-_Pӛ5ߦ&YxDq WX#O" ޑ]^~eFبR?؞~)(8O[(S$D&G+36}P_;(nb 3>$$M1dETӿ\5?Ka8RCkQBц -U1&DDF锺fUX3UvUThIuهic"-Q>͂Sᱜ"VƗVt^rE;qvDe2tb^Oiµ67'_bwEaFūF#R"ALXyQ3̗RNr6\ze -{)V~署^>鮖{j\Ebn/GΡP t r}2I6qFn&ZzQPgWF2 9lBv="w邔IӔi6BIՆbmW(~Q7 7cU$R7I) ,R+D_e0^w!^ כۖnPBvd|E.dLO?FezPc,Q'}bsr.[IOoP[0%폤%pU\'#HX> -stream -qNfMl2W(2*kmV+77dg:Jo<$A -2jhEh!}{$pjD[n+C#)6pb(0,X@1Zo>O3>?byf>7%,Ψm$n9D^ ͂)ŀ|=fG$J8?G¦k[~_fw!v"s/endstream -endobj -4227 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream - B m\_,(Z`uݰs-%:QFF_Q`'I1 hXˀAƎ7\Sa,>`z&"Qґ94_%tyX՝1eފ)ֱ"W…Yɇqbo}e"UqR)w7!߁]Vre\b |  pJTz%Nq5 ?a|K(4n/L4}*rZUb&H(O&)1%(QD 1LCX9 ^ҥ ~C9Z8_ۘF2(Y0~sw|:9/r ,>43Vhޮ! 06~xPPKjg9k`xD;; AN(6!v]߿85bʔ -d惐 ?%>A<dF~= )Lof.?.P=Y}Lh\*ᅰ+8,S-H/,Ėkp4K|_S~sہ=){}&x̙2h^j ?^e_ lwe Go -U"U\יgPDez|Q1h)&.搄b~_Maz.;74},L=k.f%GU,v\PƘG*c\ӛ݉q7&7A⎋}O`H=XQv|?lȕ!T'Z'ڧI;OG;Malqҕ Hᝃ {12hI736c_&tou7EYo@cFNW,ĺK  ❓-ԁ_4Ip,1toFd2\&SlxfEEkxP-ltggA(B&gWUWU'e1\ugj`M32M5h՛yv͓UۙH8f)E;S5ˌ9 >xh$n&q -JKG }Zu|Zρ!'FY? mR9͕e " s!D^)9Г^C?ʓ%DjvrFRʜ뉌ZvlQLVI,YRTC>6[3bT i>d`_ȧyc"׾Dol7=QCendstream -endobj -4228 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -/CSfQwuܐWmvcFn;By.]zs ƲoyiL%=AfqTNsb=b50︐aN}]is8 -ۯ#nRv-}}_R9wa'%*I/}i^ۓ;F|ŪSX<=wקw~NFz[Ѕq;HB'Q+G-]~fu!/(9x %[ǛZG'g)iX{H)Kyt!$SP5H4 (҉볼dKH͝-ɒmɁU0sG(}&0\,CVlJ【ڥ(XV(pP}MhmL /51vR'h2BGSbؽzUoF~8Ҫ}&l Z D+b?C #Y ]EfĿMA@YjSD=>@uq@I$j2֭uhHfYsG/j5Ui7q/Lrљ& sDWD}D5Nr:Տ E{N #U)UKbtZ$&]Mo-S7K= jyML -M -* ?. oGv#8)U[بsg <?\RC{mʛ1^D}tWM7q}_"b _1̞P7+1#.wո#*:ȓ`-eG0}֛UF`-KxU'UtGZpv](~ņVG~ C\^!jXU诲5*cdZWg|˳ o;c#out{Ld_:_0"0Oz9GF (kdINJH-eiA8c-rsڑLc(8\nOHטvD*Mm ?Wendstream -endobj -4229 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream - -0Qs>"\&.,ǨډKy@4NQӯ{jq3/X+L==xA擆HڌE8XiOf/yn(X\1:p)-횠D%u-qTXԽu{|;v)T4ܘ7Ѝ;.b SW"ysY|  )sBDDOHm)LprQ?w"u,wۅwo-nNӶ}VbB)hVJ Z}D_ bn0p#9qK@ 07)Nۉ3na8蝢b$0mEձ29"!b=`ވӮ;H. _ ->4H:|n5PCumJ+TgMYjS m҉W¨Sa5a)e%é 6kH5cCvɿ^*V3L+(_4=f$0]v$>; UmxՏcQsH&%˕sFD[h6:7ЕhtuAP䖸aNWrڨ2s&"0>1\|z)A LKc3!,]pf6#0eoP1ć§/HRTb-qG݇/ VUBIGŷ /a˰S9yrʴܚ;WK/vU#8\w_qA -DQk旊߳"H N()N~G - ώng6d&aL%ٯ%d4G8ϊ0N%oz -]9!x"*Y #Cqa{P&flK pްD\E>SqdEXм#RYp}`b+\ -˔6Ӄ-soQ=w<ԇz ӍI-o*]hi1ǴNvpepXj _qkeh%fRYYqݑ8OfM8qz:?*+`Y]n s -bZAIDG!`揥%jH]Oq&,_Y/TgFC[isy6D`ؾc!% bQuxrNHl7μ-| JAN@XR<ɳ2L -LIbT`AV^`c펪SşD'&53p.j h3(R`Lž1[Rlfgc>gIҧ>Q8MSW3զ{RD`aSDF(uH^ XJ*vkeavwپ,ՑۂR9R(u5 ک~4$1\Pw -ՌCemM`\O@Ν5 -zmN/0Җ};Y&Z}g?p%A_#܈J'v@iR.Hr - @48 ->Ox 2+yM8endstream -endobj -4230 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -3{YhIV1MC]ƭםͺqNGNJ3,ߤ2nւ#es\ }q!ݼ$<}_[-'9؅+ΪD<$429k|;aHkNtLJ@10[[uu>2 - 7Ɲ.HnS Ŝe!]ȫ&,U.9qk˙V ՒUSG3"Bǻ?MǞ{Þu,jY hlA2,gue KTLܸ< 2=Y3C6T+i֗ /Hcj27,Ou#U@PUn} q7b͈ `w6RWħurΑ2cF0D,2L=U+⢕$(+o%X[B|$GjSO?p|NɺyA(\Is:V]#GFs/,]O^&8b6KҢx&.ɁLy  %`hQ7 Bz`7Is6c.%9\z(o=] ^52zFt8*݌lMm TԊO|;ͱ) y+L։Ob5`l;]:X{+xBe-JxZHaIqc&F&1@gqyWe(a?fe+ yv{Spf'Tc2H:RN6P2|}.ɲފ.䪅6<+(WOZ9g>'{$Tl&eOM״!~_7JŐNeу[D}׬<)˱LZ"'zgS` !D6yuEUˡcw9J.eSEFQz#^5A P="((Ft"+ĶqŨ4 -Wm/=Isa2endstream -endobj -4231 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -R Hfy{{0ٯ&~? z v]o)z Svq142[\``,e\x;k7,c5DZfƕve|yXr&s,b}h9ۿ3w ٜqā T<Lro>$/taBsEx;NҰϭq#I]dҒ _< 1L:H Oh gd -щ9ńj - n8ם)`,bO-!p|΃Nq/ES0I&7Gn]ֹ̊ frdi?A]ghn+bV+!(A MajV7>ө;1C@P&2+xp[F t.k#Ԧc@΃pOƟҹAr#󐻤snTAcHinР}laJ(ZN&-vJU97-QWB+m :p{>{~oEhT; ⒴'N -hܬVF qcƈ[tpq+zt 3CwH# tiI/&!RcO6?b!P govj_"EyO<.:*5?ko(}=,EM\Ճ[f1k4vsuci~2x7MȽ\t?]vGh-JP47d_EbX hAV-S%w4]F?'55[:'5&Sx>XJW&gzE]ʹܪ,ޮH滔|"-LArF;> -VNj3&d=L@u0 -^(V`ϸ3~9#1VB/5;(nv(hbi}|,Ȧ:7@(Yexs̻`ؔ½TaPk);r(kY/ҥN2ї>O"-3{nW;}ί6a=JP^5ọ><0S(Mֆ:M4͎ENmĸuF,#M=LťaWMe9,X?\E 1;eTy\E[9M1.l8K-GkL't30\1\.@uG%.4Q)d /kxV$wT;3i :˃s#*rX -Ec8yc"ȥX՟m !OɤCOgSm2Ӭ -].mhu9J,&]Nd.>O -@{$Lc gwRq_ "o&SiFF&rx}:i}r6\wdǽIY W`\ F`z^5p}WU)B ;[rkZ͹ʸnjfimQV+-D׿endstream -endobj -4232 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -!ڥNOXT#"St8G.37G̠ŇWxh}bEk4kf -/1ϗ?摒Wt5 D EorV$i:2*vXqE6AXt'k\Ãe.; ˣ7>Ү?$JCb-zXQ0eS*7 (%l̃Y0׏|{ -C(.ri 05kaFI '>kysI`v)*Bts%hCx4+j `]UKASw-E\ LM -JJbx{cQrX|Ac ˶ؑhdwU5e׮}Qy W2qAdBA)Y%܉lכֿ*Ç1%6~{$ U-SrhRŹtj[\V~}J dk5ydaB{k+kucv -gc¶͎ض`=0r{jmzC+DK;*Mmv}jɕ3/I*_U,bx.tʉF;]P  - V08WInvfYv^g uGk:n40ltKlTh# Ązc{~xns -2` -4{R n{X7l<x36KCa~v␋t! --WK6$h~ڊ}P`w,G`H̴q/0x%QeӝM=s=z/u:&bOM BE?_ cYÐ<4{JZ0ada]wFnEI~c|XS#z`p@ϗtoJG -m13W/}/ `"2`hr - rͧƽpP':X |nY/E Ob-Ө{%Կ*ALyetqkW]!@c't;\.EWMYF69wӔNCb<+ 鲃sg]d@7;ʔůendstream -endobj -4233 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -9cԟOoo.".&eWuz]xnDhwa3 oA.̋\ 6fgv>TbG~g6;}._hv2zG"j`"5X{B8]>, @#:!oTs=$*!D4,wϲ^02`-L G\&,f 7{a~ 'fGul}P{ -~3]?>r2jd,65[m5@f JfιM0w~8H㢌 hƤ(Al hn͘#_OL>&3E'825+ʰVɚdQU"6Ի{laם1z$E9I(p؁ U}"YʦudzLz}2^t;D 7 )9²b}F`6r8[㋗9j , <+0tY''̅B\?q:WfCL=a|#{FY&dp#)Z|̙Ci&h! -9^&/ù\:glw,~P,Ht -;B KZf2d#/`Pnb){>Q]!ϰ2tq GW[aL9q[ʼn嚷 S % {d -]W'h|.UD J68}x *?'d K@X>a}6(w+sjmYdN\{"Gw37ygEmĘ,n& ,O (FS0r[vc 2$gBG>piu3K(. ϨFaS(8s+pG=22$`#YEOr5ҦӸ57k7) 4QJ{}endstream -endobj -4234 0 obj -<< /Filter /FlateDecode /Length 528 >> -stream -BK@x7ByϊЀ!__v0ѹƻ2XĚq7+Ee#ی!>9t3]2OɌ$k73lTvdL!P -+bM8|mDmG)KŃ>UuWfq-8qs6Ril4*.' N⓽N ɦea۬}tYj0mpղ?iبY -)*ў=)E,^&mfNèj$b.8XDG-3'ݲUGP[jRCPS@ڄ -klZ([ЕKssZ9KOaqGIVןrR?[(Sp!cx]Qg0endstream -endobj -4235 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -zGWhTMl/+L(Tٺc S.\ jf0w#8@lԗv[GZMXo?v#mHVL1e_D$% !i6oEcSO?*}o%/OL}fA '` fN7Y3\ S6neY=swӋ걺oA\{#z Dn0sIR]PaQŮh^}n=r|'fP*s>zJ+J>9ȧG$ r:T"})3`ȵ2]JQo?S,4y;.{R3lcciSܺ0A-!VIj(sʏJjd_ppM}6|>iHr2t#Z*Vʕ *,zVP- _a{ܷgAJie2aܞPjC;5F}}ؐ8 eAPWmphf胑9>t `+B$C>2\4ėijU!-Kh'萃;5p7ˌ4b:࢞PosxFau$sfJG<7TZQVbs$WkQ`Vi漾L@XUnͼ>}+v9 {Nb3(FЉ!Yk}>%ub TzƓ0{mUs s`{S@ggexNN0Nc+;| -T ;HR{f/rÞle~sn EG iYi@[U7> -stream -eΟ<[Q)<ҏA .`R3K?($| gtlO*eANj{8p?~ՓŐj)0oa"Pʖt/;/u,MړjS",/teb[\ƨpF}5'z "Wzyne]V`V0򥷡xduҰ}lfR_%( c΢Yp\,C'xmDe+qiT8{jGmK 8~Zƨ_ 6E69^Ǘw `[AѳX55|l;WB } d9q^uot0'Ek C&Uf$_ :Ncy\Vu:S {C7۲W^|lNݑsJ՚ [^Fl~+D6"[3_mLgu7G wn̩ xCz RTB4"ÿ )AW$_ֹ gtjn{}h`|D8ٽ:J.O4;}z%73#Y>]soD\ #u+9JQc; yP QC6J0E%0苽)uRL 56'[(BU[u,^.:;_!fkW˕U#v*LyMѣ@ ?38jE|}v2>#w ?2= ]Cǘh- -TCtHh3I lTV&uw6 utrHgx󢚥uf:KriV+^5\5e^sӭ -Qendstream -endobj -4237 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -v.4b>t2Imu16f֙]Zx+$9e{٪~37~@ &8sAe.j[WTLj""V<\Sf7 ~CSU}ɓWU۩F֝ړ$1KPu^/Y&>cc;:1 -!bӯ=@=(A0ђyXL[[0)TՅFr&yp@%uq%$u]dq(9%CǢQAig=[8H7.6֘LFJ4ࠒQn8OdnT&U%&SD5Bd҈,\@kǙ/9 Ȏd7=ڱKxS[dʼ=&T RUcIͣNIj8[S:g#y5MUqoif@34LϾ3Nm֧9y.=H !v%Z6P95+?t6܋7Xk>HC -mgbvsQB4ƞ7/YPBƥf`'7VI5&;!EV{{04)a?*%,jHd%`yy9\(ɚB؊x-WHvH>s;]^ -%:k| {9{e?V,)M@|r괶{rP &^LdRjӡ&r]poJ: U{C\`WM̭*vlЊP0T-L? -{nRC"'~{h~UƦubv <9W oC:\8\]6] GQVHyټTXh3oj+8nprHttY`rAJҭM8 4`HE%RTn_ sqxBo.혽* NE⬢]֧g9B#_f aLA ZHjF>1 -I6]e,y{ j0_2#.*u##ik!fa2ƒ0kQ f'[ v, -}1I2r91)1Y,QSrwKeo~'C j 5P^DJ - aoۀ@[~uِ<^0eѾJ"ar9E< ,Jq3^5'q'0g{B|{P޵/-m>)0uӬ`?G9g `CnΠ,?(́0gq,%n%.Lctܳ<'YFb\ 0—a`Yendstream -endobj -4238 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -uP> -stream - %w6G2\,z{@W#T.3L(kŧ#s'!l"Ya 㞄<ǻL AG&}Jָ@g{_ #VגG55~1)>3̬ -Јir]E XZX ;' .r1ݸG.DMjpǎoV -S\u+/8mE:6^Aƛ;~8q3O^Y* ~I{{?H c$Z 8Ey*U -`|_]mB8A_ '6xMJ{y ְ/Gy5*Q->u\a&LRyΧ(A f5c]O CϳB *NU~

F{zw zMR =RA-,buwphFC?5&Yq]vkFEK Q;8tu]7,9LjhJblQQ㏱Q%W߾N7ab&z7 fWG`umR HÔ8/ (awמZfڧ4!F@V|Dɗ3(qNݫ ̌5uGx0Sjxi)}r4FA'pM!7"cgռh^8F3d,TBp.< j08n@AǑ!'>5;7"-R_ ք7s`#7x1;?!?8굒SN^yP\+bD;P'\< $a|rFknQxA'W,mserWoz+znQ:m}SջH 1Nv x$&K?K_OItA?{}7'eWqf4xbrßԟdWs!WpӐ{mčHS1!X -&2yq^%h#uhr493~Zs!;sw4Z(Qd 6^D7=Ɨ,[hVBj c]]tJۉxv1JH,yuj;Xqy#3MN$RPfcendstream -endobj -4240 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -;UpGZJFӑ`Ul/M5GB&T_K0vҠs՝%dJgrg1׬;\+Aznl}햅V ._w4<Mô ytZ8B\ļȉFUPl9׃aFdE1ڧdџTǂHj -{.u0y&em'/V5tiA{A5H;Bu 365!sYWB|ęք1| [P &g@xbAI"քp `qT JY e?4%I*^g@C/ܯDI*D,k(4P,I$.STſ<:&"Y۵n&z}"/z݄@XF߹ SLЕWk"McyؼL32wZrkɱ+sdQ3 m+q' 3-O@#78 !d<ULGXnF|d1WƑD~)!Y#Á,M*ˏFoi.։\7w>FSq9+}եZ5Vc~Iwy5~I&OhZ@v.-bDwFyyLƓ;·3O֦PnEڌ5kDZrg _4zQcv^>eyfW׫r+2ǃ<1s -ZtueBzs 1"|vd)+N'E!3Y_vQ*_y3Mq]Z^/6F(eøw/gQHf6X>Jzִ}WHOȅYaO !obtRRzk CM,dy[Hn;b@@qs$$>b1U} s#9>I`E:rUWb#Ah/txrU!1 a=2-lJuS8\%LA.b`v&S#]t&9=]ɕ8o89.f_/:,W jQ M'0^JPN.XKM) O e O$ -ytVEQ!F~Ivi7Rٹyd0I?+Oi$u[FۃA96YL*W Wz{WL4;RDKC%QSO_ZT@E+\<_> -stream -&΀:MXv̇*+BY] rmQM6J3{ƮTИ:d -[c>ptаm7|;)Vd7Ă ahpΞ[Bᢐ9iKHyj@BT7ʿs'#jCgZS}mh:ݵu~hoyj'vNu/ӝkwn Y˧ sl<0 )˖?4p 8!M ~!WE-|u65#l($G祐f<[(TVlj c>oF83}^bi(~]h{wk}RZ9LVL!]aغ ]"+Ry6CX1ryH=xN4]k. ,7@K?zj rۭ% T.RɈ -5a rV9vf~ag zL{Y{~?O:!J/ 6rxQY!rbś}sڍoe6Wӓ.MMW I=5q𢒢ߢwQD:ZAS3jFE1̅S]L:R)%-[_2}hX-N3<_D-:RvV5#e!D'-]|$:7Q%`/6sM +٫COܵv=#xh@Y]c-'fvSe3 N -@ - tѳ~SU@soq`H@_:ATS*:0G)[aގ~#|RBNDH;7bQo5b"ARYγPXPSR f{-Ō%H3fNp츏:Jپ ytɕN{V@iR+N/fFgim8g>5 oa59 X#VxuE=8Zմxu]AZoN,_endstream -endobj -4242 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -@%2d@ P$l9dQ9$-+EjdO)Ơ݃HLΒv`bG4ptKi}˞i4@^ - uC%ژFBZ;qmP?Cҭٕ {2sKptA7D!tD zDBv׫o 2+!WZ<."c]^$\O0II2IcQ̙wu~+/ψzH4]k{=!]{밂9 1CM$ZvA;):;Vef Ϥ^@=Fͮ-~KѸ\. -p쵭gsQfwE Vs Ii,M$M0\q.'߬ 'Amr>9'={&yUKf۽&Ox u6^vCC.>sǽUwnd_qd`L/<.U-E)Hz$_cﰿ6(uC۸'Q4PulZ 8]hyL UuLgr97A>|޶Yݭe ;P?eS~%L|K`*\ :[k~bezfN6w1`ucķ 9 h\"x̗-5P#vUu#px= -ȶ%7Z_tguDVsutS6'(  IXBO3H7UUjW݇lOF];h.{ϮRX+[u[[C)ȃReUx:< b<w4֔®4|Rڏc d:|:i`)p s~ >tVpbյajםtsIY210ےt'G/YUg͓4[iǤ kU׸B!\E;cpRG01[|?oͬFrw դ(fT+D1i Ya g畒NF75O9er9(X=S7Zx˛ Vd "Ӵ3vi -={,7 >9쯽۲X8}̱b'ːI}oVt@T+&+~#>j,Na \&\7j9+WCf*lU46v'<\,g >hIr\I=CZ}]ID~34:D[6;6ޞ!R bE9KV7qʱWVrcaHA*A^u<l5 l0./u|YD/eZm؄bH -D=ª-(CV^LCEQÕW@}' -Qendstream -endobj -4243 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -=a i?ϴ=sG2Gc\t 0f>}ʤRMH-UnQ5j-4X.ygʞ9X |I}GYr*>+ORH*4ZP+R>PWj(t1sVDJWt-@`X=n֖'뜷5iSH8SL|P9t=f7s_uMWk繎b0 ܀ݭ>ǒ1;mewA`9b&KE bo5==ʿ : ~s(ІS2HX -8sCϕ+|⛵u}Ib.p!sǥB,&=aZ_t\e>>%c[T$|^A/ws*O~.őOY= %[88T$+LcT,D %͟wsۛ_w@m猙lng(^M J/O1A1웁rm]lbqM{ZK!pҟDfeLj&p3N> K:Qf 6GXͻ6lVr9 )BWH9ÚGjRX:`E뒲uv?Rv6 :s6iTD.owH/AwcvG jn`<@PR -LJ$@^/i+0KJO+i$m\si q1MmR#O,!5R5J45ZvŭYjm&V@WIEOFwe3d-R:B ӡYW3,FO:z? tw=>h#sqcaҧpL3Yoc>ch;HN[lq=ZF/C0,2I^sBZ4ur (,n~65 -:ZᮔlUɄR -NKaim2VS|?$gdG]s5]ʅGxqnVt޶V^$Jj - 6 Qi]#Է ,{J9`y3R?<tTh= pA dIL9{ÁrgZ -^%TڷNUoSI'3 '!|[^|>{2,S' -0'V.AKy$T}2߼ʛB"сZќ˺r@?q$Iz$b ƱOO1PU( s :xEg-dք>^%N@YT|=,kTiԐwiCg. |=QL>Xbr7loş gE6;.@&ߊ;NӢf3!XkZkeY梭Hs쁉&oӏc69w0bD {34UZ귚\B_2^9fW -+ƙ>bWd!em C^xp1H`R8Ǒ4)ǝ}̽D),. .{cݿj1-mmN^ur {F{?8KYx/(@S<5w9r+a^~\M|RzqvI Y`endstream -endobj -4244 0 obj -<< /Filter /FlateDecode /Length 2288 >> -stream -Zٿʼn]vN|Ie5,`b7uͯ}Fb*MZ&eոv&LQ8Z@ۧ -' u` QM1T7FQq4ڑ@֕M_`|G-N୛n>f@hޔi0;Vq-n?Fa|L -EW~qgSvk`&7)Q-HXl? p>?j! RDn-VADr)pǞ'JYsFF:;" [ YY{n%2|IE݃[Y >| -Z 'C<plpʜrS/uNx)n,VE %j~I -Φ:g ƥdєߝucxR!DDC^dqdmޛoJZ::'CܟEʔ洏"H:Qn -+W:7ے!|u .i)G|_~P.{lDIA]qzDO%&4%k $=~恫'(;9f^yz6 Aաh*pM~)QFi>t84ӓ\r-2Woe&MgNSNaPW+* јa煻] !^j>V|fznhZğ~\)fFrO:k?$=,"P:ׄ>!)â{Tp͓[%ZPh l@4MF4ՈSK?Njֈl ;DD -RP1q[s5"eȽ" ACEg+<"J>UG%?4tKmUNӰ԰G.|6 *{ΕAfr1i>f:;5L3I jC:s^4H/xعp6AD :Jͅ{7dŚ3$vLo;ΐʑ<}VO?H}p\ڿP*4Q*}qyCnH&.a y)7@%|Bsx*7+y[hқTT{9eT[[$9 >߆8x -=E.UĭmQԃ-3 iM)mR`֠,]b:ԾO)\^ڛ{yF09\ 1Ͼ3 'q@{ezhbT@B1ݣдvguGu5[kςcc1RN|\;H:s8RFrB`O,N@sNa*Fxd3J橗\{wO-Y_kQ jqA#rY_ͅc}-˂ GD3] oY"E``uxԦQع&_W -:o9FRPvu1X{|[,~&tv> uTlNnVo6/9ڢK~b 4G7N4nk}^~ƛQpXyqѽ;3NVX'H*NXtmMm#0N=V,)$R&U/QhkfJ{IsSHe˧ 욜D:}75GN8L ~nWCBajgRA+WJ/%fJah=E+%G)_xOtDY`!}tBZW累fuׄ 9>}ʝuԍ8:lęV $ja)vuɑ-R>g0'{tW%MFev:&{+OlhSOL>xO nc} mg'rKa 2픶+FzsdԖ@ rm0% A: RV'4-M$l|{s1w0rֆ$QeU-.@F \$ -3yή Eov!rKwd ̊F,m> -stream -e95k2QsPTw3ŏ>8]6-lo*pNfdOULpk;F^Vz(sazzP[Q -I(QS8bqPWɰj0YY_A`hB8Xytߗ.U#mJ `nmF! e?4bL` @JaP>2n/7!3pѐTMwUaLA\Ҭ1qBP7cFD !MY3mfK^DkMvh&WcЕoMk%ʖendstream -endobj -4246 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -܉맞ڙ 9<ElsW4W͈Bl$#՝:9(+/=9YjhChE)Su&61)~ *`/8atSL&iݸAv%2 -=TWZaߞ>ud&{eWSV^$c 8GVIF9OpFXI#llwoPb eڔsy*EZQP om4(ΟQv&whTٌ>u?@4 ؾ+ P- ax ~ޏԑ'Tuq0Ҷ\Hų@lywtO:mv:U1ZIh0$|j-}f;QrneRÅ`\+5ok[o W`d&Q!+{:beh>nֳڳ {$Uv1͉t5h -Gn@ZU\ltB V*Xdѻ*Ut>J):BJթ-|׌1 vfmɆA+ i'8hؕ14Rcx4Qp ٠EzTk#f[MK̳,M 7 3je '&FRtI~hk(=֤!o#9Ctv$ UBrh6*9 -qgNZs1z-#rwōuced%'lp[aD Q_BJ/zXaWsWmِ]:y9~$Q3 #iGMf&^Ec RwO2ܹ2o3R%'z-f -^b̢sOɬ|cUT!>ϻ2vt+sC=P[l૗_5 > -stream -&fokuti\+BNQs,9XdcBmch99>Q("Z4TWI9~FW Ԯ3:#lXDa+Q(N^~m G#aƐ5idђW൙ - x+1{--ip+.<]%nmx ns:8zi %E.B@I@@]kQe$# y{MoЌe6(ag8*g_},'$Iz# ;PeDEZٶ} Y:*I*`ė  q% ҨǜdNg".^.%4筌cbfSw#Cp`(ZBN n$L_e?-Hxz *V.D*f!5~/ͫ9Uj0ku KR9rfly,̝YVnGWACi'Ϥ 1{o)@^zsnøD#;@WC=sJ:,GT54CA&yIį2m8ƼaݼJiyT4^uzRN5LK9W! -mT65v؍ze8P_V͉D,a c$P:#<돕#ː&=ټzua ځ.ؼK;A͚"?[NgX*!7*n3f~Ѵ뇭^J[u:&풙-J>x.4 tR2@ij6^B&;&i*Kcw+] ʔ5IL0e/◠%mK6t?jNCbəAQoI,l*rQO.p._%wS - x*v\= -:A9IAGdBmdi9xSI c&{Yh`j6]mC/XFFNB0Iu0a1z숋< -,m\ʳOdZ<,2\J|Fq*P "1>y&홻i#ڊr;]l@K;MS W7r<`W>MUFu*쪉WkGClu=@Uv~eUL7\uXrse{dQL+jV p_%cNIC9 a-X.#UbC/- #@߻Z[5 J|sN~jFcwwkߨh21$v}TIsF4盫Ѕ)blrw<Ē6+r|Wexg9~[0%LE -AV!eT#630;h[vȩ*endstream -endobj -4248 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -&^[驮֝"Ӂ&~Յz?.9kvTJ0-K0Sv"ZR𙜚_ Bld&+Ȱ:68@DS&\mg1;6n:Ϸ8;M_-Rn>B {BA҇w#'t p^" 4'U -b J{G7;/^B*ެHfIo*ÍÙ=Nh3~ޜxTQp㊝>eHLDuVua޲_rdthM5:{%W˂°+s/Dos' İĐ4>Jpa68XzeF(ՌlUZM~9 -E,Q?d .0>CJ9 ЛZI _Ҹ:eZ<]-9kpzXi9ǁD㸾V)f;I53έUy71"[Fc'^XW+^4p --% COk3Æatkۑ)ľ'1cI+ŏζn*?H"4 Enmu3'bܡPSm.Fتphz4mz 7Ri'aMܳ:?PBs:|J>Wgkt)> -stream -Joz|%\NRagh?5錀ؘW9*wC~iL,>NsMʆB^ k=mV oV4A -j `춮!٭Oh!X;$Tu-.AE]`yr/E ^?c8 RW6b65鱝=t ۹7wKlj:̴fםmbd^+3?&ݭYXym&$EBL}FQ][/UB($Žjm}=h<` 88N%bSs:_axuff buqcefSѬ&(DvnPf I(ui<=b5޽ΎH03 , >~S Ei oX%g)hR ;#ODMϰ9(R whn>ϩ3\Uܑ.t'0X -O OEd:7z)_W?wb+GRʹIUhA!ZOǂ5^D]a @8>SÅ7Fėa$J>9Q eZ ֯ϔ8mSNBjB&e9-ER4 :T30d8ۯ6YM>E7SĻ)6PQ@#Pc>;7낊qTuGHx$ptt0(b!P&v#2S UH < thIm H=kYX6L?fp:$HN547YΧ1^I,JAlwO/ݔ vrkkdhaH&>9eY֫^<n=l˘Ͼ,=k5Ӈs/c75|@XQ,%vcu򥼠Iv[{>ԥ[H Xh&f-ZO,)r:rbjuEvx> -stream -<9V.N X7hGYzB Ka9_+rQ΍uW|!,3t˃z3>a]ߥ:#JVFq~ GT':^Yf.R|gF6pw`y#jƢw6qΊO>UR,:jfCi[>#zWTv - LZpxv~؍HGx^S+;рBȬNXtOj{H) !Ñ o9ޜ|ފߓ6j?*se5#d9(e40t7BGp,cbJ p)!#l4pGۄM qCq:k!'m,&-rjpNwj))7bw/<\vB.cyr7|ne5 -i6~^ x =*J@G24M3kSj)d1D7N"TA F'?'+"Ƒv؂EtI0zƝJ~bLCt=.L*tY*ӜRzݟT1m]ѳDIu,ӷc?a.KƷ"nnUQ":vE68 Q`F˜H`9}i,%֐+yʱd['Iltɘl"Gx=6sF=nt昞lofRB uY2vm5ЧPv=d&C><7=׋݃uj7"NFѤ(`;}]DTn$R 9`.~x%ye4AHyAΈS/^~v5V'ǃsmƇUؽ]:F7[Xp+MҢ(Ȭ/m=Iv6ӱ|%ƹ~|؄~!ƀ Lr&9[E!wQ?,߸Sr-җMi'7q3:]YVW5qdikEHX@\0"`a1h"e#(%(so`S-_Ǔ[[V,<a>1Tc4Hc .3 Urc/gLm4pv`7,yά>jH:H tEE2>`C}aMuYӐaY*37Gj7بdy'(OSʌ j̏Gno )O q49\2Il[†!!>(oW>\8WP=yu*Z,6p`杈msgWRw2bɏFyPRA˟Yn]9>"23O*.@)=R*NPp7. ;HmOh}HΚ/m>WPwZ;ڂx\DcPS6Yą7-YW@~8~*т^x՗1(̦R{7sꖳP3(镫ڣ_RQCJ谁2G]@~H$V=\G\+|X==zkgʲk%IԀ8Ā\kΠ!{2&X9$,tDQg" -T~KFڽ.э>c *B'uK]K/8ӔEHQSfJ:EO 9uyҧ@T - z*ۻoG5*'KGX+R\sY ~.L?~1$ -LSBR?ѭbuRhDF18-ӭm/SyXiY}OAp臵OboDendstream -endobj -4251 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -/5G+ ռ)ǨBj:c[dN0cOE-/~D)Ye33qMN{jAa8~Ʃe6Ly/.E&H:>8 /G^N8@ W5Rya; ZWUN0<IsqGL&_!m,VmW,zȝNk|vjpoPS kRgn![Vn?'{Ak:[˫gb$$bA}~Gjhu --ΧWJǸJ}Zt -7 Sj6M|LN>xBP6IҘ~!w)Ul><(a!jMW?Ҧ۲;{1:iu|^/moQ[+A72ڦ -cN}(8Kw2WO"f`~ s-&sF;;N=`_#YCAA;(Q F3͍>=pȫ`]_t&Q;׆K_X#um;d ND\B, KrօeTR f[txGVI/ -^|_0Δ7u֒#+Y,į6[VQ"X/~,e|`3;I}3`16\8tm0F˸? aH,ZjBg9_w~ F݂4k\5˓G@".$їM5J3DtXg,T4@^ R|rɦ' ҃whPi.:&o^Dʆ aމ> -o - Im T_1aed}&h=e[xm^ O)#HygX甯/x.6vZY#J+tɿ'/yd[8vBRnǕCsIӯ!",㯚˸=XP}'U/֪=gEr]6QN"<5Dv=lv3KnD4b8)8(Cj"% [Ç/ 0yRfZV {| -0p -!IF3"ϨZendstream -endobj -4252 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -XGn:Ct]RL>,QɪBb+=J5S:) d#jp[4Ha}Wc\壔͚N)7qʃ*,,~ I'q "k|FUZh~Lmrio1rpl@˝aV?bₘǟ2t$1k- wd*:KkgQ3A1FT/Lnq) f=_E5,1 8}}B "y}H - -$%udȿ[  -Nk0 Ah1;g)3UH+RPTD=?th{|G]9\)PdX%aaeP=gE=#K\K9dgZ@,ZOa!LndWW`د-VzutQWl%Ftb@m}aY|U69K6z5WeQ: 4ͩ9p#5p,PD:]-8\s D}BZ0F^~W XrN-Ze-N"M -.$}8>D#I/DF$V٦/\"Ke9#Ɂeevޯyf "iu1ڊ䈯A0{e92u>'r0cE`w -[uVY8nAczRPMvWH$e*y<7hp87d &0&UӰ_ij`&$ϵap.Xcw.B͜'Hbfd?C%wK氹¼L,xQbQ?Q-8.Tr8NϽɁǻbmOdIZJSpB]ڣ;tUvwrvsiKzP³|G{&+{nſ} :1q(gz;lQm b᪢̶]`x8ܕ>&-&h*ZH> -stream -lyxF f9!+ Izxײx |0.; '^Lş J}5^] t[T"9bd}x^!9W\aP^^ɷTqS_^7@TY~lv_J -S6M8I=YڗϼYKjg^lR+V.=$`A\Cb)ݟCܛ{j6]]V?&~ô҄e[h.|pXqJ8jwv -AӶodڽl@[A8Bxfl>Qp.8DϋQ ypu%ea.(rk<9xI BS(d,U\_o遜K6S43ԎWrX󐮌Hv<"ܸǦQGlz -P ]=I1n>9&-5hbhR_^I]tH}g~vsT=I sDŽs|eQe"DOsa zWx BA)3%酊؆tn & hwwb{?G|6C;{Y(72i+M UK G -=j.cM^t̺{endstream -endobj -4254 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -99s[!f7ec( -_=m"&S-\ҁ*fqKEM*Y<}XGĺ9 J HrǥD+Kv2:X+^& 2t'G7iw1 /BY4tO+,W`Ykar/ 46o0͏ycż?FW"-#OEB;չTb>/+ m6-O"c:=hlBOYGxp$V{ڒ X JAF8@~ DwkXf3^*{ 3bIwְoXڏZJ/*UR%y`2gfP7aݼ6Uւ ;ݣWtϲt2m:Mo, My$6.Ƀ)F7^5Q$pO݈L[^ aD9rk^CL@(7Ƭ #,&Kl\ˑF&A4:s@k/[3it6W]qE@o.q&2-k:Xb߉Em{3B+$J`b&Õ=G0zZuI7?-'@0|Mfd<>k -1zFh7sTb&<&W ` k*V7cr:֫&^#gb [=4W:r9 'c]BA# 1j.2""b, 9+7>ɰ,(To>ܧ .&yDŽdѨNw`2º.Fjosd,Iv\&c`4zBCvr+jG_oX> -stream -xwi2S_XYNegsc8p>yɖ.d$a:T(L=BH|vȫ_qVi,S6ॢoEb،q@9TC+fHP}oW!5]b `V"6kȚ^1؉({(ДtO/ rGG0 5E J*[ #VzB B`(yПcj̶%KE ~p0Ƣ-t,+[꡺d=gscxތڪ؟nA8e1WX -+a_ vj׌33dE%_bOnJo&l(7Ty M Q6ь3G%Q{7Pi=7LE} -rt,q< (Z4A,jdnvGp"חٴI4ft\L?ͩkrG԰jUX21s(2>]f:\E_>=sIW@j"7Zw2ؠFfeǪkё5͈7rkT[pWeqD%OȚLqK{!㇎Pܷt)Es4Ɛ>q;R)PFT#Ҳŋo[l3kѩftnc.: b5_N_Iedk*7mχQ1~+W0K)lӺҩH1_UkwUTqtJ4gf[xr [ } E0Ox Diz[tt[Ȁ,MYa8'5_%V!:INtÄOZ#u8b@߅"y͟xcVG!]}i@-~`{]]ݓs LSÜӴ\[+5+TrlڙCBO!> -stream -r9sju*(:Sɿ1NLמdX;F]PkuGU:%NI#lw_`]X @i.D%˰x}AlsR#K)\nDmWQhuky*u& \I+f_fj÷?~P3UpP :6d(AAuX!X -ąaösI7`{Q\IO`FNaSVЁ|wh.2/̢{p 'e ̥ i*K{#}% c*?p i&ȷRX}Q5Bp(Q&C~%_SlzTcXEҶ4慫QH9nRsv#'5/o0c7v%~2'Xc_ -RɯbU2c>|Yf?"mudžRPHɑ! -so]r*[vARoɂcÁw -qR - -sG5{RlTck`WgԽ)[+Vh<,1"9;h0Ъ3`ŧW̨ 'ilwT!@f6BO&S;h/􈱧 C%tMSLT}Bs7Z?Ch:|RkkT7ܱ$ͱ']TYK_ y׍T 312Y+LLE^ "ζ - -Rvn}si^"'*sˆ@5p n uI\TkNT~"a;=j.b.dt1 -(yvl!"8"%+'G`W{Tzڐ|h~遍Պڃ&C*t˅Ny"Jޒ>ZEA(#SơgɕXzPat@D7.c~ T֯R endstream -endobj -4257 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -`w'To -rD>ᄈbr*Z(K{zk[-r}p`n4G(Աwl6>ini9W|f*ZDiG o\H?V@UB r_F3kS ;&a@eY0vY siaFGKhb!H*.pY`v;Nk#PY*/ J:O~@$!st7mNPÅuCf5ʰ]ն_5aM(-  -fymcQ2`{[e&x`[' -۰e~҇2X|n #g Tz-`BXͭ=J!J9 &cehI>gS';z#)1e= zHPLn$B991 lwC숵u|Ng,''=VGcnj[>SyqO<^e;adl.#ߗ`(-a;W~v<-ER"j#I!{DJ k)R#xQU-`_fC|{l0m "8e6bC*RE:+c}~O~@%AAgu-uE AҾ@U9?x ~sĹ &=T98jtQ;L; 8!ϩ| %:Fgr3X&7Iy$DZ ,s:ڙegH -L>\C) KKOS1oخH'[9ru[р|/Xij| kUJx|AE /%?N{LyI9 J~o B,>0ii`ҁDeDtڿR<"ꝇR!rZ1I==eNs>P|U(̤I[zSlauƕծ _i$ D_QKg![kPj$gr%&3},6WŠ1#.1Lv6Vsƍ6& )23<#zRdXWp(QtX#Ö?/KFUM{L!XpF!`~ۧO({G3m0?ƯPvOyhh\mGJ?T%BTL=wwQ=O(U>Bl#ZGՒlcTd_1$x3RRb5 +lKI}Bw=cyrO_֋Qpl:.%2l;k(K2r?0lmb YaϪ@9ֺ18 k5++=#F*m!!(jMR3N?2h@(JayM%b,Pi ǧR|Zj|Fwj`1Dendstream -endobj -4258 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -`!RӧX=U=6pi N}$Ƅq=UD\V a [V9*mObrC+Ym/Olߐ3FaT}we[k"V ͻ|RV 1֨b -|dD ر`E ICʔk|!d{f?kTyLy#E\w4ӿ Ov<)0;Q'Ad7QTʭJD[ -;XMD_^O,2<` -\nWa (A g+"X$<pg%X7JiUblj/)cـ̟Ges2Do S'2 dҌ^g7z˃#>IZ o'AK= K~kr9W"J[~?KzjD!:'68qP[> tr@}E.' -qxrG4<T[ Ib]ţRT%v~EDngyn BO5Q˺V>8, KX﫤$fϏOiϘ71/igDiGlOq"@d\J GQSnu<ĄX ~ہN}7]&.a'uf~BT nINwCAY&NMw1S=W89Kb̃o S,Vg{/Y* -Խ o=7(!"o20ޏ?&8˶nP4GJ'YMlPtmA"X!cPВdꆰ3TxpƶYzI&Zì;F{$XDrHi!``%-7(uxT*IɦT`%ۓ2pGv(QJfE|dk@gJ;A+_4?xZBE6feTw:)2#ʘÝs}BL%V>cխ~Kyx:~I bBaD'(wra_8޽o={փi@N߉> -stream -F$ M<5 Ƣ3 JjR vv2*K)Os@ clsJ0>bX$eIکfx!]: BЦv\4^^ڕonlCĻ:,{~Q@) ^4-Wkd4aG&Rfb> S3} ׵ oH1zҼ"n`%;P|}7KlL^1ubism{ c)5% -!N1!b9c-M̅/Ԯ6I -VqB $V>9Ds5c;`}N2:|Wl}7>~)د2N-)ڿ+t̄3$Ew-97=@޽Irf?SE$pdG#Ovs8psa ~k@93{]9@Yk{BCIϺ&(6(m{_?/9.#:Iauғܳ"&õTyĺnOUT"vx 6Վw 9sNRQ߅s)Bb -Bt=9@osPn9agendstream -endobj -4260 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -/Ϳb6S1 /S%܆JG6`%!ϨEEUVn{uhjnT@@aKngt!|vl!P; JUgxrVԴ{QW`kUyc %F`$;\ÂlLS<1ޝC -TƢ(-oЃܺY%3ūoXl>-D۵Ҍ8fچ' Y/>f:$=[w抙Gßo/Ķ@\' UF6q6TL>sѥ24|v[ٚXfT+PЛB͉hc-{~Dg#^Ogm!YJ530 H^37XI x^}q'ŠwSҊ0o@t77/A.`M,yLAM`/ -2t̆҄DY -`qJ-j6El"CцT.x Lȯ݂F7@z)*ԃ`FC`:ƽ$:^LTtm {yf΅Vuu>p I%/;°(kw@ `"g;3aRӲ<K%:VW#1Wg6Qb ly0 -xKTd~`Bz%C4!ɡN"z>< -0B7Btj1.]g}F`9-֌=>^҇TQ"*޿\ I9 -NW"#^hoJ -dFK6Y)W\W6$^65rpru<ӡ[}2fYJꋷUёaCH - :3fk{!ЏmsZv3K"|ٜ9  hpm .:ӛm-<Ťpj5*Zdl*Bhk$ m+b\e DN4l1 -Gn@loI~ Cq#<(0!T/ 3 T*# J\i-QPF̃p,k::M-ⲗ$`lR' 2-}EqRqCc[ͬ5;~[kSuÒC%ЇpaODH"kyuf)cݚk'goGov-\[l>UZ|&FPendstream -endobj -4261 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -Ld34yy+VGI#&F[Wc[=lGlmw%y'!.˕l7Et3?^nzN,릷QݶG1R=&<\46}ӻa,0i m"lLW@[5ѳ'G`'pm< wGQUڒ=.cd[t\ -BM@:L1w聉b/j?k0h1 TLlf>"\E\[OOE` O<+u\ϩm|ʍ0֑ Jf=8s@{!?B -;yޣ~H#tȔ⏎ox^.d}.~a"NTCv&qu^J)Ky9\Ĕ'rnT6ȔL9;Ol\z, G)_Pѝ[ C6_ K,AbJH0]xLbB!S{.ljcU$x 14%:&.Ё"[~rC \3ڒ];T|-@Ϣ_l$kvZ1rÐ墢%^nyGs 8 3}|LҒ>_ 3Ϗ<oM~ <'ڝ-z(T[0?uAÇPW<{$NՌ_oEiJ )$FQHdl/FpH-%r3,GYnvϷRA|m ѣ a@iz8'n%ȕ>׫IzqzhSD?< 5&X@kkE!(]$~w K.3ᤱBAca=#@+W(TWxֱjuP%q]ì|XJrdC:)lHpxƕ. '("[! 'tAK ܕqY0R4˷V`4K=*+Þjt-Hի&Xendstream -endobj -4262 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -Q6VL\cJ==GV:|W`ݐ~anzpVo˟{c'7(6E! ޗ}UJ9*on٠lb "4jL9MAZ|4;c<&KIc'}l'zB>1 ~HG *̼ƒh`l?))Y#RfΗR&<]]N6P,K^v k}._A da$8 A?+wLUQ'LnBCqLº ƽ7(#Y//pI.r¨㨍_@8'8.pbǴ45qFԚG}K3#؂;A:vs$ÀMS󗫶!ڇކ;StnXА6 :&Yi(^n=: =ixk -;=s]x2]O3#^D(doz= V;4M KLqҹ,"|F0BsKHj\'~&z?\B.!erBLNj[KusB6LHrN,Hcq]_1 i69\ wcڳ<JrD8vIёҩ@;kܸtVWpItv`su1v} bVD_B[|Ux@gr_2$ ~d|v;;o((J| --֑Etے)Da~ RP*y7l/ӚI~Ho#@XWxF@ -B),5֡UG> \XUHNvfYCdG}qҲ)9E wЊ -|X에mx\i%L|4 K]^R=qJXiz4{]ŠUiĴ=FdԬg1:f揊Y<<t\E}AHg|p럈a:ר3Hӥٓiko1Bf0> -stream --ܦ6\<| I?[ξ([`tO׎->a瀳k xEtqlܳ5hX ZU\^K\SSsh;F{[o/gNfD!tvwP2Qp PBp9*-% .h !ΐ/R#تԒ(h2J W*CZt=3~ ɴ$7{ϋr]"1{\,\tnw9?c=YuÇʎ":&StaSX>4Û> -2p[7irE” ܌çV_-&j!V'(SQ>ʨ *)?G),$ -2q2hsHAKB>L< ^s%q0VwmZCb%u6|%w@78Ph.:`٩ؓ}%BRv{z7n1gMAOq53riT\BHaBFGp 8pC4c*IlEGP֔1#>\}.n Q2/^f;-x`uvO }v 8R mw?v#>~#)6UZn=U߹,XoQ8y xv -*cK+Ytv@d B4G7xyܩ ^Ynvg6 X"eGy/3 o@e-跐"5)H]q,k ILyqVLMbFܥQPf?ܟаH񘩐pU/B>E1s 5O*]뤿Or?'H|w#~J'˚e0 9?i2p[=C'.s` δb*~dWŠm6ƐճB.98yi*ІH@ -'8ZvgAT@/%[,CArJˀm -/O12LRHŸg|w"X z7ePWc9~/Z4 sr`b{ h ~+R`j3g/S^Oaxߓ~ MKT{~PFLm'iB<%[D}9("DH&t -CӜI"}AU js<}lP't.lBN,|d 0.m7D',C-8ݴ`endstream -endobj -4264 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -.NXSWk)k,7LSC$dumh7BѬJZ9b>T3'2^*9 p^.2-U\S1%''U.lxx*1T^w¦r=F'Q5wupr[dMzx\LErXV!Ha"[6Xft~@ b*ك>M1ODqHvBﳠz -TNT@{ŃufVd9ٰUoj2P4mD٢Yi dh:C7q΋Fd{ Vwt3͠OupbE2bAhHwJry{[*;;wYk-۟HZjUh2Ũ# *4ϛosl0雞]4&DC>-&6!fM͕'&ퟮSWLnPx:ap}e&?)Zuc+^y -{-Q7{&dO\8,TjjxFc~H{<pL42VFlGP[T7G6݆c^:G'mN1AAM@tL{ Sťm0 ({'buLTV$iw)4!a{d|BږSB㨲:\H%A)!m,_7_H5_/ur` ڵ<Ďf:N%tyk5^uȿ LA20z?_,rӡ(CCGsntICRI!śo2+90>' -|=5͒`zȗ9B/E wٖ)}< SnXNv["lWhcΛ}dxo5GpQҸ8í+/ꘊEaP۔Y%( ':3~>K}g2)3#k(2%Ch'U8oQ۾`M#aV@쌄9?X? \01 )AD~#Yz3[dq}1]?hpfPB?0WoR m< uTmTn\>. Ty0)ߪ]i(h-Ds9$+L4â^͎FJ֊#](`g@>a6b1km3g({-y?8󸤑~po]KPjC y`o;al }K#ߩ~ek3 endstream -endobj -4265 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -ˉ:A^$+O~_4bt&fKZEj?\Ro`3 QȾC _9U#M9X5rfeVvaf(ՍGޥJJ^7i}]bqK`'sNc9dV?g[˦=0UɃgm`*7{H ~GYlI&H3[!L/u^{QQȄꙆ!6yϱhք/szT ZLUR&;^t[~+Zuhr _4:t;;9K?n[.Nsn`W"ho<-=qR%TnIg@FJy!u-g(l]"J!qAdg`V X[}8Lj>6'r{s?0c1@5'MvRFx}˻PUBVnw|>k.MDu>Cwgw_5F~RYrNh+Ҳ=H>:R bQ|Ye?i#h\hqEOBZ"Q k&e2`Lk|A%nlܦEyY`HhvDZ?wOóGlO<{8gQUef0y_JS÷%#Nbz}/çLkdaک*7&m<FP?RTST7ˍ \fW @|wشIhf}0H+ӟ#..{ݰvORnA# shmUpջ\P2>w"I{lTmXv՗JiX-<Up !q -ܤ،Ks/jǛ3 &^>ԃV\7*R!dV%'S,ƫ(LacV%מxȹ,v)r[3}z5I7M1><ӳ ֕,0m`^`0pMѬqI>6XRQXU~^}L71LgOAWZY(rŚ \Z:k_td11d?q`P9a+Xv=u AϦ[e{#K<*#?_R>VdizXrYDȊCҳ˗h}69gG c> -stream -@hy˖xaz1#:- {EPRVi*rĀkU`g!kԚ]{RbczyFtFZEqsȟ4O+e8o 5Zަ@>VЯD6V\ӛT¨@e̶V#}i0s'Hsa!C.*+Jl{O~x&k#D4X' -McB6K1zi"< ?7O -m9/ѯ{Mː#Zޜ7~5idsGo\ KgZ=#:NWФΤVi9ʠR2TD&z/)Rktwܲqvp`TY8?lC?@7(S6ka9 }B Tz5 ox:'VsYP/۴TF+ljAVT,v:G0ۡ*:;_>u;+uoGBIxd[b@ -L)03{n+e.O``+"Q(w qi $fEYDW,i+۱ތ@08«gUhVVU-&zni%3*!d8y -k3.BG;Q= - C* -F ?eas6hj[qM4\Acm*H1WϖYòW\ [s - 똼AG2P/hzc!]q[xvp0e]o4K, hsiMpu-wk4l==3<:dXh9!O -b!r*B؈Pl ,s9dc86:~(zFF(29qThkKlɔXm8z6F/uNnPO`ajXG$r\<'֋@YİI`t 5ZN Qs$z&ٗz-!OUuK8.Gy 49Tۍ`V *Ypd?F1,_}1M!! jijfQu`fWn(3q! -٫]jnvJ}Җ,fהxfD0уēmendstream -endobj -4267 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -r&k /bE>Ƶ)9*8: %g^~vE^ad  _hcM -:{m9j/nuRB] kɒAMA>@1kx"D+'@]M{kmHPh%qLTF]Ń3s! YI qIR -R>c08=p ~k*7: AħBZJj1i5>.*OEɺ!jRjt |ڃ`5^+7*Hkp3cU'dRq!xzmo~%4S"ppr]WhGNt)u9WH3` 1 +a:86jHގuŗ1AKTve -a1cʲX{8QgIkQB=M سeVWKƄ\V~2i+sTJ)AFL9-Se55F#V^HCtP/6t[*Y+c`^BEhf@@іX]9Ҩ91lt/}a\.F,p|2s W(woBAvb_$ߍ%{S -XwXk]moBhs!F6o)ڽ| 61Η"豭.M#&Ul`9JQh'l!$_)256_n_$'4A_)D.lڶuYHnJh]s\.]6r&BV`b2+<̰x #9)'E̬N&.v*A) :} -t [cS‡3K (dAy+xXg@e4K$W>4~yDwX!?),Y`gG={T7&y*d>e8mZ-.rl-8vɴݍ!,a4CQT@T(;K| -/UUrÂ̶ԢёCieIs;(Y(1܀m*\ɰ/"{O^؛eE7Uc0O&=ꋔm$)TgV#Jpw!CIHF PfQ@> `AfT~V =eW|*^~;ßte+ki} HO> -stream -S?S0r9 CָdzʠybDvqd -:]bZE_:ܣ'K5<ߦd5 HcOy 7/zKPo/3]&ىI 4'BүBùObKt $,z%פ=`WW =VvAZBr^{|;ז  #`/fq -?)ŠWw [Vi0.)-3 {j4 9< j阡x)fErʠہE=Qe|a2V} i>JP]@W*|mn,=[XNiy-~pfςOr80&MwfxAYnH̫kXQ5RS:-V2 4 Dދ2ΣQu%낌$F?-$KBFk7,q<:l# {QCeVt;aSCD1dpokѡ~RIs+Zmb*mS~*oGg9>qxMjsYZ}U)$'@F$ qjfp- uFEvȀr5W  l6sV'J;["] EA7˦AIeJ[MR%YŨej0#/] v]NE8IV4$HiB+S X(w -@t֔Am:3kcL+ؠ^Pi7Sh.+g7UJh[ߡQNo>0V fl`DA +_r1 l͞ Y?REz9hw)G'F؂ebc9 -;Fg GwWendstream -endobj -4269 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -zdVUڂ)` ǫ]թ͚wɁw.ɴSf_g^_6XR:qPX|+{~Ey9iZ5?Ǣ_ռy첟wmNi; @t -0&rXi<]aXn=u5@dədR'%掶~q,ըPQ6T Tҳw ˲3x\R -{;ٸS!85tq^{Xi8l"(̹;Nn1e ϩ'Թ -┿>ȃ8՚齑ղjQ14ފ7RRkLwc!v&%=1'a+8,$Zԃ(X&:W竺6~>WuB !Eߍӵ|*Aaʼ G -=vOd' W;֠ln73Uj7^O >XXƢ56UOYNuiwC)J0I׫% -i0~r6-pWO5{EWy?M =6ΑvQ=2-#+)PLP zLҿ r_';@vw`2,3rL\BX# -9Tw%p֬ -,3hk!>&*tI\8^<%H2UH5FI?V -$,$a` :o0?eQ,\eow:u$7۫Qy.|0 /ѹ,(`K'0A)j -d(;ȈWiYBc8ִpYDžne`YAaiNA<`^Y:%Š\b)g{AB):9D\|x[(7XanE"gJm롲잫CmwEA}ESiK9-]X!c|yNT\B!'@j' 34U2GOpsHBW^n$Tm.Ѷ^܅H.Ks9"iv1 vUPr+AֈDJV'NѴC [ -u'4bxz.G"GV_P>GT廈# 9@Rֈpi֩He]`nw(`ǕO]~Pq=w%aaۢቆrp拒q*B M>l?Cf_<| 'g媫KEYא(X.G7plMۗ4:6؇FSu Zqz*)z|߲!t7%NV-> -stream -dS`bYU-&"w79QY\=D.Zk]>*z R"p샐I蒗BMB~ScZw1O0z LG,|^\6,5e"K}V#Eé!)3g/aY|BKţpd@鮕ȓ_Fx#'R2zujgZp1c)]5X11j}%''i (T㮦o޶(GNvϴND\ZFRƙԙRSTCϛ-lys;_v?#3fr1R>Q% h ~mXڵ| Y!qk!Nx#Bʈ58 "IkBՇBB9V#IuhJ 6Gm܅~򥸚.o/%!dV3?帿 !1AnF`b Jp/j V>o+J塶`^E.oôԴۨ)X[dR6Jhe咶Gt@0U@nnr=7jDVmkv O֭:$GG`(ޅ/4F{~@Tωn%7bzjaտIq)!yVk +7+ぜȓ,URA *7k]KEL5li8Q )P?Z|&xT>|~,?+MB{w -=-("6& - ч.-`nEcHFsi  Z)yHס *T6>f%(6R%B sKl6xr8aw.A^*cl3yUvb0WṬ.y4t ?/6q G}HYI8 >l&Mi̬AEW㼝@,$d|L;%Yi(ii@0hr!K1 wl@ߛCI?dOZ-1QiG  :¼.0>xrf4ĕ1hF}[Y2nCW =ЇT/dq!N7l™/9R4R(h\Ƀuߛ]]%7.tҷNpXR5P1-ܔ1U -<<*`$|&jfLƥ}PCaQ}q VDF;YjZ.>+["znpBFp8@X" Fycu? VPv>wL*!+!ar endstream -endobj -4271 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -ZGT4j;" ^CIB$9ck0جj:XGua#M/ԧ`_€_G74J4-Q1McVGXTQRz9Wgh.Z*rȱ(]xpO5. x=EBo' ;!' F(_?ġ"h˲67WeO:BPBZ\sX72Hq$Y=(/F9 -?;=D_ScY۝<1~K\gƋ-/b"K%DkhU(Zp!j ^i %5a/PXRς# `*9:fIP> u•4xAVϕD)A2™1 a^B_*m*X AO-Z4&_ `r2|(ڡ=P.I&4{e9Aຓ@-mw_ad?cpMr 0hQ=qFl6v36C>^;n-EVtO&{$`63"k&,;hv5cYb$fL8E,)0nC~r\Wo>яRV* 8m?/a"#(Ѱ76d +^/w-}("i~٫-!n&'*N~gie?AMmQ t+1)WO -xcHjU&QqrJזm4"n1NsM}gucF+.E£TZ}6:lt.F p~I&[R2~RYO:/@ĝFN{rc57oJ+MJ`s`!ȁ;͠lIHeYQxQ9tt!ox!&I G(F( U/˄]LVE0ph_U:%Dr/+ZveZW3k QBʛG +#wPqOz}6<du< ,oyf{/V 0.QGWϠ-Vàu1D>Yg&3QWaV*Tf֏.Sx -S$٧jKKYh1 d=N|â^ aR'(mNq$B|_IXU7ܚ<7QD,V! CFD^653L:BC@A2v?܅$xNjcTc*ͨ`X@\˖]`ˌT>ʐ/m#o|iMDIrPPp~sz?zVDv-Y -^%a%?x?op2 z|CT'YrR3 jl;cendstream -endobj -4272 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -^ j'SC<8,vXIQb[ԓ#u#=+FthRa׆X"vV8u41볢gue'r߭6(=Tf G [EE t'A)Cv'N4Wjd@r:b^N͌^}۸K;%F7|!8k`9RwӃ4$Kdt._suɗ&pȏ5C4 IUQ -Ʊ!Ըu (Z0aąYfO#P~y,U,/F/z er۠xB5{kBbJ%? K/ 7vO>2K-޺T$}.gJ6vJs[f-z# Ϳll8IXF9#_u;aAx!ꀔ6L݃wiP-θw6B&0GyA Ë-RBfn ~1r%:i&;?2R>eB6pHRUQ_B՗D pa{B]!6E~=U"{KH ?mC&zK8Z6֟&Y-SSE~#r1'aؓ|K`E>^(%E" "ꮇxaDB֑4yQ"W40"hn)28=6 BL*#VֹBֹ&O+cW[ٛȓFBv2}Ȑw'hj 1+-yZ]7<ߪfzIBkuj+ǕVX6=,[7 :t=C'wA|`jSq$h4fKD!fpXpm/Bue{!F\rMU v\?7%(Hz;[ -1| X#:U 2qLS:ňe l͝GD0vxɤas|)jo6CjU׽AUØEA6 n_Ihkd^5*f=pM2j!I$ vՑj=UB`hBK􇥮endstream -endobj -4273 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -J!M^Q@w7|*i9'Tn $l/%|BkxT|4V.Fr[:@ys{J K0%Ќy)KʘmЄ4 QYlݣ8Lì"b\h3Ҡ -&!2U5>@3q"'v_Fl'\'uKClx^!5[j{L<@]w K5oXȕX,AD]L#lF$%Q6J_+c:.xbݭ ߮bP=H!<>ZGEʔ5;\4,N֤撲Oc{?n\A2gMvN*,)~tW."oMDM* m9+4:DдlU6v=Q{ Q:_t~AV%,y(j3OR:7911ydD9tްAR+->Wj`) :B_80Oݸl@Pe N6䂔/ɘ'd֬X=w u>]f9s[su4j - N-0睏}5)%ub iIf }R9uTÚ A2+{* ،Y>{Pkr+ j;Y!Bo%ϰK֧豫;W~<ۭ +2 -;l%kŎ -y7H[T]X(^wfLt蚱#N=îȗ0 -,b%mMl;i -0^p;WAD=R:Dl! 4A4r~y.M2S)H+Ŗ>WhPuPvtTVG*A`}w$1ޘ6*l\u"-@R[gu[iPqph%+F*ƽ;tG Bx?`seǗU| m`?lM"/F*A~t1eZX(Yt.&wkKDIɞ#9D}TAPh7j22s@lk/#-"$bsS3s\ůȩ &zC>X+>S%h]cӎFr^R\$QL:EഔYWloǤو<_C/2WЎS uS,&I-wuJjH?clU7(0Edendstream -endobj -4274 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -J*¾ 6#[D =e+afpiH}!㡪Ćile{18O'9i$Or H3 + --?% -&ʩD15 )IO] X̰ -XK' uVfU]_F•ymv7feJnXϒ1 2E_}yeS=ۚ/d'UV3$VD[_DxBEY"r#>4] -JrCY,Ff -Uגx 0>7riKm ] EA*A䉝R|W*FŒXS,l#;O[:֨A@+T% u%a^e`DkU-K֛n'#|RpfOqV>)\,Jc 幰m,OGx '.5[!8aPoOrV $LYJ`g,[q1ҝgž? g^ط;^0@ҞN1`DjŘ~S:?Kke^Âޱ?1dbSّҽfChp?rp,CYSkK:EZQ8&gZT6]s?Ztkܛe6z;Nb]1`Ro#<1u\$:R`|}1L>5AoS8m;xs[ c|&O (pX-C2{sbP|( ̭1uc`7C%cohQ.'‰os; _?X=y!{?hSKwq̤Kaw hS/\5y]bN 3ʮb K] -GiU4啐DmRh,YJ>\L^2Mpd&,.s^K"BΠ!X%QDȨFn)8Ǒkh#¦Q[L[adQ>,N7] Db0 AV -`$M3Ʊ8|aK tl5vInQ}o:fJF6ޚNeq,qD.L3kc: -h_QGo ' -~iN%։[9-S-$S.WtnyqisR;{սyfr3(cV٪'V\E:r?:>soiL1$vbtxp=ΐG^oZ МowD^?K&v9HQEeHy[$9U`xw|g 8YaY}H̚6kNq!66Nd!M4! ҝ f -*# 7W| -'LaUfF԰EQ*w{OV"ɮL?L<#T>sJVkϨx0TgYo&)i7a9#pBI"KE96Q<(\[2 > -stream -smCf'k 9}tc.q\~j< -!hn\B@tV sըUx1SbY"z|97^P>~q?vmc$kB-k"T?i^C<TGmjl)}l: -?6DCݝb$2 OsYoH~Uh^pGrg=ҹEKvD%qYr|t\,~ޫb[C]k| ݈OPեH^K}E%wM}3EYYחpF rYoV`R[ޘL?J*f -OdӅV<^U|GMTܬc -xI2_DgQuy 6f;CDDc0FsDZs[<>{H\&G[jq?D2kHMW;lA7sm -fݘXgEn*ny(b&rIMb<*)IX +85YvQ¡b<8 r_P\6ó㵈Bj2sx@aQ,UޢT -7$ތtec@:(=#2p=4nH24\UWNIP$-N_>^_-4~Ye-#UKҾ5(ªԧӵaU*nP&iXF[ 9ĕP񝥶aQh?.D5sb" '7*Q!kpؗy%e hNc7A Vi)"o ;^}÷õ@|!`=zM'08bfEy-3Է$9IwSao,9=19Ww4k>&K;YpI~hV1 (@b$0 !gM~9~°ml_mCi֔WtL/Mf/kÏ\߿{Lju[~~bkBE(4).+@(^sXzcՇ*oݪhpF㗼Ⲣ9!6b3AvN:]U0ҏ:)S J`4#V#W>;˲IZ&~}n  h. / fojTW~HYCb|M&&m@e5A IIgQD(K:xcS_=A9AᮎSԾ"{vŲmӑqӆ&ry/DD 4iʊ -!ΝwBMcmbw򜟯⟼;LktnsƙeƦ>?2՝+EK.Y1%}'؆-h W]׀p nq++Z9ӓ@rE˹?(t];ɟ^A2+t(%~Pb}@3~mUe2w -xFFR;MZWpY=a5pTH 8W/F>B3waiso_Do܍o6Qa]j;|Sj\uƌr+d Ї`Sߜ=؄txg> -stream -C{~!o~m{^Q@%.-m3 sX%'fykϫ\kO N0j%J#%עºU"O[?g9{6CpiQwz@@U=ˣ -wHCcm+_-mr8>ӟRVC>L cJW% |oL";Ǝ8+x*XYG1e 0L,rO&w]559ܬ˽1ƒU%z3$bTCQQ4Ru}(`av]K k蹸BM j4q9OI۬`g-ˈ]洒6 ]zmdh$anS3s>]~yځ02CrIj~݃Jat7=FZEk.7:ɩ!jחa"PrR>bMQDANx[Z_tfvH? ͑yNizIfCj\#&ݺvpܗyXቪQw-eg2?UDyᶇ%ՌM6=4Bc%,Mo(/88<\ fX S?֯%C^iA)wv-v6pl_**= /5'l>YgW -=RO43k.oۿI}rw |g Woqɓ.]ڏdLGԑ:O%z=XC -A9uQݲ@rٯjT` tlq3M +`{4kT03wl̔vC5V(cL*N!`MsR`X RҋLn7(wf.,r_evo%'e9rQaQj럾؅ - wN@6 +ӺWqЃ>kIXaZl:Rw -Ft|5|WD+X $PMQOg%rsXdM,zKW㾔,bBgyFeű=[)so\KeBƐ}a}%ƔŻP ArK8^]Cǝ͡5Yx||05 %JU] -Wґ?ZZTfǘ ewZ^ -efW|m{TUKW35'rjCGKy-_+ xωZ[/g-d2eә2åzZ&?qnkVhTѷmÝN}>+Hkڵzuo)#((Gz", v3[[^9dJaIj0H>`8r'Oy3YЀح2z0',٥%xIYrhO>v6(uQ񗇌n+6/ߏLx䷵v7, w6uP 2eHχn,^nHE/yLC w99,Dpt5[bzX&zx`oendstream -endobj -4277 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -.bh+Am - YˬTZ% g3ϩqWE'dRz&t9U.D(0hBX"ф#+MSN5ʹes_G\> @Lj_'e`Pg E׆!?ЋE\?Ed+#m4 -=YNp|p52%ڿYAcӣS!$#2f--}~Ɂ KyEҥ&a_!q'-uTUrd46f$L]Q&mi, 18H?oW4FpǞHi7mh{- -2c[GU,kB>qx}Kݗ,0k -oO+(,dʒ acF\m V@Z%<w NSt'K+A2RZE{B`e7g0,in_慣OdU+&cl-ɇ`]Ñ˅_fƴfڍ8My>G]plč-Q56D:Vun0;Ŷ d13iO#5o~WຖFr]2CCuDGĎMw''vV`f!s6`@gxB.cNiG<8 R2qR'q(!vaeiC'X:o lh-[J({È(E;Qt:E|dE8hR{?;uZ-\pV0ƗvxgL9#ʘS1'":y)endstream -endobj -4278 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -rq9sL:] $>-QDCu*tI s+/-+) AwçEy6{;jܿro6?Ϝ}KWMU|yxrj41% ϧw^ -c= {b ^* _LZmmܟPUͻf R!83v8d2XԴjw61نDW-mP,iǂ{оn(џY}$)9BKܡ9OjYNq᜜ի@FM\OhHzTr٦ot -uٍ|"KGQGQ`bA8 8NSDY.{*ٸ=5ʌ(uڦmc)P=[M '0c!> -Z{|U&'dIA/Fʼnf䰀 @ǘQ, - NT𯞦h2-?n7tslZ#TF ȝ쵭Q8PX - 0b..Dq$O߱, 4XmFD~{,on ]e"1XaS/Y~n(MԐ%=o$X|"c7 -=-}* Ɩ=w2m.Ne1)0Э>lurG:}BIZS\/кt'A)lP|֨CEk:vSnMj -/(:ݾ1.[I5P2ZH׀G6jm\IWk93\87GSb۳i4('ڕD$exR?-ى 8/9$t;"ghE 7zwGJkH> -mQ*3)Ek;7zH] - Ɲ9ow΃|L=8^f"^ `+իή\ttCEDm/0d:9bѳOט˜bl ŀj%@[`‹Mk_0ZKR2Q:#% iVQd3:UՈT`sp7?*+\nUs4..FPҡM20bXKQZ@u+'1)VkIy`I6 `J׭Ui\nj>-)|OA|\~MqSA6ng*J8XʰwQoE0㻵iM-o -%oendstream -endobj -4279 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -)g؝(Vph v9I^EиZ)~{֛w*NC͕Ϥ޶Oj1?~v`FH9JoxK$©2"qZM pYsǖ Cư16Zbyx2k[is4=HntR%$glzw'eEC]h !( 8@HL=ӫ?|KhlNY ~I{$ -ibm!,s̞~n.zΓ0O#"4\^\ImQR[YW뽡}Ј~0H^2l/")3Хq|Dq w8[1 I''lr~xiocuHa1)zw+[NF2v2T͖A|8?LHF[qŜαږ[146I -  ->5+Z/kbXZ,r2j)MH/iicA獍eFԗ̓ے[):/b=k-(g+}O=y 4r UpoL8!iۍ<љlE8Rk}pR81MGdu i/L5b-\WL6~D1ߩݤq;4@l%x%)CY1B: (Cc]'%T2):*0=MA<:S"[0e٨,R˂G@wUCuJ~bb}D$բ/`e/8qI&+a a~˹s^2cU~/jd+k&֯ LTN}֡ZZ+R|rWF˓#s=Nӌa " -kJ@9]_GOЃ|m_Uf𴰇7j -*e!fXC -)˱hRSu0VpMt^*5(|W`N)P"3AZjhTv)-¨YQ юH@*#D|æƣkn4xdr3ms,:T4ݡ̱w3V]3%)oF7!JT9[:dܲ^fWiit*k7T%'.0we+$'nW VHVW(]&`{MYrl2NoLU84z[ uoajM{ىp p]^_= jdwvo-PjKwUHI%FfrٴĘ] : 3;^GsGA]c 6n> -stream -{!}@vPBl:D=3r6+j.}-E@>頸JD!Jn(wJ br!&m0mVi1"iuF{(y'qTQ# -CU1$ -5MۦyHplk/5{4%1epKYUѦLۆijvчO6_?S*~tcj"s6f I?ЅQHȄP8UF6Tq[t[@qFOiO -Q.=GxSryЪO`Jo-q22SCACafk|VHt}"?9 /Ir=9UiTICe ^ñ[yB'Cʽ2s}PKRA^a۸ -^pvuWX;&xBB%N8yGjYToi+5<^`_ZzVUr.;J2T^fktYG ]2,)׬0פj1͜4&n"|N/`]Urǹ/ţorb1ڮE^["!&'w 9vծ,!uEHy*4`~6tlSx a~9׉@F豠^<K ͪç; NOpDdd:1ˡGrn,h2fJk%ǠWIتwձ'\W)W3gyV_sQ lہt98/ݧn^3Hk-dW}1c(E9i's} - گ9"`I:3pc?Ԯrޑ|DfWG!z0//HkNƥ`{Ct%g/qHjʺ%*6{v(K)2Ș|P ywδBtQCUoSUwQ+&uͫZӽ]ϐ;qaD*vhz w>h@t oSt,pp2(FFO8!^E|Dkwv|9(Ysޙ9 -`Jw8v 0RB%j:%}GҴ6bI923`=Ӻ|0wZh0W"dzbw|bo,*KZ yC/׺JҘU식2f"W\"z6Hendstream -endobj -4281 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream ->뾺)ɋBZcOo9ncV1*ڿ{iֺh#8;W0GTvb'+F1K Y 51F{lۥJ{-XkHYS~]]pU6 ot?>ƛ&Yʈ0WIdGF_M;GYۇX=2F%cHVe o$cMm;D7N=GϾh-#2_aSØ%GۼHe [!@һ$~فbqS7,C\BDI%Qn}}rK.z` / &-7]:V6yIsa`w[;JmX S縪ZuF*i%wk[ӵaH%j*٢kӠOzÛn>GfZB/ 5<tBGU!R'/4#Hۥ|p^XjhH櫰7P-\ErWf Pff9?-icR(5Ss<3uYs$Y6_曈{{)MuLTh=r G)JgM>׾?g=^ 38¾q=LN K1Gf0|&^zғ`Ѕq{:G˰GV``M 77?@ P6I,[ - }|DXRX#<=ÄĽW&gI l Й5@訙dfw r=9w'2F()m^4 s-材HP$yGJ~PS$NO3 -- -:w$XjZ J`p"jPP|)+XSd&KGq _ʕh<+U3̲E3X- dM -77ʀ.m̜u}w2:ZXn^CY\z&je].-T<?!rWE|ˀZCXx@9}=ݯՐW*+ -XBIAg_iOb7dq8wѡUva@Sv/8ĉy^։HNpz7עFYߜ#D,$z؅Te͐>1xoʄjU/-eL.}akX"5XT%QyyJ# C}Y,HQblEP~jșA> -stream -U"bJ/N7-|)>l_IocG;wb7pTYMXLDMUJwy_F&Ƿh,%[PU?f-xˉo,~d}lSgj</?mJ ?t}M O7 -ܰ q\Yש{4IDz( i!Aȳ.Ŷu4VBe󎇨Abg*b9*u"3qN &26=PiIJb#^EEBįD@d o6"7 MG\-4M=F\pʜjaFgԱF+j;q9W堺\/;0\gS @?e #sK61aJQONq]cWUEsʏecOs)yEgmHv_/7<rM^(ͰV8SfU50 -g31!@̼Xu/Go'11EW$tO7\iakGJNфC{q\Q/".e G- -TGߕkHٽPߴwX$| WlB Gwuv5Sv-uqT?㔚ѷX;gx{gņ\XP.) -՛ҵx@,W=AWumYe ה_8! -H䌧 F̺-Ql ƞ`Yc2ox[Q=N ;K'ZIBāG,;A?fF]7W s, -`zT]p !B(f)/;y9MlY]w?`|oHzATaD*!"毗.[};Vk\ -`"w+#drMBdsTF N\.x&}ҿBt!Ƒ0kzCr2cw~ϚZNSUK?2R$@ITxȧ24? ;?I`=_bmq4;p01hB'띱rw`$Q o`d1K| 99j>K|n\2>Ϋd28bV#Fǘ/$.Nghcl๏i)L,]dm XTIux.g5QJ'-dp2sZ$i}st8j=t5@,nefԊ|.8Xօ+Bp^D&> -stream -G2W'-$"To 0Ae XU+-.M99}`jKz]^| 1/E`q .AɼvY10qWm $n+ -s,/ܴZ8ɤiW{8PHkMBr_^W0~0~MóIyl\_X0q.p[Y[PF=YJ[z,:ߕ5W7&3d -Z]t'=4SVv 6Np͉2z ]MQ8!~ !UN@v߼Ʒ~{o?ɰcg?q@n)<#V%:|Mβ#CsK1o!dH#ObOv.'3Q_.۪95ya2 ^^k^Oܐ~pbx 4HZ5pCq -EЕVk^#ד(\5k-eAz\JĖ5Y{52p_rjas!H@Ǯ<)K2D&j*.zIY<^yl`͘r%xR݀jxq3`}]<a(X<ĬbRD - 5B[@[R]|ϳR!, 7F46$+){W4 snn,X- K9^ߋʱez'O$Z⽚. hO(輑 -#(d IH75cϔ![D; uNi\JQ KYDPso AGF7>K.Fe&mRw PvĂt6AnF39?5ud.0>&9oėl dY(,3)2vU(cc_՞ckC/+פCDߢ}uT>uѧgH/NVz0w=B;oahxIM^ . B!#'%DŁ@>83鲬EU!6ʏe_]%U3VO~\54!jًWF|F"P3C{ۢ'YH2)/ΑcX6j9 VJ7C5Uj&/Ὠ} +~ԃ1A*{F&jܗ-hv鿙IXSرldf:ʁ@kJx?ֶ{RyIaM=]@4)ī`뎁_9'x4O8p۠y7pTE & w<i<}*q>iTfJ׏ʚ/Ò?`e,sa\:U,wzĖw>NRދzuGJT -iT vW=I~V&e Qs> -stream -s+x! -l!{KF)ydHmbZcKL)ʸiP+AGڹ -vi붦v[7{GYUgYt~v$li麮b;UnQ(K>ft -.-HM2~8„hfi5%YwJg#0Kpd9@\hcA)_9$6S'Ij=J/:UxX޹]<<8T^b#32Ŭ]3Z+]2}"Y'_'taЍS{L#8-*u۬ Ae+5ENuzG/c[Lf(j TI/~"IDTY*33mieoڏ, z]S>M-@p72,g*|{ܒۯooZE1Ei$2WiXlز'ٚ0^#Ρ WxՀd-ĹQ3l ZX6k<; :*'< hv_1G[vg>Lq!5SʖAINs&ˏX -c,86f6[*cD9z`2rf3P-,h <`VqGY/ $.` 1e50z-5Ii='6?=*2M@(,S@pաh.3O*_$uwzBڷsحxMD8fEGz; -j$kqڍ2݊ <3#pcGES%[3s@!P'lO^δW M\`CY Wcxb2ƙBhl$\M($' Md~^Qv7GU6\rߊQ`jP Jc"i"`/ 2JN aa=?j.{TlLu<߆Bշ0n_f6P_DcT&,e^ueA}Qweu%?,M`> -stream - ^n;PWbCua|3rGqIgSۚ%/ӹ+7#"{qL -VA̷>eUw \RsRpX#`:S -snc@yNU m} d H UR1/dv]kfTU|x,}",y37Aѵ*x%4'[v]S6F_҇ƙkti)Q?4. 9<*S[ȫ$X$Uq>&,V00ws"mW*Q‹gk#̈V'U)N&T|{"WF?$0f/"E!^~3 - f6CUr_zIȼу{ŭ"U64桪.K:YkFJh7,W-3o:4,M[C.Ca KI|/r19 +i_.j|BYh&B\0poB;`]$wndeem+:!O"y'swd"J_iqw)3/bCTT3@դʍym3F75d#Vh(]r۳3v7s虢O\U~9 -q]NSnQ:/?oEwHipC#i< -BcJ>?dx)'qESN.N/5ᷨ;LpYKiwb;OQo #Mr,ڮ|٬!{\/Nnxb4~lE,W]3,f-\Q( J`u'fXe գGri&Fsn /7eAd`|Ӥm=jH3Īw2 TrJҿM=7.$g!>JGs9Oq?Mu@~A3jD%yW)Xx<.6F4l0۸Cn9)_1g>44DđAs~})Pq eEIPjdkVGqQ˓V1*j53EhI`&䩊 .~Q,%6X08Mg¸$XDh.M֟ۘA1g(z}J4(G,L2htt'׶FKr2C+e$:^|f'W^!5(4o4\g)s@IF6../+"޷i--[> MV_ ڂڵdQܡM Ԩbܕ9VJ~rS\a8Ղ<ɝXC(wb -Q.5%1 %Aɼ %|$<0ZT4cj)̀@*/+?юq²S"K?S7#HvHk\ -*ŀA;XQϕ:gͺm`)lF+ -!,;jejg_x5kWJoR -QZw Rϩ}"L&,N@ޠ^( ĺh1:/>^}5Dv'/dochTVyʼn,A75Yu?Wһ_ujQlYa,G[FUѽ0oQco ${fK[-|03M7)wnK~{Tsc4y;ܯۢ%Gendstream -endobj -4286 0 obj -<< /Filter /FlateDecode /Length 1408 >> -stream -lwϞ/cy}!B_y\aw%Vl8\FTc8')Bn ^V@KۤeUt7:]nNH)M%&11 IKQ0'Mo 4A$iG ܎ߛmX`Źie4KCU‰9RUFBg#ElCY21 -#I-8ZL/l _Yv&ߘ"TI0Re,T42CCR7N&)cgE2ld[- u:;4q!TЭݜq@+?Wp3'c:N% pYuY_"]|k!TNTZHP{>V71}@h`)O *bjڡ|%ej||N:nڅ(^s]GP;)'v%K:mRn{Ji[wYF q~5&i-JIz -M^-u;_Ɏ+oH=+ -Mt.>(3|?W 1uldҷ2v_i-Q({hFPS Td6`|!馞тq(]V+@֪Ǧ@ -F~ nwp6C -T4 q?)8 -MاHb0lY-t^Lԍ3PE| -Z_ 6f}~Rז`Q(q#/9gyq2H˻>CX;\Y`7Y%w>'^wH̚HsoL?.y< ->J f]S ״kV-sQNǏnKT<>~Ad9jKjRdV.{ T]t?A?UŚObCP4b1iB njC3\;=BF-CC@ai!A(xDc^ ".+;[@a_.צW\ ptC@h?E'bc=NA<Ќud 6ʉBf]q&26,ڇߌMݭ g[{Ii_#l,o6W).nȭ1׭.!J2(Sـ5vdĒ 9͚%¿۟T?:H5i|gE\P-̨?HÝ8SC5O>ٰz,Zj^-[ʟCV@#\ }%=1fİ5f6endstream -endobj -4287 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream - UI m BX;lo̳dB3;:w H]xDQ)o>^@1m]ho{cұך@H;``Ѽͼ挽>1Nb}Mm."[)U@wh}#¶^Q-uƞHpa*x4CKFӋIxLg=K3_p8XoШ +GA#m6o3 K;vu^3Mh^&j+;a\3/mCu hi#A<+~h(Nlw;JmZкat_4-vWs\MЕ̸Vn;SUKª•g><<@,ckДm?@q$ sey{*ETZN'5ϕ s%+Ҷ3y8BᣈAp/][ V_a..b)9-:DpQsv -S /`1 ^!yԡb9ib2 y-7g_h&^xqPV|NK\gM ipi5E^?0FBmo'MAK]<[Z߽w+M%tb -=FTB -83zz8Avd&u6*N.}xHF\{q`G]GEKEϷq+fo5dI_ri^o^$:M>I ;~ r4!KR~Ubn?_pÌ?^g YMPoEs~Lo4)XU) HWm%$H6^mhfl@-*?4#(F L$Hz:P:> -stream -v@(Qsn#\51 . ¸4x Nq-KpiM>sc4ƦG,I|!\k~+o*'9<P"YN#z+Y}> yˀ-]*e]g\h8./ʚHz|%%r#u*(|ZAB0Y'=4 ֒WrhR0oi"1ɉ~ߔNNoĭ%e)Or4'S=3np%T@)Zĝt0}di -zn\CRq|?!13$A?rꔞ ˘H]sۜy;!Q;7j*`yY唆bu4 5R*Ml5@Ń_V`mۋ_ɽG}#z*ra /jQ@9͊^J|8pj4/z7vNKQ[oHyy/|TY %u0!P5oӎ]dׇT[a2hԺ}47Ҧja9&C#>/=z祆أa>3UZyI3^ַ@ cWD%+-(yAO4Eendstream -endobj -4289 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -!US| _|gCN4F‰v5`GGǀ`7yUWT>rZ{p2`ۍkuz#gQS'EN}t:@V~6T!՝Wlɳ~_ZsAՊ}EM?m˭$H~svNTVV&V]S"><G)J/LU~n -̪kL-y{Q=U1iutbx^1VWwH}Ii?_QFC8?# ^2Ot3N^S&|цreE4N֚!%¶нacI5Çcp%%g: ³N[a6YO7*FtfSȼٹǹ -$x#-L݊v0Gf)zPJ*GlP|ѣ⽦|6}[bMt d4 k+Wfn~N27 *nٞu$K Rn6 XV\&M~8ۿwcneB/$Oi'fǾ[ύ\@~{9="(endstream -endobj -4290 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -w9n`/sz]s3݄ʻ@JzfI'~㳇?7J ɨ}'=ZނHt˜< t$uTq92R+dIf@oh1NT62wҬlNI W,MܨJE9:בFx~+ԷE D]#m,6ۆ pќ靷InpNHUОvis!#K9۩ qy!ީH1MrAFqjz"}S bOk*~ H Ӎ;GI<~,(@t6d 5O^*B"ib'e cϛ? 殖FrlvfeHc$6tD!'>棃`aX)&(p1e">N -YuՉx2׹Zh)0BGIdzRrJ <9jx\WdĠK͵b|rקO=ӽc[G6i&w %lJP 4!xڹ܈v=Kn`3BzliY_ӌ#D Wu=jvw'WdU 9Ȁ/8N;?}w j]뷁uqxLJۀT+ p |憞u5;q1ꨵK@e. ( 4!+JPh E(4Is=ݺ9k]A1|ʛݲڞViέ.#q>8/C):l-0 7ej0~E)ojuN笁SA?:㘄, ˂WTA8[t{[;/n?v C* ӻZ'≽lՈ/ -aΓ3h8[ 5iP10R#tCX=@RjmL|X^/^ܚf 8l,Ͼ9WkCeAxM 45lK傉RK8`uU{${CmLSڭ_RC41!5LߧCle<B7]Gm*_0+meܺC֭t-K҂ C cާb>w&&e@x$ ĵo$2 wbd«2y`ɴ@`7W$b 诈eQ);f -{b\ȩTB  $F+N0⤀$[endstream -endobj -4291 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -j:S(k#m˸ -1rfNWK9e -0nLZu4 0wjRe(Rb_Г igZ-3ޞ&/I:Ǒ:jy.݉ojRyV#haM5.1|\[eogSfMgu/Q.,k&Cm3ς`0QQvmEє4' xt$ꬵ Vp -P,i>O:\ˍ34M[c0&vj4N6sMO,g1 l|_F֯7 ʗQ0GVeJfb0N3sjJI8am=-Zg nz;Dۛ4nשݢ^LX#f$O9V=E<u9vQ_Yӻ@%FI/^4'Xvxfrk3>Y6]FhN̆: u;Hƾ1֌Ҟ>}sF{&Ev_ai OGDRž`5jUfWLd=ᄄ욏Ly|A0%G ~uQ Hypu -ΗU6fJ5qXI˨&KjsX湀ęC68Q+aS1΅V52M'xj|jna+vnu#Qk,e]<hUӏR؅Ve[ [yV(S b| <5=K!pq$S|Wnj`&p:/vg (ud6۞[jJ;jt)V2=qrkl{>mי$2%[.,-=d=9]j"q CK vbʹCҩO/%;[ U[MZl%c4 -9<=s,KFۆ'mⷧ!W{>aO!..!YϻM*DXss+q/UN;|LS A&jrճho (6hm|Sȣvendstream -endobj -4292 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -ƒxOlE'(dγf-&|!! Bۆ7?+^w!n׬Y dƲe#Qm*+l4kJ9CGDc=4rL װ!d M[ pD"RKA&m_d)P<H,tҨ;Z$.ztW=,5TA+",b|#UNK0~4 xw)-> -?RDޑZͮPb*BLǏ3.C{[oH5w?JyiVm}('F Yh'Kl`g?ŋ#SQLa@kѷ=8Z+GtC DW.V˗ fP릈90V>EqJ, -"!QÑ4S<H#$!C+0:l8Y5 *.t=93'ax8 9P*Vػ =OkDU$Y -H+Up_N:; qpi\w1*xV!;X%t#L@z=p1Orm &^'Qz.I*嘗 3:2ɮb'iN"o$1u2g1^} 0C 4LGϔ7mJBn3ؽks -sNWDkr"j7|#E'pOz/@,iP_`#IQ;}/ؚC~bُzĹ2!(Ny'$!:$&ihnN€:LxZ\?i0o_pޔcgb4 zÊ @0D'6ε^7m6ÓF" P8o?yOkL\/V}q$0SeѪUuaGwH%aAZ+MfUݖ"Kc γχVY_|49s+=zS"aт͙ ;թ*zhiԦPW>.`}5QYif,SlIqh %>1mرp "v2+GVeõF>iu?R|#cw.kFlwk;[0e^‰TqD('@C1ϫDRĬpTRxlk%zګ\%?n"3DJ1v&?bQm2蟖M #kN\ yߨԔ*GqRۀ~^IN$Tlh\yȩtNo-og7{#^C$g GaAVO)Xz[e +)ՑIωQTl/> -stream -~a 'Qs! A\.w -и~e5oVl _Z7*#Ɖm hQ Yh=QTm5v4LcQQݛ.H_j @2mbTV @kj 4S6B4F(J['ICI?)@ϙs͛ҩ_ Rs1SHVkdeiS؏zWI2yۚ˕ҵ~\Hyq+[*|{$Q +4k=`;>}wcz]pkҔ| c*fl#BUٜE/qai](.jVW]!ޅ4ZE*ox3΍?T.e:'d4ޒY:C1qh-XYA”.y^u٤{))gQ=FwXmY~i4J-]b+Ŷ yJVKB2I,lk^]mI b`y"NC\9}zFZ`LpiB3PUp373`/y]/832ǩ IJ(K-`ޔYR 05 !c!ކ<Lb*3 ,OQP?O@Y1!3 {=:cx xk.nU]eh dsrm ;sz/~f6^Λ0$r87m~w5fǒٺ@y,GĤi&J#O6dZo".[oz v,rm_}?XSq}S@{}e}cj=endstream -endobj -4294 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -\fXU 3hWUbTvm3h i)uS2dQHq -gL:8nK)oE=/Gt]1*1jk kjm|"X: ޮhU7p3#<x³b=T-SZVN]ݥ3fpJV~^oA" -r h!s߻.9GQ2XH$6[lEaQPE$}.<B̐xa_ Lu'"==r?ՠљ~WLMyy+f!ǎmdV4p%0Z: ʀf/:EXxmfla+*Rd'$ݪh9pjv5wergf[akx5S00ŁlτcL>dUYE8qaBr -{^iv'kpV/[BM[<3( ^Ե8.}Z:6 gzNZ7GcciQU~Y=BpN -bq}41@VN0:nbF?B5xl;;tY|*LurY0Jnvr;ij} MwՉc$@H/޸  s j2TqL> U%n$ȋ"_l@%fw]Y -{JO*OrǸ: -)|-FYu;.poA6+•j.Ƕ6D[9m$//gM`f0X$ m&>q:ٱ/M -T,C ˾1՘_XN4(\ב$=]?fW6Ӆ( ZL~,9ZfyjvALV,ƴB%u`ztnsOx4ȅsbJY ?V5ϐRYRq%YhM4yU`A5܂,ttNIך~(VCD4CÊMҤTW†k9)IN0RJ[N_AxEyIFn[9K".rJF C3 dYrGi' +[i.gW#Ԩ)ϟْMڣ&#Ȱ}Tۅz6M.Ivxɮ ,erpn`X /US9X?/K8޻|bÜGVm?(Bf7?%KĹ W&rqSݾ8. w~$"!6' 7!Dow' D|ag}Pωypdp_Xj]-6B \; ]X:2h9ִ -N,X[ f(n}p+ݻTs|7Tu9@v=X.@+ɛ3F"]39p48h,JbteJcjxSWsC,o6\2Bj; =%g,Bl#z:>S'c067x -.vJxbvsU6Z3]^rL.|~"INS &ȁՓOq1Kq"hT FbT!t6bIEfbJsl%g1&Y)^hڸɫ1G}OJҌ!}ԌMχ.+|;`t@ŸJ='sd#,N]p[jfs!+ZM39Zd} ɼ'%VNbľ -(“VbA6 R/=1olsq S&lFDZendstream -endobj -4295 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -C0&Ri`)]9M927o#5oC\Y ̓檪 -ze**rv[7ӳ$Hf)c MĠO:D>dMـDf"OK!]ʨ&?h"7 ]4D7vbZ[&#c"t̂UhgBpjq{E@& aЮw eO|s~?M%@k {"43W$뀛\Qz`NVX&@Z.6x0b3 OɪD}Xx7"IPpc$5tt;R\~zˊ] 4ܲpql@AI*5R1%k_$$ -}PtXc_5ǝ~^\&|[*i6&*yF%4{,rԬif!ޣ i?Զ,4I%KY/|~  q$M6TnĽN3ҢV8u4"єgP6;cq-ׂgJvеѰ0v_a> Tlqj {7yt@ k+\ĝ(u+Xx飴D#h;I5-#wUHܷgM/fI^Ip*"ʘg9B`Ֆ+RmW[Sj*,)z8P E!:6?2~ QܠkioAEI~^awnX'TcQWHqpĶ -1]P! -v)%_ܥWΏIK)3DJ΋iў3Ǹth -;f^MgúѢy^ySRl?t Lh_#ɓ! ڑuxMqdI)ln+hi:o(.Hإ$`jPLRM_o4 Y;1V.dn4}$G(qpTDX%ngoK)T&Y Z8TvQ9 NֲvzJ҆swb4}$+dT{"`a YF؄+yhpTC -.endstream -endobj -4296 0 obj -<< /Filter /FlateDecode /Length 1200 >> -stream -9QmQwԐj67s"3n2'"sg -z+&PdFO<4I]Xt1[%3߼n fqy:-V % -JcZnGwZ,q.h'Գ#S2/㺌dD% o9|!;*MywuTq H/4!iQ,Yw}ީ~LkϜbb"9bk/zIqPmfCp2`j~;ǑB?1VyI8` g0/7>Pz.y6cp^N'(,nNE^q_M8@&/ 0:JO.zXVIVjAxZi?!%P1j ؞sw-7y2-|{?/S_ȬlovK[KYXW -jo$^ ?g~71 g@;<){1YjD5A9G$֎&Î.+4t{(n/Ea _ʩ1SF+|lv&nl]s>01 o^T9 у66A\~Ln'ktEH+X -bfkwO %[>:2kX&kp}(vO#s7bužI -wޏ>y {a|/SwTYb`$R\h;3p}l6r>'endstream -endobj -4297 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -E{P}veQjΤWS 9nF>sw'.~v"]ȲNJ+s?;XԈyjn)t_=in>d:C=e)%+x9]}*"{QdnSF`»> APuʮ@ZonY׋ =J_N\ϟMD+Ȉ3K[װ\|]b|gaL"'΋P,?I]'ihzɋ}+C8)}%[{fh2/l?h7bvWT4W G/Hn`idhOtRCh7dgl4mQds;N7qUh726كzd=fDfpvso'qk S><Ņ}i7SrwgH_!QhIȬ5ذ }A`T;rfË*l)+X6k(A`mfSmedW֢r0ǕmL `R~o0ՖG+ -G 1X* P;LekEގ?˳Rjbd-lkʈHg]jjҪj\~vvTDTHqnOX*oܷM{mkMZQ&EHYZw SA hr.lva=\vTyn}0KLy6aty. 1iyS)\)΍Tsc X0`e_r(1~{d#a?H3 iXc̶=27/{=A5͛愲>\˄2nfPhbJnZ/ګ'a6wUVCM7>-߲ לyF'Ywd$"Sx,`b67k5"ΚGoT$ՙ &)HSj^[TC-sZ޿A灀g~$P z@4|] Fd}.S򼢸MxԾju<64SR$ml6l=P]Q~N!99"iT5ی#|cMI=ls -&m> -stream -|΋r?Jf )o@. ̌ws$Y)–ƭ~3Mo+?RwyRcMxE.% Ч; >f{e1VϗiM,S7|xU|$4ۂ2toDC"4S7 u^sgH8)T'f^U_ w,Ȳ ^¤7}{jTOg$UE*2St$N9x.^kPn%, 58yݑ\Ϧ[G&ێ֣IͶB%w?>j,,]Gހ}@2@d~qz6|YPǪ}7Cl#ZHD܁ڐ-G{ Fe#)BM諿ɿ iKo3|DA(\`8?~yEOyݛzNڲ -On {< +v SFFMӒ +A>= [7&){ABc' b5Z݁ۅ+rK(2ҾU)mjZ'u:}1ꒁ0+!pTdE#`m]ɐw1@?vAGœ8L36m`9Ջn&Ô3Paj[H!!W`[%-geL^Q{toMQ%7BS) y, -Caa }/<MY۸K`K)/`@5׻Y4} d=4U op -sGwq^x,I8ӑBw+az+i^U r"{:RD65ƍeޱ ,%$Fƙ6 -^'2p?K;.T%agφf:WEQBh -6T<ŗ.YѬdOH~1ܨ@&t1ҋ5Wn6"%E-@8[򦕚r/eROD.HOf=dG䖨?q -io0ַWQuW'H?1KMUz( Z`O{V>`J( :h#3T>lPf#r&"-vWzEOv%K?̺IʜTV>w؇Qp0"*Mm\M T,'`Grjq\Y=+a5\ yQw> -stream -s0+Az$']Ãݐk^!J>1 EҦee~ V2C+Y c-cm^ݹO& $-D97%=3GBJVOl-?]kyu#`*m-#JkΈP;FsZ&Aĥ --OBz6O!rVO8cθ| &-^mΘxvxzGaU`h`V-6+w>,Ew sݷ5nCOVH{$up&- Č aɨˢzI>Zݛ6dJ6աdٷKB갺_r+K$b|Y=+6 mJ=l+N=.@4 ź -΂@L - MRRP\Sՙ/lzX̑Y]tjeY PJ> pTmY9A[,3-"̢K#&EOT}ѣ5 cn,'iU]Z{;[S+iE{arE [Q}7/fUF+Dly*#6͈_QF - #oVwsRpPGyp2,B}fZp]H:ves2pغ4~Y3Hl@Jclzy6wLF^έ-_ervtc(Ϻc–:ro ʉZ8닙WΙW.=ƚ4V#|3mXendstream -endobj -4300 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -' @ (đk':h7Wda:: 9mb!|[8"r2O;k5ۋ(M@N ({YG6y JӔXHyiIrSY⑷JO+J2nai X{e|NtHh9JnrQ\6QNvz ޓyjLG|+"80ٸVAenҰ -jO'ˍ(8Q,2e]ZFSe ت9) Y R>-:Ag ),f@lHbC4w6c<heH]Hmgb+!3!x#sdq_9 /K ֳq~fFJ -ur2MaU&8cҚLsGrTuzJ{︿r{?ԁk'z<*!0'7e1Q^ٓ) XMÞSsps0$'+޵EwIћ1R*v(`D;C -H3tđ{R 2Q0HDžF@NOayn[ 5bqғ V{[G2 -&j*C klr? fkη3'ݰ&ADO„ -S*-f`vb1KhJgũ!},rzyl¡3(A1@l8} e\XhosKy,PyElƕ@ 0c4弌Jê{c]!ݲ3Ym]aB:L wE<̥ 0#D8c[(~)UqAʀhXSCՀ>yhO=dcw@֭J&(zֹacW|G Jrι@J)P,re. !HwRtPBdGMrȝ φ)1 Oªݖ ׏6bgŽH$(x>#lWSU";[0^<((pDY6*&\ ʼn}FZ?4v-<8!ش9IF} pNZ"endstream -endobj -4301 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -LHJ,,(왗Y*R@xrO|/Ig|=_;/J¡,b$d -sSUk2Os]'EiWoqcC: O`)cTDYmή[͢ҧz0L92B\ۂP3$B#7jH:(Zh$@=mct>iәh~MU %Z_OV-4C`lGW ҸڡZ$cq/{LS5ޑp/TNa"XEĄ9OH 1k&hӈ,q5Ofص>& w1e~5׈ g -'ʩeJo"={ޘf:Bj}b; -ϭ'rz+ph"eOy h3wbcoÞ'qvq?g4[u߬^Nr#hLF)DڢɷEpNto辮Y]7`bYUe1;7Zb@f`4Dyu;Eճ(/N@\ Kv^Vf&HJ30['gHUM?:"q*H2uҰBJ_BNOŨee=Qrն| uAA,o. -˫b]MثǤM Y[6y$"[6kxE 1oÒÞ 9S -¾+( -C5f ^y87zuUpuy[\LP1NH9A2Ͳ K'5UBYy(~>Fj(Gx8^ֆS -\t?\T5ZeKE8 Ycv>~cM U e.T]/endstream -endobj -4302 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream - ϋ?l;&^痃 *LwYrT?ҖKݷP ;+ȳ*Ive 1}dҷyI3_,ZqH,Mn<&bOW7ޣyBM9$+^ ci@<{'WߎCWק[5/ȥ/7sI>K ̸ RٌS\l|Wc?]l #o㶳MG$YYj؏ybBq2 7hV ĨY,Ap?udhHIQһ[à=Kۼ~zFZFҝC `IR~SDz[EdG vt5`" eqStykjnX2s"Ռx4>OEfQB -u#T=nLZ3KMfp|=BQjaI1UL5mT\*[;WGF:sKb@s _Yʆ_1P-g3?ʼn\t8kHdž-ӥgxj܏#kdz^>J7f8b&2x!khs#BU笿4=X4ZsG ˆB0_* r۠Yp1ki /:1z5@~Tkߨ?WL@ҭ=J]ZTB̃Yy w2d'jAbhX^%⌥jʮTF&1oABd-2]-QƈԵ+Ef2⎨cD_:Hb#āk@u\mqb4|u~^M#L)Q)cmoʆD!E?jYXy֕U ?4U`G1[q> -stream -;]nSiaP0t;w%Dn%z~+cwpO&>L W` aQ:8)X`KJu8d$r͋s4܁T~ʟL_mcJW;HɮڑԖbXY]>ڭcRf*a(Cex`3?Kt }ڣ"vNj?RUm'c96˪[oODd=4lo?y]gż1! T8ƱH""iKeԸkX &'FϢ;Mh>=T%AT)>!XqeA+Kt^,)zG#BP-EwCJ!ٝ㩊1rD%+`w]@8EGE'L` PV d '|z<9.|2D d#ņs)4.n-=-*ݏP <"(Ro=sO75hG(̟ Ĩ!\ l1Lz$JWO'wZipͩXWUH߬J|rTTTG@{aeom)rY>qN1 -}/]Uo+l,S $,\X+1C%ojz&eFBUUjnt2NyÀڤҜ0rm>\(j9rɹhx&ץn&$J&{x rt@%U@*GNCoKGoln&&bIH)$Mϫ*hiWf ` -)# -E.\IK 辯2`r%tl\RT73?& GpZ{nZ> -stream -Q2>!tzFy!ݦP]jnb 6lPqaqb0)+./> W82pFR& X[╖Qٳ3"Ǵ}bf")$DbCI@(MG ޹>MR(8WYM,$*I) V*)'eE.F$_9]`CXXD}hS=1sAcrr Lon7yﰩCeT3=ќ&E -a)l]%gGj5 ,сrP],8T4ƴsݣJ>W mIJ廒'h:j v&iS/ڗuhphWPXoR9p6[lM9L7!UyKn;Iyxj/hdGmXbxFqG%:YMn@\;ZhNd(~(E q/y8R pbc8x YYVanR@L7PsmY5u;N;CT#U-{fMtdz7Nhw=EUŭ=%7)rvC Gߝٕ5><Ŷt(}NM>1 p7rӿ\/'45Rϧ*Uj-8JD#+Ƕ7olZq+)b0@Xe xmW90G2߈J7LQYZ1{~O'Su1C0(O´ \> -stream -6a]jP -kܚlAk-I\<^0~D`ꌒmq>,[΢)dTVE`5ZCMP;{rWEB~/.?ZeLP&͠g9s/ bѱ: -S xygXHP<**#yȣbՓ|4 y;b)Vޗ'z -*76 xmB OmpXD@+k:ꂟ]/G<}ġD;\A yRieu:|J,yV&#]uBژ~Y!Iyf٫ȪfV&jn "[#dK,;eݗۜHS å]` '7^TMA?vmTpG T@y 91,۸OUc_jQR.!za>0U!5} *Ԭ -w}#/ :t٧P9-Tf: ~j2EuEK[pk.k[6~; ܂2-U,?LA|IS ͝W6x[%YPWq 5cYZ Bk3tf n5DRͬrp ߟ^ʫkzP[)bH /6Tgi3>Xj%(.p(W'D ܼBofl[a#-XMR'=] Yr{fg;I_^ߟ&'pƗW?%3p"Xg'CTr[Ais_O"jkWيd67A*M ˲5'r-> `U~ -̊wr(ඒY2[u*ĵvv'4P LȤp~e VI).p6\/7ԭN;mJ2<yȫ p#rQa ya;'-Uw&{X`(hba"8*y{ ȹ_yd.$f0IjuyNxMl61.~褔.hAd(_ܽPF^R*6ipEHi~b.i.`/ޮ}:z ڲj/8!,8e`a܂(]kFˋF[}Z|O#zcn@@u J$phSF%`qhh9kX8-}\&gs!![#DV\[:( )X^GxQ*-)^1mїpjAIE -j;W=Л]̭SƼE>Y@9ea]{gSr=uQ85>3ȩrr3'r<ϝTQ8 -< ;8q3]3obah<ZK)G3{w1/|rKRC<&,T$oԋxMz*c! <%>_c{|t QRmȢ uKO#J% >_8|/X'hIfU ^O@1If ct{rAQ}ro;v1'vo7O6v) e~uZ嘿龳?|iLhznr8e@gQ'J0J2$ |D:endstream -endobj -4306 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -c"|X;i*;1ypaXkADI2@&(۽r)Ws57_joO\`:̹|Bjn! -V&vz/eA5Ұm0a6w03k2Muhޏ.e:eImggپ=:d%X8y7Rߠ3jӴT֜Lo}lG-e&?1XA?_ jg>@tB2ڝE*y({~XRyZQzMn=ckE4Fho6euqtpިEaky1w)L-Rw ^M^>>x|0΃mQA֫+,WV= +ў[0\#m,)5nmeSCi"7ea/qdK& 7z3˹aX~TM*Alf+\aO% !] p֬p5"žPl˂eWVo|&)7tYT];GY91!~)ŒlȇLL=5AJגgnm~vT/tH⿴IΉAuy'D1r -{3 @EC N._C:&;lqyʳt [0ZfaQ{bԲ̤3fF~otTfq8Fx] Ey/L:HdHvvXjT+[8A8c%m xv'X訪->ώ_iib#ahݦK:l -oD>)MgG+3:3q',UHˍT?QJ鸮!Ke,#2Esn=mZ6)κl1! V_mTiAŔZ2~{_4Q$ q xluB⃷˨OhEm5mjbo.7"t:onE[dJJ{y+@x99/Y~EŰ]2֧DLPQK@^4H'v*=K}k֋B-a)ԍ0WŁ>Aq - 3)jt!P&-S3<70jMI/Гʼ/iN 10 D1&UeC0"țHc(prCA⣕P\MȸmU$t s p\)m젗VSJbX_)8P7f+UQ|&G{PJv rLM~N\EیHxqvmX;`o MUO6/D~o,&!r"6u~ÉB>B#Kuѯs7 #RciN`0M<-\lCA6l5Vendstream -endobj -4307 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -!w%Gǵj#|iM~-~C +fgaKX;teBwxn $d l,Xm?8GC`> /)G -{ ]e>I -8ݮzWUK'9(Wȵ4 6 ygY~2G|ÑOv,p]KtgHrrs.fQZ3+9`.࠴LJ -)*pq"q9Dby|l0GC3C]~yLٖ~sc4{"1<.4HY9m}#~ Z.D#(g`''*H\!8Af#A)^Y *2Oٗd W|ygl y2y&w[ qa = S䫄7rV84.&]rs]j`X>v "_,a_`V\=qqߴ-˝pN]޸y3"W n.c"{o$,P6^z __fź)ӚBAOO6?իDZ5ҐͦQtg6hH09 [vyRv豯73t("Գ9EeCd$n{UX!iZe?T=ͱp:R].NS .>.J)@?gpD.4 (> _Jٺ+$қE H_s9 Z]ZӘ[U;̟) #* -9ϲײU Pľ8OP:x2z{uf'B{h)TE -)qeIqlܸd~[#/A,6(/HYw7IB֮uA w0έ_!jU߈b QZtCX(-T>2 A1uHDOzKpc$a5YLNm(`‹` n*q YBe7Cc9Emc`kOwjSf6zOa_A%9'PJ܂ &tr61 ,WP+/r_YnP:*d]G'4*W_|R?F!1!i +(R-U>0ڬr-C.GoJcv;8 $<ѻr.@$92URY+aIW_KuNױݧuKϩ[1endstream -endobj -4308 0 obj -<< /Filter /FlateDecode /Length 848 >> -stream -7" DODh2c7S[+-'dҷA4VzW~½l-!Z=DҮ ژXjӽK |Ed_y23_CfXr/>Zaz -o,şޛAj("Ww7(<Ud_M;j8_C2vB$0-xUk[G;j) -~^"ʕ2> -CH;_L͇ʁ恇Kt:|xۿ -' 09HsfuXҀV@(GLU5|1%}EWrIgr -M ZgV n:oZdbrm󃊻|| jz} p xKS{+'`8S^SR噂(K -y0ǝ?Jg/P{#g[mtJ44xuj@2YJԀf22^myM[BWB 7m61S*q6mMeelꊌ4"TMR&ߙ_7 !^Nc/ÒAQbb-zCI@pKF7 -,Kݹ\l`ޚ5穉@xLĦF`г6b-q,IYo@*҆D8i{t/Y~xHW -T׾z6EU6P5:-ɖ0'l,m 8Pv!d4⿶> -stream -mNgVlD0?iQ}[}〼@dc3netm˜IKF+Zږ&׺Xc M  "ynAO.E좪雍sʕ.PTSVB%jAS7 -grK?.eĿlX?)m@oq7sq.K7íWҍ~oj7(^;'V@;(4+t`ZFH1x$93C1s=URv"ڞ~4j?Wx7 ZTt8wH Kn C9v\8sr4Fτyq.c 1b:&2c^WנQ^} D %kQ AZwn.T0h"Ӭz1[8 aBG\:znz' UCOc^amVQ'D_!8 - u⼼1 U,7~Xjĩ+DͩopK\o\AOf=itE -'̣+Wf'J7MPtS03Pd9:ej$vs!+L/٠xt:ԮKAvRƞms ID>:GcdvLYR/ Ap5㖭U7K`IT4dZb34M#ڄKa2 ˶]ٓ͞JYO'A=$]A}di܁Z#4ed`34iH *iх}k aA&ܙi 6V1~5y/]U+cb9HZq}J$&} _ va\7$P vQ{ W B T]j9KTU7(ݙHj7VBACB1D +LDLD91VXČ>fDk9rxN_Eu=qv(N;Q:iE!lh,ڶ*O3|s\X/1(: 9}/̿ Ȩu`s>ec܉ğFŘw^Xli?ׂzFWCuCš_t®K6"@ZX6CRAT*Q``ˉ2Ph$]15<y[X?뼂\9y m\@!h7&HkytP`0^"V$N9³cEfh'{srƭ]}mwŇ@:,V=T8dDx*Z)8 s{jB%NbwA%4+vͲ)OF֑ .Ɩy?U6ȉQYA-ޑƼx$aܡܦlp'o9DB7!*#-[~d#V xMBjNJ2pFԀw:UzhgE\pa+q ,6OO71VOW! nZۺxi*<0?2|3W:y +n3 qV -РτxCuX{,ѲQ?H$D~SO; .<#M}uQ2KNo+;Z~<g`TRI5m8±S(0 -Tg3g ag4H>,S}X SR?> -stream -[+dXf_oTb{{>EڵjdĄ?}c7t#|ұ C?1Ib%CFo>cvfQo nh+Gb/gǮ !8wTWǻCo $xq? Ik15mSb eqLUh|;[j:7#56 k"H3lB7a=>F~G^P=(:JH -.aXQ{绹p[ٍ= D+TաhrevHbW5ll ~nQ͜!ddq| عx_0DjJC pզJC"ViDvRM K =3p :.Mn$:/T,1 RdP %؁ؾp@a@7Kw[ -GҘL*ƺJg]ڐsɄǪVoi7"Ikj̷ -UamNJ^2GcsY uk EJ37ߘ lU:` -q^6ͼ%RϺ*M{W|4iD6t:]$m_h "Lƽ&?0N<؁}ގc%|&/?Ԝ|q) w0͚Vm+k3xjP~^WLjbtIURB=@s׃ ߧ&)<RoqH-\t"JT{3'8hya*\yה%ix%,dN(ܿ9Zz>}UpU8)]YqGK(ޖVΧ+25dVkhǺMΧEի٣Vz;ZU"э.62Dͫ x]@gsVw@z2J8{8_:Y'OjG*זE_`gRc4`.1.NW8qkDRmBjR=$A;5W/7WpP|̅5"3Qٙ-UcSw -"~پ6thy=sXv@"4˟t]8E ^L SlрU.0Mma㙦V>=Oo$+n#ב32߫WJnT)~Zжa1qfr\֛>r SC?.?dQv5TٔG[Id9q4P8iHj /QwEsWp6h&hVZ mvypL⸪endstream -endobj -4311 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -XՀK;$xh aFoR4 Q'څکbɳ-45?Toi(H5@];řT5[:A(R*|"۬p>>4_(ysx(S~hl$Ûo^smzrM!x l)Y_:Bjx d4^Ra 8i_IjDּW/Y̹@^~xgZ~f"ք=.s@2F|j4jHsp R#wĬi8W-D4֠zՂn0z89?7a.%<Ǘᠨ2>tUe :ȏ^ &xxw+L KlbeW[Va6CZna-U+Zd1GQrro XVP QuDJMIO2x^>͂ZI]u,]eX*붓[ge|M1 (QBQmLᙛ=z0W:88150ڃ`*Ϧ! 6:O@?ħE}Uc9;<:C*R(c4bl-̀#iԹA2Lv2b mҠcؙ~*u_=RL<"bqO(jd7.Uc~{i;|#)%z\h_tfЕh3zHcz.w_U$>;i)o6endstream -endobj -4312 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -7gp1 ؕ݇4/ذ,PPUxwIOD-_hyRjzY -j%ކdJ6 ;rF\Zd|d#MW@mLM=BK MmʴЌ@d~vg71r%Y5CG< Lx;Zq/ [oLMZ72&4C!rF-U]#m6Q4%*yBu6މ.fkfK\UcFV֝pzJb6jtlf2Ctg@s!Rp\&hyp^5Vjn;y-g7 mi$C$I4ЙE:!CYE4n]Qpe37d$_|lZ=d^5muCgx " -fip7GaHG,Q6]5*i,_#I~njej46CT[2Y1ӔP7W9C^νp|F4Eׁz27s 93:0E m1e*\^q H`N?'TJiq@]t(FiGaWI#e<#m-l6_g2 %1ևS`;`J,iǠ2(ߨ$vp4"kcI\]h>_<QS^ 0 u)U -5xRY!9 >s -웠oN8PԞ>B+]]b/~p nkv-1,_?>"BWb1xH<|T쫐^[eqXE(/f"'Y'lٚPTP45%Oi.Cp®Vij\1C؈}]U?RT{Hj&~rfG.%I٘HCSq& ٯCPԓ)E|F?g8|55|D/ύśk_4}di^K̍;BA1]\,%xw X -ob?CՌאbQ猆2C"o*zǰDn`"D]C.2pRS<4hK~WV<]>Š6 -JRhtp!\s&u:F2~Pq<%:-{`mZR]ɩ.{M㆒bw~@iwJ[t K^R?QMc vʦ!n(UBOȅ%UZRnȄуCuhm50BXB/োSF WP)ddeG;~dCl)c<ͳhIyX9ۜd@Er,z>uQlMT3F1y ;<x6<uκäCZod)iuh> -stream -J{'Rtn0Ӽүw2Yucl%׍|*(:*0mp\(ĕ٧~$:p'ȗ#w ,C7ZUBz _Qק SpghIƅ3eYR/A"cܺSP}6q0xV uOY\G -k+m=M{'+ÔV%\Ģ8[A\jx~\_ѹXw! UT/df!Xf෕RA@lRHVns vC } -&h#UBڱ.&ȝkN"P?_~Uo3; zTjZO&#֮K#/׿-\i^13D@ɠ O)MgX]VYXKh%&/KՒp-bYW2UoR3#x`XxRY>يu͎^T&76n`aiPVNzyJcAiL;l'St$⨶=wJ9 -k1E lWqp)hYg=Ggc!Nq;X)Ě} YQaoSz' ֺy_S, kԄb6U[Zұ@$Ԋ_Mx+߽$qg ,b$r DmnA9wo>]629HKaJ"U4 -%$2$Jy#p-,sCRT-'z肎}iS(r{Xo ⾌'$C&. 6Mx0ZD -8YOwo?c(C{$ǮyjI2, 1iKwL^ jL~&0bM=5a p>fHe?4UJ%ţ)"j;˗|ewv65Eal[;'գBe{ z13B~1>7 :l?t_F~U`A/[7$`IͳQxx<~?lεQGnJޞYgа,#P݊/R_Fh{~ Qŧơh|-3B0LsQzwgWE~;CYVE[FiI|ǁ^E"VtIǞ\xKPň`H -̭~Z_cyZ:1s{פe25Mx~xiOt&/`o D=l. PrgHsWF_RG -&n菋F4%gN%1[[ʴ5\>bs&N -]K)SdTȩ> -stream -|3IBK4hpb#SRJ.^4/:iqEmxgv=֏ <>Kcak*~tD2ih<,Ӵ g D䕀cL+PHlh//嶬f ]OiO<8ʭmo*;%c> H ̎?3^k}ovpq*Y -e m?s -h@GQyEy;Qh[!l[Qd] [Yɱ~4[$!7pi}Y$Cy\GZ}bVt&.b4mXTjK柝1_料*8w.1]6dz+ٗ eHIV923<yyIu^xj$}N$1&;D_<>G̓ٚxus@zjv&SV5 Oo~[uoq"o^ g>y&/ XF{,"NՇл`7tGz5`ne#xX}Dzwφ h*\ YcW&_t4V3F+c5}3ߚAg i vOӁ; gz ϴǵ-0,Sp~Z`sFi Dz@ݛ.}Y7LXJHn W`  VRPFڸM 0C 'mNN"şI"#RuYn,` 0%42q+0h6Q_ -S +W}+:.'i3Ŝ,s,"q(ykр%H(#[54pkDNSrS,I<Щf29 -ֆU7 -P~ %+?aԑv"%{&pZhcEzKQSu{S>tgh^^6sQ0#cQa>-~\9[KiUvL=30sm )+f/ַEϬު˟gm+&xl3#8s/oҫ(j?7EuS麩}{f8(O)7Q8?a9 -zu51r@?/ -M(tܠCƿA A9\ٚst7#i  7x!Lls5 =%Ƅ`~xL מw r mVmNwuUD^3H D~"BQn#r1l̎j8}} ArR3!=/drZ.TWnۼr,0ZD=endstream -endobj -4315 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -NE02G&Jq p>\VoKгnN{ ^GEg3y\\|0xS'H+XԨ -&h<0;殡hk>Ӄz|y's9faWIa)p%X9}#yM.>JխP -%q_|<Yl[J#I9 \.,H4fߞFlsp,Jt2$%/ =$!|H7@UsX|#ad3~e-cw_,\az-q=|vn,wY]O+>j{R4Lh,q&c6ǰ#| 5rlk ژDlJ)·hA-A,?+߲|tVq-攸X[]z] !i5o]VWEɭV]/K+/\L2:D/޳:K`9Yъ۝.Mp^Z-?)>L_Yl*j)0B6s]E d؂DUV8&09eY:}sʟjpBdgm -mpK^bu=(~ztN BVx{>mEc.$%Gw~`:8U5g -Ԃ W@_ )![# XtbKҦԄu$TC]T {ު-<&J=żtnv}da=!HUG=UTKOpʞ?}Kd H߽ 5yt_UsMfnK)EI*PZN?D໼v%Ggi#sHճqn}/$ł%Y@F(yNfwu=>[Ynia֘r<{,id4 -Yj95Kl-/E!σU; -?w0zdUA]:*eō2 =-.Q99k{_jz<t+ҙ= -v@!dwH>nש/nMnIY$ ۀly8N=1k-F ix֒#nliJ bTez'vDXɷG _ŒVt `3Lb>u1sE1cT%m'endstream -endobj -4316 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -쎾潼TB@]RaZM7=^M?A﵈m$x Tc||ob)8kT?BF sl)H 9frJ_C /pD0\|AwDUP׃:vMHyVg~fj\&.L8 %qepdB<0P=R%Oiè'HLJХBx7νc +#Fb#Cnm,иu(i,d$\ler>#([{Y} {4HHS-ɧ_6؊c#=3JwWq "8%B]e֘edB]5" :Ro 76f%x_B9b_Tu2gKnKA&j[=;*AP${xO,IT`fo;ICM\3M#%Q/Sb= ):m#S-@mIsw3 #XK_Mj_bwx -N;|y ?4&@eH‰% -m *R輓gkRk=Y;eߪK9̾k~~'4'w鏔\0P͇KZTܘfܟ§Wpń3įvǎ @G^RUn1̾P@Ǜ(z@d_ʬHQЎM{T:ve6TGCQ-5jj#:9#chbijn:6yӠMܲO;"{6T/4^Tx=&ՅKZګU*UOh2'3Qsi%;xЅ`z Y,$ ]rgxc̳/vPC$rw9/OA8øQ쾮B^2_^Ph:Ǒ6!`=B| QnfMM`5FEl[,ԡU,%#H0EA#^n? #L_>L-Q@4 ٯWurmGڭUddyriwr̩*VÁ:;^C<,ިE1#c(#R5p?M'ʦ13m* Ja/wUAO򱟸&Jh"ZSn<'\,n+Ȣq5dʂ5A{mm?<'^3'2ͫ-dȺ]+VU8FE|S_;,?µI}4rmcCYS -7᣺ŔX'eܲ6l<|RG 辍=lG -B ŋV5y#T, -" 6Iendstream -endobj -4317 0 obj -<< /Filter /FlateDecode /Length 1456 >> -stream -f0ЦJ|Do* n[(7%_vv#7~ 2%Z-}z jr%' hdkmbY`Smy ޷@cZ!emjX-Jηd*(}ϞFΟس#6q(K6*Cw#}]ŨW#Dfld>%>׬y7zTI|":S@^]&&[9NFG< JrO׊ NW?d Yz&|RCMC<±DZ11o]\dIGn<`@n<9Tc|H'10~/8nb%9AJKCF6KO'%77Me"B݊3՚Hl+]"֬'$W'RXH+s%A(_l -s ;M}7JxuJ!h& od2q @#_7> -stream -^Tlx/kG05 -ބZÛ.v4F^Ht6jPMZ^3@-W++t2*WdլHXf)FY!k_Cs%- -}sTHpHɒ|Sf =owO}AF;Os\>KŠ)W` -Z,&^WbgeP'i>O9#ĻyY+zH~"//ÀFowOz>IG6E -C{rDz0b9j'z_oda\%7 DylqHr)h3;,:s$ -tι2SADS~tg^[@pe"}H7X;oiC&΋'AJ2>P-eoMtKpqz_tE4rQmM:_^~*FX< -}?.CgBvA(k3B<ܽtO0CWDN="Ôd#,. JM@'C O77NZ[Cnl9f+BA'F4N*5AZ[{y1NJG:U uT~#ưaQbWUCv. {_OۘQڡ!Y@gj'a;P8u4=>;_k>^ }{ vy΁2SD@$?\G;VA# Qq q5_Qg!'xԊ3fq9k][S2 f j aዊY0V-89bXj7s{I1zy=&g7Pn!g(yt`Ƨs t[jV+a#h:q=n`!_\wx" /isGvBF!Zj:/< -鐱PO0t"C0VUp$W;|jBw^Ԃ 9u/*dP7x&6ٴŲUO*1%^0u|j=K@>ZHpo$X"ƺtOƤ\8uN1GY,-y.z6C)h GC64U"R(^ -+xW+ {-S< TR=XjX1P`&"xMLmnq,k/{.'gB=_ j ꚪvĿk*LEvɩ -*#VuYԞw뜢CFGZm߻ -̉]qz,ʮe-DhWƗa!~o2(OX;INK'T厨įh1$'^X`Ut)? v%B?R6 pTIˆW@v:' kU?qO%tgb1׎@ĥ^ȬliOˆq:K -DkHcǒdGaeakЩ%G͗ء ^Q{jx\#Z-Z!LũJǓ;HT7BXNF`E}ZXfJ3vrBJ-Nw <NՊ a+ъwN:䱆CM>nx:A2ƒi0=m -.v~;̎*#4isjz6\pAAi%/VW^;r7d~ -J;endstream -endobj -4319 0 obj -<< /Filter /FlateDecode /Length 1216 >> -stream -@NIxUwTuAY3WPcӟ)5OD;`>k~?=RRMG^!ЇU&^{:>L| '%gV'>%0aa<0?(0} "͐RW"8FF8Јݞ($0 T [WZ&:"Azћ<.^ݤiUtUFxb.MsOٌ|hKqFV欼#Xӆ  3TtΡoKbJ^֠YL7TDkᥲ!oA"A' +x;5 -&_oZD+9vW6ď휊]bcsmMQ]>)+P<`>@=<3.?MBnVyaib2Cd#1?%sV،R؂A  K:c-fJMazjEî)9=)  $ߐ<`4.8J -)Zm7I˟Rզ?`!9Գg4T *t[=XPDtUGS 'ƙ#z'm~ER[yUzNj$ ^eh靡BU~apk7'$-L¯u.,`*>S7g5]9WN*{u+|INs8Gx한 -FrZfSMrքÈllc3A|KJrL}Se9g8d_'9lcr&2lӍ%鑋F`d;(Ι4U~L<h(uĀg5`a7EbT*ss|>Op"6 /?;SK2$kWKzF\ 0>Kڶ7tD/mZhO4g-F, [nq:;`G L+3 Y:\,D2)T^[ ۛ6R[ )H -A&H\KI(T?dQl@T.w0TB93ϼj‘:FgͲ ]28gGSoFW"{M9|a9pJ`{endstream -endobj -4320 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -^Ř|3v4E)3,.cKar}Nnlr]z۾kS~6tip;cե_'g9!  3rۥ@SC3s*f$&y n!/ ;~TTxg,pm?k/Xd 택 -Pv`d(Ƕ <烔Ju=)+T*[LEfe(z.7iyY\ŴwK;1B5K/b,Eb>bEI[rpf^'bש^,;nښ:nA_;$p‚p, $&&YtX_-w+m3:_e"}"pPqO툿5n(dnm+ (rL`R2T[s4Bǵ?2{\Eu^N!D%&h=k\BV^ue屹6Sf-iߒby(g(]&#''6I?VRi (_0S1vE㻺u+$ -8l& empćDu'7:]@AF'L" tܓ*1`b-NLCłkz6\ - iuˁC!~Ы/~zx.;w](y._1rsvV%K~ -0endstream -endobj -4321 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -Y2w}IY7g3q g]63}<]Zk(j &|;C6X_}Z:4hZ*,:e|±fXf!t=@8dĔ0_M~b@a,&XܺVQs;_'7dȎP,Zq?Ί<ݘ=fekx6:01p;@ju9'"c}u4^c yS^mB89{\?oq{ڿ -]9dx"TR,]cE jI%lOեR?^2K,O*\}> -stream - ˅)أ%5"sݥkهm~79)r{ r|6)6-O -C-w2Fh-}4 xAxHūELWWuv[Y=ԹKX3oQP |=u;K0, 7*;M+vșDn |_.Za9?LP1fd9=oQ!v^L?W)pi| J&ֲb2}vS.c;QFsɤVzx-5j@\7ȵlvmttĄp+;e8ư⚂PS勇#<F[JO Aa}QWoE? (7v@K4r_& l܁fx泘z'>EQ8JGP:¶ Gf4.uX'޺ou%{ONp&oҎTJ>$A6˿*`ocݠ )Y~h \|enE_D5'wNv+E;FFb4}&pfN ,&hwuGZe~sx0EII3 jy.&j&{K|qI;X1Ft ˹#Fּ20> Y843Z $% -k设S&n)hYaFwh 3h[;#6K -DO)LE.l>\)Rh G_lQ -)Hu'ӚjYn+h -@>((<~/S;9Oѹn,*d@4\X~Ӛ_f, W -zLEak"z`%a4ez< DӗvPa=j -طW~jdb+fY9%?%C&Dd8Ot:wZendstream -endobj -4323 0 obj -<< /Filter /FlateDecode /Length 1504 >> -stream -)O^o5Ai@.ɰ]Ɯ1 !'ѷ%WǶtA'׭/aHMUiE5X)+j.PP>bH3=%_df%rQ+FLW\!*e~o"%6!@:i07鯯 Tqb͆`WNclI߈SПp"tfNf}O}ZT 5WDB,..+zo$0q3?9?tļ K0f(eɪp WZjEC:M-̨t!Ҭf۵vW- eY]w"m|ЁCMEN.O-5bKdq`H趙6c|oPXUPH&쭼dg -lR4ڐ c>jn",*ziʲlX}2BP_X*iv$Q)PI >hLY.d|Y yճ镐.)~G)JFI 1ђa0& ̕ddZ&JTUMy#QhsEءI%LbUkW.3LTjg^j> - -E=`,I_VT/n @#n:c(ū ׳4hs\'dblEc0R̋# ūKɓ3TB$^}f6ۯZMbbҖvyfD)ǿQJc~F Lj( [AJQFb>Pڜ"A#5tw`msbd8a@a}f*PcsD\N)Im׉qDO /endstream -endobj -4324 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -N@O5#ja?J}W8(_!;K4g*:UM# -( MT F)wP3.$5Il| b;J#{1RHb=#_5dX*ǔɭ;'>d徝֭(b"s hҫeSXz+ۂy P_4Ab {h[1$$+gCS$7/ `ǘ$K`QFWJԑÀVk{է#!lxwE/K"YBW- W3b WW)%T<@ɵz+:.QU.M, -lT7y&; -3GjRqK61pmkF.susmnIϪs89L-__O*f%Rm"ܚwwlf{ ҁQATsD٢l& l]Gi(dԝ'/#P55>{ 7ݓq ^5}XC_Eg6 F;f Z, MG=Y,u KGn J{ ɽ } <F;Շ>׋v\ohn*޳@4(^BY|QfD6Ð9 s (#c8:$7`N`6 B dUL,'b^0uD!>f]'uAc{&=4팀)OuD@tjgXAzW[іHI}w"m/<]G/RPf˄Xm6y>x jFWE:dNwAA;-`kX9 J>U/i("r|}-TaeG 4I:Bff,$H[UrGhD('F -Rٹ ;'yrFY%B'(=p#@u]+v[jaW4:G/x b4?SՖg~Pӽbh,'Tշ?r'td0Ё&ٳRpEؓT{m94G ,\3(Ů5E? o5;ZTwVk%یaw< -3"Ho7}Vh\PU(N-X|8X N-RS6=jl=Ɖ֦Ʌd C{m7(.F.w&`Ca``yۤpːo`9`yp؈ɬoKQ9sZ[ 4 r]u:ϹȑIVTϳmA/ӰTIBHO5R$>J(Jِ g={ \1esˀﴋchd \wUÇc+D`h$a],9MoAJ}'!e -G2_*-xLg$NtІaendstream -endobj -4325 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -KMz.:3u &thaq+̆Y-]p$ʓ0aEjGs_p@}..E,VVB -¢[/&kPʈ/Ef|>NB(rG/IR{,ʍ&%Y($NWᡁ'Ib`ȴDCpv}R!QM{h.vаV42Φ~&e4C?0i<Ѻ >Q+GUwr2Plj`TS`>{c&Tttyޒ+n[nG.]'\#9F<{]mF-E?Jj>.^OiU/-flcCQ hL454oQy? L__p5g-imwSDrꨆ^g?H<ԓκ6S$J"r?UKqֽL6Ėk )>?o {I"@>B2`Ū kh$0n6Y 6'7HUIN1/+ ޚ@E\îm YI^EDkbCߙҠ/HZ)\'٨Lh\+(u{ -?c"BJ Y|rQ{2XF٦z)F+jOR4=p' v @ .FK[Qk˫n$^G'X:E $:Tr6*&fã>%q[`> -stream -Au]i7-Jġ)D@jRuppn6_y"Djv.Hvwf*Otl;~oO2!غ0%q0l8:W#LdK.:t'|6f{a> wd_8Sh/SQclb ^E_ؽ[~9UtR6Ί假|w\-tTlZ?8d2 ju:Q"U\R$`X8Yzyɻ1ex K6{wDL)X 7!U xjɨ n+ GckW8.OWnxS{%Z~?i -텾C<9WM& ÖGCN2*+ [WiEQH9'Rȼ} PL1)KBP[-W%E@C'WKqkXXgzCeT0&8&ym1C=W3Q0I^' -c?7AYv-`[_*EwQɈjUR]jxz)mxF2J?!MMDkm o,rKêYhplL(8Ȫgj3pLB@No| (rql]ѺZeF.{&>J2"Τ 1B>.W\L e|>ѫqPAdž&zVυrߵʌC )2$ "_Y3uZIVTCt 7eG̝#&{RjjT,EN $/||Zi%Nt5ucZف-T!(fk&rlzp# 4CR=LAUf>4<28t{m~6s$2r"7MOF#l-u}oǗݵ2yS©GZL2raA>R7.T᭕➏V8%Q8J& -ۂeҲ |gP oMsڠ[Pfcx3 ֡DSz#ŶC@3}.a O]DGCUQ@ܣf#w4XT2A(7hVN~S<^Q<|WR௻I'Ji,@ɟ(Тy[>$I"x}0B k]}G@o=6-DS\[,j: }f;ĠϘdql|001-,ȏ.} ٶYja-endstream -endobj -4327 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -7#EBI->d\M!#7*A$KJ 81C!Xe k7c4!V2HgUX0~Vg 9@x!2VN<'Fv )?lR<)Fہ -f#S'47ʧHWp9rxl5` @j3NgCbx]?toB3ס#CK3ucPbҩJؐAEA&qh>XQ8\MֶNlKo`kOVB{&e]܉~LLgNuƵOa60l#]f!?C#\J $8-5I '=D(`TPӰ-4X臝\yƔzN؀aK'duq(>"6yE@tc79#Y gFJG3kYa;Hv$]-V% ۦj-n_rDG1l#$ /ͱcvQA'73F.7򞼥+~`{*39( ? --20qמ"7ĿoTj7؁%꽻k?Wbxךƕ).[KΚaH`%ђTԷ4Xq&5zlE5eY~T60c):(=CZ/yuĶ;D^ r0CQy D相o64[!]Lt3YV3fAu'"P:SP 'Ѵy -cExئk}ӂZUjvݯM3s SGSdkI)!k@w~,7o.~yڄ [80d).+e🢏>O2E 8 E@0/$ta Jݝ8V:B\\PKRCO:F@K~EƧ81F$\̤EhP bVG:2tqFĬujM -i{v}( }xܹ:*OJ(B͔sYY>ߩ5vy6G4a=Ç6F4buendstream -endobj -4328 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -h:Ԑxf;xBcN?Cp wU+,N&g)?9EG\/'UQ -MwnA|(8jN`81cA`FeM`L~tffB}e(m]܏v>M蘍H."Zl%F9%Ͱ Ǔ""!êrX[O7q> gx ct-!CR>_4tҘ j\Iy/Ǵq7M>w.Q 1b 1dzY֕r*îsp:_ފHX <$冕[~MNigC y%7EύZ#0#LuHr!\cCv;>c!A4h)]-Hqp* *P97JKq8l,\7 ݫe[\*[8T-FOs[ a i E6Jw G6S^rkclzif Gac_0wJ>R>%n)wjR̢\N݋ߛ>*ڬ%J0*$ʖ5d]?]U>Jx۟Jƾ`_ǽʼnt9?Wg7Zo>@}_e)az`4{X|7'K;[ VNG0@Qyd3)Se+?$msD|y{8xj} -4=KʻxUqٔo@WHen߹(,6h[jұgpk6jFi$Z -1@sH4#u cdN| L6yxCd \DJDx56s+ɫ֌0G9Eȑᘍ1Ž?|@sk7R9G@ Mx^V(f2ӑyRݍH͋iޕc} -Eيv=H ƒ5zUx/P H]683q%Bޱis֍7O}~̤jXCg'yd!gp/T˝@j r$¹W:_>FQv\60srZ^i2 #(ԟxm.A51PD!s(*G~?. }endstream -endobj -4329 0 obj -<< /Filter /FlateDecode /Length 2896 >> -stream -7**0,`#֨># -+P]Q0 -%Y9Ҡ.`Ӿl7y;MpicFA g( BVO9U][vqK;ng1ҍݨϰڡz7R;!Hй|JnLJ>J3݀a> 3+[ fQ1 6DX*fy &?%܄eu3B{Gv#8'〼@  q+_1E6v h AƹhѲ. ?Y bU`46ޯ32c 4Y"ŃB˒J_LӰQvT"0g?wp?*ecS HhGtn$3sHAbY$뤽%R^7 zMVwg^9ϔ)5֧- ЌmUs ZrRm&N=,_\̆ ^4x]NgX;٘{ǬnLM܋QE&+ya $3eQ*IG#_(F)eKSsL|3`Qᅘd`.ax鐉!P$I{UHϓ%IwT‹:R,(u~nB)fVk_XCZ]nsqO?sTو+IPC:ѵuc,JE كx Me_dHkTE_[G I p6yL -$yMh̦oɻ;W]E&8U-V`O~m~YyP+uFM% $X)ۨZ5\ G;ᴖZGVV\v7w444N~JޜzgiM5Ӝ~Ej ]$ք0T}=V 05). ά69QW0yM9w")<mpž+0ϕt<"HWtX Q`b+ WLzʈU( _ޔǧ=Iy#54=P `PN -d(<fψٛE7anT7 -ͅ-}E2^W̌c1jn6֧㐽=m)_(@ |@z!fOf= -v'0>2F -3^17mBxR4&TRytGq8<3 tӞYJ]B9 -sX+0]ByF[h՚v#Gߢ0D=y9VW8p&Wk/V;2fl״Uv/=##E( <ң<|t+a5y 7_:] -co CkgVȴmẎw|׽C`iF g]2őV> -stream -:]2@+?yn,ҫ3+:!txڊ;s؝1` X;PueȠnu"ya1쀷 )hdjYajւ$:|+|?q c o}@7t]ؤKӨE*UjؙPs KHi2Jcs}gPX>@bxs`Tk/50UZ Ѱv4F]Vy'f{ɕc>$P)L\߻6A-nCA&CalgYGŋ:}@RK7n?ƥ*C;Bu57뀐{&@ c&%nK;Y. ->1+/rS(l[HNRTD; E}!8G5**؀R,ˢbCXmUgn9KgδbX+Naim-xI[Q"$6C!>D"" -~ f3S\3pg(d3v(e:N֌DY ح[3%e6mI7P\#0= KfRGƷTD]G*)PT8 -/ZU- -ݩexC.2pT1H0I[{_aޏ r>'V(j:s ce5x׮R¶nkB^--E_NmI< ƒ(Nhpn?^CmdꁑQcɶXi[sFe5o\"+E0Be^6.x/{lh%8&~zsn^q} `4ɋOF}05skV((S9`"%^ -{j:Af]#y}Aɕ.6A/S]jBLz6 =PAC.f7=aۅׂ(թCr|p:u=F<*vdC-<^ܡ9)k Pak' : -X|…¨€[ZVendstream -endobj -4331 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -]BB_ߐ Ip٫wQ?-/+`Y=fZR%18;3isTg)^m^N^e&rId>V=bg#- #O/ub -ДhHbwzfo.;&]&JWѽп1k"YP(Nt0[ϟ%$H(a}/+KnC.w5fGg#bG /-.-.0YHPKi:2>ջ&1h>n߸m'F-6'TA~hSvOcīBqLkjTMV 3h;-ˋ"ι4D\➾{`u }TWFf_-/m=2"ٞ!ҾV!P^a˞K0.v -LLʙɟY(4qX4%L14\C=~Qf#w4A#~Ђ$Xk?qLs.s]c=2򩨔RtioLXq(ʺߤ[LGvGF ԂӐh\ӏ ?[Џe~vO p-(fR=XHN!o/qo"I8H<'eX @McCj9yDM%B| muۏ|j]ZO`1%)jN}q *1h߽WFzޟ[2q8RIn嗹oFz)rdɧf*A8yIlq[tSwHQSgMgdGZZ}]]#"]ZIxh81JM0\xwMZ|QI - -@l]ayRYzGōhE4!<:y2JHLE|* BE1088>݋ޏ.v6v mGm>CG#% ; OpI^J\RS oeMf`z;CD7U q -E4 -̡vOz2TyK8-5ZSy\ -@%+^oa"YX4D@?h\*ҌZ7xYA -SůL7g)(|OձrƹrZH'H:2GB%Y_kgx7=x$o^J9SYendstream -endobj -4332 0 obj -<< /Filter /FlateDecode /Length 1552 >> -stream -%j[}JyP vS RuD]N ࡒaqr- ^0$v4:r&0cȪbHUX,Ã7ȱ}S4nƨg^||Eaٲ32L)eO$lXuls [j"PJ V>+L<pD(2'o^r>cK JBn cr&)O\ђ{{a{G39q !B_\bbTJSIG\6 gX/@:b&'CAZTJ a -J{?XÝuBn͎x~)j. K8FE&!I9L{)іd4s!C38qʬt@~8aO|0FWuW8T|/|v<D͠k! 7Y: ŽzPjƤObURݲ˳fa^JGbQuEv2%߿F6H YηH]:~vJ-ЖL0m8`tAr R^)dq'4&,KN|Ɛ -Q=flV%0,Oi*a%ǣ)FVw:s"2(ňJ`,齊¡wyࡈȊp* n.+ڿA<79fxߨ]O5ظ 3`{rR hICw=h٤%*hI#KD>xz -?c ɽ;?=^ &h -РcX)bVi#"3$wV<)> -l`8ű7 -< 'Dazql \P)qSeO"4,%|5㔄G="k!z'ެq03Eۚ/lvzJ8U|O}uI)$U}X3O53O{U$d-sskpyߴA~~ݱ=q=kb]/:"6w b];Nca3_'30:4C!lK"MNp[NP ՂMԜ-Wh'h6%ITiXÁ]\'JbsՑP@C*5xap>V/AWfQ6)?{,|1kCR,B1t<BN_wQneZa;df{3AV;{Xck$K*Pq. ^jrӞ,hp>endstream -endobj -4333 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -3 cQk_7VMCG/"ڷYn'F߻ -3ܦS -ug OCXPeAϗyHdW[ʖJ(Rd$dXr*&]lE."&/M>jKefygşeD^k:phL5cE\M/Yn]g^-$fVO}V3tIn28wbvȗC+[kZkb0ndched,s~K5Z|N&PQO \4~Z -ewV 4\ c t@R4K_Գub%{%DU&]dL[xсDa?1Rlb1,å=k@OU3 Hw!&xSw+LyF5 ЕlUj,+*p62C5=oy276ZF !ctgl= w9OTސ*+dd? !6 , -JiYCb:FWh8kX퍈+Y$${eiͩF{ܤ/0ׄċ -wr'S0ތ ^Ѩn @l`/Rl^)fgBpjWdD*y7M93/`lkqBvkxA$`{|H'caIa'6.; eQd;Tۧ !%0eOH2Wb*&Q=uL:O .h\e-OS)o#j"'qq՟{ q+9 $?ʞ܁{2 ɥbV5ZcgC5A[OYBEu$ J[<6lcJpueS oU16"fjM/6)V=k\P ͳh9doD[2p/ &k".-v]%endstream -endobj -4334 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream - -1(N0Er.ʊh.,ZGYDJO:lV}_iBEkOvM Ԭ|JEi3RGi=# BD[gS/Z嗛EyʛVn`M.X…^$/n-]d:[#Nx T4pV>_KjkB B3X`i쳅k&5ͧ4tgCIJklK:&pkʢ~u>b]7Hxd-5}-X;DfC cq}1e>L 8@h#6WzI6%'0Y3>PUћh;R2\|tY>1I.H)[qN/k8ӰgԌ~b,nჯp+:w=`Mz5`rXEuP2FVY(]ʳIvV1Bgk~hӽҿu1Pf>2 ͏9:Q9狰 (v(v3Q?A0Owң [ -ATY'/бMQ&cf^O! !rD>m5!UUGSR < 8iWx1k%OdI{2BA7U/!IF%lScyv2OgxF`6Z|rŸsmި5Шd͸`-HIE/p#FR5&*l" [$r_^(μq2ELa[%%/Zr~6:_tí rH]GaFS-z#*]AZzbS]`0ʩS;_mz%br!*EvЊyEdm[<{q -N ej^)[(7Ҙhs}ϻ_9}ت# X/vV,K]%CDׇM߈VNPm#K#[O Dw[  *j$|gjW형 Y|[i#bnPsSxƬF0Y:}/*MKk`&pe{9nJ^ -|ZI:LO2LJFgn|ƙPۉrLfsqC2hf<'p+P8vTS;cPxwNc|Lʌ!OW oGgL}y^ܦbDY~)Rܡ\鮽F+% BIE -e'U$ᨭHH۟dbv>DUբw.\2)tK6XS&n2WUtIث8y^:<>ᗢfɕ - 횓Nd&*s$VvׅUQlY3YLZvl??y~3ӱ@Ϻ Jf ޴+كendstream -endobj -4335 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream - -#C5YrcY@BIZ̾1w2aBf y (2MՀ53#] Z?jx15W2FXvVײ˰]`ʛVL=p\l0&܊;ΆZ1[MM*=;g_K9f>PATaN378w{3 !L -jي |O]Mzi\МyBztudP򩋵4S3/xz1 ͈m4|pպo;_%KcX:zhRJK97L{t8M[;x(8%?i`bq|uzj Э, -QPk8ԖmX$6 2tĿølZjT,H,w;3'6Ω[% QO2H;wMF}S@ʻE oogShM^qȅ&a9)U*I˵zQ4€D)oA [(kAslJW˕GtQ]%,Ξe~)cD%+`&b2?f$>Hx.vQoqMgR~$ pV",g%_>К)0 Q\p1wշ i}.Y U^wUϊj]@J^cu:ט FiP*~oTV -_yk9!p+2QH$1p\ˤKHHt 8 -ZrZ).C[)l4׾luDŰ( -޾UM(滺7o؄6Nd6 -[?M(kHXFlԵp/S%[uEsza2طp xZ/5yKsчZzh$7Ρ6y[›3Ԡ:֭6|f˜ns^+e8fT$vqt)tbDLKz1,PnȠIa`P@>܆;nyhtj ?ehH"zf+DZvQ-m~ -{5+1 `Z[tendstream -endobj -4336 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream - @RHr+Uf @XM#1zJ}j"Bm&ǟ`/ -$Iu7e105ւT -d8iPJ5 1uf:e`MIdEj$#_2 I:fo4o*׷qe2Mp1]{FFGߕtпp8*YyO|rRr3QԒ980ηnBcN̎ SCs:szҟ 1: ?3 ^^V\kr0_UqSO09bQvvּiElN` EE uVáS`|&20[X'؁C{hc8X~7~ - 20h|O1]kWB$&qt:jK{ 6B*HJpu(n>gp&?a (E>*tG[҃: On3svbay*nBp-4^޲U/ifxE&no)CFPذnCӥ<z1+r*dppƐM@[Kg߳Y \%JcGi!>";x[{+δj sRi~i '̰5L+]GzՅ ᔗ,:O۽0αYhsGf_P_\SL\KI(*ސ'Z0Epr|D5,z?ayU5 6~*{f?J]gma4[-7%+Pg "`ᡍi(RQs -Nǽ-? Hmڣc'B?%ԅL7[`8JHu|p8@U# Dz%h8R}K `T[þ׃vF3(}&~߱6ap 0=mivR8 -3y|%QZuFHWQӲ-e][{Þ&‚uτ> .W!]6)9 -iae ^y;[|8}x^@y;χkG;::.D<_ϳ*$ejh0Vk}endstream -endobj -4337 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -:ڟ/u26`J\kGQUX鉎S6(B,ez(4J[ OI&V~N]~l5z"^Nemu:1 <؜[ -M܁t -O%3B{ Ӗߢdtޝ\?cE ZQDȜx4[YUt'G,r~_sA 8]:^f3&Q%8T&2\RsWP͢2Va03eY8lz0͞茖,j{;ykF6pDYbkC0@KYi ht Fw3oS M&haSWh#8CUaWnu}B`g{Qh -Fu\uup( -UWEmS,Jܛ .q3#(R!'Ӯy?*\MܶՏؖ ZPDqKA*o sr7V9{; љxՐ -j!OIˤ'SQx];"y+hÍ]opԄZN F5ѩkm Fl1$-P>Ts - (}:a\a<wiXVm͍q[w(A8CAjKʚ SPB%# (vгW,{SXe<!ɷ-\Ef|%?|v WZ{Kxo'q`LN[!5^a]FOHiH0*}:1[6[_uٷD:<E>DЌS=$-͐IN)U@Sil8ocj+ 9qo5\p~/ьaE']gBKtʥ[C(}8wV]H@i?RN݁pb+fg™BfXt EAfl79MZ"[endstream -endobj -4338 0 obj -<< /Filter /FlateDecode /Length 1792 >> -stream -@|T _YbXni'UX;COHؐ֕gB9j9a//]ܸn~^RxD.RiYIP&[F)7x nRZ=²z WjDK]}mUm)ӫ"ʺ1*?ŹQwа3}ɸA y x6/^S SEC BD?߀yg8~Eцݜ,$sJ-T&}_r8w=aqZx`X]7Z\FʖֿzYU2F|s % .[9T]1\" hD -?Bnwja#䲉 TYMPp_C%pWW/ u8_o:A(PalxZn0,[@M~@b9S^" Lyw?@ P6oC\U+ӂk \B~ a&4a-\yZ}4:=@Cر޴nFɧh|4x jmv/T1Ͱ\E|6i1@8Hیh.fJzя,B|mKB~3F=SL} x[ybTP"w ~XNlVק7߇2'++ --蓧T_}'8k\߬dSa@cIL8Dl\[sծlN{6SKb@#P>ǒнӄP"%_[' U2tg8'L_t\X.wĩf.CC9!9sC(; =ړa{%u?mƎxJ%knLm - i >& (r -ȧs`U3ԓgY շ(|H[kXqtt98LWxa1#{\3zaN\-wy`y~ToteΚ씱Ĕ/ j_cP _{i<"p!rKR3yYWk'@i˗^ ,^A;Z@B -EIR8lzv̘bYi|Ck ++iai<`yfxP%fW)Mr$r30ZP(V/0P"F@1ک3d4X㺟جe*̖]h?ӻ!OtGn[K& T bzs*2Cߓ]w M MU",VP˥VjG=lJ.].=ܘNDo2#- qJ|5KG64S mNAfTY~qgHѲIySM\I)0[1o) zb*3 <[Y]yuwJԖd㱱^O7paZF{pF 猥`\~OCc͞V!ybJ`u0>ͮ+?ãCmP30z,%4tBNbWXd'aJW0X&: *Xi1л3e}"N/bt/G䬎~M X"SB#)PXt" vQx倽0{=7ܢendstream -endobj -4339 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -|P3ωi]#U*5 wM{}јv3")c2TJ>~FAG&CߏTxtb OMϡc޽h|1HIÖ\=?~懍j"̣`Yȓ/)5F5Jh\d3_{+tgU fϨJn$@Opy/˜/+O5D*G -WuEWWE6.kA{(cI1v;9'䀊zN zsu 5SwAX{ibd Ţ-BZ1UP]<%dK` TFt* zC٥fqF'^Tu3FN<&.í Oa ׺G0XfOCED4MBWdbwV<4G.@o-EY:0r`y YrQu@ieOS3HHjh׌G*̛U4y"xK~QvE>.Y<1xWb^k߰jA -Ћ rB_M-1pTevsҔƃc>ZW؇ƛ۵Қi ڳ"M̎og6&l֡Y]WEc/v -Ri_) 4!LH|DVP.-\#dSwBX-K$s~C<{T -\:dzon\&'" -!rqC=:ZR]wD8Tw|>sBʦ{<{I=T{uXp G}&l&3$[MN?fUaDp/CPom[jW嚦ZU趼*v)R }䁌+n-hŠRju1jz1]`Y.mvUc"X꠳"1Gz/lɌțA}L -l4x*$ xM(/\PjE)bw+^]-Q٫M˃wXۙ"8mt8=tJ/]>wendstream -endobj -4340 0 obj -<< /Filter /FlateDecode /Length 2304 >> -stream -͆'tZ@s)2%B.:t:G1:8;Z)ZNeҥ+wDoY/PfbşfY1W w/+@[fix8pUi,2Ri\n'GIg&U )~Dfv{xW2|bGy]Uc@iAʝ} żnH>&H;^MH]nZP)}^oH|v/^p0Z^|{Ͳ~F-ѫI -7{[:1e>M4Ҵ7~\oJۼmȷ𾸷BKet6aLzK؞HadڡLGkIcۈ I.&f=iѭW.-| VFR@ dS 5p=21;>{E{t'1 B 5R)gKdא}l ź^}m£"67潤nI}6}>sWbfPTv=65S]RlDދlG̹ݬ-8W c+G9j Gzl?׊ ؗCRI,n:QMoCUl,^ l1ؓ`5:pFXomoiu4%M-6еك<\@ڪQOF6Vn#\ E3d֜oE7wƞ]'p'MBY^(%(D0irk9$g{ mǂs-$ }*+Axܖs)*r0,9>l[,q$O5)i&hW/j蹼T =m6å{z9t+ a|5% -[-%7vtY!ڽY( EǣL+CiVFF"{쥌O)#oA u srrk?7Ckѽ}|hՆ4@Px?_@[3)/RcL/V!Kp/ԣn̜+A -$I=GFAقzLjK-RS<Q-F@D_t 8s-^ -}oLuʃzбOFIsȄRjpjDW h%r*1oQj7Is1;tot<NjvAmv&qp¡֞kﴐ/p  ct&F~'6 $,%˸h}iʂR̐ݡs.?x3M -/᎓)OwzYaVoDE8x`lLg.W3q<_f w0q2lsZIftozS7/~5@h]iRM3j -rƾsIƌU7bWzࣼ.epY]92lYDdkf©=dF=',>7.SwAVXpPO0 Vˆg,|@qzeb!S)EB@&IVJ]Bay5-1P/,{_{bUr7/\TK} \ GH{g/mk5􏟞mj!+Ov>B앜rM5LEvkpj!?}ݯ+;M!寭dȁg#NmykLS6 `ү9$fҩ.g Q)49k$;LL ދK Xh>4Y+OS-]gD;IMٝ-F3%F IO?} -N3=6nH$aC1))!fm/C#F+%endstream -endobj -4341 0 obj -<< /Filter /FlateDecode /Length 2224 >> -stream -`:^\:==ą?[z}s7k^}D>wHDq Qn&lRRŤԨ{&ޞʲ+-.@kRn܌&T X' 0Ap.3bltsKZA.-y*(=}4 >F[%Qg7q0.OYFœ@/oijIwjq8yPYGL[V!-_''ߎ@ -%*Vz;YZExqfZVea݀1aVφ=梡KxLVSlesM-bԡ_gF4C{țs kRxY_Z^+[(`GG UA(Ta R@flqHgؙٰߒ Ըci,$j!: +/K/FgBU#' -ɭb@uϸifHz?׽{_zR,) )Mw, >2,*T$@KX}ϳ%@ǙM;N;`= R&Z{/N6!fÉh1\SХhK͑|*$oqم4sp5b Cm<RP"z -4ӒЈX7/ -wsɮ\m.L|ČdVqTS Js>66 DP.I:|EoM -)%ݥ<Ȫ7nw4:6vQxzGfmP? iZ"UNl߉5\+St(@96<1 *INtdhzBg8K'񿶡Z^.Hr `@@yz0n/ʷ FTqΚқ>#|gdIq.e[ZF;8MgxSVS:J*ׯjnk{08KDEiuoC_ Nؘ2wm vV31;+}UB4jj _M|3jF&-BdbfHUE$8RSsZ-.~ XPCI!cMlP$6b*HUnM4RdZީ >.>6HȄ(t"a4x~5W1H;0vCEZ#t>A%ҠLa -QV|L1h[S X'`V.H$nwt3vcq/mw\پ(]O}IY8/=&?|Qq j85endstream -endobj -4342 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -h7JKߠwli_6rխt,^tݶV~q¬eDT&5 vcߗ~L`I@dtk%79ϷǡIA7Ur,C:d8 h{{W>p}ѿ}7btƳ^*tk 1SRrˉBX~qF$6=į Ht!$_#϶@Y\+~LWzd'cERsʹڭp# *C0[LO69c `,g -a '/A4ܸ1h~tT6sQ/n`#s3Y._˱ϱ FtU s:UBe9_FbJ,2  -i^W/,bo/LJ.Xͺ ɵX|1豖ح&Xt?\ @;5H-F}R.ieD R]d҇ Aw} 0w=6xgqXh'wك=Ɔ#\CQv|(=knhRS} K~K ÷;y칵tlBVm* -)o6MVNls]A%Ɂqtw4W 2Ƨ V`CjRԝcb$<.q Y(yⱉz0 { >`50agBкl!ºB*Y4$\#d‘ݦX9EDTﯵvHqn.<[f>5/zB+c!YiĞ7yL %k{_夹BK#i2(8U]v1c5rM$U[cԤ 5F %QdV@'N\+Yd ~Ȩݵ6<LNΡoD,5 _M)Lw_W=&2FxCއs6tocX8 -GEy6#z6GGSv)y(;n<{?+:Yor{I2ZkB m B<{sg>ZI:#>VzXLŕm6%:pQq @7mQ!@m$ڧ -aWD4Je1uY4*Kj J%TjL2/yN~:Qհ. 9琔m'> -stream -m\=v>1ܙ;Ռqj[cOtʻ=q}o:PTwW}0Vj#=%ˠͤ46%)2ㆦ@g -ss@ fhdY1벤qP2|{Bq_'&\¦Lt~s"J{wK· HD寎M_4"dAwy -ύ_y}SG]8,${9"ѿЛЌJ<@'t5Lҵ(u -P|]ԓG>|#c?FLy}D Þ -=x5Ikuu߿`AB@o֎S.D/ػӛk/G_J6N^p]9VgI$G¯I$Ĕ=Ӫŷ! ڙ'y/; U`:_sm?XUلPZj6E2q Sy,O葮댯QP\ _ʸ#cTvW{O}raobysz%ܫ -gm aCȪɈp_us~̆R9 -j!`Xu=C*8%;%Av3܍^ 泀"X,HM?0xuxL6w?f _J_"F#Q*0&ÉBl=8@P=fo]fR~ա&l4L>2Yh>nQwenf!4t%F}s7,YV_"ΗATr71- eӌ),$`"+ |2" TF";UXID/^ʍf98 rcM -Dg|K)٩^X^GbmpQcIm|Mj_f ">||ZidLp}n @* 8IEfaj]Io3"%qѐNӞv9AQ -Hwg(f>%m٬!}{VC2P܂OˀǏf?ƅPWޔ _~'N{΢ŠbY}#B|WXDБX2@VM sTmL_EbTp{+ݩh)[}5jƧ#l,bJ$2NhK$& -䠌@<_|>m3V ]VMЀHvHmպ'$C fQ ݞjXX]!/"(<'T_5 !%.8_3:0wŽqC\aj -`endstream -endobj -4344 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -c5r$?hj!]Ÿ=4ѐc] -'.xD9rw 8NA1ĺ ׋bxi!g>P[|HC fJ@LZ&Cbb-*Š.(ﲯ1wc4cu+~C2<zSb!0FdG}GEm6 i4zidԝ !FNd - RXw 0z|hms@qKLv}K$0niֵtU}Ty`Uͣ@Jr ?bO/cqDX{D?SbãQ>VԔ7j9yŅ$a!6i,'7NH(! Mc@j1/G{]ʢ}5C|޼I[P^^y)p6Lɋޖ'c)A)b?-\cg3Cju:~q) -8Njo3jU䉰_5 4JNׅN*RH^nBK|+IBNF|ٞ ;evp6h=%6nK$ءM՚I<{pkyָ~R[{oFsMNZ.<^m..f&x{O3D˖hvmU_'y0.ORN03" c_M:[^bxW!x -N)XӲmƜ?RbכAH[;b{E E]N(*aޭʼ0.eHn܁4N~TY`C鱌j|)0 @t2*9eȕƒ5;|%a0)]oyqXP2|[YbbDȾp<1Hup6.la ً0p@u HØˢZϜ<@5XHG#ڌP(ёNK>L陀Sz&%R!*WWJ0B?v Tk|v'Kg-iK Fʈ]&Yf&;J.av~Nt _Њ`cJIWmⶮoz ST+rRLpylw&îkV]EiF;ud7-mw"hk%UgYEMV{PkUY}ݸt.ǚ1Ka1X -6&dR}%> -stream - O7x#霢؍, -{<Vɯ5ȩ #A(A`fY -#<]c"{ 8@ ѫ_"uƚRbCfa-,p?]xPH %} -4˓%D_SCc4NOoHb-!\Vl17Oo޸|[G2ޣLFRU`IrOfge%9RږpD#3k c:K`A޸ .!/O^kl&/<4sVe ms_O`3[+Gu:ְ^L/EnUǎKt΀3O :d"x*IG?sQF% eE'CHEUo`0)j.$SЉå$%fuvi(AmiU~TM_jojRs` -^@tc~`ETy*:JTS*m ]Bvg_+w΋5ZQڶ} M/xw%Od1@Oce|>w$o:`2K7v G}uR}|(=!̠ŲMw 0G@qk2,w.a/uVnAQҦ:•ԛ~*z򏚾{,&X2CkPPs'#<5f8MNX{Cug Oޮ&&^b$h## /j> -stream -'<7jpYZ|I}uQ??_TY(A^w6>>(>\qKh/.$a@ HeR`kD)мK)VkWX -X&ֽ&dʗN "7 d`a7ؓ"˻K^k Fx;bG8[j#3t<0IS(ӳP)ݗ, p!ͼ#ge_.IXmC<ȢDĀNɐBHN>h!̈́0 MoT -w~,~ےro XZqA0Q?Ƌkգ*5DAY䯁u;nG̉o%J9cƬW (&Ȃp0C F8# *)ŀ?&3 -'ȘWrcEt A ^ l\Mxݛ9wMF&q0>f`G,tt`ī'PC 4sf[`aV?Gσ"s?wϒ"ˋHDGqU!B[tY(on`cĺ+V98=5E&H=v6'Rs^TBC*,L( ]u^6 L+zhpUS+J/maGDu@-gBP"ưOZ*GaOOak{`Eiƌa"mFy}+37], w26vmgI6_U~viC曠Ez&d:Qvi:F*tV>%Kf3'm6;ZGV5YٗAڞ[p/q[.W W))`Ѱab,_9KC;x7>J\%5nɤǥG ly~+)H[OPzfVo@UJȹ(0VVQP4U- f#dhm^`oi1*-ɑ#FˇZyuIn;@ܩ7J3RaPo9s5 (H,-Q#V8qϓ۸Zm 9[b cANUMS&\B9y()>R6R}R'&;AEHvJ_wC -p4'm?"mxgԝ(E3Apw/cƐ .ҟlJү0; UF0C+Nl^XXJ#e~VNAZoz [|9¬tb(\aRV =$ d ~Lj5:Uw!f}i_k|.Mj1t0CNs7>fc$+^(E0zׯQЩ_"gxA[z; mnL`؇LNj`l=(z'ME[2>hh:wIg2?xs4j|څ@*{#^]R( u?=0G'3,eov%{&x Y[uE>$e}ώ:^u3BRr %e@݃|B.-yph=0$#/f`jz4E?5WwχD3!ݫԋ8}*h uBFt]PAJ7Qendstream -endobj -4347 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -܌w>rlvy3'+ʉ34eIxNr%!P{g'*!{iݯwPL#Na5 dU洦 ?]{cQc1!| W-26WrMv8q]زψ]*F >D`LՙvQ ʱ-Cwb᭯EމM}q2Haڋ9\O%+5\w( 4{X’GkZ~0 pP pw"̈ V+!升^mB$|WiV2=(g] NqIOq&!l6~ ݤ:-T{x;tSWg31lCj;ӗ(d($c4J}Shg9蕛O<y%aܖOL]T3#YTJ''iGDAJ 3NDLR εDuyڑc jF_lJD?&*7A۞V w*! -hjHFF g{MKtnvm nm{sldβh\-azjKFTq:f/Eoq&UzK[ȱCCDl 6H~.BG`>&ErL[6폴S}ɯKq] -x؝JVƨK&׶ȑFүd{/³[~YGf LH8 ;Nb7 =\cVMW%x^5 sDk+^c*@c14r0X=Fz숧:jZd EC23(pr_i$EֈA/Ch=/fut -w{;X^MsޠEhԇb8`s8z߳"ЕpHbD4Cʑ%hzM/qS6! A[V3x !sS6<+u0,EQ~eH94/i*镯,~G+ s"eԈ bc~8N"6$_ Ap -D GӲ,.{ JfPs3/hlF'ו:ׄ?nwlQGÔendstream -endobj -4348 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -RC%3hAAbyYIeˆ|2e!Y% +on&+3 V1’s'j>k{.>9fN'$.410?1ΩmR-MDǫy0$!vF={cChG j%W7Dp+9A@#~n[QsGhw%\_5,}E.pȚoSZAҰւx_~++Ң@%l=cO|R q)[9MHmʅ~ƴHO`ķH:F5<p O?F8|.z -i^$9r6y $]z0(PL/}_Q}xZS;,WfRgF -ph - yEgu^ښ5OR_yGS聣J\Q񝤲&[C; r8E߹(9:rrV( ROHUV1Q;!V~>[os攍6jT̕ ݺ8v>vKw0⢫q$ڣ)([`0K(pZDhmVoū5xHZ [XsDy'}D᭺ÒO m[u3TE@KЅt$1go(ԁ]ZU^, W| [ NZ~~.zC]l -ߩl4‰nw>&+/},]q6wHCGgoqfpC[cDPBzmz4]‡k$V|qU>iPC֏`O}:%:ycKMZCl~wC~J0ke' -q.8~[_`u4خB~ú"ݐ1HHEKTpavEHQiHUg%) $4[dopɢ\O#=+ fW@1r(p;ǒv̡и>慲F$i-لDm.x3
xOlҹ -gdHn_<)㣳ޠʺ Aendstream -endobj -4349 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -$> -q( 䧫0÷W0V-̏vEƱG2Ր30xoKsZbG"S/(^# N>.m&9ll2!^T53}-{'|ngTvf -:UwP!9(f`޳MԑDae^Kxzϔ\=ޜ5>G}`/2+T]. pxx^["eKtY@=F-SxG{9>z -&f<"?Ũl?r#p,\$֕-s2Ne/YOrr%Ͻ_t - cgG\a4U!Cv] "=jV.I.B0nWZNOq0o0(L9~? 1jY8*8n1&hǚ8}ơ_q ۚSȻG" F-d灌+/:dܺuITU_1AK*NjVBzSdTN=䓉f [v#LD?O6hG CԜU*` k!q!;!V0`O~F4+Gz1guH)|Ð\& -KFEQܸw=.UPT|bOzjNmM'aW춚c$ sa; iW0%$rq􂁼HcXdbkq'd>UZذ3-?n)BSޖ&:n#݀Dh0;U?/b*bT|cŢ'R+qpL6X:%g溽0r_ǭE.EGY~5O -+.9}?B+^Q_aAu'Kŀȇh"=4{gdL^s՗0γj_ |t\>ֿ!|/L[&C%^l%tet#iVa*kOVaMae'p};sm7Bޅ\.e*Ͻ?gBHj^$mkJrp -^ez]H?_6m0sl#Q@q{+* -!;UX*[֝:ia80vdle욼=6㚂3GAuAM_Qܓǡ* Gc9iXJ@N9/H2gRz*<׊u-t?KUVgt2.sI-^ @i7}mRh'KKFbdY~@ -L1p9ixbtYn3ْVS6d]> -stream -Fe%t,!`fOtabZv4Mzޘm2E^U6_w+I3`%*!)O1** -v`x7s#X ,5=N{FL-:Oe. J)/iie`p >xwO7(!^Ă`xhE˪rk:;V ?~;& -gOԐ ȮeUFC參=9zs29&}=i7V nML8HA=s\&C1t2X,~HŔ%0 +zy |s2~v;W\ ];d>m=,y{?iptO6c~WO?RCy k^*=BM{Ic.TԚjxz`-q+[Y+Ln.zεXEWտ dYpe+\@{鑎}Q m箁g N(Z fY)砞g -xş[PmKU$;нtLaNDPh㛮otGJEL"T<KJWh9m^?<т.o c A6yyO-xJÝmԘZU4.iŌcK#,:1;ȶO9{r)AUS :0ң$zs8bA.D?gP C ĥRH"aV<&dɩNvUK:?z-f=c[%u?mR} W)ސQ2eݒl# -K[1\Q럒!.h/Ä4AfVF8=h.nGvٿ&uBEsH#6êQ.&t14E'4 ɎMC7.ТB,!V֒SB+T E%bBە&s91@yd-g $P<9  2GZY ȁsDd2lErDA)C?DP3@}Ӯ& 7N,qNS>i09)NjOYܪ)5΢\U_K@g<4hr=B"I4gEx,#ue:HgY?h\k\Z -p= Ha&ڈ-]˳ai~Tr)Lon#i2jXtG$!+}{/aYExvL~vJyZfT9 ]\.4w8O\ºԙCޗOXgA*c;iN܄/qP7jtcc^ܽ$kꢖn8T\!EY FϼThDXbR͏߻%LA(79l KhV_5&n4?Xbop }D^5iԳޞT ۈa1ŋ)] A(S@>G`s|/|3?W^#e?1آ<,ӌFpo ſPI_'c> h$olf΃5oNؙ ?7ǽ՚SPhg.T[16y3!-$In.mv#T>U%~ bBPrDNx8rӧhA|d$k%kb׸^#z|Ees|qKDs]kîendstream -endobj -4351 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -"XPQdXYDUP xqCGy(r4FIe_oF&kRP_#"l~*ϕD)oRw#?8R^F$dQp%Ry+h|fOlʶ ڭU!9\E|hj{^5ZWIm&1n+ ?{O,Шb-)`|gCST"G֑ΰn}ƗAhrx("X,CM?P_)'"$iP+/ќ^ĬS9/~=l/o -{'3iDWav[W -'\ֲ,"jHPu|qgogO8T\:d ]x㣖dُKVhWAA4ĨQ`~+[[5/Yc!cPU 3Qh} K9:+D ֦7'`fE"˛gkB7#Jͳ䡝D@}| ;][TO&f1J[Oq/ 3!&YU`P_ƺهJIփ ur1e) -OnM '"0P@neU:cfk]!81Z%͘> -stream -~9 ?ll=wݙs>8PYvsy1s񤟦2o:_!F8nxlHH"?kmm QسVC.r=,%giޛ[w{&6 $8~IO[[?g?9\m5%;77^ }bx1EpIl{{@@<p&${bnh>bEe~4Q|צys ~jA+f@sKuLڜ 8*BN>H\D hp-Ի5:1{-hBI4_w86%*Fu]CAen FY4{zX*t˛9m7YAD4\8s+ځrxD`~ق05}SE؎Y2 K櫃5܂=r9wڽLnd]v,[9?3Ϡ.Ӄ5##}19|Rfϴm\-W+aI ݋Ʊ#-6-m1KrONkM=;QSDcrξh&E5/Bj{˴u3G(^cP{*{[56@X0u#Fv@"G&D-!\OA(Þ#~Õ*v4t_L! P7,xtU4ވL{/Wcds4I744\P= x!)@d{Ol -`gGͪG]>^)&O ٪k'ek![E*QR̎RJE *b!ʄ梱>b/ȋj1?AjRy2}js<Poo _/Ĩg4#G3jf$ HfGBҴʮ`Qݠ Aet3DLIa(pڲֽ?x2L5oJ/r9+v#z /I@̩sp{)o u`̱xnw2Zdm i؇+e5Mc%ucog0 3B[0M,E$kmi -8~YhLzr!/=&:h -m1Hs@V `:BJ/u5 -P5f nwbdԷU-W㾇e`ǵe -9\؁H -\fjgu*fHHe;Īq8V88$nB1m`͞|V̉@hniH'*e&,)) 7Rv'l ]/LZtendstream -endobj -4353 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -b4&N -N^3? A6 ]m9ڹ]}TKZwnP1)P3xHOSoީQfʜ"6찳E[Ndi0N*8UF8Smbb)yu+^E'|W|УtTF a}r"ld{#l$G9ng@Du|-/Yw;!A"3z_&G W([n$ĸ %oٶc(:/P1z#r${틮u~9@[$%pbC/jjnèN3)dgf7x ]9͑w pESSIaR:M8^q!?v^_ rک/4Z -~BYVH.ľm ?]D@xㅋ\V{\ nj<BCR}iK? Lq:-U/=vh4<@[ExFlyD)`ZC6W^La)=rҚQWPuU_Y `1r JFpz˩\7mCGiC$+'Sr0гU`=O,aAnj hȟ%*^kO|s -W]B E6dSی= e8;ov_+gU -aRƑlSN9xA{p?Dk x۾rg 6N7WsO}ǣz`4ƸAL/esMYߢؐF'M]ƿkQ&1L-D@10՜q(A<4n%Vk5X< JBNQݗﲃ)gKM<>WiC -"/_U&*DLkq<%AһGw*١g{m#c49!gmmXZN?5d`:tQZfXF:6؏P^(~(u啬N7}Jvutvha*mu0:S#ɳ]?JЗKz#[1jcMךLoVg[0 U<.JBȵSD7|ϕx~ )ʅVv㓕Ob%yx=H ! wLق;^˺ſcnaJ;9v*Ng*Aӓ_lT!h,)f[A6g/,bZ -C[x_",KuȭM7z࿻h#"a嘢N(s bhQej -qMYorq.hY&ʤ0 ) ֺ~>]~).ks=![Zp5zwi4O|lg+KyLD=%jsA3?&> -stream -Y";Px Lkגԃ"0`lwך/kboۣԳX}pѐ|m:n .,T?@,u -D"$Lfg8FsLၟ0"6XRU0W37 *C^?RsG_5ƅ8}Ur@UD?8Ie}*nY֊t3J\Kɞd,lfOTrTLj#N2u7M&ҡZ4VTi5`%м3aCs9uG]K m<QQGI@IڜsgM$%ǚ&2FF G3V|^6jkg{ Y "`JñaCzqLXY~KBT+mxxs4| 8R8Dyv\a nӥ|`a&KjuHD'&V%%Rf~D0C&G ]2)(a8e ycy,w]8>|+chZۤ9I{-Qq'g1;ܯD"{".h)5c.0@Sxe'SD&bYm d3UB HZ2ۚPC|MUlFHZh5!s|t މ~OQ zdwSD`VMC :FSbv>D ܽ{ Ⴖ.6\꿠q0b5dF)!Kb.Jz@ ʝ#gOȒInmaŧLz}13x+A/{وV Tr%#ā)FiOV@ ^vX] -endstream -endobj -4355 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -ͰN%q4 so.G ]ukƅjAy#uN;(P#:{)i~S~0iH"Y}4+1vjIo0/GspU E'TT߁%DϕqR\OmV* }=d._P˻/)okLFF!, MbCsp|^E>W |vɀ"$SR<Olh&JZܨ]K ͟x]D1ҁ;E]~8װBohȧt-Ґk4Xf8f!d\a|} yJ6%L|w>e?[+Du2rWBZ/DU|vXHBNq,$VQ1?BX;;_"-U -\ˆ>1G4~zMwebL#&bqG=_Ǧ l -HHMy"~ V>ϟ5Sc2פFYwMׇW6cr;)NfW -˚Ljѝ2:@[pڏGY{Q`XH&VeJ)OZKGR=«Q"ء]~oGJa2++02TjqEb-%5AS{T4渆w6֍zVnKY3V=j7\ʣKCfYu WݕB~?O9P1A'4=p09)xT\Ϭ@Ӟ}EM\$\T 2 Ii/]t%yIqsMZ+4ɤD&dΪ@o@B~z %BS}Pn߁A}vWSZtDĤlk(,(*$>փlw롊I46i& ˜p6tƊ99thI 6&yT εhƻ]5|oaYcq j؀h F^\y56=FPv?k`ejys6ɠQ(WHUXS7I;3fn = 4G}خw2o&I"B+W|_o;4 vd=h2zhhj`ɯv+ׯvI76Xh5DADMc}x:}Ul,GpWm&ݫU;H (.D(*[aUlΞMfF6I -7,?rj؛8X +Mɝ! -'\[-hАFJ%H?A@ I8ǁ-^4@[E[W8ŧ6\?p79F -$;Ф4N](U>js>?TqY3Ox*h I~?P9os4sQ9á-+l6Cq曕R8^˷uM"pG%8C۴ySZf,h`r&\T[+QRa5+endstream -endobj -4356 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -4LYoS_4Iޘiyz+^pA -8 -Jy aG!~azd WEb.N\f,{/Ih{Bݻ 4wo-CvRkݎ\ByūQk::pZ;zn} -XI|OVAh3J&7 -kC:e;a/D۳S9)KATl_ oӺUŸيrV|x?>ؘCax!ZA*ٱ kq~ 8W Ev;6 F!:ҧ] yO:\O+o؈<$KȀF֯a,aǏgaH1 -tqe5O Y\Gdՙ%[~&)Lš{3_A[ՋC#.aZQEeHeR -@񙁏bbն_lϏ-%Mb[i~iX9zkЅ1/ -WnP Tt%}]u\Ӳ/ -|v*H{SP3{k\ Hln >uR+n0-!YxI 6hA{o.:$Gf`qC@-ULrm2\20d[ ():0ycf3EǕ܎r&Y3@2(X/ùEfb= -ƴהzTԧ pcU['bd(0vȆѳPf% b;3+ˉNdN<Ӹ0-׍~0Pb|GN^Z#tv5dO# %&G]+93ҍmTq p@8 hbBHM|@ ~jڗ@̓&T #/}O(ȐT%[dˆ[(^Lx;LI)RJb|QT:TޘQ'ūvEhHnXMZ{1_A'm~cGĽii7;\5JsZ,V9m8r% υt:oH+e"U^nG@s\smO8rxr 5TV Q$?J.p~}]=xsl:FisNĭ1O6]lCg.Y+b(YòKk)@I%dԈ̾\ԓlRZtEڋ Y]Yt,A^9?m(8sTj -B{g%Y t ))<0)  TB!D>R>*E)f=/#T2< J=nnBoζh3 VW}0/uu6ᷟזh1y1%M9/_bq43>ǀYmIu$y~gzss1ҩPzb(0J -zjYVendstream -endobj -4357 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -I -{ -3|P2ƙbA;m8>5]lЧbgR5UVeZJW̖X}HEH`oxN᢫(iފGz|ӄEZ{0*d[y |ᑮIoy5d́&xiju -'3 b幌>?/Jkv0bQ([<~kAhRq}zp+e#yww[C#N8hC9%eRo R{ʼnBS1k`lT> -wlkm;4H_sO=~dDAw;(.fS&#@3~}0=E2I]Ȍ1fM ձIw e(KBA3&J2k-,xܛP0g(;-> N0q`_< #&JkZRW&:[{ D/n87xd;GBMCs" UJS6XD_9W(W ꃗ,;~HѸ; Dum<+*'쫔'Ң  -=e~(AN)VUh2Ш7ؤ$X,U,/HmJMs)MLE =1 l -\={Wb9 SWL;.1xOmpjyr y"6ҟZ^[j!x =`oCڔ$L(++_c&+M6S{IX% nѺr%Q`h?S=+D0sӡV~Ϣh1JSL#.&ϧS=(oIj]XUZyfDiVՀGpC|2H+uf°̊6LzxC !8=9hpՒȘ C -o]z(o a&T4U} -֗҇?Z&oL !&vfA16ml;"b36֭Ht,C -|Ws\}b{Lh`TEf|4z_mkz~2N\Xi!Re+swo!pUKи "b % &#и:lWT\ɥt%cxpՔ4JS:f拠9k*ۚ*_8@ۧCAN֔8 #lܔ_]BOF -/4q-kp3YY`T}3\Ǵ%$F=OAyb.! 7P~Vُ3q!!x -hH`r)#S캳 *Zav`L#dJS%7k }mq\la gr/&uj?*1ئ쮗4y+[wp]]%NqDC>3bFTcf/vg+V#x3d70hO3_͊Q`m3q${l?2 -;Ӻ4M:W(* g|q0@I^vc Y*7t}(CVݬNV5x13+TRxhhj/̠i#K@8E\]0uhQ<33Qjc>K<%:*@i0%)L FBZJXSP,!ˡgc2 -r 'jr > endstream -endobj -4358 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -_RNآH@n3!I:0U358rF?QR!Ypj#F2l7,ϾD%W'IҦ2$H)i;Q @g~ tFA -, ϭ\/b9_5Oݶq u :/t%x@߅|Q#X9/SEʺ$Q3C݃6 }BrKp 5JZ۩ƏzUIgP"=^@/µ69udF5e!z=]nE>?LˀIlIE,9[?a96vGۇL8`µZH#JH?OaXu_nF._.p*!O2D`6tP]ɧ8b 4+`>MME'_B9Jx`aAyNKu'k扄; -ɔ.cbN悵۔rt^FS5H((Z^=iAr۠{2nY -_+^TZ|Tc,#f5 ']#ˠ{mQcT6p:<)Jߏ|BЃQ"1_#ڢVrߚ7r\U4b !D>Xӧk`S7|jWHܬˌ/\+[zXfT{8R+J#StZeN fɟn}eendstream -endobj -4359 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -7,`Qy5ɂN edzq(6D@߸Wd~6bmɵEցP#LՍHtkD}אD!-]T4⪡Yo1 0.XOZ!9|"[o$8c b` n{C5Sxd]$d]v]g{bRb mW_ 6hȑ] q]Hurb9 WI@Fʥ}#S[so:CIZh(NG.(xHUgC<~dI\VIϣ0PblJD ut`t6N1?S[ -9ȳ8mf #_ЭG{㾁uf<5kc<0ܗ>뀇$ z?Ik߀#j.V=tNQٯ.3L *4n<%s56k-|̲|#s YeSFx -tj-?%z+r-d sb^R99sfv#H5QLէj]2U\+& -ax882BiHU@&. o FW1.0㰔l`U`y +|sJ=껒jiNx@3d3\`9CK LF2;%dK'9z[8m/J0zva"zge9 >`7f\AOV]ɶK@6S0Ih%a^FSUS'z0 _gワv lV$o`|C8",Gm@?ͤK؆h婷vN#LD\ݤVCܴeT&9/o ?S/eZ8XKb#Wy]n(b`nYK|Iz+ G\ AGNV \AC=)/h"Tr9#~8G>~[cX<.+ A7 y= [͛BМjl2J5E,yM;V㻝X(%Ѣ6Mi!!^Q͎:=<ԫL(({/C2'yU30i\Q]I'~x`ن5"<ߐ%7}e'qrTva*rN=:~7a_™{p`㤼1&P˜?FKؗ^ة0WjU|gS?Jr70cbJhYPyT䦆KM*wCe.uwH[]ጸk(D5&+EBLd5;\m@ ->E1vۺL9sc$eNKhWϥՕ7d.G>,~:`+:`e[r&}Gh~KF#a -\к*ޖ_NRICaEQocgsET_x5iQ?"z:A/D$Fػl|pFSx~6 n2 P2}7 nqFY10jbds:,endstream -endobj -4360 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -F2;!t1X!HWFsWVBW" "&zzOx *7]i ' үfTIyeJ -&̵k.qLna,0(9:*+;mٵI%4>;ad.:l1iB]30ډ wˍ[xqկs)g. -4 \[}FQbY.0hm`@g>P]Z#MlcvZH2N=;&V W'M@h^` mNR7!2qya7m XG ۫7yErh<#lU6pbzвKmŖc|:$poM= dA╆+/jlk]D Um0Jw-X RĀMu17,٠g_Tє~X>[cx1v< #b RT@%f\@T0FCP%PUqr8+zbny!#&B%wkɎ-K; Bdu@'A 75%8D 'a^Ϟ?'"BdxpCIs.Uņ]Ԏ4E ?AOB.2m2P'?hf~ {93*[דG*²@5͑9.2mgj#tܔ%w.Q2nrtP='vE:S+I%p -PA)c}$F^bvC=8z&˚?pT`mEf|_2O>J6z+1ʠq'c7N}XD;d9:0)~6nDj]#s,vE}P0(2[`@źJZp0_ֆ!bx1jW GYLjIp N%.N@d%}qge0}-)fuҟendstream -endobj -4361 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -{ATOb)Y u~'9+ӬJrjƦ\buJG f\NŅO -ky E~>pvfe7㋣I^jXz\n5ZC3c{hHbORȂ;]s*Kdw*JỶ$@;x10d(]&v;zqPQݭr`E%]_S/5o4S_cMt}f;\$:@<-ɶ$r6 |p-IrFCptlt-#hlୡ᪙xv#'u9e|ɼCd/UC\;X7j/KIy-k(R 15gl;]$$<\"٦ l%2(0W|1EQ᱙yv='SkI.>7+ek).|W0>H-v " ސ0{嘂G -qmߒ$kkhVc9D#*52qhHY4HZ0U]NH,ҌH:L2i$\ -qzmD+!sKSyE0e%  "[/;^ABsȌBtm -k2J&|6{F^GQ#UR"  f&E) {&a6dW&T}sձ )B2"vOڟ-SUKgE`|'Da9wrqwkU’ |> -stream -e` ALZXE|ayoꂏͳ[e븽 ^b8˾PTd5^ӿ e}NI/\F*\ -p\^2F ϊ33sG>`!<*yv#cRHٱ1Z``-_jF;7jQAokaI~rc)+A<ė)s  Kځy#ԡ5Ey3JebcW+qfjT#+K7B!JjY f[gDsFL§,̎ =:4mLWnP^# -JrSPCu'h#h~x`c(/ġߛYˈvܴEٰz9,rϊ:'LXlH) L Z2ʆ.r8N_Tc '6p-?PA8#d6v5Lmᵝ-Ձh>W|Ҏ/z 0-x{d)SylŇpexW3=m't4 -τ3idAܿAIN>X]6! -)Z8em.TOMσf?UU MIҿ?;|#YcYk=sؓ1j˚$E2܅=!GVHT `x~ј%A8Q@!]m;XJn 51XrskEQljd"&[B \2.x86 ǣ_%m6cS% u~QPcjh]4PytBek:; Cv! -em;M)"2dfxiDZS}+;kxnN1[V0 ]Q޴kHxĚ:E7yz'N6tb=b@,bIX(憏Q=dvW -M+9)!@lD/Xn-\V*)(1'(i= 4I^Z4tJ1f^ K ́Cqth6x#V1HFZ5aS9 d6PEIt &PP1P+Cgrbh7Cj׿'5zv>~c"qւy`УmxN]2|Q@h]@Gf ?OtHp 2̗^;/i'A gl:gdM_⑏EE: ;n0l@*hW}ڧ$k=_47:,ŎgYY7o?z8 }x160Jf/ BӥMaanՔD]*5{:4rϭp"leˈTӏ`ѝ#wedA]a?-Pfi}l<;`iڣ3iK0u -#3bfHZ賝Мɇ {";3# UBMdEI~@$W%s?3 -D??<;W^D ط;dU ygOYI{zy97^?GEDWS\H'e Tȅ;`K/g;J35cmޤ4kE{emE8aZMLme.q`G|Ϥ<1F&eTQ'1!XgKKo n0O&^A}3iB_AFrYoԆKnnp0UqQzjw5@=b -wvԖTmBc:OL Oݮ+ԴyGٙn8~-&|mts/߉bz>ͱrEѨ;wM#Yb5wol^v+? -V]Dl0İS;WUgCũ4;Dŗ[F3z:ӐˎaTN-깭 -cМ£jux t' Q޻nmxHfZUNMq "ΥM: {湬mSЂf~ e*j(hMz$5R^endstream -endobj -4363 0 obj -<< /Filter /FlateDecode /Length 2128 >> -stream -ۃ\XꟺδHx+1Й` ',RX+FFT(F20GR dLnzKP!o[}|WyFD_ӑ#Ͷ(PE;&Cg1}b n;?Vp&L 6W2F;-Lqmwdp"jzs[*p{6%-֋\Z.;=a9&_oD(ԍFx!G- #M38>hjfMwf6I`,;L 15mP [xoch\W>RhV -f7Ѱ91Ft[f:Y:|?{}yI_HyZ nHw07YM=_SttI~ a (# O(B5'١-d̨}gV688xTJzېOZ"f"24萩~&)6a2BBh(ڸ!׍,Nɚrψ=k.q mE |uaZ*no@@LpUJ.I&`0ͦ1,Z_&])佮O]d㕨ş5tr>"[<J#!k"6QЁC =JI` c\ g^!~YpԼ9YO4`u:5 D g`Ol@agDd"" -/78cjoVN23rfv/QʞɁ{%T-\]X;G'h[3@6+ي4't$ m3M0qiT#7`6O=aDXc_2NFKVΚ>{`߬nA_ڙb;Pdl$6=YTzs߂b,Z e^߰vg@Gc 6HD{^gbeQF&(e~(Q˼VYQN_p.U `ijGPVTS~ ?^*϶rA,! q =(Yx:n@ғS#s. f p:^kdE:k+S6 {?MZ3Z, p'ҭh,Ob]X#o1~Xoe+N>Tv_OӸ+%T=s "J=WUGnӐjM? gQX44YuA^\3fWz10#XqxAԨ?'ȟZJ{=$,+X[=`> -stream -n(8MOc=fng3VA08AyeLOۃΏ[㎔,'r!zDg=RhzA9:A\"!E(Pí쁤\OST; 1GɈ]lU^ԑ|c3:/q5 b93*[GQZhph,ZG'IkMj8qbYBLZekjٴ)%1sΩZuOQ3=+~ʈ猫gcOfX 6u(!>iٔʎ((ѳӵ#l>&aYĨZ! -V|78s2N`& 큝*¬Vן]^&Jbqco1a3K2Ը2},qDdr TI2燢ܳog\ITAkd/۞s4c\M ~8%fQj` -+jaydSf2P?)Q̏U4omu\ b6  ;&s2K}Di`V7uż1o41 --'G֖]R3ZL4X-jTX%qcL-h$/0f*(^?,nGƼ9c'k`"z=qDž{KUeƮqU쏲~7ǽ+/mҝPV=1/ 9iF Kf'0$BvRU=FQ[_MYۤLQd1C 1^j%[wXծQuQ|ewf>0:.3) 9XD)t%5@m{3k+Jw( e Q$mwrρ+ U?gy#Q]A @[l 6%)))|Z6Ow!,9pD7j3I9Rl>͎fMe oې3|}yׂ<(ۥ/UL\1RE=.`y g)YvoM|Jb؁m -!7 - q84?NgE5fػ>iq1#,4ۃfH8ĆI`E뇘x%sȵC/I΃k+t1'e4~}YU;-0fc}z!] +z0 rp6#9cUO6tJ9 -fB3Sg!n ̤UwvTn 2Q|) ] f X'K75ّ0mQKff ^/|6 CCMR+lT unRzH!"=s<,Px27k!gU҅9vؒBW^6$$!y)y =Vh@SKk(aw<)۵~_k)k"S>\ E#IQ$@^ܒ>Ŵ/[-w8Vaߥrl#BUU?~OWdsCx߾Gg}kT|~A2gz -+&endstream -endobj -4365 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream - $ѩ{φN~3:ݒ9 -}ީ;F{$e5C8J.$PPYJ_S8dAyz_cy}I=K[ԶD]a'ֽ)9Ssvh# ;գ(jzg!s<V5w`_[Ϳ؇c*J);V[%\/o)>ϻ7G?}E7Y E _]xkJ69V ( $.S#IO?R1Xe`\2j$z3Ns 8`T z.wYbFW"𼥿 Wg$%s# RG?EvDi'21%V7ܟUXRof'|GdMƬ"RdWb[. { -n\cS_C"DMU0AULIm}ĺ.jg۲$;~_(|0uˠ[e@dpX,.)`՘=Ƌ[XT6@ifɹ=4Dmb?AVQY:wR?tWa9cnAKez%sO۝A*^p~\]#M2˱k"nN0WӝY+@組 Gq>2 !3U_w鳯QϷ2 =ga.`asNP\2a#FGz'R b2G6Y2 픬m{,jT9 \Һ"k}P.-67%/T!}8>)-H ςNޘN6xxE\Dx9enq%:D#$1{vjSa93j̗Eg5Im5^|"_]tfsKmam -_  fK9^L˔S )Gy6bFjQy/=y'^xqA[e*uNx U_CBGA^dUxi -jP0lF{ ;t7`envNWx%Z,RkdTAi]QJ@-%tB5Ѹwd=C6fxYMݩ~e; >m0.yZ GyaC,3F#w(mV{o=3^[#ThHzŒ}*5khLYY+?#6<}хr6!B>&XֲXhўvm]xgl~j+_qYf Mǘ0L55 QBvc{K}E&THBzJX% Aëple?ƻjaK]mapͼݨ]ڍMru4ȣO9}.yendstream -endobj -4366 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -Ɗ EsO#>V< dX?^T%3>[Z죋nFAwo[4A8O 7/6B֩F ePe ;~Ky;1_{ -8F"QFнtLѺ7"ߵ~8P:pаԡdo&EA -Q fu#l|OڂkQ(ӃQ߭qbj|$#Mhk(s3rw*BHN^dMP PYd+-dj)^aY|7pw=] -cM~ϖk/fIZ`,>J -:ikey,7N[( - --.% -.Ʒ( -~>@,k qEXD={Z,780Qf"F`蝤<#~COhy([aCmلF,q7Mg p òhDwU qD9Өg>kC%tlubon0"$^+x)V<) kəw٥v>**9U{l7WqXaU_n]ΚyC3]T.h1mHQiXm)/ZĒtmu)Gw8Wl9UK6jC*.ԪϒH /O }Ԡrumn*vh(4FoZ5YkhX<&A- 4.{]z~t(*1Wܫ {O} He*Bl0H̗T&5b˒y]xn|]", =2e.ģ?:Ej&vˑ/M nEw8+%6@N_uN#Ȣ2yE~"{\Bw"2RS!FpVNʧfigo]A[Y½/p zf)\p^]Ń> -stream -sR26 X! G]E7KwheXd}\Չ5Rk7fwaIݓH;rT<,'@gFP5犎,Va`OuAnae1H=܋/0wlrYhT!+-Vwts\qW|HDU'r)O/%_VǛ޴*I+NЃ&ei]j}nL<+rmywkI )v9 M_!΢K#]t#HE>s - -{3soUhxS761ϣf I&r#~*lԗ6ay~q h ;Nzd(?39%''$mgZ&f4 '#S(@e1]%:x-\ #>ہ/v{Rg+(GN ުuO.C,|<_tnj `H}խФG}2S6$_{&?N)zrV~fŗ(gKx5GiW+Thvγ =CP|:҂Jʆ#& P8U?Q)?*"fKY$F 01UTLx -Ns 1R$kh'U3c0eoYT~B6EߡmaƸ%7ɯZH\طoEUà2W}pK/jl5`W4DͯwߥxQ -~ʅGH̳OSF^ϖșAтnw9mw zFݼDP3Ӻ֢  ӂEO.k*ѮÅ ?Ŷ׊>=/cXmҳc?qL>64M -Z( -{<"a1x Һ CӀn_ 50*aJqWDGp:_1>O=.Nj!jendstream -endobj -4368 0 obj -<< /Filter /FlateDecode /Length 1536 >> -stream -hd!/,t?4罞֋A-R*w"X#,Im/+!kEvjY{DD&l)5 y(2s4+)VMcNCm!ȍ"&_Q&a!ME*.- Ng@Qt[Z'C tF/R/ 5H8Qa{DkaWT7"`pqUU=沆.k)_@U_~Br3-aJ).=}5I1Za; b5p'Qk!qOKq| -eB 3]eFgQ~(4G$:'0Ѷb&ʫ sMm d33.;̱4=46i lrc%W=A i: վf<SUWAW;=b`5dUvk_7QePt\L!`ϣJĊoN jzuw~e&7čᖮlf۬Nzڜ8YETm -pK2,TFBi%1Pîa1̆W]s1uϭbT@Kn K &՗ͤi6IX/1ԵTAn#=[Tk`*6_^Zi~Lj)Ҳ8joNJw -1U|,Ә]9=[H}z'&F> -stream -+G\d8Xq VD<1ٺQL2BOي]BhN 8Q.L?9bq|;>d_/qYLOow@!^w&;k;|Բo`\@o݌#:IJ:h"'IQQtdGG‰)xB$XthԎ'1t\`T]>NGu\=Qjvms1uqG~XMl>XrQ6G*fJ"t Z iBȫ< -DZRyN$N*|i>ȝFZc!OVi4 "mp Gxr!b~v$&7tm0!go}}qt1:u|>zq -GJxs>*pn -^V 29P^A=g` (Muog]X&gy- }*6xҞKԉKJ Ѯnh`ɨۆe5A*ap38Ĩjꃑm(tڙXGγ\vaoǛ*]K h_+8\F)+F"c {hWr̳=xT)e -aiq8}:GvR7t'UH1d`,^ -݅yʤ;()>c$e(SX`GW!hWy)ݷ[19zޒ* b`Ԥked,RC.oGxD )k?s¿QRu aT;)9̿l4zPnez`=-^d!WC-@\k³DM?w1Fa+Ӗb\+Ks7&l":Ex, /+BiU h->ʍJ9ɿ].Qx" oc?J]{ʖ]TA m ncv=@ JZY9ZKJ ˳=P&gƶH*qDS~!x@.uJWύqwbjzji;$]ZM xdiqF C̄Zy Df*B^}d뮕"#c~Vj*#*(6JYn0=ƞvG7n9 trט65?Tw}oi^LX̾ZLNv;0 4#ݨx;I۴4;1a͠HJ2Z8?mث3I_9CrZWmܒdW -~}rٖ} -O;x-> -stream -"*}IU{3\L8ouY![+-OpNc7Ϡ*z}S: mb|!=PJ{""0 6YBEUZTr̠߳\\HI<(L9QsPq ZHmnBf!R&RRXV)OjډO8LO.t>`~͖='Yh95/̉*R q$5so]-k'Xs'0if8,_AU3d҂mOo{ ZҘeRAt<. -ҒL4/.th -QnOG -ΉgM{Bч]䯾,Vʹo#Q&NK!)K҅WF4۝+߭KyC &]3JAٛy]-KIp;%vE9(,'29qq98yEva@~K8b;oWA~釚v#!F 쏝LQ9`<1`mk5,J9J0rde$׳7C%Ydmޮ&h+sDӇrKp\ |5zr4;0?KabWp&VIԍ $6S#YB4e2g(&Yofg(QRmh:jpĻ7^ cTb1-e5b #αdG ,)"ccNapQ'O s"['ۭ>FtbփՇlnx,iM#?@c?Hw?S``MLy_c) re:&,# ަ6T@1>{F06A+xNN9È9| -0-Ά?ՍOL#hqO]5c͝?hd4/5K -Vшdr7oضIN\Pm|7΅]ɟuv.?c}MN'Ss\TL*/Bq8琟 };*Z*cuv7q @%i6k}4IT˞1rrIRټBh_\Z_ -- h(0Ľ}@H -$JaC -(޳ri.01>nP&ʲriS(iF1'T27*[K֛+,y*G|$tu1r-qWCI'Eљ;Zy~l*THJwk5"H* R ݊frRPk@3C!g ~px2v;@[GK8G;iI!Gpr}@̙̈0<=F99;2]i4֊YW(6).BACu[$tHMĢKCQr`UL/i 0ƄDϤ.:Qql;ht#4R=eAk m.0g* Rɰ1 jnчY Zj̶c4ڃ`5iu"F^'J?r&g4T2q-U!vB/~iFϿ|6WSY@> -stream -lyv"_Ze:ˬ}g(`HbRJ~FJrCKt*PMѢ3 - 6_!siHT;DʨBӆha4C3 -IXIZ4 H:Қ^*lG,[s֎e1ng[0<;apΎ̉NlbkeV0Q._r6 6ǽ咔kqPOۧXaO%mHvqBI=qx9tV2hQU)o_PaC"~}Bt$˶ѝVeL 3٠UU<:u]`o)7`TeU=oGFb -=C{\>h\ w -8m"Lq=2Srnzԭ2 *iu.)z-~a'u3a H v=R|iDM=V7|" B}B衅*-k5ٌ}{lxlΤ'^]Q?%)-hRk.I}>j*s5L-E3rbߪW(>,Ӣ/*ڞÒu"5;sTC$mCNrqVk+lDt:rrA9Lu q]Ш;VgӋ<"Z˳AN":{f!yM#|koh -;^y0R#k~Xxzf&y%6N/z.iCv+0YΟmFWe`'iEAb7O:7uć3E@!a=Y#0k$·u8ݣF3gD`'t'D5w zX ϶">6^-J C{ pKmc{8YVK -!NMU+D$lM'c -6\jNʧWқ7s<. }8V)(OᢙZ+ :9g2Ex3T6c&j<E3.7Y[(%t8зM*ĭݳWglRXX"U1bs7{uDn^:華51W#ut;łF9nǯsYzt.X{lUчƗ!<3.e#}-Ѵ!2$ (yln1Vؚ~hʝ훳{]V(+jl姆w4'Tvth|HpYm4;_mendstream -endobj -4372 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -Om:Ɨ7ztU);JҐwWh~jX+O!H)IvЋx؅NlqK#Or_*2 5)-K1UM~TRDʹ1_ 񟚓YpV;7gQ.;PP$Eʖ9W.BG\p'0N`Nˮv8T\Hƞ~!~fua(~E4(i*i֔ PZnPļNlL}Oƻ}+Y^Xf)ϋDՕsnnEF@ z^Ec:! JLQP jLahQR72`αl?$5,'`#& 5|O|T,"WY8̃f.Tk^mиmPS'((mA'l81}xG-n\;H7X_-wZUseW-i~| 5p_mJ0 o0P̔)Pk6(h:2[Zv-iHAqtT-gO?_t0O]g -v`&im9ɻ64(/n N;Az22 X?Vz3x s,'z[wjfq@6i5Z^/<9s&jB&ecS̞P+Gqٷ=[?MXqqFQqfcV3 -(] 9-b="*&5?[{qq!/=:l!!Vh%N))Q0y6"@fs`Y˜$H)%f.{Ck˓ҿ~ٽy[赻OzQ@]0:\}Qء<Sjo&p֙-$b=ו28}@B\4fWT5Aa t^c`aQcFT ~ex?;~Ere\'h/MD -w{0ѿ'Ȁ沷R_cTa? <7=KnITp$yf%59t+tm.329n r[8_"7ie#C"|{`]%K/>]ۋ -ĸV߸n>@cUV;Wtendstream -endobj -4373 0 obj -<< /Filter /FlateDecode /Length 2768 >> -stream -2,4YzD]?.1qĸ8GO'4֕øFas@Z1i/W} x:DWU~9Kj3ԅSP8gm y#}آއS"7Vm[JlڱI+:ď8n߽,=Nͱ']z3CY.4K=GbkB`B_[fOe*%9c@Cb26Ab͐F|׌ZϹl69!@kRM&|5aOTv$Mshٍ5Z<7y$gOW1یfܯ -Yr!Sw$ !@nŰ%&$<R1OQ#H*ϨKа!-2fE0 @VȜ_`hU}p$oԳ5vL]OsYȒzuSrۧѻطDEab|"݆eA#|׾+ߤ/xO$(F-|,gP[oaGo -6j,N4c%g(tuu<?k X1~z+@06}Wڿv^yQ,nӖomve7;#:ӄuKˁp ip2?Vh㛡b[[Py!fiXyi ))%TٍܜDM3 zQQ/cMѻs?gw} ٌ5.>3<5-|_B:7Hg2--O$]u?Tcb1ѿTIj2S9Pؓt+kKw:ZLH^L -c]RR^/wq54zHo- iS#>ץ*}]PMEn)P *n5>W\~ګT(O6lv8{,ɒRp|in?f 6\,WE4$N#.[ɢ;jkc7uWC5g4W+DKn5`ŮD<3 X Ь|ޢBZGD4l~jC~j[;!앣^Py'})GJQIwp2Q$@nxmy}~Q+݅|_p/@;k=ku2P}qROP" [8n̏yDNh;Jն@\ -H.Ud -("C/Y K4L,UH0rj* z+28һRꥫcxjHn ` ח\^+%jmZzN=t<ˑYZMaj9YޜrMbrKbJsO*!#BoWxOc't촸&qC{&u|'Ŋ׳t2`.`嘬/S;AUj6 z#H0)VfΖRl H1B,&"ʩqZ"̷&m"7-Q(aUT'/dm ֗pWUQ}=Gz,ԑ22՞e5rjG;N5|p?&!,>'YPΏt >qfi,Ypb`\sƪg0O*o^pB`MlD4;$Oe1 |%n~Gf^;=~MBnwlH<1-e*&ڧiliB}e!sem_3"g|~::J$J͚btVU(ZѶPj9T*ĖOmTh#^6צ㜸N6"jr $|} Uf ' -DB@N)> -stream -c,+s~Wrf|] 0h \n#Cg,H1p̅?RRQA;JgCS>4gJ"6^!E,L H%"ka|w͙N lebP8lřސ I8tr!rN0㺤9J ;K/-dvq6` U& A{ɦa'6Cg<DuG\ uGG֌ :ͯ߃ɀ?;kwiSZ&O9/äod3ih5$4}xL #^Fߟ'J{[L>w 5J9֙#2JL;U(J%K߿'6v6=[rǽƬ5| ˆ͖㻴/ƇMྮpԍs}‘C#i~y82$vFFth10̉<+EkrSZ ({9L\sC#ajBr{p\OPn B!$N!5顷H0+D#e4HCԣ`gzsJY [mgE{3/O L"TYAӭbhg3Ҽ 3@dRX?dȗ,d ˊv3L4$1;f' *9^a3f?а".)PcxlekNBq@7ѐ Mz9|'DW鷮'U\`+Th`a}EhHq%X=$$jQ b`yd w4 XѶH1ƶ/-ܙg5 R>>dh7S2P`GRܰ:YY,Z 6T ί{~X[4,Z+ Aڎ?"G TGBj!a)^O3hWzwf s`zt)/zџ͉|.nÒbw'?~ۆmߓKR@E5KzF1,va[X\aƸ\{L<ǖGV':("_Pϝ>UIɧ};D:zE -^37-pxDtQ)t3wn/ g鞰%'T+(bCZLNc,嗻(M*N "}Q$S~CMIlL+`BBz2ârmLx-Ӑqrws$Xk@8Cq@Pja*/S5Iyu"$> ~j%ؔpnVtC5HSn3b~vn2{bzR܈qe]1L H]0iG@ug'@7x\j!@`N=~,X⇐[ -TBT- spWhTxp ?-(ݕRZJUߊ\{&A0ہrQ X|\g~-(gr4;5O҆C-,d2=E @\WJ٨w:w -G7vE~B|K9< /CAҾl*v.KF;.tr =5If.ϧ06rމJ?.??mR( -i24VYKA:`]]tҴACIi2AԭjW&_ <| ˓5`6ҹե;endstream -endobj -4375 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -2tbS aqXgD:"Ny6mkz!A<&t,|r3ĮVqW((b_ -뵋8Ԣ AUwUyطVZQjV!7m}YfzXfFtKF2:i 62Pr IPaƑd3Pn#@ϥJ}FఌK$YOq왯8]sh~ 0Yv(C\(~T:#%xu>; >7WS T&=fTEEU8[yMϫ=j^89'jMB>.ϲN*pXԗn.$FLͅu/T睢"=>Fgg4KEnM<% -/0qC5 VKpb}d[@PL&X圾Tl4q;v/DiL=A@7bu7|pv9"q1`.CsuZ6UaYxL/}Ř|'ۙSP:y6R>KI\P D/.\c7Nh=ν7g^ۉ`Xek3 -~TS‹9^n "/@_\6?Ύ7ϩ&n9H"+0=bX68l Soddc S"4~VEr5UݥRo,/ԝr]esRk&+gԥDtHd&ժїܣx}N@=Aշ7C4JTj.2Ua>9*穝¦~l{O|vxk`AYI9rdlP%:Y")^~ؒr\5u=4_hɵBT/}lOێ08@FZucU#wB'p2-G`(*tGAEe$qO {m۫Ox]YPNj5"e޾)Vge{@ϲJPKM3q#'floeBR\:Yv{k˱Q:xߔJ k-MamqtWp!RjCv)mSYO4M@~h\V -s^@ő^\ c^Dҭgx94(w*f5"{EAt0Q -PpW[!}|֋&!o":/ y3AM0}î"𯹪qC tP0~|FZZº9H1itm:5v`K[ށ\A 4ZYvLiy] 4)W4zɖWJ]_}(0&\jC60h#0>cuuFD. cZcqey{?2S=fkTHVendstream -endobj -4376 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -4*Ek* 5GH@qD)IȐ=9Q̤/ɡ\LP)]-l2$_(\3/Tlhl <R։0_&ӥ5Y}R]`)xR2ҙ|X!gNO%ƨt~4}}I5uG&ri9C#.x#|!tI ҾS8w/bp&74sxN`/r{sf04;jtimtvH Tȡxϗig4Q ںnhۼL H `o XkJʽx|$v-$/3^WƇ-KS{hs3I'z39n[ @Nǿ5,&G߽x. xڡ_7x<1*AƑqgK9BA& Оh=fmzH-9d/}aY#]k;񶖀_ MeB|}\WY#YE-J;g>z]%RԠ]q?@p^`}[q TtP0PMS/˝*N@͇Bsr\p]_ ɍH|NbGbu#j);6Q+~ɓE>{xWrP%/?6olGl`(BhXnDh_8%#IAGMuj鱶rlBmgPۂIڎ*ߡޒ}]ѳ4<ʦz\o@>@.Yyg`L6~7k>Ţ 'z8QR]@i)-<Ogk_Z\.[ uOxMyfۜ քxkOje,3xn65Es:+-Y@䂹M̀v,R0Eo\v/A2k`-Z&y jK - \ M -nٳ%8%)T)+7w%\R\SAleľSaykܸhˍF/S(M,X´|ASGuPT_Kfds}9b.oo7p|W#=8 axnM(SVşow=6P ) - a&(?ZD(sqZ[|‚^ -$N،,OcZ``Љ>ͬm?j.^εH8>kwaV|}gMh#B˗RBR}CzwXUPu";MUUf*вN6_4}J2%Dyng8cS{زYڨM - #苐(EP~,ʅ|20;aiݑESXd& AX\*0A`zԎzN}Lr8*^eb-8͗;3=˹K?ɣgN&Ea T֡6endstream -endobj -4377 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -f%YlMc0[kݞ9FRI.׻WD\r!g&&Ϸ,.q* 0t8p]y3Ya25T>K%fG|r:ģR=L8](yU@tݮgBAϳLhCˢ˒y@=}9OwZJ4Ű(6 wz'<ec)t8 i mβ1?=h|xIoXBTF\,]-dEg!s2#0̰T25p@ˡh*NM'`5 ֞ zIc/ 0â/k|DӇ, $q -~G^rŬ1[13?0B+A^ËY - cHB̬+klО(Aj2M8$%c|p@kT<3id"$`Mr0b_~EZ/:G"xfݵ4y>B{Th?[cfVs!8Dj|:&+3[-iώX6 h?aXbNjSX 8ڙ7g㶖Q[A~6.ZJ_toƫ_FU n@XQ3_%#\ҿ*5ZCA %ѽ`ak gVCp7gR Y$c@AucО†%A :jO;`pS 2v֓M~$YFFL%vlU8^ i蜰v^f-4Y*^Wm,P(,/ {lckd C/l6p*:΁@f>oHaMˠO+Ԇ U;Hү72;P,6{ՇkTIq WQQB+{&EpYG5tUy3Cպ3j7(ވ}oD{kq!duрJ5_\ WOnendstream -endobj -4378 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -1,NpGE܂;i€N)w Mۮڞ#ټ6EĢ -6''ot+`#_?ԭԠba6_'}{4+]$H`0:y_qk %EmX_ qk}kvd4_ 'Awr׈~ qPFQq徏ᓀ+FPUSYK4q$Y\a#?됍Y&e2W~4ݪpxS|8wΑu.l.:,|7;-f~[0KҎOYZgIhKZ hN] -aS@ӵm=o`Ε25o (\hmKE+neC{G%#߿__#UxȮ(- MG{T}P`2KBz,`>1&a9pb%s8[}^t_GnZY-/?,}`2$L\ WS/ъncEh'@!1=Ȣ4S3-,j5 JkH}2BdmOuT ]w=xr5@S1OTNvH^a.-ixp_J1G-aOs au}`WKJ3= { %ShR=A K<:cWe]0^Q ʧV];i]P$Uy퀖{o`$e>\a( -<ܤ*o0l_(c*WHp%9;ms޾]}YuugEߺU&FNo^vo/5BA -G7+ {*vѦX)Ra]G2`ζʔ;rDoD3&t/SW5ǿZzJj -vg6(rؾއO}Ԕ]&pNq„'BTL)uza0^Q?~ϟ9b~̵og.bikŷ3۶ zeY -Nˢ}`x o1N6zC+8MJ3|Bۗ֏Lk -qxU{e`ũendstream -endobj -4379 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -G˝( &9#H ?QHpV -Z{D5QamY?Wr3C+ʻNP=S.Q6:i;Dν}~a^ޮ8z1b X]$1ml9ȎAzi5l,qpdb1\Mj,0lU5'qQFN iV fG)|~7R CoxNɌÞ jzEbK{a2La%'OK8Ljp9'9 -LU| 1 _=&_ph&>FQ6XY)#=6jH{o0AiOj9GM;eEʢl2%Q$NuDDp_ԕXL%~hfyW}p3]c;4g-^hzMKc[;V-{!C$W[nNv ֓UM(GAlt06Afzq[BLarA A4b.&³;t&ٛ}}q/ŢwxeARN^|}8K ՝6Yh9IMA.@CTۦ*erkQ&I9x~z%mnOpw^Eq,3B~ -_%$E֧܄=wP1M%7]MA&:MbqYҋO+K~nt)kT<YWn4X8k Sv!\@p5&bIC43tO2!}gm 1d8Q_ a-@Eehh٫ *SFwI ºjRC`jYCKB. -hy/ֺ,īôghu(@Qi<27fL ڒ!f&߃ -Y Rŏ6"h,Zre -U>xv Ɋ @<}nx#=P:z#"֒ɻ,8-.5t7(t˖ Be}$$El7LAVќu L7X[z~axGg _(A$ͫL ym&IO{ rűv>xΚ"*-)'T ~y4_l7MF=TVoi@:}")$S -7ZNj~Btc^/GƑRo{ k52TÌRQ56sW}KTx̋kұ!zE?:gW/yYl#Le:"%}(yȺn|3ob+Z -r;Ū}?k!rƭ )䚙kB4tWa_׊YVVx H!cDP)^lMeU 0WG㰞 -+cij5a|\%=a-!:u66X/3m)Rh+U+NNKw;Z?Cm}%.yʴQf=^$j*GZ _oodR$q2ecB+~t}xqI~YX x+T#E6BG *@۴Lѝ߽0!efOhB7(="shjn|,[Z|eCDc0 "c,{)RPy9Sf@]zZorw:3wt<"7x-cQ>yu$T<Ǻ J,w=mbjendstream -endobj -4380 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -e<Η8VC> @X`v6T,db5$9ʶzj]4T MHfxQک+A{ dFW5ٯg^5l+u{ҦǰIY,5[}Nx4LO+[!5uaZWI]H$\4~kw!>*NMe:r{=^6P<ǀ3/k 5z}3]M]5ԪP~VC 0q~H wLn#9}b1t؁>䦅=LCv55Tcs ۩*(b)Qf*bpRȳ7c~8 `8ъQou1eS| g]g-a)h"Cѹf/v tBAjr&&F w-ix=8'|ÕS({cLFKs0BFwۙ'X tx#֗E2!Am8ծշS.^92$ohc@%RIIU=nX:>Тkrv>'6/@OJIWeݎ|J|B͔iU[{V)Rϝ[-H}mb e~6w}"*_2:kJ 6oEg7uQ+bU0 ~aIS|<0oZLćc'<>\.}TSxrf72 I۹uAQ$)tŕX>mGۑ#If&r W;?KsK#kf`rr4U -endstream -endobj -4381 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -ll\<|Ӱ.wR|)5+7D9P,bY MKoX}aI..fqId/㑃@zr[V(_ˬ$7}ZZ6yȭ(}Hin\E<cݗ5 rP,:]c uਃÒ! 9kU}^Ey`𒗋P;0U0v>zXyE46{MP_~Yqvhe܍.B@Q^1pŘ?G54 -E$R<0uT÷XZ6M!]iQ ~9GV3O{Dk<~hp[sFhxѷ+3yH(Q %*Z6qstBz$mpPT&T<{b8W lލ1/6w.wA nЪSus14HILyQPv?ƮlDH:t``%LCͷN)݌ -Z#[pL= $\S ho#_|}ǃ()QA@pav\^+/ -^r:bgo[z#Gafܛ&0Rn0AĢd؋fC]O]%s{3㪏TԒN(y$*%F9J7x!$]+b٢gq~6M[3QWg/ :^dEORSTŨWqscud!ULtN#6foعNO$gC`cP@ǎ UH;ym&XW^-p -yZʖVBfo2wj/lMt& ըΚǰy^=7dZ7O2t)4VP?w> -stream -$8ˁ>v6cw^^j`ui-yIhϏc2'vMwORf[@\U⽱Psc&v²χЌ(R|t>&918 -2fؼO -GQ.|[-ol|ˤAWgg2M9 zgnE_xp7cz z˛/iz/~scb0sr ɵch= ם0/)(.p0\+ 8!=9lްZCC /|LBawN,jNlDZZW|P=Myb(後Jva-8i*I# @e*8at4H&yF~1vrzc -xqbbCmRŵ޴9ZH]bEH30<1P3'Dغ㫓xU֎s `1fp%Tyڵ\9h YWfߒ966^B3[&/ՂЏG)8IxR *fLMC1n,U:A2+'RN=WR\`Sc:ruku>$L)= W,?sst\,xB[S Osm -~7-Z3> -stream -U$l6}i;Z -|[}B6!oຬbϓ=fAuvFaB*M;Ny|=bH>ZDH4KR}.,{B;8hn*wdV-qƢ-v[q -RyV4w^CVLx+K 8'!G~ -(7!M]7xã-`ʎYioסaoǴ,;PAngkn -pVЏxZ-/v Hr7iXKpS4Sb#/۳ -9RgūWZTN7RD-ٚE&/ͮ50)Ufj*aV>i[>4pT-\WӅk4| -J{I7S)@\@\\|r4~vXʶ~:[Ssfû>_.+ӒwOI( |.SؾPW2f1M+y@8>QܧU0Hə *Q5kX4dU$L%fKA;AjE l Qo2̳ -d0>ŽP"ϡ?NɈzjy -MLܖkw!CCBC;QŲ&ú$wZ|цa 6I')[ؘ +>0Ne 讀yh^Q% 8xn̔KE:4]n/H3*R\ZG)$gAm}  A@#eO!N1&s{͂i uu5+I4\zv}鯙ɥkg"PoUb -Yl{)lZ5^9U&>&R@hZ6ɼL%Jl^Ϙ~t(:^g'U@&19|z:17";* cfN:$EءK_?? :c+&OL'AIl -ڐy^s(͋ -^7\Nư})sLӓ?ZjD@ vP)\_,OeIOjB&!(#wk ZKZ.IlzAcI$&V٬`8)ƒ;δGHͅ@H;5.D- -J"$֟'\Y׋sA'\_\o|WgK;쎨Y?tccmySX<,>>2ʸ =fh~uFiE+h űBpUWť:zL4a""Vu'> -stream -yE_MbϾ;7U:E"h̊Y* -:*[h;ޫ0MVyK[aUU -a)yf;VPJ=kQbq]߯bE.bX. -/WʙѥR,SDyX{~˅TV=D! `2{Pyu^HM3{5Ȱh<+S7蜅p>T]7⾢qI~IND&'ŋ2/nF5`𥺼e U2Xp>4S x~65`Cٓ!]DR%rcHC,! }jg}={']Yb1p&4T4h%dѪώ>R_l ]CԈoWiD u+ tp:o+ͣfn=ep8b0e{+p'+4AbENPmCj=#QЕ!b?waYK$7$5>1Dt-P(H xeu/Қ3GvRELwar̴!XA{XXRi -u$ [GNҤ$P~d) -lKY+0w]WB/Y;z `kke(XIy.>c2 -=$Mx*fǘb m* @6Ei")5 ɤmBGڍbx ~0]@8Z::Sj$ O)J8sϽW@CĹת! x;"nr ~k4{)@S[vDZOcmKG9nOOEYaOOkg {Ady.g)ΌăUPLx5S;<߽]忱$Q&".X.yuLm'Q`/Lӣ* А2eu[F+LƨQk 8t1A\᨞.e/0g*v0&Я E;V55:oBPRբ6I *t- u,٭  Jڶv9E6*^ _G !xٷ"LIsBsW4 9*ᒹ%V(;!À#\4zp00UfK`@*5.6e;S{,jM+Aw7[OP¤ DždBO6aD"PEyop -߯%`֔!ԡ!t5AiVVu:sYS2csE,J -0Z{]2w;>'HL:0^ !.8VDfh2XFN+8Άƻ4V_D|$B11HǦW")FKC`/箕endstream -endobj -4385 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -qɘa1:$"9ŰQs vu}He.%(CE ߶[!J08!IdN%>q9Nxg2=AֵɒKVd -al<&;:JHeK[7+\c`\3L7a¼M%iNsF9iɩUq0v`sŋf -%W0"C@,慐m;"T{_JS7ǃ,T<{'Z%G1'S]%Zӵ,I͑^^m윯vH}cLyڭkJ9u;G/rX --RQIw#eG}GT֢;kY_6 `MYol/U6hӿUj eCa%k#,5a4 Z]f{"I$ pg{2s&nK_;sɩǛ㍳b9.dwA?+߹+4k4n'~k! 㽏:g-EEYa;_V vuRF6x}ݦ*1&@\Ffi>yvOmsIr8gj_"c_fa(@|(OJ+w#,^ aڱ!cݘ_uBah1r?w<ݘb:k<-`n^CձmZxn-mIREe࢐y(;CӴȰizDԚ!N@}C :X&oև99li'օ:)_}ߧ&1X<2M>3G10qY(]oX&1rkyWٚ@M{#b־'QOfhqO bk\:2*xcۿ@5GV^B Qf  8\L=gec-s%Δozp+d3X~*pknK^XVJ!ԊD_SƃZxJpߡ Z[$ 5[_,bPp^hcƋ:OR G:,P -uٱW}Dƺȓ5w]!y0;nW/`eU df6柱_+D"aG 7ui -5ӁOWh_b#WA (uߧCpk]J7o A7 ]f%5'o?F2-MuzIh'pɚrƨfӯ3.$BO~UDH {! -FOKg]co[<ӽ|faC؋YyzL<0{G2%@JlfdJ `Ƒ͟f`-T Sz f~hO⧺3o*uq'{Wsf0Rv&/Nei^29ڰW)}&vxnaX 朤{8@¡ vɪ6N"w -9 =r'=Tǜ/Ca*JLD8D-g,zv 9?endstream -endobj -4386 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -*e$y4\- pn8 8u#ϡQs|L~Ue?Ll'QّF>$Btsx)@Ju Eu N a5>FT`8x/ *=`S]WX1> 5R\M/?{w* ȧf^ꌇ}F[m6'C/a,i4TǹbR%>?ե!NSTIYRG㡽|GpT8%9{+8B:Sj }7 `;U[>oqN FBXoUƚV],osiF3D\쵆1RD&+a@>37)Y`GiA)3]@$Rs _M&*Nd3[8TB13d(GPhJ@)ײHALbf ȣퟋaQh6>jaaCˣSNh81k  Z;(N/G^ +y3R䧫$bv~3z߹ yֳC:意}TMA`o Z6̨Ldx6$S@D6= Opv{w&;b;+-VfQP$`[P)#5df/UCS|!FyJđ2-u6 w_}$mpTnWdb}Ti]g~e#eE^T:W( 4#Ei(? rbX8DA߹3^`?,L{5=>;Hک.\d;eHںl͢|eVD+U:c)El >+^t**֬è= iTa'Or?^QCs7?y A @Q]=حBHU;[OG, "dHꢭg>toЂ>N0Iym ?!qq5 yrPY$ :RFM]ZdڶdRa_u ir]s{tL0W:r\Y3c(8jdދ }JQji7}]Ĵ>H6 0kg#0A(^-Ÿ'Ó#.a L=(ȯm3%7eNK޹̝! Ub3hQ(Mv iWf0faKMV4I4 QIW{3KQ.@"pJ[vr DUۼ1׿uшpEf,Kݫ5U0,IKBFAxVAI"9k` -AhA z8 +J/OB7dꞱlyX<۶֠7Jo{硣[ J~JOnG O ^h?kjnj4^vBqaYifo6ZZM2]?A9_.Xbmm^}AOW7vM1<݁\qǂnQk!f5tԙvR#ھv_av`쌱l{2?C2R(OgxgpQJ>lcXגDZ9#>`-Q)G״a{YuKC'itT Q.094u.XpِmYP5-蚹uf:endstream -endobj -4387 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -҆1k/d2J,G 4fڨka AR,6XE3c1w„(Wb~S/B'&խ$ TՇӉfW]:-t{~ՊŴNZDzDW7},*LF[2ꅍ{MI'VF=س5Ad* Uؕ=~?3b?A*u-0u+bPmܣ29XA trp[0ɾSE==SR744u7|&-T3X1FU%~h䢒.Yv6l}k]=B -ؖQx[H~b"޾BCiKcfLUoJmy@eego-4}F#XtO-`zhc `D-НM|{ܰ'~—EK- KՉ 8lxZos?,+ ] ʭ a$C2c2epV!* 'c X|l2+'Q{NLTjdđ)Cl#s.^ -j)0KDBV_^W^~eZ]46f<"0 7HfygDb8' J* `*8@l47tV~RV+b@?x/&W5MAS ͕|LD!l ze@ S/_NYH눿ZD",i,X)HSfRgTS -Sr-a e>2d5i җq=n'fu&ܓoʑdC7[fCK f*f`${JWOdy%`)lo(Hwص_}e;Y+vut.(vn>/N6X_y8Zix"+Bc~ ՅJCp3AŹWx( a 9yq{P] -.XfC ~0HL+NћvHeiPPgeyϋMD./wMS{h=s'R0?`HDwYHR)y|gF&SdVof!Oñ_gQK~e|s&7ݯ)wq lc0S!r )rNWT~&h&M^Yxd`>1\*Lak`mOX@/Gq،p2mns]q% e!%9Pa=HCoe4$@6KI|qe(x.?54|.o޳} 9W8y?:.U s;O__uVcj|YtUhB?rWWv5ۻ1endstream -endobj -4388 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -ŭPQ|NM72cDw;sKVy"n4>uBK,Ece(B; #4#xjjD>w: -Mcw`io߉P{?|kf/u}U0JRc.2%6{Wһ4.6,oά[Hz$Ӂ*ixU qFj\Vx3E&[8ޞ˩&?x ĆNBD?vhuM3yY2#&{]\]Twwq_r#h[#M)g"y>ɞR"Enf/WӬ䔄WʏÉ"Sd.&+\~_m)&t 7Z~cD<JWii9{<N%=5QZDxtz0b23QlMO`D@_D8A.2_3Dn"ĝ\ZD?٬kc Jz}nn -B,@qDx7-MA-˟ A!#C.>j Weyh d@#V[R(y,ՠVKXY;x9KU9eG7א>\4ZIw{$F\9F9mD;PBЯic =vϵkJt=ٚN*Y6ݖ7݂8vǓ1Qj2(d 3͸n[Wae&&M -.7Ce(_$^LDƄ_yI['IԙqDY|c@'{T(ҧ<|M=6ˡ2ݟ.Ðo>9$Sf{9<&Dq -lBe!RfNtD~յEia$8:DS -Z(R I,S͝ʜJ`K8*(+S&U\%AEuq.NI᮳_c,7FR,2<`uz8%8!3%ҳE SvUG:}Mm@i2Xoюdİ>ajNT i"lv `Ej*r°LazG`,%F5@F4f.ultP=hp4!S;zFNxbm!&[]v\?ewu$Cz@ƛIxJo<$]֧Ĕ[~KcKY`)Wm;>\Wl AF402}TzLMs򞏜9(ÓVi jTS&EN7Ո3}Lٹf_2(0י 3C48V!m$]̪&P|[ÌE\Puc!GCzru@ %ua)!)bZ+~d:-S6fݹꓞT0sotMQ8lU͋2~m8@vf)e<oFSੇWLV"{uWYҵoayԪl1:Q˃ [ -RVZ֏ Gړ-#ӦЇ_X(5&d%6ꍖ-BsZڰ]\J lُh~uk(wOuLXyF4EQP!}ν퇾yǝvپIeMAw2&ym?t(b5nH҄]--?Dhp= ݷQOK@E[g@73`#pq" ac#3Bendstream -endobj -4389 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -~?ӈ^HH%qMxxGg>"V~䏠[6b} \T5 /RGI,.PǮ N:,- B/S 7V#3a'nSAoEӴ!؉s$4"8XN2AYʃOp+Mt&[\rhNXx{icޔ4LG!zN .;:2ѿE8e|RX$7{HB{獔 -G Yo>W}@֖'<m?0lC$\XDzlRpkyd6e@uWHbW4y؆ɌdOywvJVcQLL 2ؙ9X$]jw^oTً"T.fX u -By';&eք39F햩/aa]NZG|U>[ d,#%ׄxeQJu@v"lȦZ>Y=Ԙۯ|qϻ5 :o -n *Xc@R(>>^c^AR"9XA.|+~I&IJ8WAEmS67u6!#L6(sȗk["uZ'h׶YƊxpK1>ȜhS,f]mK* +Hf3Q dZ+GoӣʄS2L" rBnǯp5@SYNSwk{3ձuZV9tEx`ȃqG+VKn]o)MPUpendstream -endobj -4390 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -t;(31_p OCk 吙*\^z .b*˝Ty~$:=` -eIՂ@Mr7LIw+Ll3q'D1Y6diu;KJ>xPf0bG(TJ ˌF-5<x̊!bW +rhmJYq ]8,@>*;ܞo^2TuoqA;Wd -Rdw|5ͳě߷hYEb -Mz% Eݪ5| -Ѯ nWv[;-Hǵ GɊsA-~e,uFLK{ǒw&e$luꟀ|#xS% (δl w/}~GZ>;[ x) 0T-'B7Ƅؙ $a BGӯ;nmmP aU4JT G5Gt`ҽk,C%7$CPkG#պ:Zތ(.tClU('$Hެ:Qz_Q%}h k)=͉ܠ2y=i\-l Y2F uGmTh Y?whukQE^ 2ɅRTtòRrHuy#G<) 2W4#iXpҰJ|s<%|)*i|}$ X ?@vJ濈Ԩ" s!0R [Kߥ]dR;5%/,;\ȣy:7XʦZХ֕Cݖ?'81s*h|(?$2֚fNg61R4 %uT[Yd/Xv&bH\++ xj<FcQc3/EN4XO,vA[T d&aUpcanEs0ԄRR:^VRǠY(`:"zL) ׼諌PSp~H1 VQz4.k -i81(x9pXDå3Iiاpyd! 9lϞ)gsex)ϪFa7"s^T#}j"j jr? -vlد3pr0Vw^ybkd9dog0v~M< e){#)k7*cK(q0Ō[l-ˋ%!kxX˴*RUDM{}#e~ːVP_-J߬ᱭ,i_H(l?endstream -endobj -4391 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -jBz*rD@;ˁ嶙[Y|ɥr ܂BцTkg~ -dp>|[Ld¿>6:2W-fK"\u/oА./iʅ* 3 ASZ!e1ݮh_^*{:ᎵH."PP'ڽ ?,s9(ՈցYOH3ʆ>qdda`bSGD4_pP_Cp?Z#vܰ\w?t(1 Gwp-|}CvcvdwQF4CZ%} -B! Ã@6TCn6 FWRLCoy0[Amk5EQ᪱`n;o&1sL1M]K4 -'ɢ1f -֞x,"ڲ MFFӘhaݓxIr\3ؓ1QD鐢pO&O/T阧\gdq'Cv EVΧ=ѺtdeO\CE=9[X\'S\iC+MpI3ȗ%T4;M=lm%h=Bmh951z3}'Hg1DF=,}+s^""rHP{% V+uPT`P}-#WohS H!q(k7~ ]ݡ~10~TEak( -6~JpCf!9"x覜?yA~$˻%%?#;hj{c?8:)'O}m-C XNE;zӕ|_n!l!!G(0} `/_7^gcUAq z3L XPξrŎƗ. L6(5{{8@Yť!4xM0V!)vmzyVR/"LS͊Wݕ|0nd_ͳ\6F< 7Q5Ր>nA|6m<ߜ֌s:}F8*'6":$NtR 'K8eyϞB7*wJ|WjGiN_O^痕Uq>-eoڷØ}DfFqwU*(pge2GVeou:R8n"!zR%s'\}.Ӽщ?T ~gӂlG2 ~rȰӡo[y~\Gu 7Ua1|WyuDR`[pшeeĶ":|絽0tz7Ab,8w+?P"$Qs:i]dzNcm֘l) -6p, :%&G[d{wlQDQW;_?ab@Kvm&VKh!yfd|3hưc&pD;G_PyB8_36j)Q@ eV#}rAcۄ}XQmj|Ұ#ށ dI7K1RR$GYâ2vIL'7:eqLqV0&st,)K^nCv:$u`TNL2(жp(vQ"1(~fwIYSn $b \r R5.NeGYL35IR?6$G0ZL/ \4Q7 Adס`__)zf*0|z5YssΕsѣ攏)"'_:kh jE`z!ܝ*B7endstream -endobj -4392 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -%VsDfY"eZ|MD9(Vo7q -#; ;N܎嗋'pbe\0LIigdY:Gi2C! <Ͷ`J-5]XSURqL\{ZO^E=Oy.oY휗5#&!g )}N76,f;$OU.³%>򩢺ɉi7 t3kEG Tβm6[ff@4 f%<,JDYZ^oZ6h0nA!Xsme2n%S: 4rC - څg6P[RMY^ bSo:s)22E\')Flɓ8kNQ5P 3|]&`1kY 0]h !N|sJb1 cq_ݥ/ -JaAW` -zxh:=1lDU%ts!]ophfh tQw/k\t齼 yO/P.vp&|Jb^; ;U!˫KRSϠDCJn`X}μw>xznj20^# R饦q4F@ktw^lpy2ٹDU .r{I=0idZ0z7K-,E3v! bcM-b"Ƌ$b4m0&/oW -uWqy:E6#3ACRn[gIe.MNab\*NktӜΆ-^"%єgq 5"J:̿F˖f V?kt -v6hD. )9KXE;R0`2DÀe-TBL$uԏU7TܲLtFRb7B+M@fEU֋+Up MC8 PPJ{n pUk\ʨDV"*endstream -endobj -4393 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -GpE_`zFr وՅJfwM)RD} q3IݩYO9at绘֤'Д5u0̂ d):!r - trT:7͉ZC( B ȽT*S{)NYvoMjj]'b6BRArPFL(2[q9S9 ,4l}Z"wj\ên?d5hg#]ӺW0FBOI"%[[ads :Y*E6 lח'3׷3}őDðGܳ}<<:` -٣a0JXГl+f℔w1؍g^#1,loɮ֍-դ`3D}x#Q3{tVI4*`f4R1e<ԒE'NF/ח@;hD9!ђcJMv:)W|"(S,P/-\Lw@um5gzȚϬSv!KQ/~>4h'"-oG؍ @,tU1cC;cNk2krPS31W<{ -I(%UN^jGgdgy53VCkͯP'?D{.^-O?[~\[X%%b_.-0x"qS:1ah%}P'q%1\${!hdr͌|?1qq8~9HR]:S,&"=Ov"6൤[D* U!?,cyPՠEP8snoVC8"4G akGO{W\&yRf@k2F$^KwSlrtIXJ]+?4(J̝2v#N#;#P\vX4raژ)sN0۶t#AWy?#> -stream --ZaGJIIcf:dǎ{l*UJHDr81ۑ 0 ! ؤH"~R~=JZ0$I"Oog%P5Ρ^\Ti t)$ -g/x$E1$67O(?U@JdeP)8nGǀ[SWt5!V*=*]8<˕|z~ĻY_^~hP. TU/}{o9 3wul\:Uw|-*YUm~jCK/旔te1)*&rf(݇ 94#,xRP^ ~=Fb1%փ߻ \-h{ C\Ȓbb gGû`鿠˶7?R$Ve$PXДmTp["ю14"զp,] )͢c-0ܒCRQ+Nru y,"j^D>ʕfFMM!0XL9ܠs.NWL^p]2_ֳřNόf<' CBI2N RíAYpbG$$o}L}ᰝ'w{G2 :m*/C,]k~?TIXKAڝ~Mrs*J\(Ď;ĖPA"kZP]m>Yd"<30\a8-;2ZH̡;{5ݲAx.$4~)|`2> -stream -۬=.wN3ߧ`4S̛e^OdVZlYj4,&}d2!NoMN  -pG%.pCBBqyE{{ذ2uDN"B4[>)PY,+/F1bOfH:6kog9|&lPtJ;eߙlG -%P\bd@:!a߭&?Ҫ}O=*N4sK-O~0O>]CNK(7?3u_O)A@ۭ!rd,B1 -_uKtIݲp{ ԁfW}#ҞN0hxv~KXFcx"ď7Rd%T#_5[^+s qCQg-mw5Q)36T7$ȷ'J9qT))W0HB`cWM_$ϟx292e Q @b/~s}UΎv]? 4<9q?_TܩD {*␍ }\4 NA!mjZ- c>r;('A -"-wG8R(v/N5tLw70_"-\@Xz:⩴BzYSj^as [~*dᝢhF~iN4n_>qUp/ZHֶC &ĨNQ}k$.z1BzBWՙQ}ͭ9rJ}SUlfm@AO2&C$/C[>7]$AZOՉY!6!E3~X"$6#%DL8r[3MVb9([YM)v-51 4,D֒o\ xÅ]~(HEtꡭƸV4P-v p)ry]tkLYNi djbV~."h;厸vi|ثl`zW^fn"T̑W9' ?L4/KkI5BNk$xȦq,P6daҳ֭)- ֯W?X>R57!SY'5ϲgA.Iof.ju|ajg+]lH'.ϛ<*bNɕS3Akǵ>G˞/R|fQfmQGS9\۵T(MAIoq#=0B\ٻJ2gΐ3If\o_ -&~;fx@ Hjc)0֐'d -؅n";3Gގufءd!<ܵQi'u endstream -endobj -4396 0 obj -<< /Filter /FlateDecode /Length 1040 >> -stream -6x@͓J;/ ]Li InDFKlRJcL빅 M0m1xؚЉQ47%CbZ(XXto_˶UEa%i Z]eSAE].uU -JDA] &;8g-;\j~NC^' j?)R[^b`kXUda -VЩrDaS [dQn&;^T-|`oqX̒D 43DCp0GSv"qt ԩs0,YIU_>C=۔ݼ#/.x_$a!94^ ŝIwgq)jW],ǩ4u8c3qVn7hΑ]0uYQu"4awɹ)'dxԩyכSBNLn}>KqPڃ,Ů/2#"NBEgLIΤܤ0c -`,~q,/*D ŘWf~geY8O鞈2߹6 S "g/ l;U´wͬR - _Ť]K `OIlS =p ΰyFe:fJd%Ĉ-Aǔ\h?(`pE'-޵c.۷e5VC YF hńH҄=r6#iѦ%&8낑9pP?#'1;9QӄŞ6fI3?>-(`;Z -Dp %d"qm{zVC=7$?8CF&x; gb؅!_?ȱ"2bH(^d]endstream -endobj -4397 0 obj -<< /Filter /FlateDecode /Length 1840 >> -stream -}N=&%GzF=CeeՖ::#0z㭐V?0퀋S{)7aYf~Wf {o>c/QD,C *wi/eԺ4>:rg_4A1B2M!ebzӄÚ֘1m~Y!"YaRń&R[KqqX(e QՑl /!ڭ~kA&*.wY ! ΟDlz˥U4Oyj&>28H65oQBS4]&22pMxⳑkis 'p]΢Xlaumfx;Iq'Sn}ј ]WѸ (ճ޲wO7GhXOD5]{Z5Q uU#]FÊk^Pq']QiMmR631q$p9h|,ps#f%eL]$9&7 Ku\,|,ف$T`aiш0^|0fP.@U Oe88W`^-VG*<:;,[i=&o%;O}L"xz?kN6mKӅ"ꠀd`OcE3CMlt(v\Olܚ(Ƴ܏RJao@Rx=#n}NEgAt$y.Fmz=thԘRZ k4Z [ {ʥlb> -stream -ݐ iu34+KQ? fa}pT&OV(vhLܒ]`;.?f|:\5渺|*Гqkn'w~jXnZ4]KSs|lNLD_)ENju }Ǟ,MUefH^?X2SEP5W(Q-fDޭt{SSqE2)3mjP(䠕^ (bһ$hiehd*hgPB@Cce6*uh߁WyЭRׅ]nbh+bp)rë"eJ GtS/S~]7{;ƽXmK[tSK+F:&"woiY;u *f;yL[V +4N'ȟ-inX[VaK,$Q -DkP:%UWll8 &7~p] ,jH;'@`zbO\A'n+~f+ -S q[R-~S"gx{cv͍fتLN 1 19\}f*FAr\- -ę!bPOk!U{낓5L/)uU\qvA**[ve^A8{޸8;p;A!" ٙIHpy.LHNr{k9p'g*NѸmX2_0+; FSʕ}uDeU!~un-HCHk֛naN@ul2JתKE`⬙EM$rZco7!YE/ xU/Vn ș8%n_O쪢Qd DO\y' ->=3m&8@Ȏg߀tZ,wDxdS㨆\xƷ!pUy'/&!F9v~zW[a_\n!T~ZT^| Y ŅGۣY[g.b(0:]X*^Q'CZLoك.ӴQ(c*ݔh lq'//И#m0Zt97%=ZV¼y]o7DYRNkX\72}/n $ä.wYD f4G4"1tN_TUꉉ5xC8%0E~;j !>iYfBTh!.uA+b: RDx;h=0Af_0.U'yGU:s]cU';Z+ H7/Q{'n4OSer;endstream -endobj -4399 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -%6] DrFa6PQ5 -)y[ifbt´@"Gѐ6 eH[vVq8 lS\ JO1M>@@(r`ǥ+lL ]u 0Y=G襏#Gi[akjotd4w|s C߇4Qo m`9`tlLv+H^Vg cŸ|.S3n"J AmIyJm 9}p0xp 6<yN.e&!zӯl:IR۝+XKP4TLNuB2^Œb#M%gF1$JЭ8tHIJOC1S{Ç<`;PJVrh{]ݥ?PU;>gcЭq˦:^{rVԚi;GFH9>4Pkf .b^r# -q,'ݽZI$) #RVhr|؇JTJN=ʨH[ݿNMK;j36ĺ~zX&1T[$ְkP@=kpjz:9!$vǔ -Z%P%3&p-!QqeY`fKf\Jƿl/^04AmbzJP95qZ >Pڵ]VȴVTDA=5f\pr@jc>qD|Q.V Uh'dQSj&h-T S1x.&q!D94JL6j#Tz a|*㻖)ir Ѥ,ل<n}qBIM*>(r ?v(MTVŤI Nߎ2uٳt+Qݝgx -ô!HpͰnUԢQ4nKcR4~e%[B)4DcVMP!aE9=tDb(]Q 7-FX%h:/pa'߶kd CO1Lfh"*sNWQ~>FhO1a%x%=>%49&iIkͮu?{5;|fLΊMN_P70mnկk+̡Pn'>!ŗPɟ,c[&hiQ\h ѳ4 =? 'g돘uJ~hO+\  -+dl'1sB>DnT] t^)jLȇUZhN`lo0{&sXX`ÒOSIܪ&U1]<2Ѽyc4D_t,g&p^L~q|o1Iſ*:e$G^CK:f=zdPThF]N}ԹB/&OAF̜+'NMvrsd%*ބ৴`x⍫wӦhW@(R*J.|S(Grrړ ,Oz1ݴ:wNwv4?r&@c𕗑*Ep+No|G嶺ӯ$()7HU '|фk;J8|o? -endstream -endobj -4400 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -R8EXJ@A.»xV<5&uu_G:~Wlq2 i^x# +bwDt2WlA:|2.NȨu광HgME ֝'=̶ehgk -c֧vh{32QNq"c6Jk;kP/7>G5d\rxs׳ZfO[U쳦Ѽ5vyrņu}7e$|6[`q ^4Q -E(Ȱf|ҩ[CzL. i/,6. fꨪsGSH(Y (wpR_^V-fv9Ew/P:\iy t'hkpk{2n4l⌺t֛@ -lvF}`y󁅄 I{ePtɸ8Bh7Gfo"H,n8$fl=pF [L˦I#*N ݖ܂#'u -n9`SSzB@G_J;s .[chk. LtQ\'N0Ə;ƫ -=&YIUKXk);Y`2ȩdyj0= ߷]UFN]i O3=q/DiɎ UG<)LhtS^Gj==wI} -tW%9`{Q1KBRO]*JHX3Z5Mk2^*ak_>c?`s!^6sv5y7L~iC -L&:c/[RS՚e?&F`I**j0ߏtN)=ܢƪ8=%O,Db8!fv'OjCדXVG1j9rUylAt= ۈ!Wcr~fr >ƟhzK*N8ٺ=O"+[M'ޘy_UrdLQ N&8AX;ͳ_e>Ñ%(5yw6~WP*+HewIҥ=V=R1KTJ9Wӟ:rA,EbٶsMRM[]5!%j C:3{]^rڎ5Ģauڃ憀9EM##JA"MRH/܅ރr̹(ԃ}--#cC3I| F^"1GkPGӶ9h˭#ytE_:S2NNMbaq{tGJ$ȇc/ajSpŸ2PYqW86B+L蚹}ɑ8s86bԇ<4( 4{GkjݜSbz0u;0ޑVҸy="vw8wR7, O`vz$endstream -endobj -4401 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -8J|f͑ m`GP$:l*IlPi>He 䟕&r[Ѝ+"ARh8D囹h_v'g^2d,>(ٍp{Bu_ Lki1 עacS߃v%AӌJ%H`Guu.Pb%@s:IG:]s+urH^`gem8\- mV|3N|2^t 7U<%Hy!w}A ޚNз,0_7ԅ\}!WUn3G)(qfr -肫E'q_PD!G&>MD05ÄLy\Y pya&jnG0#:SMqy4"zdr0G}ދU /W؈ ?;0_C1, 18eR[ %瞭S:)MۏcunR:SϵOSE0mTV&Oyjإ -9!{#橜/:%cWq\Ƞ[%|g. -KjMكJDΌz.W>g:T~KC#o4w*!?Z7k|9>*)?hH2?dBO>t<G~W6$Tz)ΛG[@4q`iLC iVQ±uXе3Zrm2i0ݡ@z}Ua -+2ąQ;fC3.60 -zg[>\ҥKN46J:1JP}wzS{]و h_#z[R}75bĢoQ-btZԟېK+9Z%WvxdȚXqݡϾٛMkO:3zQᥰj)@^(‡yԤ<;QZNXP -Zs"CpNg.>iZ(W(_ #ʱfWoqsyoqrGGUIPl$θ#fqIi)昡8wb{f#S u2KIg޵ [0~cTr*am[SÖGSecYfK5*VVS'"T]:뽚 '@-1Gx,*KkJ4f8 {E,;8O14Cp rxL3|szm;_h:ci'B06 Iw3Ö;'ݍ)*oӦ謙Bendstream -endobj -4402 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -&_IZa}]re{C s>&kI#/u㋇f t!GAyKw^$\hL;9FcQ}X-_R2l/y|jyY6"%h}FB]S=!~B#(ٳ-T<#72Y0yHܕ-ծqkx~.͇<bB\uU S7Z7JIk̷]duN 䲆zL|99vú+L2$OO~XZ/+YKXl>ldĎ ؛=fΞ赔iC/v&7ؒLJQhh—DD -xUhaɾՊ-orcL©y8`zTt]`Fow@=UnW;zЧ6V61zU)^fSS\Y9JrBvsif+ xe&!Q։+2Mu߿:f+󱄧Oԯˏ4`Ck1jnT 2L[?9;LA.aʼ,$^^bЕE8g |4wUx Ԕx}2nq# + .fj8 jeC*iieekH]5ȹAMRucIȀU[Q;kwBpE"x d5Lb"RZ#3 3VNJ,\ }F6<[خ_i4꿢\d.D{voіքѶ2m4jx1W IN&^(I\t8@j«ulp4Xtsx)&3^C] ,4dDCN:'䊨U_9sKzPM?(T~GmE_ꚃ7!Ҽ+GKәl^_}бjhbo`Gw"ZD0ho&|.ط-Vr+X/KVpK,د96~]p[?_t7I~hT^ӯ%βT^c1~Ѯp:C:G[1EvBm峫$^tc~%> -stream --cU|/$tKH8ao҅3h5tԉ$+m3W{WڜQ됡k̼w[{2lV~ -{I2Yɟ2-J-ͣCtbig+5<4p09ȈG'k[+GD}0 @N= 0~kBtĐǘ$(0:ňyѹXOld21)ǔBP7O(O͊;_kȠ}_`T|wS f Zo>9Ԯ' "8?xlPy~4Bu6̌T}aP!y38i'Ine|:p̜?a#m \ׇk> 0%^n!Aį&DQE`_upQ{Q h,W@Ƌz/6 Ec_bשgn̬BASoTV -iHky39Q4-.LFs,K 42yfzAMO|M"/̝rZ#"cGyicL)mrkSꅱgKP -- -n6ibnd%x mua -eփhᅅ* - % Bfk\y) E ?0rY7]a%=\`(SӺe=2B0d̻44h2mAVME>G5 M_sA < -T"!F%Ƃ[< ƛZD\-T\QQŨ)(;p;j:2]#ٲm$Ii+T~)-y|$~`P5L,Bnu ñ[ gq%40!p朩ٰmB_ݟ/z}pf3XK`/]ez9߃e!/# 6[wZᦗM;]s=s⩪XٮuEqt>XX|I{nm2*zt -J@} fǁ0HgB"n'޾B.ZLfu,0͆i!PSoۻ~%Tݔ:X;pH|}Zs6_ݵT$ˣ(q[>Osi5ᜦ:"k_W4@6,SH;埴#o8,nuhg_!yrΘmoWTC j a踊EtXq+UK -0Ttl3vr8Ve>{:?CUS >NJ;C%} =acU,I{azC]TZNkis7 UmEJ19uqPO$ psb^1Uxh>-,Y#(;d)>j uja8o,X\ZD`tgTt8q͒J$ʰC#[9MCVTiZۂPU/H6_0v5:8kdL͛ J㹋}K20YrA^FZ:8O˺e(̆.Ɖ9-} Z3ZgYBǠk?+Ξܣ}q#Nwvz00sTΑH5\ֹ[<7} IPͺcb̂X؛xdyzkhjۊȢ2\#hOy;t|es"B-7;wSrIbs%SF}FHOfrN/T?Jvs)cojL {/lzU~gwj s&t..FKOxИnE@}cd]hlCQendstream -endobj -4404 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -h%ayGg%.#uj+REPN%IE;wT9{MpK6JC KS I"Z]L;( -nq@(_8J$d~89@7k&Oc/ =m@" 3NvgNﶦr6t|;\:%šOW>OM~I1mAXZK/E؜XI8 ?#/iidF9k֜>t6R(PMvo/XHs&HPDv ڜ#s)X'.k&%bQl.ؾ~ᎂ A >Ti"c?Vh0kj㙼Ȧ!0{rwصu *흫 m`2每| mfcIֆ4:W<5b*\.lU8EfZ2B6#Td*7lP&n첻i=֛KЙiRy#]k<{wUv -MhpxG,{ߛm F ')Jگ#k.;H&~ߖ1hJFu"!\ -v%qXu cq;F1ucEV\YNlf 7+r/67 7+sQ{FʅFӪ)2ѝhkl>!p)`2؋SRE -z) Ut"^e 4jB.N-\>ωv WdƗBh4H$XBpGUWcn3. x_Ya0Dendstream -endobj -4405 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -GRnHLFT] y/=mĉd6D.* gj 5v{?syیm?p+?F&rsҝʷO \"S2 XFRIòkCb-z5C7͜5z`wbpb:"ݮ~\oo-nW=ʱ7mОˏ #9[郛8`N?&"Ƙj B-`^[RaR^TS1l*)ͳ;ux j;-B)>mjxQ?:LhFcx§%zT{G ߴ}5+ѕRM({f^2`y\-,oyH-b麁=ؙ am+H}h@~Z#$G>*X C[-nsbUüߐt,ؘV&=DkFqMq58u'^NP"&OɛMQK\]vO0ZЯS$%zr*ky),kFnY1(gxL7Lx!"܎G03HW.{ Hu>';O(]dgt轥D}Lkg!y&$;BDM<'6o{RSg# ;bY·dOǔdYJFv X/,5i# ǒř400˔S3'w56\ѧRsb^Sp1p-1X'=*w Hp1A:Jw*BƻdEIoM ER55<00<'f=e \v#4&61A֜jZҩu nc'ٚɩSGsFӍUhx|nۣeZ'b>8L~VV T9sy2]lG@vkǜB,ErXUI۫oӂ 3 v22};^ yuhA{J@ Ɩmp򸛾S ^f++A&?UHkݛ.!i h`< `@l>wEǔZCs|UprHԔ `=S*%iGfLZb8Nq_endstream -endobj -4406 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -e1H6-W]$1IX%V]O4qJXn/>bSOy -tR8h+UȗrD+;fZdf 8j*, "vڀd+`rS<5񇙨1^wqL$tv3-#6*WT(B|XT,Sԕc*qKSi<,re+2JkvKۧk~|Id>Z}Q/0(6"* -ʅ1]89`'ZD:!bZ:աކWQ9T0a0сq<:x%DZJ{%Js,ԓ'¯pP"Ϫb;/bՄjW0/aj%`a _19A$(/'? ?r= ي;W:4><erua«;ss<:`zRݼ}. AZFqY *G $XƊ5x1x(6} 2bJESa8NظoVKL* -DTxilQ•F|r_%owÎs%9aՉ\Q<{rvXU}x ^*x4lY30;o% r/S!z Rad-C>( 2?5Dxh -gה)2"nߕ {,s%2#"9 lDB j_xHuoGa(?EI*P-a{_Y cm./N{pm _(n0SyO!)fEbPF.fsf> -stream -lМY?ъ52}1%*(zvu/|lUA y{]-5Y+~ӿR4)v'0M)N*=Z"] 'A%|fyVj~P\Q`dy#*Ժk*c*3mVtJ_oҏlChbQ #n%<\2QfA^|y}ͣL勚ɿ \a8tJ5z?ô,ŒoQrhpXIW1r|4J_CuC~ XiDk_mv8JhOFn_$g4j-HIcF%2=,ڠ[zH/ e')Y"fz05u/H'-t|Ņ}B*mLa 񌚬Q/xCPͧI#Ƈ6g:asBp'Z%Yr書q -Xy-%ȸZ'Y}4D7]). tnfsA7BtmC `D7iɰKCzKޝkĴۧ?AHkVbA$i^(l7}M? -Dp$GT&|]},=S5ڃ 0I-v0Wn8ۏiKbd <5ڙ1~idM8+O8Z2MsR2P5Xkn^`4w/jd #jvNjV+75E Q26_T\7w;2V%ZN b1\7#^nH;>2g@o0}C/i99+9, !LGHvx͉YeR}Gg՟8RfEڍ]fbxLJ_C4`ʩ+DX:V(3;v#p:WuCՄ6endstream -endobj -4408 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -ݛؓ/n#:A%eO:@lzxw6S@0E}yrE'0TV -˽31O:蔈$Fkn%z8کy׎@hWdX̴:9 w?2o:y-Z$r.9P8Z?!_^t8>V*㥙M Q^|3_+ȣa NaCKH79fTǥAwsjIX[NA4 :Ec| RӺ0o.>dOa^, Xo2R?s_Ln|2g)j0V2Eq6(mIe\^ۺ{]c,XySZ8E {׹4v6i~Y.J.0?ES7.3,}<Й^̩^J-p!`pk~ BI i<։ȢJ 83S-N%J@eǾBbcS(>ݢw8To3G P`̣UBFɠ* DV3m.KuH ; q'lYFdէ -0@6i'U/ҵ8[CX9Y)BǜxS1 AIS>[k"gU:'v]4P憃&*5U v JNL>b8Š:~CH9W.O[bV ةj+*^iʠ%>h3hwc+8[Om vxW[bł϶x=e\%'hi/]UvG]<o?'AK@"}EkJ  Hj;*dnFzW̏Yp.\}zdE_rKTfo_f&˓>(7ȮyU -Wad/!ky.Ok1GUy)b A*FZpQyk0mUQnm{3L<2jPc}4~jR'"p zǛha*jMc"$"鵷pYJuD$gjáY  vCkvu{w) 6Da C]V =)&NfHGC!Νp)&Uo?itraNuW sih^%|uuFM}dt,B%9/ѩ:jt2TQ>AJ dڷzk -yIzlB%Gbm>M0:Sq.),x]-נOtQDG;Kj]M6\0,yBef{+)O+Z+|vti]rr)OǧcWZ[mEF!SU^b6SMtd*jИט#,@֊qz -=4#\Vۓؓ5.8.(~r +>ΪhݽnD^>mp">qFte0Mf 10&QP_\U Zaԩ>2{*a&A-kQL (<|5MLCCb),wsPXǣLvNfG2v~wu*NZ11wk>!㧍}(w&yЭ.F80|6endstream -endobj -4409 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -2e!yoca쾀I_^*k5.#)€b -+< `CcT&v md%aP?hCǦp6]5[ @Nc2c򌵁^]umPոPyavj:F*n oGC%5<Ź1`A`WMH|8Y5 vةfWC4K 7`^Ek-[{thDW0!%)rHG Y\%]e\ -Ob|EfHp36 ujO6yGrlYt2Ls`Tu<Ԓug4N&*ǡĥਹ/R*15l C*5o@ U`zơ( ߭CBܣKm⩢*yU@F:ݛ.l|8(ci.;iRYSf&~DL:͊hE+p8 br`tp|!ezyW W "8F1CgIhTcoOjjX(rqڈc-}8+P/ƽ17aPlh(Vya%Dtյ9p?$Nꊌ[5?VMch CaڇiׄO,@ XjFe-'ʋk>oGpUk9A.N>#nSz?AGg >)En 4e]L] oa@IëzZޏp[~<4x5KOޙ+o5:.!];Z٩U/,؍HanCkJMb;:t'A3c2/ktendstream -endobj -4410 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -:c5p-SJD^RjW7p -=KՑhK)$dEXps"i}LJ|?ٛѼlՈi̲xbs-OU$|ۇ53Ɓ5ekw;=V^$9VCX 'ۘNl`]oz3D#46| -R ;Bx`Fͩ\ Rb4-Ms_['IMtf#=kOAAg@ExRq -6@Azi Xqe/B0K{pwQ--\FbO/8X~ʵȿẑ -Z(L /$9hL6 _Rǹ1KS[_2:jכrkP72^gOKum|ڥylpycϡi??_$~=YG%+/1Пc -`RGGuݬ? nnP'`7%ɍMB6է@9dȷl~ _u/ke.%oƘ_]l- qw/È.o Kjs$fѫt5bOt(FxقI/qǕʀv$_^HX[4y1VgGf4H2-dHYDx\etDg[p0=,)1O '޲WŔɸG OR8R n&.߂[rv~cINm1Q:Ճy @rWJq *Fi j=m qolUS5MBE:c(xkn%у!頳M P-Yv^zۯ&x2TRtLƓcT,:xXI/gaNc4_l uZ5I~tWb&"y5rͤ!v/Pvթnљwe2<-PSKEeUpB?& -Iendstream -endobj -4411 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -Qcq.5yh@ P|.X{'^kd盞3 -t÷L[u2E>E&f'SuwMmhKRa]+U"N{x'"geB0qޗu` 0aZr;&6Bx`\fx%VdVm N2Q` &$ V|+=3 +̰˗{V62@*I,_+)ȽC)_x -Ѧ*:TɿS5#@!6\ Jë1^!E~ڪٝa=jr\aUc9 gblɬߵ7a]N-w#$bCX@:?^?(ɥ?(m֥-5p{2nE! Q QI#:!z[PuPx.' h8fgDt"DJ."/^@#QP2vt?,&p͛~yˇgF'Z窕N "{j5ɀ@?-I2i> ]'d-}fL4*?DcV*ը{΄9W9T]'lAh= !g琢lvdtRhA^;imH :egmg_\: 51 &o8!Flo2r^ih#Te`k1OAZdSӿ_+U'k' Z )ѥ]0UUrN#gCR *3 S&TP}#0N׿ Hg|jS jm!Λ>`"yNS {K|sEuipڣ_oQ~Eg,ʀ3 6?mnp@k.-> -stream -K $[T6nZmE_W?}a՞ɵdYjm%BvĤ~e޵`<ξ~[r,y$kɈIL!'X;u:tF< եG<\/q O> CXPAksz}&yK!\NWyj `rz&`Қٙ ?Q+!PE!BurkcN5wVpJhw'^ -3ZwP"S8nd `6{A`pÔT#?ciíEV Tl3Ve+ZNB'Hk5{ȽtܒGa˪<4?c%  *i5sn H4ĥeͭ F3K|E;X;n4z| -Uqly5c8,ϑ q_ gQ]N0ka -sLIJ2,6$ݴw7If+&s HҵlWJ87))}nJ*EwCyg:왙*?+2_lbsx(m.\&\# 1#J̌Nv7]pC*hc|I" }&_|J~)++*FԕLs-~8d2XE=31nv/B~\}ҟOi,82_5l\94 P؄^̓Ϭ]-!Q+*>;)ʹX٨jlzG'/%`ݥ=&KBeL˽Iocg_A֩xX -;99>@wɏRSh*Sj3d4jߋl C?Wg.R: '}"%ڃ);~0C6? !gfVtɻ`;iy˳tNe[̹{jb1>4S؊ય f3סFr"r^|] !i{mʳ5)}nrtѕ&Lsm^2_aITM{I`QN$%gD|_qڔӊWR UK6#Q9ȈAuhDǤQxt_ 7] 偷dT +4;}cOuʴ΂Duw庡P.gn -Uf:}>jO|I)BSC7ߺ)rreCy9? K!.{?P*Iŧa$vR7n ΅3Rth{jJjrɆ*bܛ_u30kz ОCņ |P@k󍺫i<IJ*4{JSưUۚb aæ)ę ^PHalI#d0xifM % u0[%JcO"13_<Ӏ^ >cQ'bR4w2iEwI;7[R8Cs^G)|5I~0uB̗bgBoӋRJrykfw mߞ ND!endstream -endobj -4413 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -gx5_+Zf-\EHdxᩇe ;9q}|*l)̔e`})DZ8Ɂ48LY<\"5="I]wze5 fOQ>\2*) -`BQmOltah& #ɞ?s+@mj=`UX`Ull1d2A'Q(!rM<&3иՌ5u~t^d"_uA^ :=堟zxH]2eYRjCsٵTWu~k_(aB77nU4?& x:@F8ds@15۠Jf@i&=OxEݳZ+.dKp'MUz4Zxe{n:1ŧ9@" )!kN=S#QXx7A {j4}qInNAV+cSzX;BSe[ZpxPSlyCM鿷Ec+;$X&eBcnZ鍙e#+MyY:% Bêz"jo4'PNk;ǖiDYEi[#}yHK-(ib 72O2JAԌ_͝y+ . j^fyuA~>#|$H2GS3`C-p.}R=tʩu'% ?ērWg.zGtQ|°:'(yXD!XpA\X; \?̊͢:ޚ,9dA+zh/f4=陳d,Y\@U?6UB=r1inlAol 5_u -Y/򥮍DM -%BnYL8eZ/ݖKvԂQο W=";1N0~t C"RR槎JgRW:`4'[գ .XghAUtdoŮlswx/u&\7NH_n6'7K Y*pG;雞7l` !6+DLfI_GPg(zզ. |64.G@U% GPX KFfl g:CЃjJRG9]9-EGI'4i^nEŐd`mXtz]XVT! U𯴸ҭNvl,b#0>W;^}wPc&R9`BO]D w}$KpVɣGHRd^]R.JXTQGDzI~%`L?D2/1/Ah$859* us,J͎jBNC^[ |(bzš@ )nq8+TWAd^W!PǮ~Axtǜ#Vi5Z*?JZs:j|[e|6v/̝a`3 {endstream -endobj -4414 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -E!5l/0,3POR|G,VO"_& gI]eϥh2:xLfN5T-~A3cm;=pd"/4v~x#SJKW( $$[ +2d#wI|i ǫc*]WQ8hI%T|.HD#yXǓjqW=P'j=PH~":в8k^lMd;+v2%G{.)ҍ -]R?4O!5f8{x08.z@q¼[*tfMftHtIJPe7[QEzȯ+0]>҄W!v U`e{q˛LC*F}ښBx4;1oMbk@TycN;pN}tۃXb__OXo#}X9nGG™䯇08u<2&ߝq&Zv#}"{? _=Hemszt qk[rB>vj)acK@$1+i13ƕI3%ᬹ0%t[ ː Ѹrvb J̶7q5M1hÐ-,#4 - `Gc~0P҃?S - -5ЩFyuڢuĵ%kF.-!$h^ -m#ox'2;A”j[ɥ1H> -stream -,&yjs"BSe*3b%=^Kf^,kw[_g(`Drs){7Ă~"{.iq;y(֮sK"I?WP .q^Ph fb.,@JۼЦt&SבhO*j؋. -ߑu']O%Xy-cرrzUi̵VFO_+眗:YMN$ ^ff[QQ 'eѦ4@sU*%@oPd7EI$ pI6"a{\u[$ItxUeJ:fygn;*5bM|zX@XyJS4!$%?pJcH؋n H6*,͋y"$;az.<BBmA,6'x-4.}[ٸ"/;^Кm|3 ) ɟ1}cIL}66ŏIV'?1R>i&_d&9;!/3Vχ@SήًG +~ n;FET')"rg7ple3q<)q]^u!]&֟H5A -|>ƬU"{)‚Ћyȭ* ,_Gu%&f=ҹ\@ # -( `^u*:?#FlO2o~U]8+<̮xzC;?9\_IҪ*X$ T|) Êxr U:C0mT%,tIm -uՕjK=KmgH5\%'_.Rv֥~xpͲ"7)?Hk<-Mqj -hL@5ͫ{Xd# ؘ& ]!-,, #~2'Q܌TMG6a1t8[QӰjWzusK\qj>GQMc1f%RO|G팇 ^;S -b"PPYi1 -"1 *Wy\:MaYaezLT}QbÁDZſX>yO]Ϧ7[\ƑX%(S9v*<"e3)N ">~OeV sKW. z3c>s'nوrPX-TSiQ[Tp4ʡQGQKeTN$> qLn蟗zN(72fV]=hn5SP[3B jq6H$A[|Ֆz_<5XTkf*QRU -5[瘼A;uO,6kuRsj,3MHuTK[G1#=ӕA| VUU5uɽ!1~ެt-'%ᓉ[JPum__}bC9 a75hKSݯǏyچ7x VBJU(ڍ RΆ7r}c<ص \n̕OFjP_qWSXA[n47U BN_ ' ]#0wk-{W0 -![sDJ6˘toxZq|3 $.(}\xհ Ʊ+\:(C;f$%`QVUL#\<}Qmߡ ́rD>CtM($9)YU7ꤌ*u]+N'`<[KJ1jYZ$9;md#{xrpGv!no -Uc4Pr2MhmDPwGM#Ģ9AV~G`3-/4<ؤ0`(fK#>It<+7endstream -endobj -4416 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -eJM8cG*7e\Ou@ViY2y2CNd˰ezPK3ˉPwz1MBW73%kTv6:bPpAȂp?/Q&/p$`NUZtsjM4S[ XkV#HE3ْg6HfK+4h;Hv +VQ"Joސ=t~l@r:~jB*z)w 72ǰPX{K-' -dgJE\d<@?ID 3R4HC:SDU&V?$TMK" )q+rEMG.BbC߶XTWd.!^&<&7JҺ~#:/Y0 K1.ڧX/]y^D ULrRr{)e)ZKgf~+ VmqYԂV2$+¶1SG(az)4TvƶOi2-#v0E NpkGOZ#Qd> g:e狝8jf+yuU~6<7TS:JA:.zMJH^wTޯN$ۺbM8ΖD~v. L A1OZThO1偩8vxa#6vV'Fbj>!wy#C~G%*endstream -endobj -4417 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -A9VC{H ;&9H ]/3* %L -~6ojaZkۃ:Sbtu[4J\d^,B hO9`eI8ƀ76e#֎9ȋ,$Yp;S%k`72su#,e @iFުʐ\Uˠtv5*p,aV]Aٮ4/׭i(&'>)Fk1-ѥGCӅo3:5} -!a[cs!o׀R'فjEX_V_KLw=A 8@KvwK9)x~NMem;'n8}sX$( ͂U^Yߺ,um g까SI̜ƞ%a} -ݪ9+\{4bNblnNĮo9?DS ¢W<0VXysC~!V9n5(ga(1)GS& S.C1]`W_ad8)yjC ǾbB_UsӃҬjF)j'9X]b3=}:Ҍ{Q@[BxE S,OV߀!yz|KO7o~hZ@.0?QRh.Yb A+}C6rO(>mT 7G$؝Nb#tfD29˨AM fgvDǎѵDRlݡ4 TL؏3 Nv.yM*J330XF\GÆ[KRP]RbVlx,=Kݣ{ZHNsCjpN'E wuPIF2{= E+ fX -CZ?dy0LB;_N683S. '{ q(7PbS7Rc Ylae(@v G -l"h!YB,3OuhVj.o4=I R>!Ƿcgv -JvXʟ<JaL%gm^ϊAq^IqxhbeԪ.D1f;5'4]q_W;lS6a`ubCujf J %rfV;|[42n|k55BLЂk`5 o£R1udgɃIp3[dD*yRSqf{*Y'{bl--tsţ 3ow$ ^֚MAj'J QOA&ۀkSr=QzI[>4kG?ɪq̬u -RYҦ!g(7*endstream -endobj -4418 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -4=]QS_93WKL>641dyC&6B#3-p~{KfUI5.RI"r@F)d8tF@T"cfJrP;՞4Y~c.J0; 0CZV/7}>$)o:%RNkߞGvN gx!7'AXjFԤ4 hZ{#lEp, 'O -t0$B9mƅ'&+_*MDkMc:#i0*m0?8S1q#u79'9#n2Kb-7AU0q+ڙ^΋/F+ N=~qΈ/k eSkhn8t\`@Y7N'TY0fEќ9(ق -H'%E}IJ 棎?oʉQZVG8o}CWFQmi'BxhyK𽃽N\œt`^cwˁv9Bj% :/uEf1aKY+<l"$PD NzR'!GVkޟKjDFG Ue&:mNvs*@_Bz2a݂s^*շ|(%KY7O$l&9)G3R:DZF=4K- vPUsH5Q ^ǿ\ceyȨ{P2?}٫jjMyNiȊĕ)Q+ΊԜ}qY!΃k{f -x94CrxF=Y&Q<[,H᪢b 9+FϬVo/oMmUR n֟D)z\x5x- -X3#ؑ4 -,Eir6.rywR4`oNYp&?[.N+G\[ N'oS AoL]f=2Z.(U## g@E-G׿KI5'3[1]lKoR(ny!s ?_-gxuqDYu>l̿p7PU5[`/rsRdp:gɁkendstream -endobj -4419 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -Z_2*I*CXmʟ᭔`zGZ,Ă2/d(tZu~OkߣYG@ԱE+GYUKIm*2xP" +3}{2OTp"pO>iӬ`6Zt5JGsUqWKSl eѠˈL8o6jdd$^A5!(X=y%? -u3abO %ZTG- O9tE@a{/gfiwZE,ɍhZAIdu¬-n\y\єX8ؾ 3]hsiFkS7 k\"yW 79=ogPڽ7q7vc]_N "#0@/tk]y"Zs?)WI:$. -HQp~j[n%o6&wՕ%ҧ|1׍:EƐ0 ?LܛCU)oH},C,0ΝS}qG5_ )u]KLG.?G(;" WnpE+4w0EY{AI.fkߛp7%ËJ+5 -rӖ"l3 -س3%P~eV$F^2]3y -yq2˦qm-By9!UH[K 콨sWrqXu>]Dx/FWeN ;~K : YbRyK7QRmSÓA araWz -Ga9Ie3ɴ*_P\Zꝛ& -B# ugL3bX`h"+cj&D>` :NB?kAcq@h cys]?tˬ!13`آ#Mk"%m N -k'- uʌQnTcڛ`ڜ$a?> -stream -Onq{6;y3zJ#c}{Y ȭzn]m%z Xyr[|GG(%y%IP"==朜[k;Jh8/~$b.Jgb8enCrZ -.^uItBk+* [< > //s!* ^}X)CJy@0(]ctRtL=] OajmJP,K^~c@:ؠ(>/o 8Vd&gSwD\Cuu MIDzVYR!B b8CʳE=iJ|:h{U8+:$WsrqJtK!4VT /ѳs* -mQc -x"qB%[~PGE7 0ڭ~EW  x8Ё=}_A:\|"cܿkM+: 0W*e6i,%{v){fdrl=爍 EţDk*od_;ɘ qwSY#Rc -9NUx  -o.LDc Db$>d)~ [őWOk+ށ⾂ t`]_^CNhNy>.rh -楀 -,Jw/vs@ZOY&ǶXlbH4Mim\bs8x BA14$vyQB-`tK`B!H Uà'^Ks"l~"l3nYLD/6O-o"f5&.yNQ25f0J~Z[ӴJgF˱7TBeE.+8\iY(**3r 2~jj(eXۃR\UNn܍ p;7~'SMpt?],<6AJva:rfѾso !4 LT؈<'|#3c0".9+)Γ(BLILoZb¸>bф'̶חeNeendstream -endobj -4421 0 obj -<< /Filter /FlateDecode /Length 1376 >> -stream -re!k1u`sf'ksw -on*kQF36S޽1ކCn/nfUyaܱ,RrЀ*s qp -^r^J$ZvM[FkkL$I 0 >P9|q{nGj'|/ L~fvq5nدho6cO|$(beHsZlRt'_5m$}䑡5Ux:n~/CyӹͮE-P?DBǀdg\QIhJrg/ɇf'7y,xEL - ]WTg 0Ő65vѧV-,=?%STo-"~de\F^ITl+]$.PZ`k]ÿ9!yU-{% ƿ/g"0apFJޞGŖ7(MOX$ލCm I 8l(1Ϳ®v\XdrcYeg)Qu"ىKb1nZT^$Hl`PN+Rz@ 4* 镮Îqh~\9]`MI~|j) U0D=ez߷oOLV9Vr]u%f$h;Cz~8a_ d7eɠ1&%VR$'$E.nipf /!V2y2d%s*~ W«c9DL9# /d77`B;[QŠ45QAwl m;Lp]+ ^h}3j|L^;F^ G=$JޔwBj -5 Hѐ3WR#7"Em/atѕ-"i#u?pSLɐZKY0@l{jAOEaDd!Fg@+MtϹ p -+\0hx |br`F 8NBоdZ#bh7P.wEzɷ1( яc?ɄL׿1 -$R(vbnKc6g)k,*VN9'yPt9xpۿ@1U.5:dyCaYݯj>o_?8I (8S_cLadƘq)?z~[FR0:/oȣO#kjJ]or endstream -endobj -4422 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -Z`" -tᑯ4*13^ސ~iW[֧B2;NrJ((L&3dV -Š'R59R_j{ȃ)}R|V|w!69luֻmǂsėńݒ E?HZkL IF#Jz0\NzMdCڹ>} &.L;4cOÃs'nUkҵNdԸr؆ߑc~Z=)sjSfw?_Ŋ}۳mNgXٳm)ݫƙ}eMW[2[ޛY -mپa_P/,H0@)%J>e$C6$ tteUEC]_ m[ t@_b;|0H}͉6s~x]IV.W:9m7>-b:xQG+>+^};Pf3H! k #@o#T@3kHTnoug[h ZRbMGې?gnTF7=YH׎B]lRRh3 -x,N34Qک&|1!w/˔Kl{}:qQ@ŤuiJ/W_Qw&^ &Tri/=3O J8@zjHq[Vr2E@,rdpNejjU}h&=n#{36 -!`J#͌›cS' ?=rvVĻPp᦯֓k]0¥e'NsjmKل ^F_!.p\校Sg']pmʫԄ4%,j%+ &I?H2f1λτ)dzzٴCHu'ʧMK&^}u4^+9W/gq9/ k^z+w.ׯ/f#~}e+ GMVC -я 9*AzZv l'pxo4 ϑoɿ/zSqď<+ݾUuA<& .Zi fi -tGk:PٸFJHS\p{ϽQbcb&2Ϫ\1+\6_֔+̒n?G`rlrYqŽG^F -ҝG v?5Өendstream -endobj -4423 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -|z l-_%q[彾/L oKW>f?o.Z6/ua(f] -^6ERG aXzfU۳S7>F=YlO;reGvQ&sySHK> IVmt_2ډ%B::kNgc]Ϣ|peKi4$\+GVO4.Bo\5u`Fh7J-'/](aF}t9gPd qɽZ;KF4d(}ȺSs[1 0s!&.|/ew hVcgrw^q?4_Mi7lcarqٔ@Y¤z^JR! gzİTuaе:0u)16He=AJئ{@F !)[Ԛ]DÑi~< oߦ8[_n qxU"KDhfZF0ϤDw٦gRT2#u2foӁ$n4nԈ.jHىq[jMa{e·VҘNjKSB~$uKs1=WUR%+=057EP#R"b&E+93x{E[zہpzw@RrZfZe n/4F1ǐٴ|b i}lfTjgs?O^Y|R.un!?=54 {[>YlC0 4Az@FqS,j?D5{$'B/&u A STrETvڻY\40LYJ~baJoiWu3rܹ7^ZhXqisj@U55"h8vk ĐhAQӑ;> -Q.SE?ZZ_F'2V p½͖-!rdJqQ,o%")EDm{_b!Ŝn@b> n#o50,0 ɓJK_L/ $7iu^ ¸#mCڴX)\:vEɘ`kgE}"Eh)u*Dl+SL`~qʈL/%0/N2JZ-] foGV4<:¼#C]LzZp/S R4V`\_PMMD$S^ K{-:#SNP[bͮg_xQ],'2X5mrendstream -endobj -4424 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -AKGM8Po*m i^UKVfno%u+Y??c(T V1BGʥ}nqF^ܛ:C3k6ǚ鸂EܘZfEڙn?GWs}=[>S}!(+\7<_6Rp+s|E 5;s/xde&D3fQyveL%J$sW݆JT -åJ_k €.wtn*iAzV072A{rٜB|Ѧ)7!]&$ǯP*! ^ӯwXf9NPw3>skh6%H[i7KVvxT<2%fݠPyc閩|6bRMmKdC,uم4JA#b_ ߓ4n>M GrKgdj -2[Enn `=gR?Jy/G_>qjÓZef9BV#33?z%f0prs$)P_<&/l^V8>,c5:p-܊ $e7,'Y\IxG%V_V!/FrĊ1ʪ/Ϩa'=^_u S.F_䫙pE`aA FȜK%KnGendstream -endobj -4425 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -0ߤ2 ԮhԼOky :?ݒcں:a5r2o{JfU-CcMuK~G;onRaPx% "( ѵ'zJAγ8`VJ.+2ޱGt^{E%2<^"F~}0|x7{:L:|eBUwO#V;0 Ln٧jB!Us~HU#e>PiY<#gF] ajm}2g|%0=JEiwQm: T/ر$P2ce͟yƊo"(7#.NRx?v PȚ@nJTKv. PU4^](˾̯dG3< -غDyG}̶7c gI[P@vv.&Д=*fϰ(tkU~;N^mkkW@iKQ/>hX[( ~Tp)(j2p5.(d@R& :VћiY!M!0rsC9]b!s_SRgG\'8G6% 2+O:.) n?eGRss"ڴa0\D^7؜:3)XM]G6A(AIM_M"_ck;qHJ Uo5{D$Cq8]쩏5!1'h뒜W􄿌QٵwnlNoVÙl_~Ã1wR޾$?n;x^ջ4S{J2W2J үB\I:6WiRz.[i .85.Yz63<L9ptejN4)2o5iz̰pC +Y$C!GuY$B- -TttjT9 bўz4endstream -endobj -4426 0 obj -<< /Filter /FlateDecode /Length 1008 >> -stream - |Q;D'K|Us;.=uϛI4ȯ<^ cQ]]kL^bZE-eZz*ro -}1p5ψ 7i`o@j w?cڃ*Ͼ42a; $cTy%P[)Ǚֽ@xP>(MRh*/$'/FW xaTh($G6dJ}'qNʄ~G$6pϰVm5lSkA XON&jYTqPXdÆ̃35t6ꪦ"r!X(MBm2ڎrR5!WRA}ylCQ-RZYS9=멚Of@LėrX{6z( |E@E~T@mVI uvd~D -i ::m~CwLդom~CV *LisV7\ŋdՕSaȢuH_^&sZ,!Sq }zd0yѸ~TA Lfu1yvF@I.Lmqڽ&\r-N [Jo/ezA drGǭ~t2%_/?񘃾\D* M;`YJ 3|j>"ZÞendstream -endobj -4427 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -.͔ǛD#y~86Dl: BP>\4s]9spo%N;fVOGl5.r \ܾ6$Ic $}ZpԞ9z(. Z-vڛG5_*P0[X-"bN0y+@F:J=N@Zt(ӈ#tfn+Xr5qWhTV{lZ]{"H$P&|iW'2TCoܸ*6}Ou,d)R"vuer8֠@dO9I4pz\M7QWFY@Ad[aj.H^]p|hu.p -$ՒSBƤY.8s'~GPb)=1lᯛ՝Ns{A_)!8G\%ڻt]VLԐJ]IB3Sd,ݶDyL,s&'3~GS>9"4dXlǃ*ӎ+ʰjS.$P^PH{P;nDW9).t:Wȼ cXW[|wb&b -_݈VhyѻٹPD`Q^Ј>M+?|:\hAړzkc6I-#_)=OU[DU.`iI[~ޫ=Qغf`5"lRu= (&I5[h5h-^M2dw6Vx&IKdqvV u/1ttK_:8 QE'o8Y N*ȷ41U -.HX}'F$K0xy‚UyF SVX4" ߩ.\KNzMzJ%jaH_Ac9t^z:\Z*lLV)N;#Sj{\È_܁;7^Zur9{ Lu[=A6e9[u7C9:YG݉!d6m9J~1/BÆ|uLObJu1nߛձQ(N@2<;=4 G6 F|=O>:%bLF9E=3! =js+[R?,Da`::Fm%Y~Ќ4e#!GޅhB. 1z+}b_ O&sv)蚰׹ꉽ“rR"ݐf:ɛ]LfF7C xendstream -endobj -4428 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream -}wc_uТ r)jY~Rቜ9oѵZӑ 6N5 [4aYi*&<\6B~9ד~ ,L.Z)% [ -nd:c"6W^j=Q Q2CzIm@Dp)\{^CNV+{QH[F_9`恶i<ܯDqte1Eٺ].IG311H˝/1^)G> O|2\I\BA$%Q{mC>-.VdΗk_7nͷ@ $zX6vHĖ[zHY^4.י#Gf۸J˿7˿3!(rM\oN7O2&+q-o67aT7tߊbG/A [) x%$$ceȭM5D;G=, |IJt_BLΦIBK`6Q@Ͷ鋞{.İǑHI"i?K˻+4Z'v#{4~VCQLq=+u>>cu-3Uc~I߬Њn~kJXw8?ڟݲ\ħqBwP")iV]Q{L2] y )ܟ)zvF Sሠ˷V%(Cr1geGFLiVRˉ*[ hAԔEI!l `5\Hl7};r Y)!ktJ -v\-(ooVZ !s]b$C+,ZU;UgL B lJ٩\q!vĐ}f1/j_M殌*܀&3&'ͳ\,*13BT% - {SVxWyz%IΊQx} #jWÉ0O2endstream -endobj -4429 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -SeAy./u&/i E*x`q7 SOpU0BR9om2%3.IL@6f=l$'+;P3鈴@$J6q8n>a=Pl EӶ-3ӝ~r:fK6hp}J:Z{ [`ӻ #ԩB3Vg $V)-&m -=BjYf, -mM+m -87h'yU*S(dv1 -Zڴ.9stwk5DDܗO{F&4_1~tx% %-ͱU2?@#) (3HK۹WpI90bJ'fR~JC~T޻F8aՅòhw4fݛ+z΄؋GDd43ikTA6ijTfqPđz@OP1G Y>~W/ûKaON>& /[p=4qH#SX`[_6"cE(mY$u)CtӍNt%U N`}#P?iwFW&ǦZegi0vNAx67xk~biV8t7Z&!D -`['KH$Ӵ?%,TonwL-B B>1ά[<(ȍnK)W#ʑDӂn#T|աV^Z,Q*|z!4롉T ]i'Sd9YL"SKZg!~>ǚnBFƝ9_h^< ځ=iHJћgZw3Vԟ0q AV1eCܽ҈6 _o:V{\wZDDRÅ0~pVaaJ%TOAxl1Va wE^-S"*'WƁuKV6b~&wt'& B)IcpP86pú]^Pɬvz9sMԄV4Z5f>/rsq(y[¢d.So"m}W=KKu`WdãL[X{fHމR;}6(h)n@ *@!g)I - {;~t;?& bPp -b$תduzvzS~/fތMkkRUu16WLcq?z@yGUx߯xlԁFtk|fB/]q$jDܖ+>۝;E~F(ya95x>vgq`1zTDV]y?"g$ lIy"š]E/$0ᙥR\ec]#d4/ vL.h2\E -˧zxk`+!(3n~g.ͺf=;O7GhAD֙Tendstream -endobj -4430 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -YȘWH)cʥ(i**i&ՂS~]bLNʲB7=:ۦe+/f'$ɍK=6tSӭjs K SΈK&.+Rh廃0LmdNJ)/ULA -6 qYK۰c%QͨSDŎ^_ׂӭp<m&:0>ulr/P+`soG>%eRD#Ϣ (r ."Rc=Y" -|rT5%zP?hz H XG i=8r  [<%TE q*'4(`*Z 1L&+$,Br8yz$ -dp}7V!vaKQj{ĻCs9cϊ^|b Qt(.)qb3I$*e%K,ӮWaxA6M1-BZ9x} vs7t`(vow UE[S{QBcCPaXcW6Ł0~~+7 <{֚NJ%1m`yci.|XV%)65VMw;']- -?]YɷPusݔ5B怙hhhЇ4tBa*S:U q}QY5Dž%Z - U\SJ%Q4ABWOe ?~:o+a[WO2$ `eXw'_wg+' Nendstream -endobj -4431 0 obj -<< /Filter /FlateDecode /Length 896 >> -stream -'nCPi*mAsrx߮{\g?1_u$nmCnm0< ǂw5R͖} vZ0 eEw%uDa[aJ~cNɼ/'-< NB2hKtT/̻ @&Yh{Y0db-! -@B̔PBVaJfQLCmLҍBEs.C\5-`[{P4 Lq*uPktY[o\ʝ~ڌ@!Trj[=|oq-GNti3!Z%CWR,['lJ4`n -6 Y -YSOE.7|1ڗ:|6:R)ikMB%S{yHh0UcQBMw'!8Nt}MP FsV0e.:? &{q -CxMpeV/zlڑ&d֎LY#m~%{'n$.AFEz9"O0!M{ڏ||>@(hi}OMê^urQr0Z,!JyoU+,Exqo' -j!UK m!kA8 x Rא)ztmS$*e؁g4KԆyNW]Q.t~e<ܠ' ;z棴/R$t뻛2̃=v[ M.%?nа;0/R!hI II\. h-AE4ztK"3(ՃCGB?l߹Pi8G:cA8t=Z)nz!~QdऌϕhƻJVAuendstream -endobj -4432 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -6K=.j -OL,`<ύP91Ӌ{M[,8 `Bb<FO>_IGu][)`elsw.j!rXjͶHhG8b v!sncad{)薕Zw@Z~O  ג+2*yf BR"ȇ!U9=.5|ԥ9YB+=3&Eד~{Fr|L`ǽhf[goίeumC;fH򁚙SQbdrD7% -81g*+{1Dc4mxDjO+v-a'$A܍RKR/ßf^fqx+bH Q6~qfs=SIcR84W+tmk:VaiòGHَRg#] RPOġfEKp+w콅=GgN@E_fY aw (&{ƿ;˽z1`AOb&r0c8?9|:HT2,߅ݚBH^-6"}]%ZPȮ2vM` -.G|{g!K-Yc5@'~KE5݉y]& 5= t/{0IgfPDX'TvD iPϬA1$y6gx(;vv$(wZ٦^;-oJɇ7`4i>KbʡIHgh7\pR -^PGxs.\,@d`o2~VM5Y [N蹱 *w|Ata!$7dQÖؠk& MW=)NODk8D$e 2C zqGUc37'J~sȐ [Ȼ`ٰX/Ghet|00Uht_`y Ĭ2zm'hB27t𳫶H^?qW W٧6XH jph]q?`_q]C@!Q,^юNA:%pjbNf%c7,t֤ل"tdp|xe@w-m -'Qi-r=' h+aCG>Wh&ֻݖLib|/◑+.Ye:q@2.SއWKM4LXݧaj7r`Ё L);д-'ura⻎ Q.6h}6N4%`>6c]t-UNF6UPf4|%Ɍ"~h I{RG=S)|'䑘{$:^a^]ƚ /a%Gc>>^W  3Q'F޶/绗A9Xt}Z5D -D tƩםcuꦟ}"fЧ”'§om^n5' 4A~pѮ- wSW}t4n6K#9MN.S+kOnI - 'AM*.gnMm AT{i?'38v>D22k%Xڣw zendstream -endobj -4433 0 obj -<< /Filter /FlateDecode /Length 2384 >> -stream -D}t8AB`x3MٴнZ҃)7o܅AAԌËجZRݳI@5;Zf}u4 P/EcE|^%5آp+AE#=Sp;=a2?Cv+ jnnVQ܍H諉E,@`7 k|*ʤ)*:tvml@u_I|mʫIK[D@_ ;Uw9 k2&gۯn4D]`҆(bZs -w}ggUH,Bhb sN$5K 'i_x\0`$pevg/n!In{07W &v!s [껬)SN Q-+X߼2 -KVJH2  .FڎwJd[QTI֛UB>%[GivH MO,>_Z!Ɓ"3%O#Q2GN.uY W[U:l@qL3EYf@# 2ɵ&~J?nQMpw;=[lۧ3~. 9S@9.;0Ѥ؉վ@`X \:`u{`QJ zBɥ2&V%ձr/KYAMB;Ւ2uPGQSwqqɧ5/6R'7+~[B`9 Tjip?72DWoU[9uXD4ulR8P4R54t./%x}x+RmִlcFfZĞГOWAe+m/p__hN<|V+{<@ -͉[ղVW@l尚X鐶 -M30|iW12]i}5YPAo8mFr@liL"GVVE1Ia޹{ӖuZ'Gx "If5~E>~ҥ1Q*?%N;̑Uf(S[EmbRfzUQfEYmcov1dNp6 fR5Wt\+o)Zz.T:4kk%ͅQĩ>Lm ! ?`O`Adr{q@T <+ݧ 1Lr鮩(אx")"!PuJ5xhcJ#֕OX\Z* *%Xu(htfsNj jVνUW #1hi<ɼgoaG@C ) ]tZ:{wa."RZxUU߅T7F̖ᦜ[iuhI?,wkhQѿ3f1o&%BS[&`יьqVqu"I i=nSA z9c+a^d B.x+z $N} -l .wfLkODp\P-hJhobZFxdRhJk\ՁZCQZ/Lf\Ax<"…gfvK[(j<1>wl󜝱.endstream -endobj -4434 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -8t@ Kkvmsb=A/z6fA#GGj9[,%~ߝG/*̭Įthti"*IUN9 Lgrs1BhzMTL[f=fTm<Uq WYCQ9]g2QNH]\ttQEleL*5>Fz^'D Q^uJN[T{eÙρVlĹ6dM8>p{+w/aoh껟Gp7 t[X*!:+jBWHhxF] -ϋGK+:_{LJ?0L Ε\ A^no| F )*.} +"?/k;2\e8 n/ jRpm|Hv_,V۽/VPog(F|Bcʼ wQ~z: SUX9&G1Ӌz_JoJ{<5Z__UȑУ;ޠ;6Tp~vIdDNoEI͊+'1jdW@YIf&O:N:X;[Ȓ_qY+#Xᬕ~Su^M !.*n= sRzgg*(.>T4 ʸ!c2AhgR7 ƎHĚ@2*hxY4=ΕsDb9!+ugOtgoR,oCU\Ob X2]=Ǭx}FHia 'DV..wA V,>y> M@CwnKF(Y$ uјs3:ssl43z9" -=2cFYkL&BxΪWE=A\ٖY.ɴ# ~camW΃][ j.TuEʘ2A#a 92iC%Wp8B!BP;Mx'ʷrI~MYXbM'DXXe.NOg&,+@k(Sw XlY]GW5If\aMSaYq]+g tN 1nhl0zqx7wg()LϹ7civZ%tFc)?J~cOfH\ r|-4`W: No ¿7J(Ϫu[.%ֱ^a3@$FEendstream -endobj -4435 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -`yE"Wd) (vMQi5ZcZu^+G0]wŶ><&[(POps:7(ċ'a. _atŸ;Jl[x#sߒj@׬R>iGKyhm -SKcbҾڟkɓ>(`rC+րɉ\je C UKI\ -e@di(l) -MJK^~ZɠٿmeX݂˿;ֹQB)֙)֔,cɡQI:ү .N, xRBl='fv<>LȘk%GfdNHUXs:jjNE1[a^%3ehy<%$,|wY@8tH(>EVYfeV](En{! rhszpB^7W*9\ Z"?=DIxG b"[`;J7lUw)`XH6em% -J9wY+w;?]yr_RPԷ2}XASn, ss, ?tU -ZU*ki]%(2.CiH JԹ3ʥ}_ec8SCʮHLO_VՊQ$# -JMEYXt5GXfEKe:|=d QWjЍ*^ZvF*>/.7$mboWbUuq ۘWS4׫+ AdK; H氟:O2W#GA~y1k4Eŧhw_U(i0cs)v y±(k4@TjoA1$9!WQT_ӽ*h:'kMKEY3[rI‰љEkPIW)ShzQu\F%vݭ:$h8>ڙ 0AM͡$ n[ϙ4džtO@NHVʿJz%kF2ܟ*yU)?5.kU&y%?b؛v:4X? 'e@*Xړ ' -|큱Kc"ZV-@,^(kٮ+xG8!!iʱfrD_Ku}lc*;KS gjm $yٲ_I@ߕp.-[cSuKJ_:1|1Gí /f{Ahon=m%\"^~6߸̓9]w3ƣ̵32>XCCcy,.ŴqF>)7"{b-p^߷ܼb(4˅}I8)endstream -endobj -4436 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -4%1 Z!XLZf#bS&"W&](s9S0% @gű)* [Au{gȁpCN=(T`y+y[ G1 ;QU e&-zV\lL/4M3)]f Mfh=!ٔݪ,Z]D2|Z%֠"I~cX|~18OLԳ"H>x.`I9E*h%O%xK'!fq(Tϡ@2V6oHsJƜ|!g=ZXw,pTgZW3c]#޷U>V9|R$Le@&s_2o8H%fߥf`"# -!]OBr֚9bz!l"tôlf$.Ȁ -ߛJl :=Zh7Lh-o/FB8TcHO^"x;/,K5, ˎunnQmZ6gky?}f 0cK]l$},4FJC+8mowչCQ#3?{;+N~]ԞHu V,pF Y%Oe0RK'_c>ϦoX2QV U청V6:DKv'{t˹^,_ \]8U)V<@Xv9u{nŀ&^Z2dlqЍ(?rª_f*na`U#ZM rݖdHC\k-݈Y4O.I0ZY9>`jg7nR)lHkqc[wh_Y/TU[]cWMŚ>Km%MN:z~.<>VZG'MsYuQ6 -,ʚ赾b'+JWv';ۉ9[nlsFM)FYv9TخW=p=.\#LD셎*Ccԧn-J&bA[k"(sw0^E;oW='B &]K;GU&;%D3aaIaN܎}t~]+1MzpDP l{z~B'}jbDdP݅N}uf4w+_L伋u=b}Z-8HŒb2Nɮ מ6\֣rw*pRISn:@*lDMQn8vFڡv)5y6;\O,NW"%KNS~D\&\a,!GvѼ!eei+ዴ8O )ͷ8Fɑ-+E^ϞJ |klH[I<sܷ"(gB&vjfg{8_N*S*i_ѧendstream -endobj -4437 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -{1U_oZ}{ qIva#n"EUB::*ߣqW [̲l 614%FvgiCr~aRV"]zSg hnWn Or:̨:'T8+adǎ.Yϼ`hpÌRLuHkn<+ Ⲭx¬Bgˬ㓏{CEp(l mD!FO.@PA YWzSBeVeЊAuQ@mT 2TZ=xQ%BURjrP QJs [\_MoGKy8i:ڄ{QpM+D ƾϯ -&+1\d:犾> AnMKxKRRG S=Z(g!NXrb -u4INr\Žx፟}+Eރ}k3-#hV&"{yl3*/gz]Sscssh[aR@ A+Oڨ5kqww`4T+kt|6-Kvv#=kq,i?_{x⧎664vAegPLS% 5bcc3Z?5,~Npi:+G'U\tp(8 Dnҋ#JѺ+.4A"̞poI&nQ6|XZ}H Prh?v{yI,M!dNb-"I*\m}ʘùqYRPlDž́61l+hAY1,!9BaV1,ȁĖ',,CuQthFN4&NnL Fn1elbqpUJ -M}rJ"NTtt3_*Gn*|?f􂀌-eL -vrk\ ̽LBMh[(/$qmrq~ZCd&S f I[j9qsfſG6FDǩ=ˬ6{r&O(4٦ZS]o* Y!CHU `Ђi2g+&Srw[H*:߆kG2趃v[؊$84[V],ES 2R\)*)tf8A.num;\bCOg4ۯ -VpzD2_p i9mJOa9^[3?cKA9>PdD&Mm BUͮOEk3t̸F iu$J:Ufx܁> -stream -dYheQ` 1==7@b}G+3g,VK(ܤ 1k]]EsO騵vTWo`?1U7C6A=ιf>sVc4 c3K|݂b,Tf}"w~:Wf|f]dA<"z ƨsd$ -\EFmXFN!d` -ln&SzwkF-ֱŸnˍřPyF8Am}eP^zEқ^CXVNU@q qr`&*Ex{0_'֨0$ThQm>Z -44()s9O W>> - A󌓫E$QƀFg`jagI)'[g8 7ϪzcC-S~t_k8_-eY=sq?FDCQ| JW 2&DbfF Ŝ^N;B#¾xl.oN]O5}-[veE#=U,!z(uӳ@;;,endstream -endobj -4439 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -za;0<ُ\n QeI,% "7e՜d@Y.u֐ܖ!׹9іzQc<eOQV$UvHK!w2wǎ)'!)ȓsV"R s|44CUuea_' .ߋPQZt}O{-=' J:$߫,?^P^~\'zM/?pw+k%h+JΨ WH#ms|TnEZO$|?:P2)%gxl$G.z GV})eϤDx3G%SM6eN 4SRIx%lm @,d3L$h!ٮ"HuE2'W:r,e #H$5m+tZWT5s'b*̮i#w)Ie8GZ@CM5CTko@U ZMt<3D: ]&vCUJ0¶#('nǀW69MWahΌ{*$;u>#C?:8, 4꺻yt?pos4Qu)ʝu5oN\X^#Y谡@ ,ŽusPhiP!:V -PjeA$A{QzT^܁َkS -xC鮫>F6BE%)D v5{solSm|n9($ ,>զ/:|dKMf`˒/'%_vaO/yoD-ެɋ!3e^A_sRJaz -ZM34/yB\x#/7.|g~^sV~=v>z@6NT -VCR7QB+Zb`,籂sh9NMRendstream -endobj -4440 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream - (يmm9Z/nf H &p]5Oχ)XwB/Tm+maZ Qs-NayycY<q=ea_‚": Zs[V #x ^k(o8Rô$s< hDpPQ#bRNe{&qu'ˬ&@ب: +ܩRf,>!3F.p.w) U&}^5bti2zSVႸ!q~mP-X;z]4@bVB]8;MEiLbQ>=M@,sTf5 zƪz~ikr`VC3im=(}T6u%%P mv.X?~-csx3uimK4[ғ#Zn}/">sh5Y{/*%*f`Aq}cQM3( -JBh?FqYЄb1}=F*.8!"{׼®&Sb 9| RFG1! 13dkgD5RH MSU'qx:mӦN ?;6E'V ,l`#[8a/N0=jL_v浚J Br5{^uu]PY`.ұR#S_hr -F} eΓL@*R@:xpm֒a偊 &6N( ~ `qeNrP{/%JњU2V͆3> +;aWA,@!Ff+CۀY$ ^8VE>n9z2 +FJajEX{#&B  W506n~uD.~=cuތm|i7% C r!rXRi u|FC[aJz,(x_JR)P̕IbvK]1;0Ǎ⁏0ݍ8z\1qD'w5=")6; -i= -X S -J3 !L~ a`.k%+ؘ`~KUo#ZW) $`6(zuxvc?LH:h 4퀈_}w%;ը,_n"JŃW_ET=PP8`藍Ձ]΄WD8YO E;oLBnYk8~^HM *7l5\.1Huu8&6ND÷}X(l9)۸t:(@ i&vx&ر -l) $ngI۬2X62܉-f+VkL> -stream -z3.zAq[9tgD]{RH-.KQy׸caTBR@Nw-6ިMs6^a,VFO{ !9гN*i(y-|e@6o)Yu{|1Sa?TK 'rqޛpp -Qإxy@JyF)߱j^fM7ٴBH9Bi~T\6Mn5$Yn;9Ů0?i/ UY0HAsT -6%FU~QҺ}\esbxwE+AͧN4[T򐨒QGJNV,"~@UK-›8Qѩ-I;w&Sm lܕcOTn32hi7'L_H'|&ƶ74cG"R=A\nr -#F.h9Fq J1p /%ɠ7!-~`IA;]M|~@ -.nhIQP_%B\cAIKM -⠄O%[Pm zY|4| FוK6 b(0PZutLrWᜇV຋jC?/l^{ArITK-؃Ŗ:8Բ%{pAEt#j.ayx>tm.8 $YH+oc2+ ~g­0{Ww0ã1@aД S{f$W+@Pl?`S+#ÀHꀄx>>Z&&vVE"r'=4`z"] bXw~I`WĚFe~ - Ej ;e9ުYaGX- F QKt?@-ߎǜ&}iGؼgd -_ŽOOeRj24FC55n{_r{wi@YY`k%h44]jUb뢴H2RN[y]@ ?!ěѝW|{[<;7] mf{! J - >JAChT0d YXA3.&Z!?#FSInl i'dHMԔU=$WŁ_" -A!yKdpANAcQsd{-M\\(So~@S)+fmWn[_Xb_gs"_s:'at(,kJ)iCǹ_t)@kR~BFo[ -y4Z;TPܬ-U U?91VuɃ, nHH2ݥE2J4w -άmD`,̇'H)4^(y>5PN)PstBNIHPO̠+;B *jARț26uN8b|"<9dMI U%.fW{xD1PXwH$/Y[)N뜒 %B"3j~*Wh+̥,'Xm`)4\Gt%4^>yrfj`ڟdّkruB/endstream -endobj -4442 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -U=ɥl"}.,9cX?7,?+E a}WkP "fP`dbRg yfې' =Q~Xfw`=U},gN>Njt@/_uKhw`>k D \޸r?PSQi"P ! k7,QTNU1w픑~ZRG^k![$<5 $AxSI,8v!⼙>PS -;<'3eWgةKDh̄L]ؓ3ìŨZ!0I4R^(㒫f)$I(qbu|f| 7H<1B!&h8=ukTD ] g7QHͮf݂͗qBi]γh֡;`*W>CGA20qC{~?&F0< -<.RԙWO -D,j"zMK 0p0@tRX -y/G^QVO)L P#GACɢ|5oĚ\KPpcJT{'F"i"oAݲ'_ł ׯaCTGw6^eaSmi5.GGT面 4{<# OVPWXFqY̫NZi&ivyv+QSfdxLS Z`OrO87qzQzTQ_,iO^8 &c. -ҷzp}l-Ȧ[2ӛ{+II j׳#>ây8"W*!3͈gXu^)į.m@E>h-y~Q17 lt:WOQ+*mq޾xн -yӮ;f.}#~y]oʳ췂WK<ɰ+a310xbޕq\v4Ăw˷ބ Fìug> -stream -A}j]|C64cs8WSґ]ۯuu Gt=.?74rh<(4ڎAxfb'ѵ(P@sPF8=SNpz۵!lD> ʼnm`/'+rHN)Η5dii*Ql+]zƂޯWW> c+]0Y$ԽM`?Fb~7 0 oEL=S%/nň̮p'  , G7NyYu#Ԭ=th8;/U*1mOu|g;zvk1Y'GHd4_I94*C |6$0u,z9is)ۀupM肷S5mNGѡ!qt6qNVoO5 BF I 7pSET"\bi&ux3LFÞ.D=F&,{!“İ8U -sbKzƃL:MG"yFxdNORY+bĝl6#;Q'f\+ӵJ00ipY7IR-%Cj ЙZ7 h`Zv(ά?084j2yKA"h!2s8Aˏ~OCfT8dCn -&sBבLgᐿbx],fD҂#}#Ô Im/`q@I\%M¸ l&_mrWwJ˯wU3qtAzP h+79{ [0~x4 ݢ5#[Yg9 -P%(nZS^r4Bh_C ZK4eJ=@P! U; (nLFՔVm*S%!WYCZaj4E{X|S^x̡|o^ l][[`;ȝb_A߈ihDs5sAc3y,GMendstream -endobj -4444 0 obj -<< /Filter /FlateDecode /Length 1072 >> -stream -˒@Os ƬWo)LYZpoIuHD~"SmrFK,+(}g4d \0.4z]{S2)\ŗtB 2u&ul'Q5Y/4[ - I70#9al̥QroqIsNu>]q·b]Txx⣻gYi!H;Q «_£xK"ux;o9(/=头}o H5tcZ?ĊPb&^TP/=i7TU(8MW֟G0P7GG E"$-5P;xۯ\aևC@:fD OdI􃞨L TW% gQEZOņy}ih*d;>@ާ.it#O_zͰ2"^,:wϩR҆9u3ҟ di'tEZa8oN 7a|S xҦ.sՀ-6Bf &1՟./R?IɹM?,Kw=_Iy6I$4 H&2BK^)HJl 3AG x^31*^l[@DuvXS =82*L8`"wJYx+>EOLp+X'jYgT&3'ueܟ TL|(faZ0F] gW,6r%gXQO8+' -ejoc 6''Wak)ꨱr[[Fz `\E4HT\Z(t=19gw0]"ϰ(5 14x{y;NVW2Wn -TfyxHrhl2hI,?9KUQ%"WKpp4qpBl^퇀7Eº;M*0M벓fG\b!bfQq4Z1u4Rw)4?xTF$UqW{yTT+`KZ$[#1Vb}Pӿգoưjky/f;6#vCFfg4Ah JNb9pJ!`SŅR0ʯF6`AiZ#0z~;nZ?g\X¢n'?UQ|xFH" fɘ] -T]H5ǑqN>`pf ^wUzk:2Uw5&%h\F4KY0ȴ~WmLS -RHHNzH<frxƵ8^s<s鄩::hsb&n>Q-pG1 -.1P AZ8yBLP~БW+wAe 4^Kdl20:NNy D Gvc<âq'+{׶[(&21y 6Z7ʝ{(V;  -Ȯk8kenvM'̠&1,ҁR,R'9NU۹b@Тt񐳾@U`AL4,_"lঢK]endstream -endobj -4446 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -~:6 hiؓ57|c]!l" wH0<-H2 -a9ֵ0zT2F)j;A#1v rx+ETщ˥m,_d_n#C^Hn?;~w-]P`)z<ō;]iZE_4>`~T'Y/LقEA?pV?wi/j-c>O*,b+ -32bpRSv&&=B}xq q -m棦$kJsL 2 yͶRRloWa%*N!",ɏX T0c*9iB*OYt ֎6n_VMSG\/.Ӡ=qEYSUMRJH]xO,DC'S$$-AN3أ R/T͈._uZ07DP?M 5)%!9gS|&n]?B먑XߨDxiI<?A62p8ؠHX͜\ f#+x~rZ6I/c:ݥ;ÙX$Lr0Qd gͽ4DyۢYzʟgS' ` @o@<8M}R:uN>83!{{h"LAbIboGJo@4@j!I)(~fӑ.3q'ͶrQPFҟj%(M_tLMXH]ΨN[1 -FI~:]UGgفt-w>VƋ8Yb2K`4"4LqъfPvڬ;r-:dx.HyLDͧCUG0qز0}1u?ރdCUD׍4> -stream -h\KIҞ@H.2x_⤮vl5Yhjz(0;pG Urwmg69;W@k)|J}lWb\IX}MNFSR*Դؘ6T4c}q'e.4vwcu;]ChiV>zȔq-O +xݽ:ӞEz2.k=KlLcAW¨(Ѕu̲tʷ bH=70GR*0* ̘pC~÷5KX7zyן$fS Y@a=Y T2Y󇏑IjVRн1{y$Do9iH -( -N|f*FRwٱzPC릱\}W905nG3c <a!R%%=gU?.}N`_ -Bɗw/#-H#r_A *nj^oi%=SGK×[ -gXIgi`%IshyX"FWtFw' XaC6DZw{[Nm_(GVԫf6agC6gށ%dKRZ1ܷv8cٮ |u囂{3\XJ8 r8,QXCYqXV@r458Fo Fj0"yRʊe~: ԃ1ZvQ|2lf1N R|.Żn?%-YU"-EZ> -stream -z='׬l/Y/N6U?SBt*BٍDmGn/D%PѮ+ -%}gy y |;"NI̊"r{#%Z/,cTb[A2s/ԉj:OSVi\4vnyإ -aY/H/3 ,WT6+qve"j.sz1q9Ld/ͽ3f5Q(Z-jr:MÍޕ -=Gˋ?Y6 -;bogdAʋ4ɢ*c%XIp\!Cyܳ -Χ-2wR[-J%j+ycEQgv=/WuYfChc^ nF!ߛnaOvDlÎA$}:MaNV8B4w,C_*6tӯ{4b1eqX, !sq:"0@FD*]SHhjY,:zy]P!LvY>Ip XchHR2OW{+wQ#)V& 'f#1V]á^A{ԅ+63 ں)< n2~X]Ӹ)6zJ@f:V yjo@zFzV/5\VUx6n\Nl;̫AnMιhO98Q XcKojܳV2/?H*$j\|JTAy2 x"6[qmѫi+\7L6"]zHCPM Rmvț"y!e9`h3U`=m<`_kڌWؑ`Z+@B~1O*ּ.CDz@މ4óVĐOM6oQ:>jXͻ̾2l^#&8r >>C5#uT|!d:zP&U5RD,V%e]GbhDu~m<.c(NgigLGmIsnƠfإy,Nczֻ{qˬQ9IM1p`+xAm0fFN̹&4ō~Y{4Ѿ;J UuüŽYPc;'E:Yi045KөsdyRs. cc ѩgK#] πhK=S@{dѳڊ }6억'8`Hc \96s.̼O7x- ^Xendstream -endobj -4449 0 obj -<< /Filter /FlateDecode /Length 1584 >> -stream -v }WpJ2Gd=XjLzVESn_dY<$ mώp/گas>5ٟU.CCKx|S)-J*E0BeAh5,)x5}@dQ6A±* V'l )'';Km*edq=J8)bYXod%z(ML;ȤCM_؏G az=Vg -y Ƕu\QRHf_~N P.C#|pQF.\8v%˄wa'\ ޜOR?!%oSmyށ 5l\I ܇}''m=.fu!`\f錋A[*%ϱ;2FфA3pO[2^k/rlhBtN([\HZl`Uɬ=8x$fEV8Ij -lTջsO?R`*ĨA-64ƛIwR|V_ibOXbq#jQgtL VȧqMg {ٶhè^ -cY'(Z6'M=TLzybkF .6Al ȫL٭d[sѮY5{9o]x)s`~X 7&>1:ƺQ&XaWvs*ƛo^Yz^bEDShMxfVF4*fD΀e 7Sb6 {}/=UWg+i\;فW3h6w|cJNyڃ厥RDdh6k>VͷR `DoDTFnG~?jMxϲJV>- '',ڲ -P !I 4wax=<834$WJf-8w{XL4E屮Vk]'NUk?f/E%:~0Z;N~[Aj9 ˾C,B>m"&Zq}v|! -|d"" ,hZh1㨱 3 D ⬏?@c8(+ -tϖOn6L WRHvEWICGOV 6ov_\V*qt/W(Jn_ZvQMO;hz!GuU!:a%P,m!;'u\{>0nK;l18BۼRdy&*qrfjcdQ}ר2HE9ǍK\h)EC(Zh4 uC4+OsN!ҙ.̗!e6 -?vaڤa »az.u->0<ʢ9wd8; tq+jp-& Cm -zKٹĬiç[˴F:[G"#wZpHxrɈg;Jy-endstream -endobj -4450 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -P !DV1J/8J% fLޱâ *2@|tɀ5\fW!TU#:BvI -vxM4;ZPuzٓ1rBH)ADU׏otWoI<~$ zw4/%e|GFѩ3$ -pqr xI_&zpb | @ r2mcP6nʼnuI`b)%qԬ%ῃCjj6VתW" $Vv^ʨٕ8Am*#J$Z[eoQa<_VE+"p]'9X9&qw޻熑$3.Nة l`N(I̫Ə_" Aaa|I︹/}x@jFZMBS5õFAMs}nR}L nk3_eF%uEb~g_%8D\jOŸu(3fg<Ѩ(O%a*.[:rl~d;c9u RIL?W)'㥤{nH4(%-IaYv3<pELVj`?gO3k -NV=2Qh;rkZ]%$$Xƛ|4_ -f5A+IPalP0Xw"W -V:M2w%tw*קt2`c**l%Ԝ 'NE*/f?Y D n'=ʜG<:d*kX3+yn//TZgO [G]׾9ɲ`N.6%[BwDƴ , ͨl3#Q\@&~z?DJ ]|d5+f3hkG.;l@.FZpa&,_U { WnrEBCL+L'Rs[Clږ𥳐S."'^g^CSC]狛bLv|>&"r):68y+?/Bgp@4RWr-aӣFVoo{ A'Hԙ<}ǸT-q ~cR4DQ]>D:XX4A)&B^T6qWhL+Up@^rZ:76؉جnT^`'7{KP7>[ -7 h63ɷX Ü@{%G\/F9FB:n% s/K5%H+H*Lwy -J?> -stream -*ĀcJWL`<Rup;4'm7H^2> u$TtVUW/Ŏ=Lrrjk=8)[n -_Ld<"Ե+@1ĎPq˒x9]f -y/F0g՚5Ը-J:{@Kʞi躹/VRs~vE$ WQ5 '2;ޠL8J{y>#;- -Z⾌7(*:PfO₆}!R\΋sҟؽ0MJҎ;g~db3pXα<@#^ z2 $!<ʇC>JO2Y?S&(A@P-0B qN#At Ùz;LFRG"vIVD d×\J3A> nկ,Oȟ"i'訕ey*(|5mJ.˸-WiT7p4`̯Lk)eCfƤɷ|xO_zAxS]DLd!|BeҳcνѻO>1 W>h@N fy%D.epqe1S芦 E 5|q' -<s5]8eY7yendstream -endobj -4452 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -ӟI*' OM 0B1vרּ2>Z0*eLvĊ;{.UY֍cR+qU+o5Є//E^]:Q"4M͏l]C ܘGM"~M2]7 nY'ܨ!k|\hW(ԁ@VG̔CϋGS#e0<3܃O׽owJ%X b./o"RBǦ"- -=ռ$5!CV~#fۣG9@:rޒ\Q.(PP3`OjBH`Bآͨh2;C?0 =UT.0pzGd"Y P*S.mNkDžvn|9pk[63*IgQYo*[/lGA2pM`W3qD{Bv)b[h 墹n5AXEj )b8ctg:Dↂ~9"(v|Xy@W| -V?(i~]bk2%UA3y;J= 9ve߽ksljYum[qlT12X)>+hnY_XE*P=:P7BݤI6d꾍>h݅b!`)|Ct}=a#kBV)HpymḬXL`O˦(t7 ̔!R> -stream -9BR xS sI|ezy ٫ra1}V̈́k!&ãtu*̒| 8J6\$l;-{$8LN4KӾUy }KN-*XsJ#iJOȿȺ1b4+Yn2`np+ThL]c13P` =.*+x+ Dgwoq%&)pcKJxEyK| -C]r ̶124}oq󊗔l?L+ikak/靭d(}GJbBaڎF *'?2'<4f-*7(LKh7oruB3LGiʁI34`wN9A er(I5feo2/ݞ[!oqҋ<< u S_U;um\e9;%YŊĨR  x{c HQm#ߨaCZU^+Pe%(*pQ3ӓLPZZ/,}_^5<~).%~E -ׁM|ƍ2'dVy-F쳧&4f٫&>ϐqƲrctL>[MYjF"Fx"G9/#]׮23ؼ+GDMc(>¹bf$Rgo+ (8»_eIrͷrL6 Vκ[¹~NbweoT9>,dB'krI5bI_V|bVm侥pfVM|N -/aDF"vӝPbQj9xӓnZS>͡bߟv+T w |Bs9Un|wۮƲ$*?;2%t*p5rlˀ(]sEƄ lT'?Jov%0@,lZ2sklQA/Bh q%~9K -J-3#mocػ:mcd(jF单wXˣR󖦝#fQvڏX!X۲^t>KZv: |Pa~I\$2{k?'sH[:BASj4sWCMRۖWb{P~Jqendstream -endobj -4454 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -3llu+7K/[!(3zڇpI>?nsm s^BsvT܃ ?V2M:enna'W0AX{N'A$ %"h͞+z$&}$ӕm/~h׺ߌH'L\x'V_JsW)2ICBaz ҹa(б/rA“y()e ~~QV\8u͂RuF8s4V|$q\3dBI.JdDP6oZ-5YtO!yq'e -j\Ѧz$:U5ލY9ּ:m@vJ /? gϊ$!yzI)C/v4@:zuBB~!W3yTò2%V$5`:SFut]ϰ2ځ`\ #vu(}ZrDyHtfXg|׎݌ |`WVΎZ>;7} ZKبߖtGK'Ulf=:3?MLxyKH9)嚸۸[mX B68KJ5oך+Gߎ'[@ЃA| e'hhEP`Mvz=aWLCZDIC s `B3Uto[/˒xF? }n6ө9bn|}b u\rc6lVsN`}<|"9V qSYإ!,oO}C(u=d[)gTh\hF74,ؤ;D# -Y,Up##7K&!(V]h`K1p  '0~B(U/#xKƾ{aWpP+5h/MG{RVe.67gDzA?@tgÖtjyt"$3NMx,eesWO+EU0^KFjLbF7"LOI;M6L$z9_ MK!(R.(y?p5F v 3 >8 4\ "p:7m/jA!I57Ϡ$p`n]ĦdAc0ͱaؙea.$N]6lmnp6bH VGo4΂`'Z@8nwRL}|zendstream -endobj -4455 0 obj -<< /Filter /FlateDecode /Length 1920 >> -stream -e]VnlFPL,c#FT!se ȄU3n,NEzʤ^`HpTC밇Q, qBĽBtqlb܌x|@t[|tqm^l "_E]cŋ·q bkq<3 ? K=Y6X^׋a4UD_a? ˈy=[.xd-ȡTvo@^=u^r!<ŪrUOgc*{|/տ{E/ UkְD@ -^mܪJ&dhVT;KqX% %u+J k<| ꔯ,%'Xx(Pz堹+Pzk3H5ŅG)]WasߺY r%mݱÆezPkB٧^>vbRߴg<кE0¢ӹ'qhUX/ciYY!5IBӮU5f{[\߯U xcksRMP^::6}*zUKgA -{<}*kNm$¦v" -cI?DqќeWQ=MOUMpm r}0Ql1%z u:+ҋd"gg/3i=E UкXCf[ʬfjm2m֢f2%s_*t~au`#ﻍU"h7yC %=G r ,nͷO9j'<EgO_jf\BP)B{l|g^eސ^ b[*bݘī%Oou}Ӣ0l$!ٌ_&B_K^aƯ8B-"̲Hb5B_,ҍ8_tُ{{j,ɚ;#yyҎ -5 ?7=UG&[+%8n<Օ*Ib $šg(<$iJxA -kg, aA◞8ݗe.Q$$n F-7mlȡdj~ū_s 'M'Ε;BYg$~Gv9ØиwGG/d:ZeVoNŒV*pJ5&R7& EtC"\{1 cC@Ɖ<Ys%%0@˫4I$QYrT qNYn = hehhFUv/YY j_678ٕl[JA@7;%3 & '.ۣVJ;Y,k?7M蹋Xa6HfOlL4xnEfendstream -endobj -4456 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -cKgfjx8W33,׭5Cߗ;Z"E _Yl_gUg+:DnII !DS>`gJ|5oϰmG|xWw.#TbAppNn@ޛ9xkA֮Y!l)e(4Fc*JyϺ4+KM5lWRT4#} 0"nwr xzv-E:[9a /Զ̾ jJP`5+X) _B"fY3XPRtH>>Y`|ͪ/vp]$'OX -]®cwES0?QAOFơ*zZ̈ @nSsb|+V+ȍ'R2S4v}µ*\Qr٪ڟLaNSGwJB~4mtށ(pCV%~hdpd ]MBvU!gt0J!?Z3gyFڜ[U<(-[}4:gxZX$Lo$BLE'6> a<U_/ Ke6/Ә̱顤\Ju m*6* /dAٓjB߆%97;+QdJ^<ɾf5yFҿKMkة,z'K4ha}2B.CsmB(&cNV1$zZjcࡓ0\)g_Dσz"K@Q.)Ksh-;W7&/>=a:jtAˍID]oHΎd)S߫90w uY=EEzQtVmi\wpK"!{Śsir1>~W%ÓG~IvG y創x԰=?d -wQdXGh5:u@۵jLjIuNCb1v @GN$Q#6A~Cj?@AasO4 &j"& -jBɏ#Pמzc?lÂntFuEAHth퐒 A_Fn|'@>*5^DF3M[0FDLMAR @4-&3,+KT|<3I3 зКs;:CMrk]^ۭW^%x\ɣy> FTZiL8㸯K-YC 8Njj(U|Hhvv0W -g2V'N`N7}ML!@o%zHղinh.Yfkz˾+'b`+ְӪBc;E W1ta@endstream -endobj -4457 0 obj -<< /Filter /FlateDecode /Length 1664 >> -stream -FL# vyS'b"ʫ /cl#:o;>}m 'B#&k¥0c1ѩER䵋kޟŵ9؂s7}PS8wH0̓bnYRkZ"Q e"G-Q탙"֮?lf6XQd9V#μXlS&Q5ڑهUBKA%@9+FH;M&ޛPPZWZP=}E՟cÒ;=AfoP&gx[|!fV):XXYJ`l -xLPS0g. 5Qg_p^{d [q QZ7Q+KZ;Q7=0 DĹMwU7V&y"Qm~6-{d8</Up#.א)9FyJ] -B-;6tŴT7Iw=ԩOJXKDcuI޽l.w69aS;@(#w%9㋈o&iRK}υkcF8ۋ|uq՝ޝ&iwy -2 JPC嬻u`qˑCȴGo01՞0nBp8IwBrb)gm'kgziE^(9JΧ4YnOBZ~})qj-{z'*vPV_Y{_cv QMw?>H7Kk H?8Pc Qܔ8 l[2_1VUA6]:'+WJO$ I+(AFk_EY'nS ה7!7.࿁ G6W4s?<iZ΄\-K޴B$.=>,iV }&6.fY >~0T |ZW Xm7+HOvuUS x,y]OK,aFM*PypK70P baPdk ;+xR'% 0sW~TIe -0D(n_'kITMҕ>"4ᏯG[HR\ 2ho o 4LP3i\ %4n^M)>/)`xvt !qBbCBGN5;w44aMptxdd3_NVsd;-l1U%_k(8Iv:[5f H˙\A5!L kX^2,[Cml.JlW?1}MC]z tZ*l+Kl^$cx,5.av;`0$B$0cendstream -endobj -4458 0 obj -<< /Filter /FlateDecode /Length 1456 >> -stream -[TlND<2^x%اN}Nmb4 ܐFd -Ͻ$bAݱzHjycǣP%uguwT3jFf} q!МS郌j! 92$ L;F.&4:^M eǥ](ʹρ3V}^ʟ8C4=FBC-400jb9!\/iI~'%S0Aʣ%9>eTALKuZ4PvM7%p](tulVFZgGL&7{o x@Ahg3(kf&:TƠ/, B3|aO}@m \)1J4#(JjC{澍Xmetx(=mHN>QpOxv` -\}u]}UՑTƼ_#;d64Gѿsp=wn,yAvaدn6[r[;@e8RW#ŕnzrԲ -]W[ t]Ydb$>>gԘUuѥޑ}DeOۥ\N`jH"G9) o𓏲#ᑂKSGpsHj]HeQ%,ky)Q16Szf֌1aa`u@dt x`#KP;4qDK XURa?aKTFlșA3rC{qEvԥNhWl% -ʑA(x}o{-ϵ:ֆGF 75b<ԥy:v]46\`\7SۆVCJԉos <#β7lDyMG^-ifiQ8﯏i{ܺ7<*'%Rʂj}{b@l|Fs~ل> - ˛.X^,rE^lJ+DJ x+ay7&Ɍ -OS7%Ib:a nb<tQ=5@,:2>DH&WJǘmű'ֵf5stP6_Jk F^s-MF,ps.#;(XxQarTKoۋKLdcUFc= c07WM=:xaE#?"RR.\5㲄D\GF!l^>`(T&!{ "n©BrKP*]iIiendstream -endobj -4459 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -8ۜYKeؑyB|)!zGiܺ XlC47~T10C%{QVXQs' ;žSSU{q$whE |5ً a#b̴ M -o^hPHŬT&?smW\|.GU !&cÄ]rM 0ZNw'[ǩa7tvh W-#- -!EK @&u0¤WF̲>䉖 Eڬٳqڐz+X}2c?EKqpzzGnzaKWFgx젼^ Փ&7KtK/׃6A\a$vNɛ\ ϡ=kvّlw_[<#Pm4C~V][Fdcf <^yvP 'Xh n4֧M -?iyxA{o{O[Vjy'}),ϖ.0lH@1۪vݥ}acwwɭ zfNzZNyk{C4n!숷i;Ӕ2BILhAv|<J,~2]Fi|QI׺f \i8**yp{[ re 5[rc=`?X%ބetSaPt=EV O0+6fFo%*DC-~rC`{h0 -+;N -1OsK6X[ͥXcT0Z̈)G9{S .گL}?mRj˒Wc,ŴCɼ~gze9qs*1˳jвF9ވv" $+tYIR&3Ҧvw~qg̋ Ii ]< 3_Yff b08B9ׯ~c=(MF{^y{#rȸE>!uVCg!ﷅo 5`` C6NYecwa^-ȷ<ϽVw.7hJ4Bdj_BX~Y4 lETuY2 n5)gQ2P"Jp;ub!:1hÄ>N4.-ܷ >"C"C (Z0<}X_$KьU҄m! <l. -'^[f/; Bggb/H뺝y S:8:eD{4Gec+<^qvX7T«NXh=}%(!q/xFJj҄Y@TQ騈͉& (7mRxyG$N -ّdYÿ[W?؆ȮEsI'4*Ɇ@6s/endstream -endobj -4460 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -Xp q*E-ZeC{0™\6$PmW!BMI0~ث|Yij#+}󺌤^`5 J *q1ΆwUW~ :KXSpPd@1*DnLb@y.utw=Kq)qk3_Hp3\X0aQƳF{H_So4e+2n? sa\ЙXYyMDA^);r\.o8/pk%$:y$3'-|H[Y-.Lϑt2B`)TbiuZ#S" )}$%^TA256'W8fvтF4[R~eŎmif` zUbnn/50muxr-N #dR23Q?V_ D'D8e=q Qe Y'qNwk;4q5f<#U$&ĥ ( -8/V[_y3 -v#dֆ:g||{[(6UL-zv6.ak&x dfs\DΝ0="?ܫIL b"p77*-ʥDRWi,a 6@#0'׉A.pg%3)aJ[#|>̰D'H7Z)Ѫ}9W^7si(4.0>rucs12߁C7Hlh\C5"H=|ue@᧌TP~ -Hzu s㡂S-'u7E~Ub#G!VN20jfQ u$ 'ŗx+5&X4$\&Ee݌8MUsy,]Ĺ_>.i9PhфG n,ބ`MxJ}x Iʙknx=/WS׆P+= !x@f| vUIz ' ՔUK{Qa|6Wj1Y=:Peg¢ UqQMJ_UP( 9ΛXo3Eq?:@jWo&kx026g *{^o< Mgy& o}+8Xa~O]_ga}y@[9HwE*e_Zd>鿁v=DwL] [t${ƱAl-࢙(mY*6;lWns=RVY2DaDIdP~}EH!TO,^Xa5B 13& -0݊9 8v(E> -stream -@׿i$w -J9 -MпhxHr o츋eu{*Gf#v f: (hibTYR+3f>yX4JEp5 y}8ߵmke;d8D*{n>fc`h7dE?"Go`?$tҖlҘXѻyyv 07v[+>prt!=yy>HObСwty%=ʹ PHpmI`SLB!*xܡ8*Osl[gj' O?ܑ Iԩ;L~Q'Bp? 7AgRNċF1 )}D>a};+Ts),7kic袔sNmt0!@-B: Bwqu) %XpJѲ6\2g}għoMMd(fi[ow@DBD᩶cԓ‚fa$CcGCHP~I{J=w~"z!U]rΌlз'fZn_tn͒OS')-D6gDX8[M)]Lb |r6~y6e/hdDΧ౽!CW֤i =8x;rhyL9v?^ƨNviX2+pZf󤼛u[&4Dbjabw(BW5]nK;ͶӢKj(E:?5yW|y 56w}+K1"UH -vP4M!7Q>HEOgn>RN¨rå_k DԜfmzj♟pPehDѱV % -B0ݶNCO'9ŵ7B.]cAL:JuZYH2uo\lM1;6HኛS︗Mk dΈû+"S̒k#QNgp}R47l0C5UoW|n_Ʉp͖< V:7^$F5+)C[.ĠF3YNZ3^A-~pg!y86qVSާ.nɵ2^GjBiΡYƲs)1x)# 3XWwF7q[Z ^[0B杖r_ֲ,B69#q=Ik1EFM#݋ A+Y P<2֤4*~~~gu8:o{7oiyr!j@YUp7PKONFX3m'7TNjOqACl3mXd=н<&poK@˵Zp_s81Frx?0NdYM8endstream -endobj -4462 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -iBvxDv.-$\&P>RhBe*/9>:>@f .L(5a"sv1lU ks['g[h=Ib,c[<`& rM'X!V30G8rS/1j%+ZٛJ,羽4H&՜-[0 dIDFq% yO_|RgQ+Lz`w:m%ZDjJ&SI~5z8"Waf_eRlANQ wg>P$*T^3wb7bd{Jœ*Oj#QmSHi,X]M *B] :t/Fj={ JȵP"ś(x2 GkP\!MNk_548̗ކ&*Z8'S?]tgΟ^}|+1ۡX%MGȘhTT3a^/3O/U#aRfjKmG4;Z`nDaRRU5yXnT=sSN~v y74$'",4z015~=2IVmA3n>våH8mHgQ2rWdI%Kq)-W<3hD]NT;4@U~s)Md7>pny\B ͈{M+OI2CyTg{8:58C?qqeF2RvhrАrhHb[#rXH9 <S`+Ҿda< ZtIs89$ed|}S**o\,$rd-]0 +|. LYv7H_`b'&ȡI0} _S6%N%[ǿ̵;0Bà`xwYJe\#'r@y::4]FNgM02.r}~M:`x# _ -?@š)~J,q<=k>ٚ[n[VޝVu7lJ`ִd8ܵo&wo -߀TB`[iw -! @D`D8A1U~{Tz5U0H/aŃ@JCS!ǵ_opgϗb `u/7'AfJ$1_r49:-&|/ h1>" `hJ1e(ۘGc N(}ɱO4@'~@Gkl_TOendstream -endobj -4463 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -&>\ -gpN@Fkϗ]\GSv{J%y0k7٪2e6B>@KW*e~ ۉ0{kǐ>[MˑZF6 V+r<<> -˝_(gX` -$Ɲ B{o{bC>qdi<70h9E qۑ[zyqfZa=pQV]8t0e՝Ӧ)l[=d7}~x8]yh%i*YkE_$c%͌R`R9tO4 2 zzD -4Ë>kl JrvLɈC#endstream -endobj -4464 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream -n$+-ǃ/< p,y7AMMU8dS^(a n@QD{EQvr9R"z4yeB^lW#>Z:_x D‰K粸JZ_J0}d}{b2janr}{ #@yc#_940(2+ R,V^~y Y&Z@b@GP3E _3n,A&4ZLvl瘹 l1d{hVɴvո B2y^تQ(Hb_6ǖ5zR~YoamFe~wadULRYunO^6Qi HχL.9dMpsmCʼnPqB2 WNc)ʴ&8Zަ< cARN҇VӬĵM3EZƁu.Хmb.}?b|; }nI69AR*ZΙ_dWϛ}f]{iL+8- \kŜ$o5de@Z6m?SX8t'ܸ~EBQQ֨ -U}B '/\z`Ӂ:h2yvQbӫ/Bb*gkw-\\4mJ[mFeL6ha>'(>(s~FLvq+)[irju4̍pFD髌em5H^gҤ}FKFo33 FS}-\XÙ*y Q!؆'(. ՚qi)oXWf# /CjCa[E.X<Nٜ{ Nh~h`=ʳƎ5惰`:|Gȱ6sh|T̊oN4G#֒.mNBh&& S7f+9 3tRN ~S1(oQᘡAA*xI@`S?[&}4x%t wj91c0ˏÉuDz᧱JmתV fv9Ok' 4>0t|p`, -lԀpg\D1vmeZғB$TE:͓*Q({>%eG޵@90!#~<)2*O!v];#"q=ߪ]ycfZIǖD¦TrY-bC+Ԉ+^%a -)<|ߣ-&5`DDS>es}"-Cmey<̼"Oo& >*(1o+4HShfNQiM@tf53ceYA"'K_bf'qK6³L9^wrfDn(Wq/ lUOO;b̿T/8Y:a^sp/&H2J -Kr͛k}kQXWz67o #Q*UyR.8pCdށ8?D.Dmy`|4Y aZ!G GF0/OZ.Q-gqL3'_o$U4\,@ĢWehٮ(]>R -x5J/&8!9U_#q?<p柲=endstream -endobj -4465 0 obj -<< /Filter /FlateDecode /Length 2400 >> -stream -거RvyWef>^׺d۲*=my?Ƒ"Uoն֗d5ˇqi5埡fٿhFBjSL|/wR679 Ek[)b˗X vv9 vr{St Q U1j<~d"N>0@!̕sl{S>NU hk~M:NxwfZoova#BhTuѡd?.muOR\D:5yШz*ۘ-X2+oܪpu[0Ԛ=y&.[~s6-JCKpwl#W۰x}n:6]JTb,0J&*wnB5Kyy#)lOUJ)qX~Z J:u_7ԯ]\Zermql+7љ=ʦdy$ǚRo(d$R)fIғ=1ShG#' 3ĭ]\%[>7G%;*aL8@~Wjz|<='f>$?f-ĕiy.PV$oSlB'Tq|է^`8-Q:%8DwX RPh%%i+}W3_3<[qJbFm+:0CBe,+nNVeAdJ k-`Py+Ժf~cF|7X "Fa3Do9MnO1o]2S*׼HT/.,v]NS>0=,#_IeD5ƓAyC4' XН)!k۽/"eY[8E}t3/ny#%sg\E5RPN̺6- o:cQtlwݰ tNoLxz)(\tQQ?ף"6ᖮӋZqjwWxLh^i3 @`͕i";8wP+O@A;n1~2޴/J6~WT6lɧ[Dc֧̬yh+46f<{LD$CJAe;ՙl!lwE*A FCZ.ڭ@xd6 !M8ν_h'5H(5>*0,Adv(_t߇`i|6eJ`g&.a ,1.=nopbR{7o4<endstream -endobj -4466 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -Y =m?1>)@˓, SWwdʤh_K<'O,Љ1&|pFBq`4(Kse6ē{фF]5݆Ã'BO52;N122+rfM̌1ylA,u#SQ}׊W,SE: F S4xꥊR8ιm@_ ϩ[B|~K;3SVFH\ XZݳ~%^7'V!5R 67*pg[՜u^'c'p(|v֙;ڞd:)?bDYV8wij$|+_͞[u =[›hFtnţ!kӵG ߔ^6ďO -Mɚl!f N'r5aBnVxwê612ѧiF aE4bEIʮn*#*fVbڮ*uI%}E_?g}+j{1p ӝFiL@@e=c>.3&v{C Bpp7)\,嗿=WU2W*F>%!f] \@=tbTuhxIuP|ULߩJgƖJ-bSSk"o6j@ j T22 -ovд/P\rb H'Dsw|mg#C$׈1O'"{/}u h.y>@9b.uSlؚV$.8GG@26V0rs۔Q9bѝUH|njr1SkK&Xoz^M蠽>eͮS迸~6^5afsL 1›rON/zw(C=Grg4¹~|eb|ECT),Z#$jaٺeCajg^L`KV-7lr;uS/p򳎧=[Ğ1=]SdZlZ1 *XM2W-}JGzڥ7))0٫$1h\b.E,`?ѝt8a?<};Ɲ@k*1 {ܘ^u@NzD~D%¾<1u+W.v-XM>4 tzN-"H犉> -stream -KeHHh:d &~h eS~lSi! QFOӤ0n/8?H:rvBٽ7Kj݄;AGS_} 蝵 -E -0;F3ګP 4THyd;y3:FOݑ.qA(X͋]`7wBNE?j"#`*ɏ:DayJ}PegWi{U@E*+z Jl$ԆéyiXdH\' ,\4 `vɿ[!}_ԨT-< uJ}5S'' nQfq=lq^DQ{bEY_p9-%B*LVj`dU4U$Ŗ̀z9M+x<$Y>QtIҖJ+/x*åaoqukfc]xv i9 hڪ{.H\rhio&վU8a: (KM78Am_WǓh] --U)†]=jTMjleka6߉+j-Ԍ5p;}Zl?eDf)=wT2Q+LNf/By|?x^n뻁f`AGo ;LCo!ք"^c<䗀@婘jHf -‰G!d=\` x^aX9n'oaUɍ+Ыoy! ([A,k+1vwEM{+;ݓΖ'&۩YG9xF~0θUoCY>>UUzIY^$cR'=B}EAcn:RH/Y^_--Q6؎Pֽ%F@E8\輫tBFpNoP;$dS4"9U &F- --N!`f)SOU/i]^QAmѾօZE0=KAGM9璢ם͑iv5'2l<˴{I0Z`WSS -}W#Ik zIYrO#"]]uO}͔ZC/1MnFf)Fj~l -ue պ -YoTtˋ1.75/8۲!j/M_%h'«|P'vk}b}Y;7[8DYҸ072$ 9`/$݉r\lzC@㐙@oF>^(NR^j\7z +iMo?r#\d3;{آ։s2#K ) c#npɭGRes>lz&pPBV v z z_]OR ʈEb m{bb-:ͳbvыԝ'1 o|m6Yբe#x5;>tWƿ> -stream -Gdt  ۇW<掻?"q p $ZP9fLTd*m`~LPXNECW1ފ|NhGDwEPQ -)6BQ#>xCd>V/ v8^bϪ2kMIy(C&0^s~&޻{G>щ4;뾟={rbG mFYHaϻ3.0;s2TQ'[5%"8:^ℼɤEA>Xh`!ɹ("/+4'eƛ!6N;aZ:S|1)o)3%;N< -/PnHσ1ONl7*c.d#7Ҡg Gաk6Z@ -Pt6wvK -z.D`8*b6*(T|`ɢ+Y [Us"yKyR@ox u0<B;]PVBb@(C@2" ogfe(dB:s}+GT5F5ƵNm~gP"2f -Fj';w+E±5?giiۆdx7DH"gy"K9vcWI4BgtiSxb$P xDNkn& bN06Rk- =迎"Po3Vs.{9Y5D0Sr䘲k#P(p62"Uu `+P9uSU{mTifnct(q]Wۧx V`>z~`a&/ e5gwQ|+EfO[Wr054UU$Ε#91 A> ו*DW! l l2ZX6L ěLe r7H=ծ{|f:wIOlA=Sɠ]VYO۞Z'LjKjjX+V>#ށ* xJRD?g=E!Z_pV~>gx_[ S&NKٱs⦹ltϽ+JoǍ[癏^zad -e(n&D^vK^:2/>X?C/(}Q $t^/\,}i_owsiNY.(N(>ժbNC@W_W@bȔW}hH9u*xӒG4G È2#NdzY'~u=}t;:kx3` -QAέY+-k8t2=Ǖȓ -`&LϝΚ`][ˮ"|YpB<_ZzE\Z#[dLSCR@$'o{m%QŧwXIzw7&ݶ:P^#`Rlmh0=p|Pv,vkendstream -endobj -4469 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -b.i#2RJ}ZҴJ#mE wl+#Q`?6?7+DZaX 8|mm_S$(\u;TVI -|: o-2YTi&$_"ҏT6B|~͸>h[ Ы)5]*nsCF-XlW%I0#.SݤtN|'v p6"6\nVeCp/',ʹ;Uƞ#sx7:)'fe+v P˨~ñO*-A tD.<`"wjz6>KO^fW!5ds!i48V;F:ބb`W>&w@3J(鼞F)LBs䫁#DX-/R^Pt,eμbG=-i}?{q4 `HT[ 91p̬(YB GsYnљ\Nrc5] XST5VwDqe$[FE*͛%lDb<5.@(yXVtH0Uug’CcxLOg|?+a2!vGԚgVOG -9+jC Fǭz ٖUR&>D`u}fv,ЄqI!IU` -a_k@1 - X -8 hT^PN@@iR!iqw|8 * to3p`A.l Jm_LҾ=]+ 1 L$ 8XmM74a֠BP$$ Sheө5nPRektw'H+T=PaV HԆ^@t 7?G]52br<stke*u*FNmo oۍ=u9K׭&y"ߨ젘u1 gέSդ<%ۭ<ɉ2R?mGha)^Kfʹ /)3r]4/gW"|G(FUTosfŒeSO-v=IWwtޚ6嬎!7T/1d۶ endstream -endobj -4470 0 obj -<< /Filter /FlateDecode /Length 1392 >> -stream -g_g[@<ݲՀ*ߑ_4.'A+;y |=!0?$MdJ h&KWFW`J|.4„ƶW; 94yF=><A1ar -s-$IYjb6v 5".D<#gPSDMq[ 8}:wn)APeA ϸ%?)KO }uB9aC,Ka - xIyl=ɗXԍ)_"V -Ԅ)w.19;2Lsroݡ Uꜩs?oP.(hɇЄ jlD-:T/k R~^^(=q#7Ri؏%!<WI1z-Kcv%7C$hD.i .QK^ ;i ǧ'}߃ B_"3 -+A b,@Bk `dKW -}E;$yGXt 2LDHk ,!M_'|ceS|cǕ$xL -(XuS': OMŮJRi.H Dv\ϏrQ^Õ"۶eS/W-Wo?] ( <Dv}7Н^wUUO:U-5p~UK׸/ -5=d;} -Pv&@!!7?S?ҝ35MwFMw)VvU68UT]&N2g?4+Sv?wmʅbdAT> -stream -: 2A#c -B'%hʺ 俖M W#xy -'eIݾ LVy(uh6h[ُ;,[zvt=Pr(y*tSP r$ -ߪ,8|Mԇvd* 1Q =zo\ Ս N۷) 3y6G]]u F3(;p` =vDc*YDWT{cZi_bV€W\wmeR@87 S){J|K 4 `ƍyN 4\x^ v7@XD Y$܈37ہyY(])~E-02V찚+Ѕtڑp7⋨3ؓ+ۄvos46|`Z45 -绡px,tlsHw1J0D}#ee*d~m 5Tx7/k,oII=86CHg\UK}9p\c.!45/G1V˱M-wo@rXƭIo]S6&pn#VcɖK -]DSn>endstream -endobj -4472 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -|dʗ&He3{ 2q;CL[0i{-`*4RQV+ -&^W{S1uXaL 3|^ ]GCՈ8lԤ#*ݿi[fЏD$~Ȅ51un+hEL!݌ܽuˁ(_}+uN͏Dp_|ق,2\~Dd?v2vTn3ޮ?:vAJVb*7>cՒrDe? -=U ̰l^4|cT*`%SѤUc5۳lb0t5mNM+f'7s,<^ X IQOo/y$FFm]`3 LeU Y)@ꔜMV@ -N`(Q4^.s)JԭVtfʄO!EY2@J3PZ!7Ueu23MLt+TV?:X w'gKqu=L y$vۆOi`^йG+:A)'O)R*3膟nx2g\{MnvƓ[ꃘS_E .ÅyӃI{$^EcMMnA?enjŋvqhii#BoۼTP"~ _'PXPBq<OePzT Ȉlw-\y V+nk#^e94`??\^)j*m.AU\5fIJIVI}kWhG/ZNbhݖzRn#=蓍B01\9*ygX)*+de'xuf A-g1Ɩ3^lMwLj +yQ7#<D%9&85u[@ĵix2ltBç®7G{ZҕҜCm5/j}"Ý9FΊQ0SW}&D?ipDu 7y*e\vJHbsr#~A.1DN[6E G΁B&3YteFvTtXHSS?`׵*dpQ#M~Y\+j tȟYlȇr|'sҎGlBz`fSgp\Şq`(`i6=jۜ=c➐uՕXi2gUL3b `PWD 7aט?ij8} EȈ^M -A+Q 556Pwu^ԫaԄr$ėYqla$V'xE`Gx={?0*ct J oN%Hx.BT=­ݾ[@r',?N?d鄋|{oRVk J,߷r/~!s\I&Zޢ_f- K\Q:v9 ᇾr{ -)5!<+pendstream -endobj -4473 0 obj -<< /Filter /FlateDecode /Length 1744 >> -stream -L>4r7kcUVUX^|GaޠTaLZ']ML߾D@Hzl[6J=Y@{}ǩ ٩UX:v}[ 21Ѿ24:^>#tMpeih=M?n$M=Pウ>rH;xUrrٰMӴjx*qggW} UBA`^Ä7t+ -h/ϺPcw-4y5,E('My C}Us="Ӧ*?G'K:FWupL :kiM:L~=J$+=r~#Iq4~Do9|2K `{֦ 7Pt?|0jփUDQߟ6bW=Iȫ%*YLpr#pp4fG_d\_jkf979eŋ*v]|SNA"ǮxmS^߼#Ђ$bAʡ?h)-%e  Zt|`>#ǐЛct{M sU̍܉\ݡÑ+d:Yc7t9citkX duCl/nN4i:&Mt-eab4䨿Q#@kNpX^sدIsv+'j*qy JPL Ϻ@UaHc?HCB>*uJ?{7a>q[{;i@oar Dw-]LM*!ZۺVg-n/|;3jsVOjeХ׈B#% -T*1#m("OUǸ3SIAWv(L B{ Rv 4cHh '@7hJv:{@pq$(0۞jnZ4ѿ,4U# v%޶8s~P+2]'F$w-@jB|[+mR{ w悉=E#Tnbsr iGFB{"*a.ҷ3= -3xlJ8~+(}}d4,!]].σ QNJ}NXYv_KLqUkө# ;:+TQJΟTꮌJ?0(?M>(=S*K␢RksG g~2-RaҠq\絿UD^aբBqڇr[q]ހ:6J;h|2lLZ3al2X -zJq BQHY@$>+ʶ6S=AX 3/'D="ZD&59IZ1vr$CʜǑ_N*[:$wftv3>gT+ ιbPG xAoMa^Yfm#| Ҽ?,ͨDI>Uq&>OhtbԼ$bƄP4C˚JE\tI[} )Zendstream -endobj -4474 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -]INp_zQDy9֕;e.KP3x)F}_- ޗ&zQWvYQ력?FYk-ˋ_)ꖆ"2v)%rB>JS8mYxݩXAv{~[E-w_"'+ I`/n6D40[y$pԇ1fdkлLckEuD-;e7KE礎!Q?PI<'x[cfWpIV5qOU>&F -|@Pmvo%LAM39T9PENY%C1ҭz?"o$/b yy1B6rb4RdcRaDib -=Oq-,6ufС&v ZjEbS qMky#8y):adZF & =ݣΫ$*5IZL4(>syYxkM1w.iAP;o8eB_ZMIU1~_eNa8ޔduN6(gUxޭ)r6<U&n!/-~{+3 nF`nO"Z9%Pم1WF^'$5g25~e [8]0f=YFͤLB4c*DC.v)oʒ$sG:ݮ/Onj`)[4m3*Au7"4vŞ=C5Xs؀!0DG7ˑIǜiG(_h1x[4F-A=6~ki^@OxhP'6T% -}NzD] Q7%8o􅪼> -stream -;8o׍Ep9B'R*F2t-<64 .nƗ_]*Un櫂}Z NrZb Lopq:CM. /3q\ş(Eژh]C7 e>1{.WeX'S -@t\s -K]C+N["].G<+dމm>pve^]W9T⪝]0$|%tW-d-`aEh>pq'ZkhNm,gB[ȌLﯣs}sUkb#F.<˽nPM8{LzGj"BR/,tmeutǵa F^ -ќ8c.9p{=HNgƘ.wxsтRj%"8z3q5jmO<]h6iOkEXHD=ȀAg %ŗY3LLx}oo>Vϴ_yDVQ_%CQSeِ+9ü]htU|_etq&k1Ѩ ԟztAevpKE$Exl.h}x=(ch=rsH>QmC7 -FR]g=S|Dȶՙ"^%Ⱬ_՛='1^ nU%p_T9w>|)q VPVtukXJ_JހHyܩ3ZOF^oSpeftih+uOMCՖQ6srf` BX@ 7fa7 <9rXj:2>^B͕7 >o=H6Xڌ@ tIb A&}\jZbfצzv&VCawnJp#WGs-C\ L80I߭da|6jzi%Sh@ 4Zf(F)k¢jS>Zi[;ip\#z~ڋ`6&Kwjv`*?TDx3I/!G(trxrIfA`$9lr VX6A3GDWA&ߗ,֟yd -a:endstream -endobj -4476 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -)Pݙ_ےӠuF[R2$zJl}12gɛElCO-Eu[ՒV=6JXYby;󰟹 boSʕB"Lw_~sTQ2{[/'>4Ch9A&!aO71phֆ>>)r`Uh+`!!FT|{pT9q8p-rql!EiTĵd)cQ}O>ybZ:Uȋ}@uHMЋv' rD OfҐy|`qQ:NO;^b'['KulQ~ 92c n70mآ'R+D.m{-u]~"͏Son Itm8BcZީxk]0)Z3m" Ani4A1wGjf|R5rQ7͙6~{Ԟ{fyZRnm|*c'a0[0R}.1|Iw]~/6_+5FRS$tb:>svd=g ~=#iPb3;s!/=}&X[c:%ZYE>QB?Usӧc6-m;qݽCFO_;N)#w?ALjplHչ )OccQϮ!C6rD6 w7\\D}g)~T&*\0ǧ٦4Wƴ%Gri逤^V"[3LOb+B9I1Q'K(AIR;<@ x9<bNb|&:C5suv% -!dJG= ]G\D8lt~T*Hi^TӼ6 p@30kZT} -k -GTh: G$_@--yDC+w }lsP*4/1>&'[/q,5[V,V|"~i/2zYCgϤ4G}vVE Wfn z@<5h!endstream -endobj -4477 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -T[_S9S:w\w^ƃE+ڨ{j*{ #d_f.//Ґ65kgŖǿz Gג( Igi D{S{|f*Fe3'.ylfDm[r),mɽU3cUbiæR8V1qh?wrFq-&:&3ud͕RtN2$*yaJ"Vd|u8\-7^ ##^?;Yk" N]|o_Y_!aNlݿKDkYp0<0Gޠxf:o9S?zISH/9RXM5F)xn\7ƺG c3FFߝdŢ,饣 ]0U?oاRqLJͧ pӃaPcJ3B0n?\_t4ݾ4GC,+GCk KA +mP]R%4P&cN!ؓ"vC/JEMV$-xdq! t?W*6 t";檳WxUڪ԰:g4Bٜp$3Vӧ ZQ L}#}}{%^9DD}㇇:7*H u,6mAۧ}ڷ +a9^ 4^CPO2_ˍP igAN4CAJ~8C={Zmt-NH00uRfF,p7@ul.pl:!7jC{fSoY}w2(5%՘EL}Z[Ug'$%t[F240sά 91m~{Yƍ+D+_:jݧ@s*y@)Ů կsnRͨd- $${w!(h^xvQ.G~ S|B%2FT"rv;Lx* :<Ͱ]?UM8IDfЩiڈY~3sG(Qe-gE{'~ա1uѠ-#8'fBjdN8bMn\Q!(ԍDӔsI]p1Y^" OP 4v;a}#qZJ?g׷kQNɄqo$jޖ↝5 B?+.%ndFTJ>QD=X - -|#[.%Fr;(=KrU`xaJW^h%Uäg|Ⱥ$% }Ƚ?y^\7=ǧR] Ap4Qa@V\*{0ښu+B$oJƾ F_Vo'[ùjWwM%퇌q5QSao+G-Lned;iǕ$l{qp_2HxحXcaҫ)zGpڊSw }v“)Nʇ=p2o&C{[$Qwupi?Iq>^%ޝ' y~+;WRfgD/endstream -endobj -4478 0 obj -<< /Filter /FlateDecode /Length 1216 >> -stream -5MeC揌kj"Ї9W`-cxf`:r/HL\J6g I-]727\oGW0M)C\Ac+3׈{zHKhO[;h_BQOhnfF;'Z@UpSpyiCћ~DjmXfJK.GqG$ܩH!O1f^:ܞ)57 nm|yϛLNFˢ4dh6t! VB A4r .v-蹤aQ7eꮭڵر1ٲ5I,N Sd^n"_x ;e.Y!Ƙ -S)E: -vlZ6(GlPtgCekxpݰ3 -~0.IkIv -DBصbO B]@_O`Uy%iJX4i|kng$Dl(/ ]p{qƷ*A6]޻QMUYdޠDO H؏l3g'HzTQJ_1y2Id#ԵN+9n,sMjEhB>t ѓۭ֒eVZ-,<1ukM\:$,#> \avx.gȘ`ҫ2pWQ]lAvd+ p\BD}p9ۙB^9&vO#"F}G5;WυL=d fN! ~k؛ĕ,6tD lNI4kl5Bv6X:g{c - -=@lY/\J57]Ѓ/KFuY*d`]vAE0&b淔LP耿IcM0OׄOڳ3+]H3RG\n~&n*l'#LFC*Jq K7ne֐G -lAJ -e1>od h*μ?m;KEk~S6(Ī1[U̺*h|Īendstream -endobj -4479 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream -AshucGI[ܛ=pMVLwb`#[p*$捽ىoIrrıP.[  [H)(܍"ʗz}H<7fUSGc˱'y==H5&%9`T˚c^L4)H{뭼|g;ۣZL\S ۋH0~hFUKt,b#|~[@XvzJtyG91[SA UqBSYT•ЎFnVM -[z{DT_iVD3 kw$|%KC!c]9*GH(w9Z_ R0bS3 |VCgYZ;x`I i~V~igu q݂:!Y$gNeAݾI$&!U Q Rdpf#Q8_gNIرm)Ӈk^KenJQu@HwWvrȊ׹loJ 6H8&=98ɒdtQLD.dsWɺD¤ Q]j<Y֐o% -Q,$RN^啱DH_%!TIg:'?`& "lƹ1^$_?9{R2rtP|k}^Z׭=8o0շ]IzEAh@W1zUbEhF]tp̡h/΀WZA3$P_;š ToiZĦD >qoj,Jo;#lHÜﵹ bQ&f@ BBͦ=\ohV]ov<_n:ںzd>!0iGq,&؋UQd/tl&8n^zxI΢?A HNr,4*ϛT6󔺆 wN*vP?)E|=ѷ]13h("aM6`/:I[CasRfsPxa0mGiI_ pF"u i8^59@`SN݇1e%Mh(SNYo> -stream -lg7yb("D$՜ U)5/y']ց͹,8y(ha.xa߳ T_F}Ms^4+ Г26Z Gń4G}ߊFϐXѮᔡu||F^~!r`[4ұS{ް0ۅ-g]z\'C<5Xd6;siƷ9$z_Hv|L8J>3\,8ҒU -O;)kMXllUGf^EȀ^[ݵL4W>۳ľUΉ\x3gp#NI/Ԓ@|6 -Q0j=T[P݄fJt{f< MY\[yۨ @&ŀ!\Lj,!L_VQ#k*T`mq Maq*|p4\ }x*5/=e OT& u)&c?(RQ@pP}5L((vN;IVđ/+~w)Fʃ,q -,{{ BB^-0fܷ Ξ0syuBb"V{S.9 <,7ܞ\$]ݻD</<ĩ&9ŃA|9w2ӮZ >l7?+XY[Yfaˈ^+p2 1K:"ҋzi)8MTU+"E!p$@ij27z^dqhQ;tvQ A<5#aQk Fg˵lNg@m}<焧spI6s>gC9\EXty%(@1-,/1*z$#bET.B`VtsJ:DO&k#B 5CvYt~1:GH$Pd`Gg{T:(ҲWavphO?Um=h@@}^Gzzb޷#?%Wcr+rJ6*hں,}L r:,%Ç?lQTe*Ccu[αI -&Ąd3#rKPL"BP ]!R3.n[ ,R; -79Pu 0#'ˍ/H"'6H LM"\Ar6Xk~Qa -9+IK> -Bx|7j6-ÖD;XP怯q>M ?;pHXY8= +XI`p #QVuhI,kj>^w ᰥsТW Opa6u< X!ћ>WN-,Z]ʦ|if~j(endstream -endobj -4481 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -r~ ÚS|Eg)^GJv$/IaN~ln1/Q/@)J3H<-=M"5{ ~cjiFp,{}OY˝Y2&ܔ7?7|t-bZ#_nQёqI+v7Y լMɋHL]-|E$ _mRd6J޶)i7JJMk'旒Ua?J۬ ~àD - >p)sVA0ߝ8z#1s';_"G=ݺ=Cd "~WGp6c:C3[Ͻa:"$3)~L̦q -1:ta?E?(Aw,$:Fr*ɮq锎>xr0:"9DNot\Bx|KۆfYlM0Zrv!]FYuR%ysx 0Kq1]E `%!& 5R~o_ޫha3|ZP}[yG# jWtSl(ډ5ئg?%{)=3XȈϣGDDqcLg wV2hަ3nJ`g p.>%4vTn",i8k-i9dѪԾ.xIC%d-Bc{]c"sRJǟh tyna%]aٟt(gY[V$o'`1_LKB[xWf98C`ȫpf6djà#"Z-?QmUƃi.3ߔЫz%X -''M/e.:An(#rDc̓\Fs 9ݍ!RT"$Yws9/_񯖧%&u);}㳭*>gԜm+YGx'||4*!iA˿d}gni-gP9ofF$㪦_TB]|}OwUݩ*%[!ZE Txf)lHsβnSP9W ܟW&IҧD{J -¯ne7@llb.ΥR6 2c :{zTEP5M}]ab׊ SBY.ꪾeLY'\y$Pwa)c8 ' ™|K>e$_?q1mxmQI;&g&  +kpf\?=U$ni?=K++8N֦C5Lj`%H<TՇendstream -endobj -4482 0 obj -<< /Filter /FlateDecode /Length 1616 >> -stream -" bpG4Z_60;{ -ʠ<2n(ҰSxIᅧ뗤]-D~DF qFQ ]s'7@@\1[Vȿ֩_ݡnG(z+q:6ҿHj2/,XZN2y%t|f8;ͦp +d"BjO8b|AwȽUqfn5W+.}]@ąm`]р&s>yQgbm\/߀vUp_5Cدgd " q?^"LҞ:Oe޳gk؛ i'Kh "O{Qd{ͫzݤp߿n<⎄:6W(bV$pN,CǓp6GHXJۂ_Wn$?BEcB}D om3̬iz[_x2ٍQ *0Dq-J y{v(1#^g~"7-S׀^PaggETMK&U Q9L̗_l%Uc~{qwBl2+? sY9+?/m:B/FVq㬜Q4O|‰{+An@JhHs2\|>._QbA4Zχb~#]جܸ1]btG5X_=i`8@m13S=v0+NܶW! A=M.|i|0.hΝe5@`swB MDTC39J=RrƇ{s@>>w 9JE׈z.7VC5Ohk4|Wnfdښ*Q f1oKu3{uADv +C.Xqgfs PJ(1^HJYyD NjD]^79霾cqzn8 +߮/_Q7>wW>@[vfWvfUgH{#mKj3j`by/Fhw(o4iw_lB)A앴KcI.6endstream -endobj -4483 0 obj -<< /Filter /FlateDecode /Length 1600 >> -stream -;º&u[P'D3ps!C E2#ۙ:ȜQĸZ-/NTQp3l°{;1.)~"3}lnQ6(aTdq[ ^T{[j8R -)+|_ =]1v-Q3Ym27$Ejty^4Zֲnb{[u^Cզ+FOca(2k)vR- >a' Q;Er!z^?N -Nςm -.n<nr(fPj5ߪھa;?8TT07TC\ - iRUszĐҨq\[!{a 8:Zڒ,xvQѫ- -SVwu7O04TG2)M: Azu)ZW Nip涂ĸWT}zFX/13%TTx߃T~CiEC*Z{P hŮ~Zt5 ?WuGm$ز+.| -O!e?8of,o$kMխs6}"$Uw}Xqe7@d;\׍ sz #:ʘ|x41)>BDCX)E|pW;i1'ᆱ[c\;K:2HE83pYI&3o'(]@yuRl-rsk|'@rK'}JִQ۞ޔɲHnwF&+.![mC DT*r~Q`[p^Y\VSo'𴈡4O5ĉAd e.nKF3&XhX6о*ɺ74)lY2q Dn&<@ɕ8lS -yZD`\H_UnȘ~-)MN0S$CZ˚Y'7ƗQ*+\b93Cy3#W#~VVӏpHEl?ΌΑޡA:mf:(|3ۍ%Vu64/FٵTĹ<ʩNQϫ!QR#ڐrUBIщ1ⳳXU - ґqxEgaQY qU~,"W -3'idz@Víw.$zN" KÇQAZݼ6*}1 @@#hR4 kAZAl^cll ^B1|L&⊇L wa)DF_^T/%3qZ^ۣg۞P`vL -/)yNpuμ'桾j&3z)N_F‹$hi*N6rzgQΆFJrUDkendstream -endobj -4484 0 obj -<< /Filter /FlateDecode /Length 1776 >> -stream -듔xbry@e&rQyLg[Lo/./wj ەv0 Na%'jr~~ikEv@j4sBh T'&~YEw0+tT+A)ed?k z O05 &`Q+)ND,Ow:Z˖K G,*6ڑrj|ۡ_6md?AᛛֻhIu9i*'6ZBsfjiB@!W}g1->Zlɂ@BsՌ+gh{(uS?AM'hs"Q -&sN;rQz -i[tZflPICEJw:;DHU|󮂱fVDA7"ʮjLQXHBBVoP -)B"P,b$A7jrtѿF-H2:T5(K:>L]Ʉ3wi(PiOmn) 0hӜZn.Db#y{:yq 8PZmlF~:@$fie%mO̼,9z p|oP5t 8Hfk6]&GF[ҼV80 SnjeeH?Tl)4 Q*H -ggC;3_3J='4 \ͨL3'u<˨H8Q6*X(n)SE.f xU4"H'́ .%s  *-_҉{tՂbށK 7+bu[~֑ -yG%M8ĩiv[Z@%:wpG&C\Qi:\T`7$n5&! tiISSb>nyH=T쭚 -Ċ0Vu m5B -@H(>#(F.R.qcE$"RXpL`s027<ۙ&Lhą8PnˀECTYl8] 8^$2"omuھѽ̈Y0AN~Z -0䗾UQvÕ0.J`Ģk{,v.&TQ[oD;jkUx tmu:\CKˎbqCƐ< R:0ij&~ = -xP5&]Wy1tyُVFYR~r:FҫzF4FQȱ5 Xt%"ptr$WX]X>Xt@uG-Oq!󸐺d~1vc{va6vużEL֔jJP|iu>APhPov~~bDӷ D < b⎓Dk𲹩Vx<&ݤa~H7GzMM^CveS%*8XK7W(Cl'ɛZs&e##^st\iuhWlI埆eb -N>e' -x?2~Ĩǎ|7HY`gՠgzx=> -stream -; FKd|9{z(m40zu<]kk)(%Gڻe`_%>.D’ou^xbF?}cv Y6QxNxam!ʟ)\ ] -)!;wq<: V!ZwO{2*^ъ>6_=>qL\ -i1hЭv铙H? -s`ؾu QF=H(>/@"zʦ64 -YҀ]p0s3ID`ȯ S](7il6x7S+BИSW6h'u|U+5<Ҥ7N0Z&j L⠙,Krդi/޻z{#5Ku7ԯG{:B4eHK6f-dh'#!G$ In~ W~PV29VS0 *M,D_Fz#l#ٰR< ${o<˰Ъ]bŸ ԉ1t5n꘡0 W`endstream -endobj -4486 0 obj -<< /Filter /FlateDecode /Length 1136 >> -stream -&hB'Y9j]_+u/߶[{3г5RaԷm _. vҩA„zMPcϵ9ggjlɧWT00zpR԰+Ap0rub!?m/b -ӗg =,'рaCb5rI,1nJᤱH>6%e#ђ?PyY직DGR']ZϚB+([ Sw,ђ96'x% ɉm /E DJӖzs}F܆Z&!l)=pp*zb@@x-ƈ90d'|Y);N_kCr*f6Ĥ}l2[ -Glj}M}gW=4H5!ZP2 24GqJMRo;T3!vEŽՑ_]_[yNOFL*Ãy;'ͳ.C.wK C(iFQƢkBwChK*"Aˁ`MW3edzup|j*vLRq~N|{)|>Q+`dDboZpaNㅝC{4ظ2"B' d#by/K,7SIi^ =/n4`Q>؆1yTJ̓tqx+RvlA_*Lr)`S ג%FdˡJg |~sfzU[{^_W:pʾ[jS]n={?kVV]K^ Yc@gM3[Y2a]Z͹@k`ʁU-7J -Q/|`vL!NAMFPPu,BF(Y3(yGg/C0oy0?Pѵx\ ~L}, Ŷc7InΪJ`r.e)°*Kp<,DB:a"dޗՔ.Jˈ&s@x/DAoprBfendstream -endobj -4487 0 obj -<< /Filter /FlateDecode /Length 2176 >> -stream -*ۇ!`pz)@{@}\ZYy*ې͒O=sVn߅[]fQiP -65{4|)F$@4HNyyQ{niؑU>wa Ev+GVfie+V~M>Ƅ (L1Y@B`4u/h;͎BeFz.ijzst7&`3/iSd#MK@ho鲱7^u -`@X!l(Wɟ92xݯhԥ *!vRC1_zA2'K' hm p|2žml~.: 9x"M T5Jl{M42 8J/MU2gD -Yx ؄ߙ1R<FB 6jN؜tdJk{m@HY4!gTղdzM+˸X $:"<5}e0!u{ꎞ@GaJVX63Fb1"eŒK^sLnlbcjT k2n5!.é/3&}wR7u,b ҍ~@$闣zCr ,f|Y96ݮIL!j8 \Qͦ!XfOgsf>/g*aa1s&YJ)R_7-7fmgSVcNx69UV|ŰY@4JOO5psۧ] aՔzc!ЯmNK(Ԯ@|9.'>CQK.נT)Y&Px_bX?S#O֐ZHJ慕DFo#BE[8K.cMSzܰc"AVp!c Գ83b&hS8sfZjgZM+&igdH2k4Uϔɔȟ֧4Pe8|E EXAl f@s'!b+柱m ,2;є?\[ dn@k֤')(&/ e>̯Pb}Tm3t@Usӳco_H,@mY!ҫ(F|Lr+Oylw2bN`7<oD@m`gּ)\nZy7⨡vO#>'kvWA#e@V"sk4&ه^=MS+mg6յMǟ8JbHmzޔ,DstRlǮm> -stream -f}tF5$r>Cfl4hUg͒1Lי&>^S?K !yVL$? eԒ6PK޷]v Odc:O!tz!SPF4}t5=kk -T2z.a?ОI5#Yrlaw˟:Np]*ٚ0Ao1788ybۉ=Iȕ6JAu&Û7yl'0^m__y!γh,._{l>tGso)M#9O^ቆJ!/`.1 xU5y-Rd%=Is' ؁ͱP96߻ayl韲tS\6H΀eM{HXGϞ̕MB:!^Vާ8h ekP&-).'! g #=Je+I^EYIn|i^8Q;k){m+t?a#{[IIn7 7 '!HcHKh9qN]S8=U;P;(,6 Sb ǩ#gkV HԮc0,^4Hll덠sdkB$;0]+ЈlUI#cQ'I5|iz΢2(AgDӭԂ-B(ǿ -zEf0j"`jΟAѴ=6`y%DĽʿ\׊S*Hg:QL`!3Ķ'F1m x5 K)p 3Fn&NBRꈿj^˧?S&5=-78"(970جCssK D\+1Y$k#dϰ#]C2\ s9T>!*πyP4k Ú;=/hhziH|+.'rWBU0pE5c*#Isѯ\T@êH^4{'˧/u0KA*$а"#_i*DX˨k0N7%h,/3A*]%9LDQ2k"tLXF>g$LUyEr/%#޲_GA(8DG~WĊ -mP/Мq-kB# wH2?gjFFSzi}Z9#HadudstO %DR&6{2WJF6_3;י\(KY~t.?)Ni\,#o( - ->O=-:4Q5blϾ'6#- [c#|.^Q˺S-5z$N`(&M+R|˞qrcHb l+.wD#V^BwCBN:1Cr$+rC 5.0 x\M9GޒyɌF:ur$c_o -!VtxU3`C2g1#lEi1 0Mѓ!}R_Դ9>xIdVa=@^sMuK2|M[-sKj SDV.ܮ!c殁4 -"4W*{jR=|-MtEGuJwV7E#mRΨ9wkwTӯe׌e js1.& wUW4pT5y/2߈`fU@M~V$=jq{it\?.)XNdQ]frd+=2zop*AIgNz_nlLĽd~!Jfdb3z~ъ; -tYv 2a7&1m(\0!#*fwm L2֯ئjd*($L}u#AW>1\X zڢlfeÔR6H%s5VLMă-} {ׇ $zOA<46]  ݇q~,1Խ9> ȿ];cnJZyRDl79F\3JZZ掸v}1gendstream -endobj -4489 0 obj -<< /Filter /FlateDecode /Length 2208 >> -stream -|ھ;2oe^Y 'P/*܏jY|^[<R%kvXg15@g J\"]_1 i4F@/OEݐ5.8/>ИO^ʅnW<̲TDG׮~6_ -2)TP.C4Eﭬ]P_,ȦkAYc+F(S ԦPCM}ѐSO;1ezHFQapk#CГMI~NQD 8>zsIQ[{$w[d'|{7u),>־?b%5a9L|̇/;R.1Q&,XU @17-\jdr[) +6qfU$-n?7|m3uٵuJt<ħ$nupVx[`ΕtLMP:,>_ڕ`piUnhIJx,7X^~wV- <2 xQ_V(pB`OLpRh/ {jr9~/VyUmFf|RrU'ߠ:-֮L Q3pi<Aa@aϪ 6s+K.R**pkҘM IXJkݽ٦MJ3Ht4ֻLtbҐI*Y >y*[_bu#1/ >-xܒ]uj%נrQM{w-G/iLNMz9e=ҋ?1mW"oFX;W: -rtՖԪSnjQy"6\T=#BfmCM4$%:]K$$NxFv&t/z<+}r[qq4ף WiqAG㹇{&7sG8EV5`}T[d)ZfޠtRIK; ֊vZq&x ˠ-}^t͑wbv83x&7S]o[oj=#(9r4\cǭFr6VLW\9υlG s#}Z͔?@jØQUH5V(ƺ'x<8Y{OW9Br`6Pe+ X  n}CxSgH BBvu+I'@eJR +S@u7 lMwJx t)hqt3, STJijW †ϑ'+„j 4%**y@tg(:(l>y\r@ mV owөZb5m"xn-N~F` .R%N5»bz^M(x "BJVO"/Iendstream -endobj -4490 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream -'x/'^ `XQ?O_5a$GO(R~Kj+>?k"U&+cX"0yp𲎪l]MG(s!)z:tQ9\| u%Iz_oDa&c̥GVn #n`OΤL,[AئAG~T*;_ Kv'>W=%#c6mQ9wI_[ڷ(ՓC Geu!٣|=1o*@9;nRߔ;T_ՐuW 9l/ki'HeWR1k1`)Lƶ@Q -}j%j -eV֝:zsCь.a.; Y&8†Ҙ 4BqeLg( uiP0Up.h0_LF}IjlrVq",]q[73lB[brJ"sc9a?Q^ N']K~8Uy4݈@.&^>UTz!;1<h.<* a19_QD,HES%iЯ [Hx7yr -r0,3 -EUrD - -=Ęs?U{m0rkRUrň~}8RQ?*0a{ ŽcM0x} w=.imFOw0ʍ_ )p zHm`hFۺnKiŢ?voQ̾!Nښ=3YiHh;yྺEg:4Rnٚ0`.KY( R:ZLLܷR">|A5^X42^%∥|dXQ@i*n$IjolSR%r%;/Ŭ ?yqI%BlLu㈇Üvcp)pr̮ۛ?g*Jg\tym1Oqod{F8BeugUFD6@lkFd>W>= 0`fC_FyΥ4L%{=G C1M ]?%6a.T zrT@IN=_uI DK K}dmNJ>p 7^T^\avpRe O`Ц> cR |fҠ"XsE& 'r\ydcc-j< -]F@/ڴIikXù\11VJw\\|qNBH2JǮP N;H xp| w30.1#'jy*˭7 pvRUяˇDsUwh8x>LUK#96!;=h{G 3v-=M;% *ɞL%ٴڠ;OSL9ПV!ʭ)Z4K( xP cx=)53F=EI@ -a(-sE^9i|ޞKZ} -c^2Ԫ-bʂ_x߷R}v\UL@a_ -|&\VDgzqcUۑendstream -endobj -4491 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -e SA?·zoz-֞^1bݷW -`gN|tiz@nu[wvI- pfq?~(*nG6Ft!p1終QGozCuh=%8Ч73@aY?v"> ^uUNPNuWz@/IC6_BJh-==^pjKR1aP)m_!`S9e *45F4һWpڦRk-(C.l/IJǞ)PmPΑ2Ϝr>+u]S, )3IZ{RS&4|`dIW`vɶD - $FÛQYoO3/ݵ T_rlޯ#y4LfhMV~XŞDb91o84I:Zf64&Aׁ -s;itGij@̓f/Lv< x̮g\Нl=tܾEsKN'A0R{n0#!8" kkW0*sͦYi4R7]P{Ho -v1~Mn;XJj{\TBS1akndI#DNԣҁi,f +0a; -wG_DrŰ9l X6!<&R--v1Gx7rRK ^o -8byΥb-j7ETaАB]M#X!n-ҋm,;I$u]F{i4\9 t4cGg L2 -f_ha^jSMI!ÿCk14N3\t&8qF>WBo63].؜P'H=Ĩ(oCԿqNdMBKc -2CNTp%w[.(|ZGV} 1:IBƣ͔7V .<[X*p1EJCV̅Ej:;PTe"!@.䖲MI +Å[J7;S:#x!$Xf (#J e`)K ^Bo;-le -*zhxϿOn" QA39 ٿn * t\Xlx5az!OF~*>4Q."^*a3N{K:>n)+&'A'>B0 fsϏrZ[8St%1qΤcpS8GIl/ϴ!wvPeoYAbw{ⷂkǬʰ śGiskB˥Ou鳼,SyY8rz5.XUt!Lcd,rF[տ&IJp3s¶F=-\CD^YCW&43}Dih"};*-VB[aG|vUL616AF^3Lluo>hBBjM;чio6M2^Ǭ#2k0q -| 7YpBj+<.Y{ :e0C.޼ Ҥ[ "M0ȴQRA2.ö]T#YS/g)t^g8[^I\w#.DuBka>Zg Լ䠷з2+і(">a*;=K>j ke;bCҐ'~i Yކ{\endstream -endobj -4492 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -8|T|zcR .bޭ;kvYm-L&WgM nAhiSu C}W!:6J=5ۢ6n%Y&! -D+ndtzH/!S+vf0-EދdD̅e)6ʘljG{E}NCoBd.P}_odlqc3Wwj>-xgy]A)~/>cJ7/ MΕtN>kOPYg -򢘎~b4*Ss.qHP!nߣ[sh!Bl+RlWvike# Zt p`F =@!T։5ZjüDg(طT.3F.f7j '05 lE?e@!W[!φ.b3'ms{vJԣ,?FȠsb.nmkvt}SKdV `~Yi0:8▰?64D':!}KE)%QC<2x`bV ؾd1~gb"lZ`x*9^ \Z1SDAHlqoD=hIJ>yrK) 7UzB4Gm7H˥ 񮈭!ͻ]6|Șb }զ-Ox011ltv(Қ| IZwgC͞(*01s6zG9eKH_e|/e~՞QΖZ.iTi.wmI9rC&wNqy[~bӷQqi!V -Im&:Od|WO@JiaXCSXBLjg֏(+&F=ImDUN" -2Aqvepzjr4/Z"T|;v?V/?F6`g:s^D:~xd@jlL1E;C3pˇpEr,)E ٝEA<a1V*˙Ԕ8 #-}YzIrJfw4WԦu+QL|X2=SB])TO"eqC0 eDX`DZNsxx;G?mB+=zF)J2j#"Yc% ċky=t<*t6@AB%e5 "HCoIKhT -D2„PmXPB_XKSxؤ\n4oB~7L9@Pa胷+1_aD֭}kBa'HѮq -Ino9Þ+ؓy_ s'.[*v00QiDu-l.*ߙf(Pi*Xl> -stream -GWji9po5~ZxYp샴yyCGPY|-ⓘB=uѩBXCyd(m4`/8g_. CIRMOB̘᠐>S0G0B|=ME4km1sU*9JLU0upƷ>Q}H8C9׎Lx,k -1#uǶ;3![4ŋG{endstream -endobj -4494 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -?}a?H!^T{>ġeἴ u-eF!}?Ikn'Q A8D ͧqIS:<~8+hISC ݣK xg(2S>KkW4TeyQ8ޟ7RQ*F斋]DNt -_=.Ki%\dZk /t+?Q!vbs΢"I4i+pcA]G6{U"s?8.L1K/;M:|;l{g:XgXꁜ3KPщ{/GVmq fuhѣz`>1;MD#ҍ͏fjF\e2cXj/X/}PXQ\ӼDc|y`CFX)FI0&ӭ6p˯gژUY IvsEYPO?4{xmr2ko n޷>YnX -t(Ca; ort*=uS:s> -9%W -!Eɝ)%3PLOVh ]Y OY6I>nwyܦ< -1'?q/"雉;uK! 1V 2fI[2dY8c瑜YZڎeu ȼOѪ W,n&hTLm; x]@=>&u2Q]TBdro.9v?RgoLUmX ڬz,M;;,CznU?/1j -r <H~,n kDc~-U[Q BQӒhquGj2' QfC}Fz(ڢ8GA5úk0j(ty 9*0~F@W̮O0j0sUi.Vף bV ynBjK_6Ul\/QP8 #+[LTl,6ȋ#lM!>y(ڛ jA] M%DF#<tjb=:&%!ڇrk9UZgӘ1 [!"Ozna#{.),fzp/@j>3 W| C<滧'Fő"Aa) $c]YJ5o쩔21}zi!/ \Ȣ zU *t'}\eN2-T!Bn=3`FNw z\`9:r28BN¤Er=,qw%k">+QcoDjAJĊ1O$ޣ&ɓE1n}\j֥4c&JQKF#IpU|GHjitHI-2ZmOi<vtR{cZjGDTabؗ[1C,%"v*:gZ}KM.^endstream -endobj -4495 0 obj -<< /Filter /FlateDecode /Length 2304 >> -stream - }7>]dRlvb,J֠![ԢZcFAiаJ}u-2K s66F -g3U{i- -,6I>f͛ycFO.B \ `ϭ.ܷ8p8 #U?qgrcCfH][s'LXIkG\|2(OpZCa(Aj}}vSŻdzzȍ2~)׵!*o,`eKCATСI;SWW?'zNP#Jh k-9AMx0['mšh`o$':zxH%jîjk,JIgI -Qq$= 7(x5l'A|Ǻ8o?|j8pn/O-`i+0BdQa5.tlE`3 FG #h0iWҌKi ->,j]>3-67 -SZsUm[8W.-5_}%pi)4#Һܐ\+O"6.l:5ݚ{ )/@׃:S8x&T')9QZKohFŌbmRgkB|:*S~3m6CTLFٶe(YZuʜ#ߎ@iE 󮯿*Jd?{%(Iܞ HxW2!#<I_O4\g5]ZYYqP붭8WІad]nGύvz] |a6,;ytC0H&nx}mQ[{.mT׋oH|iFT"nicN̠RxskOs-c9vD a_E VLλtTl Tu -bv[GCHʔ0V/B#hWi \B0Y}I9IkQ@Us`k/Ka]Ȧj ".d,ޚX~"#W]HXFb36(>99| X֊{PBpgPY:zXs`ܷ8sVl]ԩpQo4cpA.n&^\ah gKVM(lee֋'߂|UH9m HRBr.&Z'u"púq 3߿v qWĔxa 3w|ϯ Cxs^`OT 5ShtS6yWc8^+lW*&0C+׈Р澯{?q/ZYrJT^*= 0ªd\o]Y;P' 7hh}6nnrD*퀙Ruv cH On(C_xx8(ׄ ] OK8p̨-ms ?^E2XjX C8|9d%[4t,bvZ| kj ܖ<#:9 n2Ù^Lcê- LS{.T5?& 6&U4esQNZ8zgJYL.-!l`P>xϱf.3M \|h9Y8'ҹI~LQ꒺C%/iY eѫ_sl/ڎ,6҅0y%U,3wK`F[8?aw wnmCȦ֫D|Vkm,7޶ LjnDuy2esB7g6w"'\bI> -stream -\p% VHU:ϛV$:Sb`X8#oF0X/8-Y{uv!k4 ts3P3j\B՛oZLjgK +:ݾo!P$HU&N@6Ɓv,I<ځRwXP;T،fwة%2mqGIPVsۉX\$wQ - -pVe 5nʗ;y#ޅ` )uJʼzo_N=s2Md 2I4z߬,jQ 'b:0c Nɐ^TՓ^LJ"wTʼjp cu`D:9*+HR+qz/=%_]0 ͯ @\+Q @C_3]eccwNEsVϤ& -芸1KO pD~Xd TuxC'UѾ"0Q@02܍ސjRi\} -8˷5;HLna9]K \g54Uz!=M@*2 h$gvitUn{i܅~iBd8ɇ?zP -I.xz2n[sCՈQD⾑=EthCvkhx^1$|.?3;X>t:S6@&;WT -^oD[7IQɍCf6uJj#='lmSAȨDX2&f,v8>BKz?RmJDQI[ -~ruF@wiMh js8p-_L _<>n&f$d9 XT\K咛 mhϒ nwPc' +9Jo|X3DLx&A#nkQ[tSV/9Ɩ|d_+'E3ƌ.ūC0y|_M}{@EkvnpBsV΍ElKwynIeэˁa퇍fc N:$S>x&;3s,Gzsٿ"牉 !n04̬‚2({K\T/v 3P̬ޭ ƈWtTIr\g J:A!{endstream -endobj -4497 0 obj -<< /Filter /FlateDecode /Length 2096 >> -stream -lW7ϲkf]0zϨ=$\Ex}+`wу۬1JzY;X˜b֘s1zfOɢ!-\QX1=j}rp$Bo mSDM CpR]$Xbʾyi:RY3+LIܕ˃x~WAk;K+j0Į(QH ^ƦRS|)q? nG~,?O37 ~~/+,=8%p$"/]/Q̦乂iB'S>1;63pM H(D\cM|evTCg/ eďނ' 1RJH#{FKnYZ^V UP59"k`icmIN1͛C]ȟf`S=E)!\j@j'c`JX&p` "o9QSydd~b}涭H0)hÚUU}Gn aoK=Nv -[Eӽr --FO;T`\[EIai6Î!0~S^o* Mx^W5i`(vQF&b&v?;im$:ڣ,-ED\ZC)0oVS;(Ek@GPt -Cܺ$`呚ۢ}|}w'9&lhsuXpR4iӊ[#_k`EW$pe.NIk庽?>^e?{Wi lвt֨`zZkklyiv甿tlyY|&]:,f=O*[#܌բKaTf"] -& eWrsRuZHLe&Hi󛟶Hp%c"?+a%mQ `oIr~" M@ \UD'$ZUf"MxY[i3 -l7z -z?0aHy;g/R)h|2SHP]2K[~y[z%MSю*Ş`Ikua@v:%2si#*;YU5V%*kg[PqQpwR1El>- S8}͆rf[l^‰J kαɌݸ,ܴ>pY_*#$ȋc|kL%l["j3VmcT TE7Z8 Xe":BXK P8f( 9`^Yndô)h$8:}* ~.[`: hWSgA2~KTx-s|\AوCGӰ_4+pYo޾n)E8N\wTAo$k6Ƞ{R Qy2(Eմu`OpHd _a}o'z?W*)eTD5=*Uh.hgˀӥZDph~IDjNé)㖖k(f8zysn5FpbںY)lӑ4gDBٷ0]QR.k-& 5ݹ(_b .E5ˇ,d_3`e [.+S#fD5b9S܍Xd}Q $x3 yb3nC JE:0i53_ -?{%RV) -F ް`d$efOtrkfdvƯʟaKCP#l^<ᎀQ8p1oLSkx_eMrg,T{(ZCҏJ>.ћ*ZLs3ppP qj:ɎnS2ڂ܈Yޞx}~b[j.@DD-'~pPy@0~uakZh=sЉFjP8h֌c4}*YDO8$_VO`!(endstream -endobj -4498 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -+U -G!f ˜5m8^mYVQ_'߮r+q(?ɲcc_JˇĂCpn3~ِ4T -Sq4q=;!% 2u֩(|2' ziXX##(z"ӆ~D2 }Q)߄#U134^5rn!d0Y,#HۼV2<]==s_!2oA^[[>+ʅȎ)5#~ -h-w!VdlāQz̮Zw ^hex˳ͳ }} Iۤ-y'ځoaC&:Z,5umr nnR+CvR4Ҍy b',N_LBi{uCܚ]eT uD "q$r@/+@=Q$+$jBTj$h "HFkhGw/Eau/\F:6-WhNQvY}ΰ$&"4s]xCsC+R9҃I@̰YӹEE+Sg{MQk "=+>` /z:[4d7LŃ_|L,0gm )i/7K|enٗy,-.{Y%✗tpJ=\q%vI2hs=ά8adh;7;tS!&XcC[8e"⤵.ol*Ey(q_ሃ:N 8 \!2;nHR4-14-ΤGl*cѻa!}Z-y'E dTrN(])}R+ӓ_8 G$P]X 3bƐd ܅:ɻϻߊ~-WwGj -;O_Pz vGF^1-Gh^GQ'e iI+8}Qn=b~fM%ЬtqR!٘)[oT4m-EAFWheذhh-\H}\D1"8`3'PE;Tˤ4B<x-Ut_u( -lYlSɒڹI}[=Yu{VT4 bQLLɳ+ywNWnzĬ4Cq WAzYQ AL8P1cRV܉WW &f'G)QV|f<pS/ 5doF9EN3e?h"ƕ!M;}Q)Q0tKendstream -endobj -4499 0 obj -<< /Filter /FlateDecode /Length 2016 >> -stream -*MuWMuZ]!{fU?Cfa=_3M*S? 1{m(AVR^H15cbXFM4`:n?nh^z8 -;Gi<[wY9w0OPN S AF鹶2W F75A, 7& !ġ!/[t-3s*h$%\lNN`faVV8$ ء= -c8U/*Y(F\4Ql1á LZrI{^pesϫ\1E7環m"|{=`vC }Q'X}+x O{tZL]/ TR~b36%wچҨ%ܢ겆Y:Seq{,}4A$SH^meal(mg5T>}^w:!7y )b,%6'4iԎq7^ǜEOT?nu{"@q$/]tl\<> @B)YپeɃq,GU J$7QH}`+Y3%\?J)T7ȷ {S;hfX4o .qmYьOnQk6p4<]팭@gl(?ʟhut3Kdбh/6rѻ'}BqxursMo<H긩OWOEUOV>Ǹgx/6(xBN+R`ST=q~{>]+Ϥ}gK -)kځC-j]@58mhͼ=́K`o7G HSmEq:D9p,,H_w7IO 9 /5>xQ3PCsEn-yiY"7c<ҍO(d -ɗ)DgI ~!K"`,&'w3\/nT~7©p ͔l~սbMD_VX5tiB@yfg;]\5zxO=f1/4; -qLG} -&Klu)n}nAn"YD:.㒦~s:8v>QOޱ`!«* dxYZZj/wn2K -yz%2 V,Ve4w x . L'+ P HE -ʗR,jGLԣ3Y| ɨzC9Z2pHdo A -y6_pILʙmC=RAjԙr7w"=|Jc|)K՛>[ DZv8:4uejAHWzG4FCL-:!ͫ$EKbUOd0PK{ ~&0{( }iȞ.^ zdĩ@bpپw GѼNXfc -ؙN4ͼjml<V2na <-'7сI:Irgb#F)0IP?I~EGDR<L I_%}4Sk@͇p,<} 'm zZFr2Nq,?0ɪgl"AtH1]jR - eZ|%XPF 9 ߨigCh6tL'*ҞE/ae0 \K⬠ۻdBendstream -endobj -4500 0 obj -<< /Filter /FlateDecode /Length 2096 >> -stream -%)%]ɷob<(FKՅGUB+Gpݧ*m  4nt7o6C^9Eq%?U\篭@(ܔvc~#7Xe,P6?ɗ}bJŇbnuTuu~U#u${ء@rDc-U:}OwcHLP:`a|B bT/[cE,o4JU﹨xL%(ymYτ='ᣐQLt3:2Ƿs:9@PI@Ӵ^V/8wqH\_nޠGϥ|r^LUe5DdManp尢cegpD5P^z Ъ'WK e>~aYYrϒ+U_4nJ"ίz^K/Dk8S.Ehig1y Ai`&^fҝ#^+:ӓe- -E }uM!tE -Un%+!"fZ[#Rd[Z]/j\k{ӭV`^_E~8 ʡZNDCf^'8I66yR)z0Ase{E%v -wrޕZO6v?,ZŜFW5|Qnb1hYd+oFf&xTPO@7PvآOJ-]dJzVC#B>~Txq$5 wyTU.fEߦgd*G Pk{:=. lM?ٯ#+C6dBAv|_%fsYFjk87.sKpS߈iW '`?["Yyb+LQ'R#/d(Q 1QSzXGԥJvǰYIL MFl()#򖗛`hKQG'./ӔFڵ6`/%GvΤI 9S"x~ - Jw/s?Ff?Z 4઩ftmGօrҲg1(4Ɛq+Vr˴ګ2Hv jG|g4 ?m|Ye.e}BbjN_7ʗ&bWMiN85sk_1l+ߺ~4LU!|*e)D0/&=he>^wd%nSnsW ww0H$A'z;#Z& Wl3+ T0=6Ͼ{:;3'FsKQ#uvK b]W![o('~WL@G7k36ƇXC̱)rOrs%R=0mA;0OHvo]ͬ -]<m]H"aT`,[-lVG1+Rz' <%E.~8QUF#O*gg޸lqvK"Иs:6pOcpZWݐL6~:qwe|&g7Pizd@32Y(|q[UeZ4ymKGf$&{b4vd/ *5{"rrArи54C3ʺn*h!.E(,gQy+TPXY6*6p.[KN:#!th'.";k:$YM \*CP<8]+;:B?ևp%hsJ6 'r(j`KJF¨i&ўl>>&h@'7Rd_{"7+smR鲪-5eXEf$$qCd}:&m643bsIoo+z0w"s,lcSdgٶQGB%"67c率:'V4sh*5wl+endstream -endobj -4501 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -a~*G3l6z&L2jk>5rNiPdyltmF"wJ[PjCjlA t+ʭ1~?Sj <OǶݨZu§@Efh_G -CƒD6.=T?Śwh5ݼjx辺 Ⱥ\ȩ@L@ N 57aWЦnjupEXeh" -`Z( X')CJ=b]3ľ"ùmr !'?xܬ$݆?KĽ8ZCyϨDBNo&w x# -6D˔hl1SAg&رҩDMY&Vs>dG;^m -$R"vK& -Q}`.:aoX՘J cG Wǻa(yUx ˪۶B x}8&]/(JߑS?j-&5VkbGgZI+8 9RI7f-,˘-kP #"] -w԰? jpt(R;b_wZt8\/ah4L9m50`#&?Y8%OTvåQ, cMJ NG -5l jϩ?@փ(G|K6>ԝt{}NT'b]rZrڷZ}o74Np/ -l/[1$ ا< Wv{\]UЦGCqL0hsXnZ.0nq}PAC;^qKe iZa<IG7*w1"wjH?G [uLREH@Bi!cLлj<}X)$of<$D=CbASlM%&~}| 0\04'- &hק;3ĨZ+JS0mmhE J$>kp۽_Y=EeK[{Kq5Ӌ`ZIrHy<R9oG\VBq//qu,<-A=MBMdx>knk曂 \nMm~_^eACx6TMIq u1z/CJeN 4Tdf H*أrYNFI@5&>C,9&Q.|Ш'^nQRsHDsyH:! pǖN4}ah("XW34u[ v1ɤيM'ԉiu)sZy[`Ƈ -U!Ǧ<ܛc$(zUBzL19 AU8endstream -endobj -4502 0 obj -<< /Filter /FlateDecode /Length 1568 >> -stream - -Lx3}ol2n@т 0t{ˈ;}o; TOR4[=WJ#7j#򌶎)vfOU*!k]xd=7a䇌ٰz(pؚ_b|NJ[\LVf ǀa[wEQ Qs8k}ek=<:e/Kka$g,8t>6Fz0R='ٍYDlG;bą|% UJr7Ԃ hg - ia4 #W [Pr\x;Ѫu7;#}y@&?[/64!7K I\p}V&.HzeNNWm[?V$C/]]bQ@1c;Cvkbb葏\I}mˮF' !cTuf :SN.ងZ0cp > -stream -xWڙ#^ |0/gnU8,'.Ī͜r;1MzHJ5k$״)<WC0j@%O&)V @ia4gy]4meS@ -|l!ۢ͠Rgբ,A5]AGਬan ` X2d.yԉV9,`ز(::l׺+^>P˲< Bo76RGqL6=u|;9.rrEK -9ō:V ϱ҄5aYʯ \t,4W:-e>*z˼;]8ULUG."ocVmOC[5)PgW Vd&-GqvjejjHjۿOj8GuVÓIya/ cq +7`wȴߨ*u4 6鐝_*I}' NV4P,e KooT"ߖmY+k P.ĜpcoXNxS1/Mi$ (rb\7n { dN~9t"r - ZȾ?'~w?Ţqu% ;՞ݐ;ON.Y=M,F5t,>'3*d*IZ%eucG*%͢>4,TƷ{h?U[4nbteٶGY$=H*~:Ur8z_wg!t5yu.J.edH 3#mQ@,<X^Ķ/$Y_kƱW$FF雷oOw}=X0b]G>WwU~9ok^7Taq ^;u]Oa6ѓ?^lknD̦#vL/trnp[߱5egc|Fj'@KZ?VU> -}z͝ ( -!T (H;6rz(߰8w@5= q%;w>L~37VQY'E+h=e߷ J+*.sGloS&L.qȡ] .{KwUҝ}5}C-~^vzfxܢ\oD@QdU}Dp>B -q\+Ig\NId-ߡ{̜ \.j7\HC &'.m83:w86Qkp[vt^c8a~}m~SJE3RӳCL/pCbJo$gsea>%%1Az'Ó4Q,0"O{}\Ik#pH:Sm3yJl¬ FevbȓHD#]ta6J' -< BSO;/>jFLxcw~s<ٍ!U,g$Xdž&9i3U.]UgXͱ>4}롑vqamKLs/)ԑ> lru+O&#Dӽ׍K0m $cT/jp -endstream -endobj -4504 0 obj -<< /Filter /FlateDecode /Length 2064 >> -stream -[L ǎ? gL@V7M9O$a淤/57&6j0YQ|RO^=>V)˸ȅesfЋQ#kgcf]J-k5}X}Ɵ -'(TAW8̔T?ľD* J .WA8> Yk%a/2IEq~"{^ ifUhG=@ۺtϋHܫ kdxA, Ln̟䣰TʛPB Vǖf0Uc{qO\1O; шޓIJR)tWu1Vq\_/6wrnviBW]+J37{r69Y9,˻ku=Fn wHT@߮bx?fn ܫ[ifNk=>Hn0vS92vgA} , - -@W;sOaX`%>F -T@H[qhQ [sPAj<Ȋq,6-k$&ꤌl_3vڊBA)"9W5b -~yJ\]em, `C;j~$j/w AQi,&+^3!.H}&afbB5x][ 9qB`OtJCLMPo.⊠kh$so!;&4tkp Hp\OmdmQE֐ mrb_?EH̑Z\ެ[/3Z&:PuHvcLn^@4BvA -ѾSSޅڃ1%t1fTb>AψS!G%@ -usC2V %4_ @G:}h愼*`AGHzj o4{A+)#wUC9rM~`e -w) n/cD#DٰJ)+!#7 -2DCDw?1Ch<h9^Wt!@Gj"uhB#| 4P]3 ^Y0Ql<.wF=j(b|bȗͻf92)t1&؜A `Dc ?<=Cj;H=\vU@5ۑAm87{.Sh72biзxGrMu$pxr -pXw'3Ӌ܃)`Pbi%a(p9F8}`s4v. -IM잔!林^ࠁFPRZ)pRd7j{W/5{JT$86qk~'etPp\X$>ek7.C;H7Ⱥ%%ԟ*٣YwH5;\@j > B$w3SE+xksrC -0ЬdXt1aȶQHpsȣkIǏG R;-zLpgy5x[Y$NA`7jVѲ_AB!n' -dH?<endstream -endobj -4505 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream -T.*xr~ Ky7--ٟ AP#cC -v:tWӥs+i=&H ǔ7-qOgc/f ÚɬC,ET k/M-t6jpG4os2XOu97dyF$ G ~TmqA~s 8 J <˴ *XơN{l עf }qgi}kZ~Q 녣%9?k&PvItIV̄EϒtNY{qxӓ`-xF- IZD^pܘ(O0>xoY^`PtًS+Zr>"{p } 6kp| ($Zyq6Uөq7M5Yw-eB^XFl2]K%hHyنXSFeYSjT`P{2=m0wkVo~Ş vKy [=٬Vo jDߍ.fEHG=R"+`x/{{jDk]vh] M -@Âb<5< -cSMֶ$!bpNKaJ/1{[В,޷~zD=ذ@J+fk9IV=y x CG^WtJB=rc63y"R@2V LkDć.a? mQzxLV2sn,FS˒N!Vg%sgu\P0skj1sa{ 2|'ˎY ˙s^S_㭩ĸë Z <4HRTHvh3q -@涝X&8!ގ"t!^>AE#" aPj@3x4cx2`Bllh|yh7Yg2(ܛ ZdFcNB_&7@ܹ1ƝʾDG$*`TPuHe; WPx3 ["1endstream -endobj -4506 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -/f[ bQ%tt!uG6sulSpt oTUy -ր?xiQ,n1M}SU"WʓFc_%߁.2gHp[el 'A^4@^a`NZG9E_rQ)ʅr8~)@9^281(xB]|Fi A؊۟QG'Jv+iCv\aEf`DA[)hCNY!|̚Ta:m_Mie (FSl8ey.`2ܳ"ޮi^v V,TuERlA]5d|ϼnnI?k/gȿ7h-o^xb׋/1y+zA&_rV#at yA%K>,po0;'!\#fkJ$e=DexH<9A;)-A{:r{4Ò&6#D8i¨xmLYP6Y?D#7j6B(Alo:2T%oʳE;t{]d"R#5yev/l"L'֩xxh4z$GEazEjhzt+1lÄ`",Θ nՆ8~yo)yF *d5 o G6qE CNMnLڟ1 HIԜC$!|+dADɂE7M K;i'`ELDw9lPCn1'HRBw]F4J"J tNw&/I!|-;D TP:wSHLyR$QJpUU)5; S+\ƬTx.4O#ik"]uUϣ Wh0PSS;x#1| \dWX@?XJn-(< ȸւ yYS˻':\"Z_NCsjZ -!8V |d|e],H}^E !NMyCmi^Tx/uW-`XoA2 w|T(ś̄I9iW&ܱb둃xZ (Ss=qNij9|:QI.K8 -BR 9^heXgYueQ1$HQz5]ʷN__$C"cɆU]k6)PmSM"T_mї^5,+[=: -zgQP|gI;# K_NtȠ@WjbH]:RZ&fCX9Bq[>&>OW{AK8:ᔎ",E>?l'"ob> -stream -i+Έ|wv)$-=eĦ 3RSv*3#4K:P=*#UǕNiѰr%aIdCh 2n5IeAj<=3[1@u{zс3ʌ|[]J"6/}'4|,,{)Wש|d ǑI~K`4xMܩ׽-,X)Fs~4~FgOE]Q+]{+:آcsCP a&[EϗZox:qڷG"*m -U\d^\mU8EUE^fjC&e[Bʆ!QJ2}U@@u9هz\A3un! Q&.]H.e^X =az[malU| 6qc)hkYWm ^ =dE?m=87}3wm> -}IUU0A80LcW 3OXn^]COCc63Vtp wpذձQrX\aTc|܌m eǧ^ *#tzfȞ7!RB'{0Klbwߤ4":DCFt!?'^BbƎdP3oޏP@bAn[<(N28ĈWQH${=F?@rȻ4r -b>&]Iug(پS -@HZM9mhy)"ǎ \' eډ;[-K*6iU|Bo}fNlo͂E\ƟKk[?X,ReT\ϵ=v/a-vX8gݶ1^W_3珨Pk$|>b6ټ@xh< U3u&+E"& Bh -x^*x<)UX=)er\͕3r6ͷ8( <t 3BBڅ3T0J6$27U/%vKbve]v#U 6ufAVx2層eoCGCDʲWcjm<5n]&rzwzBD1b2-h#I_"Byk}r#Pp$+v>sw'>KJQ7[5с(/h 75dVE%jM2=ߠV)с!kY&T8 gGqTp^r -xY7 vi>-ƔUb ,Q2#os.CΩ4M, Irr r1;oـe[]ʯh+n}^쌧ѕC~ zM/W֦Hs+8A ۺ|{mx.&qhO?fIKe/9Hօ ;PYxPmݑ-nQÒD rKoM~s5wZ,:r(\.TX2aG&;vݟrlFv;^`Mq`b> bG~_oc՝?ҕuɀ=hb -ەi]zJ_:ӭiU9V%Xxϼ4$VãeؘoD ?K3^Cװ5Lm\w_~Kx7|Zg0ܮ4u=P@ΩO=Hs/E ΐ< H3͸H_3ע~HF$QgdH <4; pPb_xK,%}P Hq(ܓ Y8Vl}cc;K?Iw^ "F.b+ |UFG6k ڢ3@&#tISНendstream -endobj -4508 0 obj -<< /Filter /FlateDecode /Length 2160 >> -stream -X'TTD'L.A8͐Ajf+kʝg8v MgmkʈDͶi׶PZ֜dKPk;H 7}ayށ`^ ޟvCJz'Il܃j`=Ƿ?n& ( $b ه<n׸qτ@r_}uEϱ[ 1X'[򁟤~פ9Z `fvQ-Bfz_+vE>V=oA-J)Ӥh=OWjHhՇq҈5QQi^̑ >x@Pw.fnx5M,f!xR?(<>N,ujO1s0Y֫ -/c}"ƒJkUflHo}GL".@z5x4Pʖ8Cٮ#"U (߯#Q)bXŏDv"8zV59)`E|'R!AJ6P ݎSDaU94dtvdME1xXh RR [mi -sTkZfHEo"w -P9ͲгR wʀѰQZ|Oe~{(-0܉_i*\L9v51vL~5Ia{DaXY^rbGE1ot|B419޻[{M 9&oo4jbO<^ɸږhVە” i:Ak7 zeO wyLjDoFmu(0{zM -Rc; jLbu544YAj1VHI*Gf#WOEנUs2SjgnrZJUdH탠Fc~so¢%9AN4yP[jg-jF(_V?ܔˣ:7q7NߞJ^o]1 1 -<&\mZQ1SSF*q2JNס֒ےۡvja_ [( I - rտ*[mI6p.M|]sYY_ż.z 2K/d+OX!+Fi,G>%r. {YJ= )jm m~ -49A GΌ,.Jnۡvo͉Bn+kvnnķ*>eۍ@I"`+ri9'm,ǞՔb~$q"C'ҏ~(bHUGx1e[уaXؿ`Rxy=ـjbC1es^<36nU@:u;35- ؎)Dtؗ/5}6BƩs "`8f _9C^`pݺ˅<a4/"ZiIl(0JR) -:G< `s[2uQ7~ Lf iO<]|t'L3*zjwYǠD܎0q?cy+ڮvFq>sglW^,6 AȓƜ3 -iK)bS-,.#=bş*1臺J [5i\1Ζ$\Pɋendstream -endobj -4509 0 obj -<< /Filter /FlateDecode /Length 1696 >> -stream -]p n-VOANC/p7^EnmP, F(נf%x+όk0 j·q-pʸ륙6-Q^#v&QolH#cęb}@i<X"_7nIy8)g#}*֤'d1~ԛ8g(<[unSsțz=s1#.L:_Jg k).2rNTϝO5͉nTCliFhv"VFS~[7"ujf|t Y @Ht[gW._;F2Ǿ xA|c+:aJcJ&9x{d*ǥ! [G=RmJKD\VAىXBP:]iJ* c@Ik-HA1+yXQy L߼e*<&%iwX~2o&R*A@ΆԌ}X W5s 0NgY:3MF۴PZO`S4b:EPp95ÜTU&S }ӆZX)zsd9gÙ.ѸvFbeC(4Syd9l@" mn0`'j"lp ?Fk[ |Ŗ毀2[[lz4ޮu0 ~,IYYAȯYCvnD3P -_y. Za4:j: B@j-s#9"u\{ː -x(Y_g-:_y"i^%"R -W2z_7+Qiu}󳪴PQl.V Tv7ޝruEt,P/c&3WY%%2="`&TXA]fϡz}zoF}6u|sEvֹr8@V/8kE"6eIu^7j\N !4wk>$RE]= ϧ蜡z dAO;'Uʑg{wW$40=%VZlQ(' +MǍq tCd kHmV{G%";e--m`wF{,ʞ H.|jLi ;u\4B)וDJ\+fF"-&Ѷ\_v1I_Ka@ :kh̰9pyJ5Gp5)MvG9g [Cn TΨsASZDžH"4=7-Z>BJ.{Lc -K<9 -&^O7<:qA> -stream - 2"HTT㙿0tRz:K %R|˟Ҙ -l2v5H։3003XM̗=yeV7? w$[ /XF09WrY@aP-U/z-R}O! cPI&3='C sG}Eɇlk\B<#u`rXD&#G,2@Pϛcer;1Uc7Q!vؤY -1vv^XG0ъI^hX@SH`) B]Y1`,k?]j7Fi/ɐЎIgٜ`Fw| 7㒶RegԱI/B?ljUIuZ52[˲{8'Ox ڢ<xzd(N|SH_iY;àR 4lO{ -Qrr?GC, &.E&mٻ0#]/$3B!D<C}nMȈ`x?6?!ɁY!3}:$a3oGrP*B"Qp)!g¾+5._//D// 8v1lYS;ȇxW\xDIDUb8(]-7=OI(fA`ۦpkIR# %3NM!uJF{HqN=RE%lOy_6 w #S8kUPZ.\7<7s,B]ANÌ;&p@! a<+-z!߇`]T(?N(Jo`)} U?ͺã[c Aݛ"xPEPh6xHoEr7o7mц{ g3 հe =Pɂ{| $:"쏭yͤ_d]3(,( f]C!n˟09q2> afAK -G/;KMjv,O٥@q;\}wF=R2mn/U(>:v5 Dx -R\WAE2dMMuo,*,h7ZH/g<:g^Y?nBV>q8醽IbUl޿a z/GH%fA,F#s_eڧi8ذf. :Y Nz=֖Jj68Geb.k iccϲ| M ׁ( WJyBI[i &endstream -endobj -4511 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -De#&o+Cj>fgk$~$:bN\0(S/;nj[{#mx3lض->^~ۗ"bW635 )yZxᗼb6*a:bK& ]?3D ɕZ*T-p~= -.Rҥa[\t#Ž>UJ(u :^Z2#- W||ONR;2F0_c$&%_i8?b].fE4:4L6dk r-\Q0[ĦUypM;꬝1ornV=݂gL.,a8P7yƔg\u9wA ɴ~Fб,8+YCZfޑ5uqGܯ -H9f8re?WEm׏wvchWfh9Av%OChDbg ȅ+>2b8㫂 B@_g{t{zmgGxޟ;LDU%?c%TD4P(q-ڰ Bn63UCU.qB^DLZLR|4pGL l\/[,%͙52=AF+ y4 zoj7wR{\TŘA9Rtr4S+^S,9`4l_\-%!l"BsB ?SȲ -g2|:-v G(PdjƶbS1w1FVf#nwƸ9߅$_8  n3Uu*ۭbuC$,*͹ PDؓaRgS],I$V &muD0үxe,jn_S/,'2ab*Pq9~~. - -e՝45 =|RJ@?ăRĹKFii/^xuREg{~f''r@ }?U[)m}H!>$R/5+-Yewe1OĠxWK%(ȷ*J> -stream -I) 8vO QpZ(:T8 ҵg*A6Q"͓L-'!uǸzjUsu$А*|.-Ml61^r *ݩsPIL-3mqu˽k`ݨ (MyOxglS^+S$Lw! }j$C_e~LĻ[՛8@b=n3Li=c_`uY8g saeD1I:a:0pƜTPig[:gmg"[` kQxO DwD"7LI/!vW5E [Jщbe -?_Ep X7u>&Dc6 51̈@R(& G"hu L꠨'ng)V-/'8jbqHZd)8Y vnF)7< wcrFNS+%Tӵ# >U /idM.PFW"mjs]E␾d8&?X+~sߝ1p}k\%d.αbqmd׊͝&8N"oiBrw6WA.<ð=T_ -5!leBs$UFߥ~'|A%="Q5^Q' 3fE"y ;x_e$(1[89]5ǻ#0K^So+ !׎24" y|s[ Ƅژ̗==giaa#)#IN߉tuKf(U{轛 cG^BMӏSK}l[g+ 7 39u]g*]02V;%(`SE`!Ҳ޸zRbsh=X;',7|$I09R*$9yX}nHݬf-XGB7MM. 4r~4~ #0ڑq/43/FtDfbFSTijXيn0-Sx%ߝw]G4@GųeE6q|LE"M{hY.=?vP=v Q>NHb}8Ӑq+u^hi~ δY`bR-9Rob~r+V BPě:ECitߣn:*U5~O7GfEp(4$UTe5Hs_ /uTϤ^X?v EeWOvM ?O< t/&كqוC"a uXi۰Rm!3P@r=?9fwԿ^!K#iu`jU )(4H(x^L/]iF_T,՜9{cvk<ϱGIUcSuuoc勑UTaq=#K9zgp,mI*cQ +jh0v 7u;=؃pc+p-uXƉz=^Ƃo*{}Q9Z0^ _2d63üz1qž O5)W/pbfo@Y=Òu!j<$][yYSuG7#vpg,~"f@:fC= 4kJ덛!@khTmHy vў!pbռnCZ8[Oh'ߖJKsYFBQI@i__@G2DyVDžYhC"Ԁl-zYR#{AYZ>̪,. s)L}2ˢ]FPYbpAWusL6)N cU%E?J]^}49~j -ߪendstream -endobj -4513 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -n*AD$w2n i!ݬ>J`\a; 5zGDTlZ^U•yb7( 9QVM'OШ&s-GV.?ś;>%{< V-\ҲgY8VrLM=Ynie\_8@3nߺ*Ä́ nr1&-q"RxP34XuwlbocTnW|/Mu1R}GcFMEaK75o8.ųEۋ >vWTqc˪=,﷔ֶV SË'?V&+oA7ͤs+}z4c -Z{2]?Y7rNPe?P'Ý4u)3 ^̄ځY(m}Pq1V<dk?SZuA.K8+Яy 9ŝ~W2ͮ-J^. -sH,L4Qr:ُ)U uRZd-]9S :pȣT(t 2ݍJ:9[&<ގqw_%6v -?d~Iӑ MΜRǖ,_Eimw[CZY5 ,'mz13Rݳr$鞏ZvBTt YPcB|ي՝bpgVQjGW>M7GAjG0+W\[6bKly*Z(FF+m3XB]^j>.쥬rla=@KUID C8bg&ys_ gHdqSV*ڦv9Tl+SH_CɖQqh ư=Ⱥ&`/6-$X%w-OaeŵܗГ\C,{a%բjQ O OY 9&ӅL%mD R}XsBB4B JRI%WJSI nXvPo;w@lD~N<ҵ7:m&4ޔ{B*|763y@m*6ѭ!F Agh,Qy "H61> G$kqz:>ŬyM38R`U^W-s՝%k@K_*U>Aj=G6d?{"C٢2AN.¤A˙yz(|õ֖,оF|g-e4b[=߽A_c36&,8 )dUAwGIb b4{MCLuNs;ؓaUd}ƴ8ivV7Z44Tx1D-= ׾&r  l!dYKCf(äN(E;+$֒Xghl42~QH< I"_% /:bE'=ՠbe=:qN.Wz;1PrAƎyTŒ#Fk{4a tJ=mh=q0lʪ%2[ *y> -stream -xjAfTGi*1<țz0MԪTދjxa;r,P"pL?Q.@.MM.P7<~kZTQwS}mdcv*H7emCAU=lБ'|~=00O. huIsJ\O\,|I-8ϔ?ڣ(<|fۀ8)*UiF"y<ydG"?^?q P68T4a^ʡRaH-u(0Wkؙ5;͹fR]~fi9&qWO{%1`}Gtg<&e\*Hà] N9rm*dqKy,y> pRakYe\ߖ7A)n>lŭ?hMx񞴻|Ź}Z D-#Ga hmL ܔ{3җa#?k#\DՓWHǨj5Fς'Jg:ȡ #exġz> b=*,w@^h=xiRƷt\2cmYc/aj/lGkkIGI7w*kG÷vNfGvIRXHƀmVmy:4\L'xWذi˾HeikF~6",_54ҵ I\M%֬fgiZƁj -6J7U"|;`7OGli L%Ob_ P` CNHE6%@B". dF8j1m[)YN3ܠdg2Pi 싼S -촅~҉u*&ofAָtQyc -iD9=SEӁےaIaG.`4ֳU5{U h~V'N҈ Ui5-ĩ:JM?+/IV -W_Xx9%bxH.AkoZB\`NPGzD_pU'MF)&VI4~)(ڻ]͂O@6^'ϡKSecQfJX+hd@Ϭ$}XA0 **sV~ 5 'δZDݼfOM:dB 7SW^fש !lCkN|k9d k\Hg6~phkEs;y0RKk6gvct7% -r4?~8#4 -bWy(;7%D&OXKZ(rH >Α_{A7u':Oh7R_UU's=huTKWendstream -endobj -4515 0 obj -<< /Filter /FlateDecode /Length 1808 >> -stream - dq|Q5%4@'qt`DY\ rv&X0rm\Qg7m4V/Xy) ~&nnӋ>73|Jrͥb /+0=Y?3G߈b9}\C58&:@]=U!%T% w /ɻմH-mGD{Һ&KƸ،܀M -`=^[ξaKgϒ -h #FHJD Vb"oN_KhyͲj?Fh  -`whRW>{ǹ]ZGс!MZBϚucL+Dsķd"׉'[8{|>~hÎT[eB_N4,…C䳠jYsKmh,\Ga2M֦vw qk3@( m_% -Z`ryȦB#coUz *LIsȢuI T-ήe2qn3 rvVj -$"kg eiVmʧy2,P eMpY;J-y_Zyys>Jd(l[v -3*%'JA9p#`JܹH>tRoRzsWWaE4췄3Aٶ+~ HHBg㫿 -M鐪2Wl``@K?Y -G#pbSׇfd-#e"ZτCV܋ҝ7ǟ-W2^[G D8X`3; -M ^gj!|&*u=SaU-$ [+[|9+pg]cLkaeA]2U/FR#84,ƍզvTW̉b͈ aw&/ Z[qg8rMta 7ʏs_ ;GDaփGB' `P @xK6dUt4Lwڝ<|DqKw咽ƙM>`rAjmR'}֒Q6#RVLw4Igx(>du_6H:_GHf07GM.6'ϋ݉Nt^i1SOڪ|u0ʲ^@*hR&&Rٞ#LS"?9"~^li0Q@#l뢞g$P??z 6hs#}i;nr?j"/yYfU xc5?Aendstream -endobj -4516 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -:VŲ, -jZ6Leߴ=49 #)nXCF7f$eE#m>RE{; #&L0JQIAr9nZ  |S] PwE'{0 ],B֩Jqn֬vRf(=g/!EBK!v k_bz,F]ۤV =P}.B9 -5Pd$7"~DCAunE4UT7HR/5} dwp"A++jVptP^#?D)A<-b7t6z35se:rL=uJSdJ,LMi7 ORΧ櫊6fu0V깐qFtLԔ9z팫tD*AX* %fbq=kVٮKƊwTRiT``7yx (i)N$[FMMOB:q3œ5ѩM -Ņm>H-wC9-PPӗ}A x_ˤ-ʕq. [7ݑK 6x*dӟ]SU(p N͚qO6]4mEyRgdbYR^7kRQ/VA ;X/^bhYnj*bY'j)vCR+ܦ -l#eoql%O#F8s{ME: ArvhJ缳őÏ㇑A8\~U?]ġ/Ev_&-X :$0^|S3q#JS?A:RХ}Icw {? n)ŵ2ilcڡꞴb#0K5Hh@#DHV<h\VZ4b8 ߎ$+_;czzvm$vpQMy.wZ5\S+DĜ*c;8R"7ת ~-0LXx+NuTonS~_-˴? ӢG8y-Y)[}Id)&wخ! |M-"37ZŰubfkZrz.a6щe7D@@rrj@>UDu &W}zڷ.dm?5gV (^/g\*{:E6(Zhq336"NAp L|4?o ^xMrvo}!:V H%KKϫf< z~ ;pwbȀBu̠vp򵄠O>+@UE^Cɍ_Dtv]$}/+me<R*<-,8qJ^zhF #ET*KPQS!BV7k5o#>>f>:Zx_ZnT2]I-BQbN^5sMZc> %#Bke`V[*[K p2N(ϓL5S@FP'GrѫTjebN19n-jQ'xD_A՘GNfCJco䩓`J[n)|Z3]ֲՍ[039婺Ҩ'ۥ31 IѸ&\5__2f1oKPOB?QܚQ6g1)?Q+zYQ9R|ҍWY2ox":1c @9ɲVwI%rVFNmH;6endstream -endobj -4517 0 obj -<< /Filter /FlateDecode /Length 1504 >> -stream -!a"K &3:=_Cx+"+hخB"E:oZ "B C I\X:G]҃`Pg&xN(&" H]n)}58J,jf+$hu Hol;%j 3>kkqⒸ+RQ2rǖ;EO2d,㫋)Dt')0GAE3}jQ:]ysǶ?vC3aEN 5G@Jב\0+-:C+aMWbTu^+ow4.:r_bZT 2wU c+ 9ݙQrLjI.ɷ0*ø}}osېgḿqVN<ݕ}~T"F05#g<}ۈή64oEc9NO*:!?y{{+{&Mh ݉"J F@?|EZ쬨X1z -٩/ۼDs5bMl0QGD9__op@itsw-<' yg|Qu-XK&b"űT NJ6O>F -"eJ!WU> -stream - ĉQO5NIJ\"d-* ָbjXfUB,LD)Ւ ) o@O9ӒMҠ_9_|,I6H<,Bt| 1s9Վ!esVPOb6V}UPLpJA!GIs҈AQ K -O2$UTU2¼nQ$R'TCpy]\RMv>3t詝W$}duܝ4C}of.Cc!"ЬOk P!@Os!A*ƱVUfy/=7O\Aкl{&fb`XfOFCE]IeM߄Xf7G|@챬<d{z+u 9~ -Xk!6no?[7dWc,+?#XB*M ] - -nsendstream -endobj -4519 0 obj -<< /Filter /FlateDecode /Length 2304 >> -stream -;EH]*I}tV$K}CI]H p>Th>z1Ga\n ;E.t)nEoUR ? -|@m1r*dž[|?v\V_FP5q))a&ScѲ' k+Ȟk]ȷO,_{TO5|LY+9>I/7.ʠ6_饵6[a*fV$sDuK+qe0jgy1 -L~rnQܟor9[궭6V՘ <lvM%[`0?<{aM|X}roHwՙ핔0;/bGOI5J"go.νQt$ŤUtsn{'$Ĝ_[ƀLU(xV>( }5}'7[xd̓1dzքO։9ِ׸;K5>=~\,'h+T_?Sv1k/RMDzCb9flɵL?蛎u;e?EiCK{jXVD1Tö@IuuE`?Zg\rʬV6Y {觷eq`(?j*,-"I7{`;=.XК0*fݾɲߖě27E&!IY3 nYg_@|NM13-:V`: |QΒͅQQ -YGdo ,B%ĕiMN -j݄A,tݑXFk#7xQTtgrIņQQjb(`3Q.U2-k갓!jj ǛZ2xkH>zA NzloK%Itmuq ػGxQt;њV\<"AXP#do|#F/mڪWO*nq͟S -lo@ҢgeUY}ݷ,e#|M թo?5q@ˆ;͂O?n#O0}Od[-W6sJszw3)j+?Vu/]<4L*'^.Z7f;g?YkӨ+Yi1l8NNwWPaQXendstream -endobj -4520 0 obj -<< /Filter /FlateDecode /Length 1728 >> -stream -c0ig˂$3E^ KcTyNC;]ЌE\#MR( OLwf_Pr#nsS_BǢLzwyXx5`AܵU]Շ m3(dǺ \Vf7'( ui\F[8H1#. pʥT8Cb00vuJ):Fq/,w\FiI̵HjyqAt%NY"疏-;w͆׊'Al(n!%< +l'A#61ERN\C3Pbcb,S3 s3M"t:#Ps :S`J;fnaS)1,"H-֓a  0rů"Ē3X'(P~*P6)ة>ISҿQ&1P$iUڕ⌎iD}[nccI-착5v[%Ad8D -P7[_YYr38BFn$ۧZ6cӠy;e% %C}ZMd:rb<'xJqL)}4~mO'v5n҆E-uc0'4dP[쒡' jя_j$&h +[\c&uP3sr#/APP(׿S"R a\Lo(Qh"X}x:#2XMPxjjTuԽ2׎yP#_/@փ|Q}g2P)7<6zzէvmkBʅt?[z8t Vu?S -[#%QUm4wVH{/H> ՐCt!z1N94MbIOjNC%(s l9hJ}Y3YXJ*> ?=Vap9֥vs[>`X4\m##,6ūwmF`Wշ(vcЃ٠@ Uqӿ,[0?'yukSW-5Jڕ> -stream -B^fw2 J~ϟRO[x;I ܮ~]T}1{Voa{%+&>5H=[n.lRg2/^/(mT=c6?3)篁OF=c4jAiWšSDA+@('_:ED# -= pV61Ұ~n&T VƢ[83ǵY%RōfSXh吪йyvTG sD._D,z\CY8Df,u*?"gs\+kY)SW. oHɿVt - |?52!94ybcAs`%/iػGBLtq("0~ ,ol+"锡s3ph&F|yM:>M/?Slז,6OJݹK/+/}aMձ> sܴHȦK,[K-B>%Yq\Þ5*2wH b!pYEt;SΞ2207 - Vi@_0;ߧT -#\Re Ƈ[cᔞQ-GiBip,|9Ak/%=nendstream -endobj -4522 0 obj -<< /Filter /FlateDecode /Length 2336 >> -stream -l az/{}aO?޺+WL62l ?$ʆg?)yL+Þ,oޚhYR߽kןpXNîԲ+FR'BjO^ې'OJ(C|<=},/ bfJkk:}Bs!ӡ]0R2-m#3$o槇g\l P~m=QCGT?9U!ެ$"u\w6 QUȚp:_F0(ߺ$\ 1+iC..qXi/B ][u{{ŧj؃bZVVhZ(ʕB4Rx+p)UGVE!}die7QSMo(/ba7S;Sqv_8W -z -0?ccʏ s8lpuVl*k-1]o&9CV/Ѝ:;!oUSg1Mm 1`œEX|[پ/iVZ2k\v;F -];dk\X+l}OzpO -D+>AU?ƭknآ[UX]îֵKu~ɜA ΰF%䈲o;^+}"[idQբ_cNzISwEf/km⿬i*1] &֐|:ר'? vuMP7{ᛙ6Bf%Ri^*otD/!ievۣe!`hkm=;]ԣ BA0N1ib, = nl7/oPV{4cfH^ =oԜr0y[G!&6uSo0^[kXFBkي=6Ԁ2R]lgus6DB 8No*Z׼S=tQۯiXciķxXs)l̀9 -oYyn.,#enEQx-[L{|)y u;7^:c[O揟/ RKgtB:d&􎃐UȬjҀuI~`a>񢡬,:9T015!R=f]9B U,k&:Shz#FT8:%ՁFGa)Nbc+%jiBotbjYQl?[\E٢-Tf:7jV CfZr|9Y"@-M NY)\ lg絩QX\ 3Q8(۶l"fQ|U6huW3&d#}h *K#0Hfq5F_Z߻Xh@}6@p>CAu3sjLy{( -n%Kא!u9=l<4D0"!I %1FBûRg|[NJsC&Er6kQoc/ ?\q2Y,ӧrj~rGgwÅh3X7D컥H28s7ح+зFki ^`Ž+ xn~i8gw߻*h4خ|CIo3b # d%6:C -^\c[$Nj<O6e60l?P4fFLQ7lWܕjPcn40=hs4 EuxYՁ?:@25M٦o<,F%+1e.^QzT2 /=ϕ 4q<@[kxdS;O;ߠF۟ -k;j=.윌 ţI=7Qc8 29}DF0PFͮ"-+@ -k2>Y;\`x@ЪF_Ҙ+yD$IK˔0]Q̳-wуs}H=i+ rdd0uzu_⎓4:Ι SW2t-hdw&Zendstream -endobj -4523 0 obj -<< /Filter /FlateDecode /Length 1936 >> -stream -?Uz+NzB:h 'v~)?xB2['\-r]={ЋôE/-Z!q#r _l -n;[[0840pL$=cmHA8 U UhDKj\$O*>q3҉gJ$1<׉5Zg,B_W% C4D"K -0 ڔD!X[!{x bC 7!AE?2t;v:7@ymylh2.YŔ0똀 Sy>HQm1icteI UߔPb s4>#@aqC :W6ҥRVclL}hWuXowg˨ABv;㷉s,RrPwZUq/n*%b8:kG .[ןmqȠͣKM[zaNS)3rdA] -L[M Ǎ (jఱ;X TS6T:L,LU|AO\y15@jkdU]Pq&(GRl;ʫ*t:([&<@-nYgNu(V8|ޤHk@|PQZ,dEH^ luޠ=&./r%:>f 7Y/4ܰOHBzc_NYi(so4L8Rnw,褱lUD$B%^>l#pM3|EϬYTόPe:@Ybm4pp-\b0dV83(f^UwWHr14Η ]r퐸EXl'i!}L;}Hb9BFC .'S-2boQR&ԧi=le/jzФ1R妋n Ԝe>~6:A`~S7`Q}a'FH@9b:[=y@KT1 Up\],w̩^N>j |cV ]{u9ˊ胎q6d0 8E".; -g=Aʝs/e5pBvg &;!Zi$Oxe  rC_zk6?ݦmyXumР19ո߄op鸾ut8~:BaΡOв:\]׆"7>.V<4b_Ug9ߟ 'x=7CM !Mx3?ĺ&io6gi`L$M 1ȇCTy6\ .m*GyezU*Mo9'` JRn@;+{fhN -@Xwe!ѡbsz6ASz{C+|* - ɗڤq0 f =K0R-XWP>a#/c^,].GѺڪh,R~ʟkնb:ޒ CX \qR n^kZ*%lD whD_j̓lDOpN b.#o - -ג>-Kp5b\yOAIT]a>yb\[Pw1$EտZTaGV{g"1#o@p /I0YVܸ xЈ klendstream -endobj -4524 0 obj -<< /Filter /FlateDecode /Length 2128 >> -stream -CoTfv30j\$*O9oKj5ٽ|ꊩTvfZE_߅٤(X;æ:6}UK[l`L` vw"x -ajD W['bjX[p]:f\Iq6G#/3Ÿ\ v`Q=bZ놹r);Y6W*R笐϶B$@^A_;+H_zR#s>͜u=ՁCVNm QKٷc]5Q$$,u 4Օǯ(¹"gǂqi=<^'`\(HBxp -K% tyh&G،B]hjd&OOk/%|թ|(9lڄj[VW7u,pw+b(GNk; )M$n#ą#R WvrA9&#y0U͑ )͎Iטo+yX m\I\lM0BE}oR,34'πOqH:ʘy`}")dCqU;F1h:-\)Ck{j u"pyF[9K~Ѕ;pJiNnנ~csj -HdW8g{AS*W5iֽj]dwqS/ʜ_ ;SaXsX>",~gċDMj#=u_ uŠMX@ q)yhfk_{rI%ɦO)קsx!ܝkcR9wXElȯO@.1Rka&ygT^ys\2%6L奰ٹaZH=A`v|'K Ê)~!!.y|P+)e"2Z] $jXwشнAbǭ6rG 53s&e@yD62F6 }*ӄYۏ= @#i--)M>_raP2sहy*لQ\9Rwc뺒mN}7-U*@AEW`neV;eHLqW&Pä{>Vڇ,K9<_L;F5wn>1A -r'aM<~Y >ї -/ {33aGoXMÌKhA -9m_/5㖫I3$d6bp$Pα$#7;"RK.?RB 6n5t -endstream -endobj -4525 0 obj -<< /Filter /FlateDecode /Length 1088 >> -stream -o[oӀO:c68%ЙGM{#A/][lH̀2Hmw;B$yJrx+7p>,ՓQ%ɵ4@=C$<7j4 -Ĕ' og4DQ P$Bꥲ&6Gs4EY? j"}DRxH60\wT"*]$:ƽG ohsկ\[㜶Gh0O^\G}բ(T ~,`/C -\ 6ސSM -hQ_GV,U0{}Di^OyI'jI,K", mE݁-T˶w+.u` N3dkj`qUٛRx8EG$Kj,dGǡvߗOXzR1{ɼTˏ'5O"l%mM%G ds5::U*YQ~10c.SV퀚P{,j3Ĵ3`+or^<~/l(61N(X7Q&@}@fn1H-1-k^Y`2,`譭#b<9'TaDɈW֖HHM Ww45YC8)oAz - Y<YC*3?+il6&um^fh㴥oYO8/)C`X%(0p}q,ɤS?>5J@]oiPR|??1/pTX+iKDM?n'_T9uVDgR\WT,Dw6` T8x'qB;eF/z1rRr҃rf#P$0/;8=3' HeA-藌PֹXe}!0߮ *"9:ڻZ7oC͞6 \`Luc' Ыg`0J`PLi>΁92"3CZF[Pb&jE2p,]ToWyGendstream -endobj -4526 0 obj -<< /Filter /FlateDecode /Length 2192 >> -stream -)Ȋǜ; 7P-5-ﭕ&mkY?xF?&Zmm-0q,ϝnEm @ޛPk"U$|b#cz7mS>?*ؑvƶ3S#6=͟BrH+J*eR9rɴSS,<#0_?G]i ~.ҰߌKjk}5r -Ng5:ڷ>)AJͮV-FRD(Pۊ.^^ԡ *E8kF: XΫ:-L֣1/2˨n>3?*"ruY>8Uo/1eim:s(?a\iE@0 0Wd̪*ӽ%2kϰR_HҸ#8D|8q{!娅Zdmlb#O:}*wȍMeᶲLíB<$-X\b.rWԥ{&>ᝠGʎ)Ly)PqoJ aTkЕKgÏ~aĄ_Xb!Ry0jM}hmy: -tʻgOA_e]ZŃhq L?#m9x\s1VTmNa$!nspZ6=s0 SdH=؁!<ƣA|v" `pyYi f}TםrHekOc+kТʤ:Ă#! ЗΣ̃7X(})2r΀5L,ίz0"!U% tDrxNGP$#˯y ^Уc:YY(+ -P2񉥖g~+AUZT⫨5iCz D?e'6}5͗!njF@Ia]OؤcDe+hŜ?5Wo5ec̦'U.d -#s;G -YKmqe[{SA5d&ё-,?bnXAb }%zI ]չ ~ 1ڒqӿhakwFȇ{(jɸیz呋qk^TNUGti!8[,Q1%C>G:-lĠIþ'x7`>8oT -ڎvݷ_X zK^XCͨf+@k|=9≚0ujYR52SC`Jj.oBNzׇܳIS[PdŶ$~Yo oC&_YDs/2@`_sQGpWC(ܐܵ k # &5q6ࠚJ,2CmPˋA~0y1*}?mAFdG< tE.ԥ8ň4@ ]/SJ?L# 4 -j~{t[kB1$!~zh2sp݁ _Tw&|ٺnsVzN'4(0O_IYr3@s*X9qW,v ^kk<S`q̶p5ku׃" -dP4jn4wckП#8|]X4եL5]iԓ؀nx<^;!-^̐NU\6 -#ߋ!}!. ~pt -%%>B.ZHLNC -NmĪ4q<@*n;eTMUR߿&bZ.!UckҌ$Y.]q52-u.D8p ϒcLg9 -fFꆄcsC<qle}_Wa8X3ei-}ePc HѲ֬ ?z5!?kX #AM#Ry,nV2N1m,Кn7YߠQw}=gSdKxޣ%<ű֫DT 3%o|ȸģendstream -endobj -4527 0 obj -<< /Filter /FlateDecode /Length 2032 >> -stream -RX98[0Z#^̲`O>g$|F1'* ud kO.)b>9սǀBak}s-m[zDωdIN UePtXSNGޝen9:6O&& Mc`n>XW ʞ?7h*<;V(a=Hg>TLi& -{*"UI4)L ؍+_.1*"Pl`ZOg`.Aln)sGw[ f༧h\{z "Cb1(kνPF;Is'薍ss\ mU )/ט;O ㆿƚcmOR>wb50w){AH+ U(grߌn 2f ryND5%PKp!-  -`$Dszf^ Don/[Zj{cw+Hk9FqZ UFk3,dIMBFt Dr]ޤ|]uN6m<,i7Mv@Mm;X>"xq1т+~x* 7ԿN#IO}'Q MROng3ݛ!23CHwr}YM'P\qEɦ=r\l~Is[x1W1#D vx%VDfd-JPKDB}=K\)8 VнBG>i̊%U5+a HEN0B_葀vc0ll/-Bf( -7T3G߽Νv$顠sW `lj)Z3m'(YbـKFONRȹ ?o]N JVmP57Fd׭`=OF+L|B٬":KvkL.[ '*/E["jo - < V<_Pz:n95Y 3Ƀ -AId6 Crbff xC#V0QD43lcB͡ 1wg}ͫ:P[-lcTYϯ4qY3|mbWDK H3y_\ %5Ώna{r̈屩RJQJ!"Vco/td_y@]}M5۶8F5 -N)4+eχi>.R"h)^2* nSA@ K,;04]Ѐ5܏UpN M_Σ%~VKU O^vAZkֈfLgX{endstream -endobj -4528 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -mqU?4xGh2B0q!߅T/*Te4Љ7 ׮X6%56j0'RCwU9"O55!a?`2FhZ%OhӞ0M9wa=47V$ld$o]!hGϯxgI;`ntvĥGL:a+eTpp6#ףD^|)-PzW~b~y S#^)g_P1y.?Q(؃}.ǫsTuE> rPqh-,/^oW;vR^. [thf -BFsiSM '/O52ʤ"Q -fJHd⣤  HuohzSwdy|9ef{6]M+>vs>}Y{lvR5tYED(`2 fv/MU mC~S -և[{*OˬԹUM,fFc<`JΗW8m* cՈG.ZsՄ⴫jy6K峿+JH ?$sc­Y$0VFt ZWᘬ kn[|.-lK֨TI#[ذ2x$SV*<w=4[Lqn~qRvl=a 1ShBr%; Bװb#r=uy$~mT.91HoZbAYfiX -hDm휒vD‹ ^6,_pW%;|Mls/%B˝w7MoȺ6Ym=C|9fQaW<O?M*~\<_CJ%U.jH<~NeWhۏÛwZCB^cOVMsXTI oiL!zd =97M i˩lµ(-*9}>Q (.Q%yֿM+i_zZU^Կ h[L|T>̬H#YiyagG. 86~R -t[~VPBCrB9p@{u }q26/<;Ϡک"zW{ (PF O reʤ0M|4 ~fj5 hQdsnʙڶ8@QeZn]<,buh>&(d/6b4Y >)X6na2tL'2$P94AǬpb*Hѐ"uFwj|^x~c -ꨠn-5,3S%kzZc D5@LSO_2'XS!qP -hJ=<fM5Ϡ ,-y 2dSI -Mɛ-aQ'Eo#`M)PխrEBoa -Cn-XgqxmrWd Ww'%{/"Yl3/!] oKSṚN_/< #iSgzi!dˡT0SԿgSQd:$TvL o{- - m5vx!3?6_p;swCyWU=DǼ -Ȣ+ -4檪YwEt!u%Ќco//k]2"dMQx/MU'aZ%rt3J!oiK<'uendstream -endobj -4529 0 obj -<< /Filter /FlateDecode /Length 1984 >> -stream -j%xndHT~o܌(CNgX}gPS15!cy )t4B8zQ( %x,L`k\y1WXCb7ip+~HxjPZ?F[ NH>lzLE6ԛ ,`š43&VL9Ps -wOdɓj^"J3Q/4@\&X/ bwAsE}1?Fa.Sm&Syy_q app]')cMC!T_xJsں^ E{"n`7WP^)-h1Ը2ԉ>{r£qb48!~.gs~Q -OjX?Gfu6c>^êI%LeLP .|Xýl iMb^0pg椶#Xv93H~$FҬc )/{D̓`\LjmH]{*Sqdjt1~BԴ%X22!_F a[@'fK$0eߔՈE/UٝGAg\%!qZ[V X%9 v/Dn z^nF~f/+t>3isuq .{D0&M'S#*GA[n.堒H'2p<5#]2Ȧ{ -,C}$S2A7&DofB79?ݓ[H0ѩ w:Q0?gB6}{mCo)a"Q:֊b *J4L85 GK _G0\2i@ -6+zioA7D'x -:׍$ѦhLZ`1}j5YNoj9J̏,M9P25ީ}{Zbnf^hB%{W@b:TfkB1!i9鶺˟ad,1N=eNbjQ`%@NTfk% PѿL;lhRHuh3\y@|ᥰ.Lwb 105!-pE Hdve.ѿpz9Ŧ2=߰+Ѧ9  sf|̄byDq4hr5:`]FT,_Np.!U|",\Kܧo=I\\Zu[o X2}@Ay.3=Ƶ*7?uf*SxPNl?]f׏ ";0a3\)_Hs*JE}X:W ->?q׳,(/.Ӣ8P3_C5?4D 9=0{U8m$6qM6şP}FҙK8 ?HgݲM%ݸnBBčpٽW-IZL_s3=GX4'}2 I'| Y1ÿy[xSv"J-9sk"e,BS .b -hVH/~7v 빠-2endstream -endobj -4530 0 obj -<< /Filter /FlateDecode /Length 2112 >> -stream -8@CdHy^fdpY~-*~Ӭ&Ȁ> -y-|g%jF͊㇜ -'Jt*{w:+JD !_#Ea6腙==*q|Eit5yDg Ch8hVgA::oOힶ 08?r~%5s6 -:z1!W߈mP,rʀiHlLu"wNcC!_u&;َ*q{_.=jVB]Hќy) -( 8XWVV Eo0Exj;tPhΊX=gkER~䍧al|ywӜh,Mt= ]^@Y)k(2Qgi 2?DWA5/շONL@~nN|qWFwFL[&вG/{!Ǣ};ծEL[ȼ謖pj{Gj~Y - M|ݿ1&ɾ hW0g޾i1l=ӍS/xǒ4DLXX?YpUVO؇¢dm'N\FFO^ASQ-?};ip,Ó_ -\bxPNj0IjT"S[޿mFyD)֚?/~Py7dӰcs{kF$kD5"Dy?jd:/!P -e櫙(}j,A5yfwL-F'܌tԹ+#ef(m{xmSߖي9P鱜) IP#.晣Ts“Мlrs9ϤBs&`9 \+% =ʮ2(R MbA3\)q@fMq[~Oq 6 -7 ;d ]V-F3 |GIm4=.p#~ \*媅)ʫ@_9CҤپ![ Xs>ho23 nVR宒Sι7Wxe]<=&2bqCjE>ºWmtF \RxWB[<ȄfDyHHSd10 -_Q@RPGEsE"[&VNxlo^(r]puyv5 r*,K/6=&55nzhtp.IZ1:﹦T8fALeox3,@r|c),5pt\$CѪZc3hnJ#b,XtL5 Ⰾwݹ)V7 MAAru!L[x) b3(JhnUfaigo6 lڧL&:.j gͲSM󱑬ZUZ]VҔ͵"Us/<IJHGjvHh$p[+/mbk" bL7]eaRL*6}?@pwhq`[,:)d{&%?mb Azc3Go#Ae35W!(ū`RnXC}|ʽѴ]\ȅM'DuĐ-׳zv٢-E3q r|o#Yl $%WǠv*.ԲhrT `NyQP!>+Cu ckйϺFrC\= ~D`.uf_0j~]5K}h0'&aD ȧiWڱ`ыsޠ${&Dzوnc!%OP6+ncSrTΌ(8ʬ7RdqW/~0Cm42\'%F ?I8-'ED/9}1d d ;4ƍh, Q> ~mӮa{B"#Rr?Z}aHt(7&u )r{l BRq5"tv7-A&ok uBb\ -;ͧ{op:~) -1Ym6t0=Um\;}T`SnN5-̩!vuCuendstream -endobj -4532 0 obj -<< /Filter /FlateDecode /Length 1712 >> -stream -M*3mZָ'RA2r@_ - кx:HR1Xkh\naꤾΠź ykj+ ᥖ04-#Iάud~ƛvS`SCFx CG`%XduKN5v 8%J|n%C?0{U/2p`-eay02y"o7dUנR^:"eZLk 8b(Pˀ\:QY()T$9ʴ>@ o4ڸE<[Xg \@S]gu+.ab=t2Jl0>#څ 3*"P+ti_ffB*Jwج9JzHUưD;Nh0Yi48}ضG?Qbp-%NF#Hic& U]gLB{}7ft[/MoHcY;6Kd6)t3B.4N|$M`BK-3 -?2*8^{:x -366SԞh'AnVsV ϜF{gNFwjMN~ە<4ƨTV6Ç$Dn;ȖRy̦Bۋk )JT߯g$mUs@iy74 xtcS'zC Ǿ- -ٵ#G&T#g6*ܨ?m}spbQH;kYV]bIe&6Jh`틸jPy#{(GK;#ƌ++s/W#Kb6CЬ@ҭeϲ4Nv3z#58$/\_}B61!N)nWQU&'Pl5uS+X3Y-nOd`UUq\j2zߠ~}}!\҇aTKxALG~bo݊_ vҿB2 8KT.GGS15}O&OEY#VHUӏ)GTEȝe z`~KE0nT+(>GIܡ䢩er #$p&h;C#rHI+\F0w ZFbMӣb_Xp=@>CBAjpPfj/\MSwbz3ڰ ( 䉧?3-~1wZ<G}k4R\ &u՟C5RRth1bn{u -*xdd^.aڪ}Bx}Wb?t9ItAۥ{-oIFUӪaWKMZsCL=/ 9)WCe[+!dXʇ|žRC`$Kn<^OˀϐluȈk=Ŀ ]tND8m(VHMendstream -endobj -4533 0 obj -<< /Filter /FlateDecode /Length 1632 >> -stream - R!SHY*G*R+XyU06ZlV 0S`a*O$c}ݩ wm5;߬k:73JU$04kO9 cw{Q|@8iN6L -f<& M,83 JJHD=J:xKc^gTw렪7>o7ìD {$`\CJAX`nzBP3XxG#UAFH.پIS/Ƅ uJ. %FJVjbD8^8`NAQ.K7939l"zU?KD6䍳 rb䄫%{kZXlX0$ +i\[Rpl tw rN iaYL\:0j^yj:m@JƵ[GH8&J٠ -oDaR(:a9 -ʇPwW./M!ab0?`Pޑ6 #()JQм}x`e4_&q0*yں3Ųpګ=@h $/szl5fNUxM:Z5 m] -&gkzuD "KrqfMߜ):~l0d_ATL' BIDY&fH ؊h5R-o79ۯ_%<|&9^V;`bx[mo3D"@|ZR&bGeck^!S&5/Iٗ=4@ 6j ݰh[mgӰJ͸ yV)9xZƿN/Ju Ztϵf,mzQ[WК6&-S-?B+ -q@h? e^W{P$olՋo"=?k:0*ګ'R1#(誷f0 S eʼnca5A TGF8IKOK}#ðҳ$5fL3_L ÿJ^> \UԶ_/ΌE!Mqo_J*0r:j\iG"7:x[!,A !|c#|GEnxEEw5vz0ոpvQ'[vMA8 W <@9656diʯ#,=t޽X|/#zMteiݺl;&2^Ix~FkcmZاax^Τe/T3(lzfMd$# hF Թ`MtЗHgvt2٧&sЁC`t4mCWИψilzUr a.OJ$$TjjA >*)}<\j> -stream -d+2WYÄeڦ߰wk_&a/4H}V q#jMQEʣ)uݒ5c$_KhAO31f5*6+CAkP_7o?QPk7V>i@Djzx}O8`X 5eātҮ$~;?EWPte&$ }iOH: O1O/>ELE%bQTxISl2l l8SG@&Ξq@^m87U86Oe_&<,\Fg*ujpy 0aGopc:α:b0/$z􈑮 \X U>Uzʘ -W27CH7/XCCpݯ*R/~ۊUt=dKMݪr܇S%lqOgUIZ1'o\>O2CHʹW֋@ǭ_BEO~La -SlmZ;Xdޢ)6-`x`whK]sݟ'%ZADqP*u[[ԑNHCxv 9nt2ՙz&=ԞeΜ TT3DڬjϜ9/jx~Em"]w q%9WmPģk4X#=(t\hjs)XX^-7\9y@!MbDOZ Olj#1Q?1%py\~f:=\צVO>hǼ-=W<7WqWTdUߝ9$RESF.7I- .ȟI4<_ 9E"J>!,Ĵk*KH fRݞd0c 9`? *=Z*BM U{ NѽS[pr-JO1R$,F 57N~b8[0k)d^2Ϧ޸i<4n+z$IȾ=%M*QfZFn rRG*|%ǀ.Ʒ}*)ݸVHhԍ)~bRFYs95|G;4> -stream -/v(ZgEXX_-;Weq_ĿNm2MT"UyȼQ_K?ɼ5w4GFG$Zv7 H6Y)`Y(Y~ܡOfoYzVf!qYZDz]܅a^ن9nrqL Uy'lLG:_{O-NUsm.:q2kkn <"*5އ&y|&Oʐv4ah9D#q8Lf8W~˭;9 -O=j3qW@ |q\h/\[6h+FZO'(T;toy5? !QQxw1þ)* -a\5/%ˠ1@wHx?+WJnW>۱ǐ)Ki(67YViR~}82[_>ȅs' :M03W)M1`d!pPHR1 ;05NZ^!Y,90h -X#%y8cj_0]x TTsaJ/$̏q76WhCO =YЫr;PS/KẎQ~k-ǜdِۊ+'>1TJ#/Udá٨h^vN\Axz309WK҆w]v~\uεJcAQI1-ifqv%U ~Sm(҇.` -,WJw\.C3u!p#nH2‘0dbu -gA8z n%SjANu<(w֤inQ9~F=F+cc y!q z j(VJ$w0~PBE7DOW:,lBYSS>#{=+LpZfN#zLط 041> 6=E$x{ئDှe$VjAWoficP$u Ӳ]"Ph#K$xɎ'h(WeЇ$9kX3P`dIqG?#qf kDyOhO%c^ðOjs]18 A10g T(v/^0G&Qy|4 .}endstream -endobj -4536 0 obj -<< /Filter /FlateDecode /Length 1904 >> -stream -{+zjaspRGSifS^ 7c2|uA"k h?%eԖիK:J?sPNkNv2J+Gmb>assUŝ?qӠiJ8]8ݍ#,Ԧlvph/hC+Wy7ǘ0mcgkelϝ[3rQ,ԨAJp@ 9" jyUYMHfxqPH&޺3LHu.)Xb:K${mfE2( -+Xl\ƎL 2!n*hF|T( )0h` 5vet?HF*DS aq%Wo}Z,#*f6E8!"X2;堤fE|<1lКY?:VCrT(ȸxӅ4.J97e;u;ESjF>@Sm F!?,%6A #aJ~IżfabcVhP1dDsm(y:*J}պl6bչ*@#`&P`;4hlfwic[I;HŤ&Y*vhԆr62mDcHfKC+)/̽#` -@nZ C-*0?T~L/ ~k)StDWyHm_ggY_xÙz] ]N(Lm#,ϩR=fi*.X%2|Ҁok|3 H'V~$.|jYoG8|IAR+U n635(%XhEB+r:G ܢYYM~>ġ -L H LvxeH4*y*g V$! G}(wX.~)DX6/C{yBR79d!0* vCK骗vTELuƠ^}⾿y@kBr2MuNWd} - 7Wc@WHmd!C™ sx.*96q,@ il/,lՎ_d0rQ,[ ֏:.L5rRmD'J n0} }-8veHb M!jl}wf& ܒ4tuaR2]G"3X)@"lYh5K vx75WT5jtUqTךrKxM /NVk](NÌ0Khw~uNΰI擹ףݿ*ᘐ:˳>6Xyu{=o[b/pUr\ۺ&4=" pB1#'AM :P[f޻qH١ %b+6Em*#f&v<*M.S93k7J9#܀}F-UBdv׼(G]qc%Shrn3REÍsZ6V(7 `.* £GxYz֜ (q|yBen)6e8jػ -UGnm("}endstream -endobj -4537 0 obj -<< /Filter /FlateDecode /Length 1888 >> -stream -Ù;948~П -# Zh:&2X[\h-#67DrqsH;30дODmh:Iwb>KԲxy::  ZzѠvdZ,#ul6%|/}-ZWD`Ō`3j-61' uqi%j^Sܳcx`if]j=8["21'mo8 e~%/̶>Tw;0_uR-uQ@ b[/?U U~epU\-԰P"x0O 2 - Q~"My,(/yiJw {u_] e"aAD60ApMݪ85䰇mj8~g=GB@\'؃C_4;5* R =?K%E]^j_oc#&*D>Ir)Ym;'q ,;w$m4Ө)5^iQ+Y2w\I()@HT!?mF+GœmKL.;SЗs@[P1ς /XĨ13^Qkݐ mӒ߀yƮ2Ju TJd3ygG}Yy_.0>-dC6xx%woQ9,їEOCa{Έ8<'c]6@NP$Q(͇ܚyO0*YCn>6:JXdx *Ky̷vaޥ4zAN߳9O1) 6}3iY>sR/IACuP`\k՗z?/aWjc*RL1/mPwAS#1ta-lƚTb&yՂ]m}ZwC,Q:Gz( [ܱ& ds{v.O 扣wCY(@=#3(oz=_9[U}1gmdH"_A>s'HX !ͧg-b<=,ܖ?g /}33i ܘk6Yrendstream -endobj -4538 0 obj -<< /Filter /FlateDecode /Length 1760 >> -stream -%Avf%ٍb5N .J\sYbA@ח~WEy.]wҏpgj/CCkѰH7<&j}ZP#D.W]۱8[ 4B -]OH.r\bUHCS;Kq;vP96-/jG :WW jwcJt115͒hQjL`ǧ$1^wlz"Q H! ';zC;t``)Rֶ: UsVm$xT0jSltT;*ڰ|SBˬL) Q!rѧx&.6Økщl܋85e uHZlip8OemEf}YJ`!KU1BYeme8d'D&Voi1UtVeGS*!x1\t - 3?Y,ꜵsBdw -LD0>8ߊm|E3XJv_'L=M;N;\D%`\[t$EY?|b}eW2ZwW/ıh%v|уԠXz!:r6/֠4@=2&dkmk35^1\J5ɤz1bYhPŽGe {fg -AEzQt[WRf -+hxcgid()8|?|#}<;$8 .CWs\kx -Myݢ$߭s7iu}u[U\2lXpCS|]q/*G?Op2=ӫWuC_fiz]dea}p Ko!& I3Ef2qGn{=йAHQ阦bZhLD7q6 -zR_ˎ (vb]4R|*D>POam:RQGr+b㦐n%` $R0p*Tq˪'%w8\z<< gm]$E>(^dு;Xw)JgW~-x_)D`> !L|tD94Lx,n3p L`<,{UR7Sfj[* C|jZM,s}@9>)7o mB^=s~c)cܨ8/ij]NJo_˶W# 7&6;k9zo>o0X$jÞ c$R1iݬO=*=?0{~ Ba0\QH endstream -endobj -4539 0 obj -<< /Filter /FlateDecode /Length 2048 >> -stream -3_Ӑf La\Q8ҫZ[%DCgTrV]Wl ) /.JL fPaNn^zM_-oc>Ȇ$ժ8ߡYATSb*u."q0g&=eى)#Փh=zA"<]3YMr\Dt)΢6jۓ= -Fo D89ƌM1Jk7$.ӯPqL{ 7X0K }S,-ĶGK'mecSSHzx& |xKQU;Ɔ&g7(+2M|GE{<_F"*Xޓd/mq -H?[ltFhOUO m?,-eS%&AP]8wv΅khWMz}FW/Y+VJ+w*7Qn%8PK̿\cNjLǸ'| :b>Vr )5;h5FJ\%$Jx@㏠SBy&endstream -endobj -4540 0 obj -<< /Filter /FlateDecode /Length 1824 >> -stream -ϼՉLac`ڋ>"qb05_~ } ߓ 3]-\/TWbNENb {wA_0< |Hq!te*D rp4/,g x vP }'7K azLXZz>!-l$ -C 5 )0ExGL ީz)Biج}PL_,eN3ϖo,9BgSK` j>k!=LHccN ͥB5ӀSdx&aހͪ֝P.fU\#WuҧF2Z -lw8) -En6!yv\resL"nœz#ɮz* ɂĺu5 GFgH$yaBq'o W&x sM:_څ1ym)̄Y-2T-Ŧ^LE8JT(t{ ߦ?jC+?k7|5ru -0㳫9?IwX?AyeQS H0HT=O:yk+&G,r?aPqtnblg/R-9[Eoj \%cOc <'DrE?ƾPbUM81,."ޟLvDW8Ev3/d(z#g 9wQ ]0.U^.iP -TE)T?Eg8bލ"Qt.Eȏ֥}+7haLP+M;(JܷU -/}8$3z=~ñFa[o=\7Vr>Zk$&ˍpV02]lޡ_lFRBd2!?C[%&7!"تPͫz*Y7Gq,:?ۮɶOR yt' 2B7I6! *O##%MFҲ)Z"*:r|rxz8۩ߤd?p;QeԺWri#B JVaZh5{/*vtȍ"p=k>Cxi@ՠf<697kI܋R˒|E+:/K1.X.+a^&OF?*,*|ܧF nKW:eL'61isf}f }sJQ|$(&G=YFk}`Pa~e?i4U}}^l,+;sB8%Ma4LuHf&G V;@&mb.F΂Zk`G8У>耮+ $҈yo,y'ȪҾ^!zY?OZy_Q> `#Q}m3!sA;a :|FP(OH`~e:ٳ%|Єqt{KR8%qk]c@){HYtF$ èu[ -/ۋ^,!ზCn)7x]8 D@}-%;. W?h3݉7=Y(OQ tVTxd,cK;y?{׮bݠVwU`JW^,aendstream -endobj -4541 0 obj -<< /Filter /FlateDecode /Length 752 >> -stream -$V%5V #[m9lU0gL`nR'TSh1q5WE3${!* ҃0p5x )Dw9 sbtzV)7fR{2&S0%R%"}Bih%wB9$쫋/,!if?UAfoٿl8E}fHe.م),U‚henI*BE>0Nvo\pmU!3Jۘ)@0 ""ghK pkkw~-8D]n7*`MAM~ܼ6+4ɑ]$M,أ\݀tj fWnm맕 - 7%4@@XA}ǭB|[+e@4r%p'8m:6e S/Z0||bjV߆GO`pY$erW pc3P[+qx& jn}QAP i!q?wendstream -endobj -4542 0 obj -<< /Filter /FlateDecode /Length 1648 >> -stream -³i>YE6Hi++ςɀ 1"*\=|eݯ|b)f*hh_$@B&^(<u!KSk7OknKX#qgAψQ:vL_Fv+Whշux .?Dja"ӫWSnJ {E1jyzDZ;=YejEmt?eu Ik -Ƣ,奵U~ș9+ O=Y\³JZBMc@HQ7y jVO^^r >e :+4Z;#QH!I)kaL=m]IA}EH (hA8Πڛ+] X -;ՔΠQY0m3ǥx!^\`ή0&0j}񜋜U E@L1Z GMd -ZřQ} %[?VI'&bI0d> YiH9ߩ"%r81v%+UW FH,L5BJVJv_L|W ?HX:r< pZ֬E!*UؤV]\·cаjG6#.if<^'N]`@A+(5hpT5=Y?p匸_e3@L &a}݆E#򛞄UnrhڊvӖPl&V_4SJ͊8ddxosw|s$fTCTImʝAn1N^'_jO -AvbDI} I%vq tG]_z5h Zu4H?wohO:[c@E\[zjvDԏm wy0>'w`j\Qr=u֞;r)O+B*g+5dMeS۶vehQA. HD1+#/؀jvv~`Iy4sb>7x$+ QZP>֍ltrl5ԭ$dy)jtK`<⋺mdmҽ,9"x@@|"O)#dStOLgD^/^o<[Ae5endstream -endobj -4543 0 obj -<< /Filter /FlateDecode /Length 1680 >> -stream -@]܍H_ሺRH%\ϼ+-o*j2 ?[elzm#UhiKwK&Hv9#E+i֒m7x<*_qFZRZ*K̐lMlL:]ʥdyOFBt -;1ֽ-MGJ嬗S(ąʏZ"ٹ=]W ZlrC.)u1-$|4'>6fkw1XYtKD?qL,OImcL[74\֦Ur5e4tXwtP(A{)2 j/1\2.rܓU^"䣗,04\ OoB{"^c"%*?tYf v(6h_8>u?Dob,u^[5ӵ+(OG.bd0o;@j,*(LlO)'@֣ў5=Ak&e^}D?o8--[XߝMxvN\;Yh#N*}!]9m׊srYp1|EL~ ]SՎ[en,F\d=8&qN.|'a -9цxVXendstream -endobj -4544 0 obj -<< /Filter /FlateDecode /Length 1856 >> -stream -BB]2c5†z=#hU9Jf1*=P䗻p,*ܭ -bW, U޿fŘv -1ȷ)" cou%E-uJ_E砡<2p|a,J @CW~bY?019#_d'OkLC V:ڠLYϧ""윔E}/ U݆ P^ -^䙵Jr-okS!YPQN,(jK~A>r]j6 {*؜g ^Ca=_"pb)ܣ)3f`855bZ(Nr{wEmGMIQ$VhTWA΋00NfCфLͦpT XcHJT~̈|0iCJH_?W 4O+zQvEHhkG{ծȔ΢,%ONt@9[b:J6T ?R{1S(esh3oQ'XpW<4Syϭ6r@r|f_{ږ/nh8T 󷗬LUQβ:(}2A-z>e",oVO9'3bj|4ՇTCޞaQ&bӇ*h%}eɛ7CTrx ±~/\4a-t WwmwaBŚ腺Q*pr1Y,rCJr#{JeCot$`p<2,izcL r[|H$+_ܗ2+١xnIiર*6ǹbi"࡮Xl1uV.dNs ;]bIdoU:NJ _s$b 2N8s%3]3TGE"TfJzN30.p&̔+uS6 7TGbqu&v {۰a&QoFeaowT᪝⴨cZQ@)0h.\vi'MxTv2 h}1:6Y[#c6M`?(my~/kMV#8ZB[ Wj >)6ιlZMJ m_;;  -w$I`1{d|ӊ* ;R[&vm'~W8 "[l \Xn aEO?Ugkʖ]ЙOcX2%;|]<P(ka%NEP)L2>r{=v$6C [\v(k +#})"Chs.otR(Hzm Ƹr&OK2M(WD{Lzb -iKii]"ͽciӿ8I/Y3N4՜ l[endstream -endobj -4545 0 obj -<< /Filter /FlateDecode /Length 1952 >> -stream -c -l;XCcepl5`)Sm2ێ*HFv+%;asY_F{E)ptKͣH]ܨaW|x"K q_Ԝ`2'FE׀ x1>I]k "-a9w~B]T\0nNmEہ?ƙ&Uc45G.:Ȉ,Ʀ821g"jMTQr71#'.*AiC=X7x ]-xuE."/'wGzT]`}2+SPG2jDR1Ehz,VJ뼘ҋp󿪒k̿x(:%mL68SۼgKX_R짣$?vBV6GÌ&*ٕ[CW.Eblqadhb*vhJok.gÄ#/)-͡0&B :ByNd ?C1:O4I }?i+:s*H@ -,fpXb(psn8q ?y̜ۛ'@T{s@32su xkV|kM'(H\ls |'ðUSa/tM صu.$)smnlǖJ9uK3"Ye^# -]>%[gޕ4H.,i|ek04hWGM_ g{^7^ N#SCz~O 'U<)gE@vhQ0n Ox7KܖғOWz9)95A8|HHLiV&boю - Z\,:\+KOWp9lk Ba&R` G/I eNjK_߲ktPEXnZhM@cSQ(k2~6H=2#&͙(yGς}HGM;28}Iw/vb'a$%4荏,W -.J09R[!ˇdp! dO?=aS~jvEjSIQqdjIc3Jʠu8n(E! "  Ok(sP#zL4ӛ - 4qQ{YwݯZ9zZ_yMlգ~FW_naN(†`9SU#5xU\W~k#.C`sQӃ(fPץhbendstream -endobj -4546 0 obj -<< /Filter /FlateDecode /Length 1872 >> -stream -Ew*kݐPTc&r_?eZ %{ qS ftu#g>lٙ1m-ꈣǘdQi J6IF_rv/ -Jx%k(k%f`#ɂBdqj,@k! ˪;)Hؚot^+,2w aSZ"&@bBoMRyl ?\59*Mj@4z 4y_Da_O#/͑ -%F`.2XAh-7)sor h/5gO=^A9k0Xx[C>e@ӌ@ZvrI<3SԁMW)Y\^=`lMē49E¬dAf9n+1ͩ 5= \:ݞ7 $$LѶ_?i&Y g[ ĵ;nB40]4etd3;rs)gl=qҳ_OiҨ1e-h VHG)=EMpV:&:Gc Ū{:NLoEn־f Kk~iRUw1 W~w~`%EbHSq\hw<ĸ5B\r pHH3ȍH\ͭwwWE#_pH_~ޘ#TKTшTIk#o$-LR63e^t -^G)pYq>c6ڧKao޵zhXUco+>,%ϓ~6;J"G =_xS;4əFE@n 91fY!O q]"5(h5YSh]U'XQl'tD'yyYd73oEB Lc38D N\yJ{iI Q[5#Xq~oH*rsxyp;^0,k%ϐ] J#nSkKU4[wR.M=/ޯfJ>qGArQD^ITrYyRF7)\3P5k-_\"u1ɴ )._~_i9( -sZbFK!48G+dryO̻)>ww -K|P+*zŐ *a~cO,6u;es3iSte`Ae8ux9ڳi~84Wj&X,I*S;S`C_uZFBa~g.$HwEB馆M+v.]k9%wo)Q!"ĵfutN/4l8@﵉i}O6E:t*qyy((TB $EMƝqm s=J۰ɬtdǵ? 2W9;zBu#C&XU}%8'"6Ԗk&FaZIV5nendstream -endobj -4547 0 obj -<< /Filter /FlateDecode /Length 2000 >> -stream -iGe=_Vutg5϶Bcƪ:wR=",BYY `yH|AOV9B/;gퟤS삵o}B6pJܜWh:.u-f~9V例2=7!WzQhj= 7܇R ~\a 'bؑH<ⰭBV| ςz>-鐖 j[rZ2iƒv_}b8#فL|%M>:{o:(Z:?9?@Eul^79bU~$26{T~%:〲{- -B(څمwu͙VQVƁ`o1fFlL>:*#O>IGx+ޭ< Z h jo|Y0TFpM#mG'}oy."߁\MP)(G3ncQ72( ;ȽخML"k_ }L: 8VWBuW_}$\fV`pzJ>ݵQe20wgG*(f5޿e,iJWx]e'լ(e8ЈJf }dmJ+&n#hԍRo=py/JFOfCV$,qéB-aN&=2&6g*otؒ)^c\ǤC%̈зpE'ޥLtoC2W/sG̵̎Bu5O|H)CR,3Ŀ H޺R)j 4yKc8@vAtXtZϓIډ>PscvUTb wūOssn㲠ۯ Ξ|8a6^"Arp$sB[ C$R,%LKcxLl]-oKxn\h%`U/ފKrEev+Cv%o' e&gΪpO6k,u|kxߺQv (ʽ ;ryRpoFL*%RRX˕;L-NF5ز - -Yaz_@8M܎%jy4ݱߐtI-* q"Yxi$Yh^AYc ~O\$6_L__Ȉ4F6+3ԭ;kvv1O~L^ Gz?pDŽw_bݐ-DW3*[bnu> -stream -_w5GXJFB&'>f{<,x,:EIW"Bs:8^EZ+Ċ±ZJ'| `&p&҇` bō:b)YX%UHJ't.#@/Dl֋dB6X8}.^цTQQe#|ti|S"OÒE /URF,Dkbf3OIq ? aw -.$4|+Prd{{-ď?53ihvbb9.d7N>ːx~d=g4u|c&;p e/xz+ę)_DS͏ -S$!z'нl9'^̰jBP<:Y.G> -} B m|NQ}ZvFnTHlJ,u;( .Gq!.hg -D6zh#7YLyG&Hgd U>|;lE>iCGpN{X_C}ۂgvVr8Rh8@OɃ}Rm4bD<odH;(peR~L{_p&; ;e2a`-f3lޓZu[ے8es'#2qk4x_;55Tsr}DF,.>q pݜF/uhI*v39OI?g~ے39H`|>^$ J_c﨣x)ʂ58b҉8դȲ Xb͝Kl Q'@j<ل, dDΩr[8N4XV"VVr6ֿtG^XoapF0. KH95 &t]%-3/;i;3 1ݛg3 "ތAs: Sr6˳>Kwi"-X婱gNXm)X )jTz]~L8FacVS^{L U)"#}@(vJ6.^:T 5=h1I26~'m6J^n섃rLYH8,3ˡcݔR@r¾͏s7tR=@T -T;zgx -;cBhAʀ}Rbo㥇} *m89P9-̱h01zإeR~27Hž~ô ޴pF 4HYI/p 6yz -⳴4}aup) ]Q|wϜ9Ϙ{8t-k0( BS^EQ,?؆IpO]rv~UwXyQ7pkDRk@O{@CTS&oPXzÃ݅,T${wL+C!KrØO-J7k#[d2%`~2 oA*ɢ \~tcPfbC,w/ iUrR犈g|x.}V()MҮ:w?罺'YOrRW`Oʈz8?&endstream -endobj -4549 0 obj -<< /Filter /FlateDecode /Length 1232 >> -stream -`45};^rE]e,_qy>"%*K@D{f{& :mD sC:[}_YbS# 7M4&b~ޗHڷǁ4ۉ7rƗ_T ]+>c*;IE*6#1-lMͷ.!; lFnY3)OGaNx24{]+qkrn+)_tt_MJ OX*T,껵}Ve F#ƭjiGűygol%3JVADts^y|+b /zgI 1  w'XBD,V2-8S9!Gմ Hdđ3>^x׀]+SH(erTC,(y^[*!Pقoi5{9v /a)w+8rǂc0Aٶ>JB&k}QY}D0%Ǩ|{S<"9x&}æf%؁4ْV{t4?GAe:\ 6g3?-:!mNX$f@}+H5QB {$o5ؤH١Xɂuȭ#oj9Saf,ڭ4 V/|v'IBiHծPmx`Hig`.#ОV$c&БXvI`3<]pgcd /-H7bEf;eJRŴ`> -stream -Hvg< B4e'_Ajh#å2j~!_$7F ӖiYw?Ġؒ\pgVrIU!J0hre{}xk'hry%u늣 -;*>Oہwt W4@5i%elڶ9˲)Q=ţb2 Bi@7b8< o;v Ɖ[)ۘ.qP3${a=/VblL 2b#L}vls. *R<[^,VC`>/3QHʋBFz.PS \RaA=LsHsBM -VyC$GLiHۓUHZk-`qo#t@A8tP1'|[]aB$ETKNS_?ϡ ) -}If -ހ^ŭÅɝ ֿƎ| _4 -!8hslXd\Td̊=Th%g s#duj7A\,ss+yo<$[na''lyۘeMpVE2̮Gz<}a$)x/453&]GlpN%xiQеJ?߿7t:@w"! -E 1u;x5&Erl m- l S;އWwr}~~X#o!4S.=|LRoSAaU]ːw1 1e0@:_bT&L!2&|F(婺)f' |urq7]<8&!ȩ`Ib sPB ڽtt$- +i*Y%z@ 5T`c ?@6Κt`dJMJHO̰È?$@3cU=^J3F%3#uS&mflgGt81uVnMXkpX7 BvR`XlRx3WorgaAhh9Q!;j^C7Ͼ=1"~ji="t9?Hb4݋_&TH87`Cy11VBhMiGHMM%+/@˛=t -A˽HK.8]9^@y*j,B(T婬I ayg3B -}Wk5Ei`O/ q$ -) -#``tF -.9V^px)h6-Wp{BiyhP%*.jxyQ L8闡c9S9BIa ե,,|3q2Ӥ(̽ܟ>L!DV%. =pMSuh -gi/'SM{ՀZ /DAكVrB0N/By~RT,Io i.鍧\!O^^:A  XA\V vlZ GpMǑ&NNzƧX4k_k.Czrbkm3yrw.FDAΑ5pU#P,WBj>7#$'|}v9ߵ%G?+2A \_L?)C<뵶(fZJN/6Xa55[B9 @vb8%x|A4pO$!zeylyBd#J0)'-E0RdLݖd?7W+܋+rڡpA˷uלm=LzonƧ/%Y\:\'n1}&#؄52łTkC޸:v%EC?os[3ziu #Đ嘧U3WGO EDz@ -f~8ojP&80ܝߋ\DW-tAHi&wrر'8R - - J7:~- JȖ)6Egq|4z\*gŭT跻q;T#cQQ-+8;hMZD:VI % fC髧nI֐,22xQRȢ6zZX-b7Gyφ$dg{@nԛC$bk>D( h0OU]ܒ=M.j>Ffdhsʹ\Ic;9FZ٪ި¦t_?ąI ggbs!Qh/3~MܳJo ":K:o>vJWUE:NW;rꕅRie*E>8a[vSsźubկ%C%^ׁ%vqb=&綟T_?a 5t}3DKoя8LɸZoQ:~E_*&O'{ʊx#omUHLۗ* L`Ԟ d̦J/4u*KYfu)}ɣ&Z^ŶV?)maڞ K>F`R,B3GQئ:n}bͲ>q>߱0f퀈Ž;IHt _0q݉&ԫ}`Ft) -$;"e>]_Q)xeEfA )^ mՐVe#;UUYG#htr[6a9d`{@>\ߵ31R5_U7}5YBOGe -JؽMz-h5a?E_MM:TR΄ؖqH1v4ک"vV*eY]11OufHՒУ 6WJ !ii,w7!TyE> 8Rkd Ri -T7vU9*Y:` Dgx wTD/$Z^X(Zo1ŷlCORMk6Q ăɎڠ-S34~ wWNH ->_}$[R\ MkBX,/?11SJ`oѸe}>WĄqK8,7dgi"Bƛvqyq}̉)5ީ񐜽5`լ|%VmXw( &_oLcV9F`8/U~N^_6JI_T*UcvZǗ nܘhUO)`#RmZ9IFX4*8%`-Z#Ϸ'EK g 6lȥa,ሲ; g׳ASUJrUN/Ih| RP5s:BJ -Uؓl cU9;bľ A>cW/ -wM?A^n~;9̟5vE͞Θ1_0*ꎩR_l10}'6_/#s)`HsʏoXn,`iNsaPL q`BǑT+bCˢ&6wzgSEF*]QHVZpy89GםSxg]i_j:4A\ܫGU2Ξ:(MD?ʇĻ5Ro#'#Oĝ$F=ٝ vzc\Xmߜ0p O88S .myP>~Fﱟnt2;;b27'vqKL t1߮^pٲ'\0M 9.9qޚ eE(SjV8^N#KDiCo0o'ۉg7̈ù:. ,Z ?8d^]yץnSI/W 4šIP1\D9cRiYSß*^( -}Zz)9x2g@1J<L$ -W2|O5\L麻޵ԭLp9x17Df24 c9#RNJ9bbJXfg¦{$6j⋰WˋYB.gOǍyXsցjÁBFTVX}1f'G$-b++QQI,A >4W$#k0{?>4cwƧU[6 "}yфg4jwaa@d0[@'٥ Hb61le1Xn"wG@"ѶtBȯ#By/<_~k_4Vγuln-eq,BkiV=q:#ߞ&ꮃD3OXҰrVZ2R'{5 > uab|6KHV@_fjwX.Ut.+'Ut$:ۇ ^;p?\xM4.ׄKՍRh(xOnP.o!CF~XB|N"'۩gIbNxq@(QHFT>F!<;)9d O_H룥Vƿ-ZTm`s 5.!vê/ȁ3TM4ʡW 䑁` -ofvd^ZDj#G啘OBomZiGQ ʼnZ6PM?l/dw4'JzFw 9f(7SSu !3In^Kalendstream -endobj -4551 0 obj -<< /CIDSystemInfo << /Ordering /Registry <65193dc4924f0f19043a47befadaf3015a46c7b6dcc2cb2fd452f242d511accc> /Supplement 0 >> /CMapName /WP-Encod-0 /Filter /FlateDecode /Type /CMap /Length 864 >> -stream -M ~j8E=چ6B? 5n4 탛~)cJ[x(?"Mpԉ(B%uҞhy!Z@DBp bp,6mօp3'f(v\WWzҘWEsqPTQhڙ;nv$/Uw]Z _Z ڒKyϵaypp䳄rK2nFuɾf; -r~Uug]m#t5Ls/,lB|=f:o̠ҽ1:cH{unEbou'oBr<: -E)."8!{6B>~3hh$v `83\fOƝ{"8| +la;8Hgػ:FHYyxa(iɺCrY z0i Ǒ^lSfv7G1Y -LsW0thn>fdz* YӦ,׻41*> -stream -a[pax^@`NO &΀,[U >^[F[ղlc8o8{~2S؇tC5"B1?X7ȁbam\oEX{V4VfP>F$_Q~m[t<5M>*gG>@.t ӀZoTc;>ⵀ,NEbáv3aɪep/4, lL=ch2s@,dxs 2I|̭pE(ko4mІ '[QOx81Sm&R:pe'7v"ϒb)LqeCvʅ* _?XvTx`[JL V^*Y@գ}9 -)hitOPFaHd3Y K/¤`׵jtOdgq۝0/Grd[Je34ܸ& YC?~*xw&DhM fe;;i! "=v{G{HB:YNƐXFڷ5mmVy|0EMwTEYJ>9ܠ!k u?9/i3YLБ߇>nڿQaZ]H&#֕C4mPڙsyF,X)MfmH+&T*WIItsdoئ@_}ߺflvFT(ft+trrn!/9 m4|5bq@lĎ ]pW^]}.x]endstream -endobj -4553 0 obj -<< /Filter /FlateDecode /Length1 48132 /Length 10784 >> -stream -?L -^]d[+%޿^; -9\"?~EOeUOޥ`0 PT4;ٰA̼`C0 2-<[11fě?!v !jSMw5 stT Y|Tg'C?P|di -DD,u܌z~VbXBpSvU#&'گ^VsVvLAfPģ=s_sc~8<63UeP+5?5a2sЀ,yL$cmi՟S,D<9r*QI~WD¹fbJXtĈ]E Lf 8vdU h,`vP#J~Onکu%jgcnr 8)Gݣ#lF"y12:q<̒aI/OCk tNB!w --rFk=eubz3K4Qnf~B%bqd(ڝ%}!pZ5|r'M8鐮ϼQ7)ɐ2+Z .C<ښ%6tǞ"P6bj?\NFJpJ@ރ3ltd8`yϕ_Q7<7u72l}SP 5kGs[*Kb#ũ,ǭtˈ8-w4Ș2׷T vZ:B*FrF#?TȠg׳.F9GdpD)`/–l)E^uS7!DIB ثGc(yt&(b ]GE;;o;Sk6a4Z$[FȌ>% IHhU6|{Ҳaj}$ 3ي3& ]~\qr*X 5fUdL~%tJtug .)(`G$]:P,d<}J*uJo;`F +@O8Xn-`8~"t<:lfW\LԖM,a`ko:dOg")d6&Р+<f33zhHjo@C;fK|Y0'9<Rq_"da|£6q ɠl"VB%vudj]"wt6bK!](EA ?=!Tw[sp1$?_">-9І?P˵`ՎnGiψa;1B AaifR?=#=-z WN_ JZCђ'7̑0KrR1S0dT} -PBh|gI?(XC\})qPDs$'"U~ -wCߓ =^#FCb |؉{)A{~L@͂ 5RC߸: -W֍ ቫUL=iDeyÄ7!~I_CE -_Bxgc; + 0ˣ3ٕ@#NycU3op(ѕJZmCb+;YQVl ~0$Dy19P`R -EWI8 <z.O5*zwt;$ e[\">/RX }@v<9$+JV9'%l26&hn#6>8##*qcrfY(GV xbI.:@{3zlylG zP?*u6&dF cb]QL;coJ^;LM'1wi< Hx5";pjчcJ!7/ *ĺ7[`_rIkIM%w[v3%B`KE6 r$[*\п~!(!oPƮ>$i*V]'knjX7CӎwoC.4;fjAwdLJ}3%L@`2?hWcmgUéɯG?j$#ϊCS?K myky%zO/}ѶX(r  x%k@nw쏈Q`@0oYPp-D3pH%kB8Ol~_m le]{^1gt:-)8$98 -F9;M 7cR@zO'*BMB%jbaCZv9Q f~#r0ĨU.V5Zp1E]揈E4ޅ6hHζq#}K;*nD@g{tޫ=76F~1pN(%?U,,p -G5XCb%{(G'K2{]0䔣. -7aeo N 1jJ:-VZNDk³O|ě0Lڍ%LNnOSMmvSOj(_f$!wuG854CaMgyCC@2}#DLWun.E\؄YTCF<}-oèYFӯZ3(s,1"܃9R;u \w@t:[ʎv.on—,UtCg}V_K\h[BP?$ ߹9I}?QGte%>5ւUUFOYU;wC.:FD=e'ptV! -|3EbePƧpb)T,RQ5kypRO8.jk u#{+0#9cE}Cּ+=׷O;Uyȴ-B굎C-r\63LsԯH<7ߦ%BD8"|"ϻxbA9?G`yeA~B[lBڷ] "C}< 8_}"nP:8\LP%%\<#7jBBPnn"`8xQkEЎ>i\$ {JG3/mԕiS] fqOH5,/A_y.ψ$M=ʀLmyK B`ǵ ʘ2%-|?x[FԠ ѤiE[; -Czhl#Ze Qvw1eCkgWwԼC 2W.A>hKzCꭊ#Ehf޸ x?i,צ{_2c>m85/EA6ށycessߋ4&uJ*XU>('~N\ɸmeLf)@9v>n5~}`wr k^#4t[~{?/%H|۟ [Fexȭ{0t.,O@ΜX S@vn+$ Te#>my Ocʍx>GP g}>&BPP'b*xr¹߉TPqusD"wRe(ߐمkKZn,;K0P"ˬ4yl(I|Y=+$˝Ml͆zʣoouAdSt>r|'CR[}|qhAɵlݟȸGm͆V<8|pZw7jE^.+77on>r8enz"QG~9r@X!-2 -+:^`wR6CnT SR.vA1n}8cjWY7Ti89 }*!+|Vyi)!X5g٧1>qވXF3^ *(^ĕ.wxzAǡ tDr2͒-^Kdov-IF3,11.آϑ˱6zo{<}(a8jhש2]5~)hQoB g(9XԿ]hodAdqͫoKۆoʷ -zq+/q FE; M`-KjfXŠǺ0Y50θA$ͥEd{'^fǚ+LrMD#zG 9,?^S\YֿWa}Yu6:`oRIESD79;fgȗigK HI|sJ sYPE^+b0&@ -3Jn@.n?DvЙfbI-5U16Ua2>fQR);DEZO z^aJiK[ t?gk֍>^czAN!BJz 0lm-6|(˟{MP!.2 6"CkxCvYӋqQVVL8q2ư΀j7a_HL0e/4R) @Yh= -65i^6}UwfRP:$5tev~D*( E D~&S_k*uye*`H$إZ,cI@;(򲱾dM<4db̖,MaJ@$bea Ε:Lu˓ рBx/)}D#%c*R% 4C7a*5O>Uz{XaȏH6lU X -o0.|$Y03j?Mzٔ27)qvz ~gIl` +l~qyvއfݙJHAGK1&<ez&5_Xy5Bf.\{Ė+.Xޡhw2GmX)My}EA0{.|c.*beToc*`Tu;jܳ&zCBB)3ֱ јÉ܊ .fqѿqGzpZEK#"5sR'G1еHVZwd"hUpKfD&Jl+vzdzvtx  \7Y / -^S|)V,9JW{plXgvfj8nrtk$"ZR!kgi=Ū(fj>h<SBîv:_:buswxFP\,.^ȺA\yT'~`^)ME't޷0 {מZL],>^D0 oTн޴?4)4D~ZH:4If(LLL9,C/m٨Z%T||tIŘa.6w8/B#gZe% ~0G 7 7"eV3O .PQ0oE퐘Bd;?x9n-# -^5Ǣ%5Y}hնǬ f0#kNAIe 8N<˲Oa.N44P)+~|Faa7i YAg%<~ul|N=B$(xN}bԕ5e ,_DPxhipS:x)1Tflico`znTL`mr\kqmi썐I3񯱙<6Ckil\Xfͷ^5S֮OQ8ʩYy -Ha>o͸B>!h*|V#JԱ0ZzaNhAva5(-57iZcØ et$V@ZGU\.+NyJj. -kdF/':/ ҦiF?JQR>9mȑe@SzHQ@ ?WvD~4a$u %,4_T*mdkmQ'r-1S9YoeLU+J>LT cP\'2 s:o.sYg|-c5֝ώp0jxn|p5Jgfs#!6 jG^>o 5 /Registry <71c2befe54e997284c0a09f994f9576f9073ba9441c00870f39bd4c57aee8305> /Supplement 0 >> /CMapName /WP-Encod-0 /Filter /FlateDecode /Type /CMap /Length 1104 >> -stream - [5{VGWLBrFj( ;ξ#y13%w\ V& Uqߧ/o*zv(9v}L(ś:}U^mŋnn|qOqXK;AZhꆏ'IC?jljKR⅂@z1sj@fj;Y(^17n2yN>3Ju ef* gvooM.~%˜EiBݻ,glb_yv 8*9)c{{PJ{`ou{ѥ%E~B`:8,֢bD?,qþ7oMu)bCqH> -stream -,$7JK9ԦɈ~}ބkgv#Q0 1ɴf_*qԀF -/۰LD!WUrU+NmX0MV ԇ]Hϡ6uERa0wk{krxĺchc/#T֎?D}BӾM$5Np=嫱D l+䆺fzT|aQk]Z5b|pt}%^ZfOҳ+M|*` )'Q*p/zsi˩7 Gm,=-?Z&CY>mt/E?] ¾nIx"|_qQCF\Bnʉ8{uC -duҤ?x3LY}M ߒ":͑@06avVu'Y3!V3QG_ -RL\ -Hհ7Q5[+PJyD Cjo-һC)~srU/mEu}ủrEiQ%0-'қ :C xLJR dZ76!5T̬:$ʔJ׈?K8 Ox'%Q01ӘM<#f?Fc0L<|: [[&yjń -1+r3E@f KTJcKlPϒ2z^%Eb`|N{,ݰi,1P>{0.2 I$e -]h^%lDC}S) Ua1v-_kjwwQ'sյ*ME WP[hF_u]I!\] %b\P Y[0-%5!m6ߘѢ)lܷ%iInqz3YׅmjͨܬP+?;h@6}Ӧc%A*QJ q7`B`:Vrm9^rkev"hCOFc<^| z>PWa90*XJ^`ӆ:R'c%:B$}tZ،EݯV*ʈ_# ccP ͆ |ZKg/0ftw)fL1;K?GElQW줉t10 ZB~Sjn|OC6> -stream -<)eH -.=g 2Np׸ -;)6~W(s WnĒ@M{ӗ%k VkH{#@|@Ys(\LKpadYV**\m!r(H^ ?ݜO[ hI61)HƎNXRsQd.Ǘ^_e*Kz`ާV~C"(s$'ٔQU}SV/Q &ty^|VcﭫT䬃\**K&7Czi&w|xH&{=L?  Y!"i-Hɣ$l!Xv[ 0;7^0aY O)RQ'rUi[ ]yR^=+jioG]wZ5\Sw8UĴAm%lxmYݺIhHF#d$1XZUq`f&8j{ˬ~!' `9|t))E`qMq  o>mXE 3OT:ůǞ]6 1a6j鄧%=|aCeAHnysLQ.t] LٶXt+CZmJvc:w۱Q+&XUxS+uүߧ{^qv<R*ʂMG;7)k'aC%}d4^_e}R>ƃB`7SI)ogMp-u{fqձX -nxZ;/Ѷ~+yi ftvWQ8|-B!=ٵ튿-j :/sH!}X+[Yǹ{}܉xxO> -stream -0+P6jh}X'QL Ik+#E5M5/29T6闼o){o -}(zFQ -?΄Ⴏ<>?6꒟ޝܔT4Β-=jYFxc+&cnk7 (3HpT`@[1w:ԃ7eAx̡4zE;(@JsbjWzۄHg- @\Rs~DA# a8Bd #x+n44O3/ nendstream -endobj -4558 0 obj -<< /Filter /FlateDecode /Length1 42088 /Length 6208 >> -stream -o=N*W=/6p"ޱ[kl@+culЍ%(Ʀu30MMؖjnS F -@]#c.tl^ !-ُP|@ BRZbiCfˍjgWȟ…44tr]Nw -IL^RHjj%hi7Ϳ8Uk;ՍwûHtv`Z=pS"/xEm`T_ьt/8/_BeR'T:% (yl XY@~ bY:nNiGANs_m-<$M155LqzAaViGYq=GjI_2iMjr='743UGe h)䰾8Y5ʾuJ@™8bt2ۏ٭U'%~OuqKT3zpQ0L -}6Vv k{?#K8ls^X[Ԏo,ӲF{FttMXg?nz+~vYSgWGvɋA.PMlֵI74K'V&/m""|^ȡXzٞKLYq7jqIi$zFPauBW0* --,at$Tn&b f. i>CXɒ7ZۙNYDk♗f6#t`>Ä1ClFZ7]Mҁ\ N#DyN Yrte9i+Ό*-x&K-(*3ѽRnS0fX Yѽb'[j{7Hqa =]Cg{vB$Thv*f0{v t@{/;k "<~v/OYfD՚&*-M\$W仙McH~CQ`_ޘoh dS"]&0|X >S:ki]qJixQ|&)zIVgf^xaqqVYGVpEepqqXyP\ɬ׭zn15a]]h %i.FZ#vS7TXV!CJ9W˫n}dD3(*}!@.v3E78kD1u W1Fo~"=&TA2SNssNxW9O2z&~ >E -#0gD:rBVףּq:|Jj`ɯAO_XChjhܧ԰@=9XMi4_R#UdLyh'4.ﳧ:/e(T:,=R4u Vj.@>H Q>IX:W,HWmjF7%|=4bثSrCfev86AW_"ٽ!Hw>H~ۧEyzh -dꑛФ Ӧ0Qv V4Vmqi9MXƑ^gFVǁG -(x[,Xm3G@ZNGTI+ryRaUmD4X/FenڇN _|BNykfM F'(mL" ĽIa', -F2l9f`RљQeG?flgOQU'A4;%p)gr+k IBm휞qERR R\ qآU4j ɉVusESw³Gy/mr6<Г㑽\J M!܏5ӳ 3)g=ir,(Aрb$V?1ۊ6=찬m^l -d .KOMS6q}栒m#C;_e#HeR3N{1م(A:zqa㗝>|v|#ݨ/5n?'ČV5T;'=(}t e^s(jMG9oB!LqHh+dᘉonghda -Vo%_ 0֒C'9B3_hmtiϵvcb<"Č-^kݚ`V-ӭJC\+g3wOif'?bHT. eBĞM<4>S&ж:k;IY+}aX=l z t$_fSed?3N6O hCR.H`ls8*HDQ[][!6|@ -7vѪSƭh%2:5E1b0 #tiY& -1SX4Nɦ҆ҙu!PwiomHd_We$#RP1*\fHg[z&K{c<_ΗڴZd&/ЯGDp-Տn#4&_##7&(z,$gNd|?#BQ@{T@/E^pFb%*xOg5F{nnיgSQ#TY+6 :[FF$ O#~KDc @oG#գpDH7tpX:K|"E;RWF':rZ*9'k7(`E!tލoSsX -,d߈qYOM|D_CkiWnLR()8W`iaqz3\*;ka&#r@} kp$$YQ5'e{q4py$ TQ0ٍg}UXT[䉵lb $)ᨷ`o=KC,7E3eCn`ЄOu4xua JB{h  ^Nmh2ylgm+)[x1Dq`!*Ōz%\OxWirghāY }|fGftNS*΢ط:Hazt"IcexceTO>_n[l&h.s*qY5ubv&T.Ok|-l;%gû4<\ʝGQ3;z Ue|ﴈ6? ̬qm).ÃS;1ęyM7u]t8Ӟ֒,u$w -rhOOJ}Hs azIRTpLoaw=eߴtދymA+"4`'Lt ؾ-\Q+X,-a{ΤN 3yDWs'Q?Qݕ!;Ĺ B47|05%k}n0bt+lNv{wNE_pw S>}5S J4y}8@Fp[]% -|]kjeI}E 2aQ>Qa EInK)ʁeWQB>闸Ki? -i|b506&_eU^hih(Q~&`s@'xu-AOo0n#::9ycN>E~:8Aef Cd"8zҍC&z\.%$BsUGŀMBpXzD&A5Y T͔>Š -yl{H)s[^m 2X~O}0m{gˁ<<^ h];?C<6EY8RLM<iJC}*Ϥ?4mH"͹;l5=BS:F;aEW'F}_L?ta176We"[d觤wN'&!@A))O`~YԁPNv܁t"]%h)6.h0haEA_]2 QL/^]&ϥk r\JYPNwƒ/T#_ڐ/pU㣕!6-\ɂnG2mN{]u]p~\Be\'.>C GsewتE`-aUuv66&OHv۾A4jjص~{&iMOkgDȐJd!jB/'9ޖ D̝Sҧ[~rPQR]20"~ CFM7'g*kW-f$Ai Aj=s G(3\\MG0u}Jkgx@`Pōmz w+ hWTXGeu_Vd^w)Z[ u6u'H9nOpA@ 9endstream -endobj -4559 0 obj -<< /Filter /FlateDecode /Length 672 >> -stream -j*OwHewЃHE}:j8L'ܘZ׀+ܺUWY$}y~ff`pkܙ)rZ=ۘF%,aϫ˔i1^cTeESqf0}:I=,L9U;2έ@jӺRPusa 1Q6n -ah}H|Mmk{xޝ<¯0*xCg11_pPjEޮ3S{!Vý!5802'ܳUC8[WS (PD+uXk96|s&V&N%Ȑ|V.(^\ @n\bTˡ ^hWЏH p#-]S.{'X Zk>*> -stream - ԩ/S$ V JU)nL")Av5a$Tw R.\`~Ț:ME-]KVN"AQ4S15pB[^ -o82w$m=`=e8Z\h>B#~&.|{Byj:8/r7+}Na<]%$PV\ ckCDq{;m "|wM`+ޥ^<3uR;Tz06׍AƵSR|Kҍ( ,_.0&)`N0qb3Ҋ&CRdij)kwBG6%0x+lv5;wvQH)? cT'V[v>~Zj^n0xt^L "cOU\A^+7DR#dWH䦭}LV -|{|}wϪ RBWoyK\dܹk`xՒ<; d-Z_y*` -pc~}9vlp^"ո9"S1פW6+ՁFDť=?X-,|{ÎL/s a,sCTȊRyXϊ-+o Aay"\| 1Ȃv O#QD$).9G[bH-<3r 3ڸB\ʺ VB)Z%t~ZU6&'|_u KStK-w&ڙ2+0Z4maToLAzпPf0(g>J_!W,eri|vQw\=+zK\_]ya>mpܼXm 6jmvŽao -GX{{*5R h'_Z?ќ7.[Z&1=}> 0sH# ϟ@jm{T<{Q3xn5]h"/mDO B٥᳋_kNPd9E`h SPuw -d]o擣þ c:'Q,0婹QNY+hd%zYʼnf8bϥ5No,78s_! -^S=O@`ըG^]qA#4I8؂~E5 io . =e껌0l7JhO2NIeZL8CJm|Gi%,f>MBةe 2hS9W7׆eu_'rZ2z&{k#,5FWt<lǙrGkuZ2;ynslf4Հ9.I$775{\^{E C]uKC8- Ő:b5Q΅rel* -$..J$,^¾w܉ 5XݏUj{[^_ƌeMY` Yi7)};iƫ`N,{sz&YNu1 fߓqsGgÊ!1r=Pv1_)hwieø_h)(&11{w7F}dql̶Tw)k"Bx F-u?[5Fu$6 -.4yZx 4/JYfBĠ_Y-NB 6WHJ_dBix^b9-;>ǘsb{daYmdoR#1xdvi4U'{NAH wdxg~=W1g&"P[HRi`VT[_h!(7l $?nTYy!N ts¢f2y -=k~9zef"Jֲu-iEЧʩ=9(9D$O&Ht}Va O -F/(-37ZT `SO`+Cʇ<RV!&A&43ad2/A)G:<\cmiv8X&UV"Hy25 ۣ\]/߻ -kW4\N[bǡe`v2c[;- r΄F|I[: [:d j_q1Hi"At[pq -/v'WP.z̰T5x`=|s["L̗iO׌p8Jk(W5b]d{X#;]gC@XΧCF}G/%1ӂE(`b^>DR! i`e?8Jn -|`eP}8 dGw5-_8NӖ$ jK cHh͍t8FC-ېI@ĽrobehN,x % YWҭbg.,}d8FCr\[7F.Hf/ގ!q]kfȁ@?5<]+~鼗2V'pŠ~U2®ָdUKq#W.BPpg3U*.GE:9nUԧMe@+Aq]f)4g9=8m e_#k.CʀeI^ :~2nV -kJPu 0WZY3*qmRDݎKVō -# - n(mY0Hs -k!- `ƕ˨sM,tt_?r|I1u6;R%p+ň#/G 2}^>ib6~d$w~H|T1j -c)T_ٖ=lB)F­ݧ{QlQ3Qh4F(9ˋ>%HVP5vA3tWœ}|U;%"|^ef|| ӓYՊPs;sX$3j Ha,k~f7XN vJ]3 LTGJ)yc~PМ043*3̸~AH/8Qנ!ڈsRC>$BϲE*R LtԾiT]pKKuٗ -6ݡT` +cO8ޑmdVڹKhszHu&%)C6aË=2}qHO,lerQ5P_!|vT.5(ףݰRW{s4p -:.ZDU tA5Z\ICGyϐ}a(٢x o]RJ -c4g.ТL>C@1$e[sɃ)uBQx|,'kQ* W(x듟 бa (hW;ZgsbO^`aJ)uvf2 3y}=sZ nfr!S0gtC79i8ز vWfC<(zٌ85^E2rp. -B`7u8rX`3׋$F Ӛf;EDfXgyOF-nkbr63tB4Vqɺ^#`B$ VkC[c߳s\ "u;nhd.pݒ -!@*oVf] t؞e= -UxէGYI./9ۻ*:5ؘqB0(^$Ef!;Y'X0.Eýma=(${S &ngAmEϣ>{a<btzM$0r$֔xVH# ցdm9r_n2KbV,OO >3/xB*H掄#&;@ɨ,PAJ-3ρ")NպEo?>u:mp>Og/W S -V\g)i@&ψZjLKv]eź2{6k ݂VWa#gr~Z Vr[ lRx1})ATz-=@ EAI:mn^&jȗ,'H/sS:[}?F*^Q;5 /Al4[WY>TՏw} _Dw&HV^ǾHyՍ,HUԨH-$,hS'0^6 j]Qo>܂H^JPTTrp_P7tm ~qKaxXc2莋tP -Е^=/9f_$+ӖSM}=Fݹ -'>rK wC iK aMD0ذ"a !99֒v)726,Mҟw6>E?y5Z=˱Ŧcf9@CmXunta dv1Egendstream -endobj -4561 0 obj -<< /CIDSystemInfo << /Ordering <4215d07ac99edc62e455ca415683710695df0abfa0daa0c2d64db552ac2987ae> /Registry <1717026d64fb67f9d83e565e7b93ed78f01aaa749c4d4522fe05aa7704deb211> /Supplement 0 >> /CMapName /WP-Encod-0 /Filter /FlateDecode /Type /CMap /Length 1024 >> -stream -%Ԙ/|sm"kOѡ"%+8^9eыXrz8jF^qYyaՀM\@y)m W|Ӆ0~B(IUkʈPP u&skO`oxU]/d RLtG\EΕKں%I  I+mS*Uy<_s{_cJWK"$3jt>$BZh_4x8A\62[ 2AI{{)ld,aF̨ ?3uȅ3rԻa;nio\m(VpͶm$AĮ7;F -I]g '46$F@(,äAW-= p Qѝ7ap'!V&3d3T~XzD9:8ҶBI04dӌorI$-zz$Y)0YCɚM/e߻&Jb(=źaObhj&gZ.]˸ CVQv6dl9bRQ{oIa՗}[b-zkEdqWf)5ݛ;m(w'_tEԺ*Ӆ_Z֤Ɔ=UPj+3ed=+ (Vb:9l7]^zK| XkvҜ5#S hYQ&uCEf[Km{kRMJG8&iϚeAvD.G<\cǛ£ |m=7sajzD7Kܢ]*LkDendstream -endobj -4562 0 obj -<< /Filter /FlateDecode /Length 1120 >> -stream -4!BfA-!COƜִtHnUmQEd /Ƀ>`O}P 9@%:q#bz^#~zҬ7`?^&Rv-Ϩ_9-9"|RS211"I<|[ -Ѽ U]?F0txmabDh]۾$ìQUW/eIN zPB'fP[Stmy4Q$I[ ~30fB9\ޕ` 臲9=+"}Js|'q.Y;`z\mjwq] `.&7eo@`mehiư|ߠ+Y,?j'6PRnvL mb!K$L?1εSDIrt&v,qxeKC>'8({L`f쪝o[뗍рc5$2J$'詻{Y#aԶE<6fHh>0叠)#a/5;YʉC %ٹ>C y䂤 xNݙ,09"_d<pJӝ~exR5;uԥ)gWZ]WÌ3 -ND8*'mN -\tȢ>22KaE$8endstream -endobj -4563 0 obj -<< /Filter /FlateDecode /Length1 27648 /Length 2848 >> -stream -  &&Yy?@8pO'^-U[I?EB}I%If9kB1#2)w?TФ=RhuZ,6J)2Gr:(-B*f&4|2iI]?IГA"fíeQGrAmJW -)J'4N"lQMpMF`NCm}VEg!K_=k#+<&aetL1PI(SyDji/=ȇIZB6"riM6DHw%XwϰG|LhppޑZ,gA -o7( `(Ul93_j9\{Ez!8,g{K91YNԞuY8 բqt)%iYy~' :`zȩkue${f}uӻp@(t,FY[mEYUӷݒޟߟCr%/[xU s+b9ſaF:@K,Ql);j0v#dgD7.3ubfoP[Y Phǃ#٭`A7hn5 -!~ev@EU]^h𳦑'}+`Ovtw_fM(\^0QXF6P?:B'ԎC -5 妣2 cqgonV@Zebf=x8gC$48 q:v;+X:JgIl _p[zu[$gFzӳQJi6CuM&VnVCҧ{=wixkAzĆFu`T5گPI%-Un1fjM+gDž L_vkGնD_Ʉ fڂE%V*jC6]~:/U9/=k%LʛqU7-͢ݥCn4WBev2ϱb$[DŽc' -o 2Cu":#[pp(LzD#ughǞ[IzzzpwDahYXA-bjuG0 hqп54ɶz6xE!Y k閷~dCk+A -8o|PHUw{fdl)&JPb+Q )ު! -b>wz08Cq̣z24|"1[gCTnd̆3odLK򚜙o-z1^>1[t{)ĹB1?jj|JG-E`e7@9Ꮌ_ fF3=h~F[!Y`}Iٌ7.,蟿b!_NWw/:#+k}ISvOldяy<8†Y!b!S#r/XPZ>&}&&VkZ"IXK_Y8Hbr1zl&`4zI`U7vO?te QCq8֌!&OuA@5@)k.cd]#;.t!+P)NQ),d:.vjNCSVvS^#8[NE(ߕ|$5k# jީ5^jh%wd=ۮC\"zMV!f1(`j}-<,WRѻbuNpЌ>Q`7b;2UtԦXJVCGQɖq|kؽNPQߒcKpk曈0n j;5Ԃͨ3Ëu)^CK~MJ7Q(.DwH 1E/h$kBL Doe+KE3g6#hasI%7[x_qTTd #@"6FwʴMӑRBAR]6fbW;XX_\q!F_ubo $卆{WlӸKR) -UD~(Ui^Sx'd\BJ|ܰM}+r?qNtaApl{253kyw7kgަP \$U 95u3̟o4oاaorɑ?Xt0Э$ --̛O+ru:X\k`g0A{!q&_V0U!!lA$&8u(N4(~wXtT> -stream -\Ȳ|Ïtӧ7D,W}#ɛXN775 RGbZp?zOeF""}VODū> -stream -!D_+ 8~GA~"ƒxUAAlyjsJ֦2o^N b1M/&yوKd\60C_ Ja5Nޔ6@urHꝺ]j$Lg3j# ۖJݹ0omtQ:Gu$-qDE> KsBc#ejA`ĩq;xCO,N9чzmA C~jɀ3FL7P*;5,x9ɸ*<;vWPԅFU {hd7B_13B8E# ;+T|M -%}qvt+)Qf#}r!ҷq&TǘJ*zlC]Z,E}4Q?~#;s>̼ 7p -Z\T iFX?eQ/fئ[6@Y}"4R3phy"^Kt=)&} g0r[lq8:[zkYxs1hHКKٻ)OݽL[`ГDN^;B#pVH'ь+"8Wf@b^_DO6W:RFĴ38Js<ث6gCC^l||=7_]2iB=ѤT yb"3 =Sw0NkZr*&P2V33B)dY![(~ns)lk,>zwb[1j/sATxp_Z!cwpZo5 -I^Qb )duDF S~a!Oܑc^*'PE-77:[24%֢yI'!+sO1==%,;]6 X12*jn?x[d41AISޅ'SJ+@ļnzajڅY*!0q-hȐFbsruL꽿I [ĉytaۉnm3f19n4FZ>Z5IusAr="V/ - ~W&V̦N=֪si1`_aj6FMl#$)~DY նI50( A!vdH - y {mO?QKnmQ- O#*:%9-)W a1{.kjNoSj -9^WJzi{XM".CtnQEֈO&Up▞=aAGz}IŔo vv~s2pG]yE?n`%^ڌ]iSϕcx`d:K!$?|؋ӿ?v}*-i/6D8%I:]ݤ;[z˙N6APl9&3Lў<95+pA^mey,s=7,@.hg ITܒbVX>x4S9RM`[Zz7sx ėiNhfx5:*7-RD$-FMe@# -ms`Jg.lK]Q:Jſ9{xȗ姘hB3> -stream -;׆UE~>{RGfZ4P>b1Nxjn5&MN$$A3+1@G/f/|l_+tq`zڿC]lrҥਂZ@>W]΍D05Tu6Xx3n#5%Rhl448X #R%RZ8US-<%5DEJP/S"? ЉUF+ʔ<\q0 lWOX\'wnkEc|TY}9bT.-b,*M;XbY3ښ+qjDendstream -endobj -4567 0 obj -<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O /OE <853ceb41eed172d847488443baa7f03f471053035dc1cb7c0228ab1962998880> /P -4 /Perms <68b2137c889d62330e8c621454338b63> /R 6 /StmF /StdCF /StrF /StdCF /U <6ee6d2f024a6403b5e93c37a4837ea01ccf6991b6b4bbca4c3c259b25dc985f77677fbb6330d5f821f083b0424b0f48d> /UE <42dfa2ff6cb1944b78829f50ff9a1eb31226b173a5783c2ef6555b701e3ea670> /V 5 >> -endobj -4568 0 obj -<< /Type /XRef /Length 1767 /Filter /FlateDecode /DecodeParms << /Columns 6 /Predictor 12 >> /W [ 1 3 2 ] /Info 3610 0 R /Root 1 0 R /Size 4569 /ID [<0aa4dd8f5c294255fd18a94eedd58c1a><0aa4dd8f5c294255fd18a94eedd58c1a>] /Encrypt 4567 0 R >> -stream -x];F%'jaN 82s:zueh 8u F'9ذyU vb,_=/ #H J/ !_@@@@=B{#D Gp/ !_@@@@=B{WiWQfȆ6{4CYh% $qG[RmwaV^fHۆh3gldJ6mY %fb!b.lCj͕dY^E7;r`%ܚؒ -NE m0 CͮOKU-膬"l/[v}h՞iSx%@>>y{u &"KZ -\R`;D6%UJVHdjbCs'P -fKr.((%ԝx!a'"\8bp^KLf]ϑ<FYu=RtMti ~^$9o6:`nY/1]o%7#?]AFl@k("F*qY -TX~ - <{=x,NsnĽazgI*ǟZ4ídѴK#nZ)ѷ3,3۷c/m_fſlm{bWWn˯vBY -endstream -endobj -startxref -2423656 -%%EOF diff --git a/output/documentation_complete.html b/output/documentation_complete.html deleted file mode 100644 index 1490aa1..0000000 --- a/output/documentation_complete.html +++ /dev/null @@ -1,30529 +0,0 @@ - - - - - Documentation RoadWave - - - -

Documentation RoadWave

-
-

Table des matières

-
-
-
- -

RoadWave

-

Réseau social audio géolocalisé pour les usagers de la route.

-

Concept

-

RoadWave permet aux conducteurs d'écouter du contenu audio contextuel pendant leurs trajets. La navigation se fait par commandes au volant (suivant/précédent), inspirée des réseaux à scroll infini.

-

Le contenu est diffusé en fonction de la position géographique de l'utilisateur et de ses centres d'intérêt.

-
-

Cas d'usage

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UtilisateurScénario
ConducteurÉcoute contenu audio en conduisant, navigation par commandes au volant (suivant/précédent), reçoit notifications géolocalisées en passant près de points d'intérêt
RoutierÉcoute podcasts et radios live pendant ses trajets longue distance
Touriste à piedVisite guidée audio d'un musée, monument ou ville : choisit parmi plusieurs guides, navigue entre séquences à son rythme (tactile/vocal), reçoit notification push quand un audio-guide est disponible à proximité
CommerçantDiffuse une publicité audio ciblée GPS devant son commerce
Passionné autoDécouvre du contenu automobile près de circuits ou concessionnaires
Habitant localPartage anecdotes ou bons plans géolocalisés dans son quartier
Média traditionnelLe Monde, Le Parisien diffusent actualités géolocalisées ou nationales
-
-

Utilisateurs

-

Tout utilisateur peut écouter et créer du contenu (rôle flexible).

- - - - - - - - - - - - - - - - - - - - - - - - - -
RôleDescription
AuditeurÉcoute, like, s'abonne à des créateurs, signale des contenus
CréateurPublie du contenu audio géolocalisé (individus, médias traditionnels)
PublicitaireDiffuse des publicités ciblées géographiquement
ModérateurValide et modère les contenus signalés
-
-

Types de contenu

- - - - - - - - - - - - - - - - - - - - - - - - - -
TypeDescription
Contenu courtAudio de quelques secondes à quelques minutes
PodcastÉpisodes plus longs, séries thématiques
Radio liveDiffusion en direct avec synchronisation approximative entre auditeurs
Audio-guideVisite guidée multiséquence (musée, monument, ville) : plusieurs séquences numérotées, navigation manuelle entre pistes, liste complète visible, guidage vocal entre points d'intérêt
-
-

Géolocalisation

-

Le créateur définit la zone de diffusion de son contenu :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NiveauPortée
Point GPSRayon précis autour d'une coordonnée
VilleDiffusion dans une ville
DépartementDiffusion départementale
RégionDiffusion régionale
PaysDiffusion nationale
-

Priorité de diffusion : plus la zone est précise, plus le contenu a de chances d'être diffusé (GPS > ville > département > région > pays).

-
-

Algorithme de recommandation

-

Le contenu proposé est calculé via un score combiné :

-
    -
  • Proximité géographique : distance entre l'utilisateur et la zone du contenu
  • -
  • Pertinence des intérêts : correspondance avec les centres d'intérêt de l'utilisateur
  • -
-

Lorsque plusieurs contenus sont disponibles dans une zone, seul le plus pertinent est diffusé.

-
-

Centres d'intérêt

-

Chaque utilisateur possède des jauges d'intérêt qui évoluent dynamiquement :

-

Catégories

-
    -
  • Automobile
  • -
  • Voyage
  • -
  • Famille
  • -
  • Amour
  • -
  • Musique
  • -
  • Économie
  • -
  • Cryptomonnaie
  • -
  • Politique
  • -
  • ... (extensible)
  • -
-

Évolution des jauges

- - - - - - - - - - - - - - - - - - - - - - - - - -
ActionEffet
Temps d'écoute longAugmente la jauge
LikeAugmente la jauge
AbonnementAugmente fortement la jauge
Skip rapideDiminue la jauge
-

Les créateurs taguent leur contenu avec des centres d'intérêt. L'algorithme privilégie les correspondances mais n'exclut pas les utilisateurs sans correspondance.

-
-

Interactions

-

Commandes au volant (conduite)

-

Interactions simplifiées pour sécurité routière maximale :

- - - - - - - - - - - - - - - - - - - - - -
CommandeAction
SuivantPasser au contenu suivant
PrécédentRevenir au contenu précédent
Play/PauseMettre en pause / reprendre la lecture
-

Like automatique : Le système détecte automatiquement vos préférences selon votre temps d'écoute : -- Écoute ≥80% du contenu → Like renforcé (+2 points jauge) -- Écoute 30-79% du contenu → Like standard (+1 point jauge) -- Skip après <10s → Signal négatif (-0.5 point)

-
-

Voir ADR-010 pour les détails techniques

-
-

Actions complémentaires (application à l'arrêt)

- - - - - - - - - - - - - - - - - - - - - - - - - -
ActionDescription
Like expliciteBouton cœur pour liker manuellement
S'abonnerSuivre un créateur
SignalerSignaler un contenu inapproprié
UnlikeRetirer un like
-
-

Publicités

-
    -
  • Insertion entre deux contenus uniquement (jamais d'interruption)
  • -
  • Ciblage géographique : point GPS, ville, département, région ou national
  • -
  • Interface dédiée pour les publicitaires
  • -
-
-

Radio live

-
    -
  • Diffusion en direct par des créateurs
  • -
  • Buffering pour garantir une écoute fluide
  • -
  • Synchronisation approximative entre les auditeurs (quelques secondes de décalage possible)
  • -
-
-

Modération

-

Approche hybride combinant participation communautaire, IA et modérateurs dédiés.

-

Contenus prohibés

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CatégorieDescription
Haine et violenceIncitation à la haine, violence, discrimination
Contenu sexuelPornographie ou contenu sexuellement explicite
IllégalitéApologie du terrorisme, actes criminels
Désinformation dangereuseFausses informations sur la santé, sécurité routière
HarcèlementMenaces, intimidation, doxxing
Droits d'auteurViolation de propriété intellectuelle
FraudeArnaques, escroqueries
-

Rôles de modération

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RôleCapacités
Auditeur lambdaSignaler un contenu (1 clic)
Auditeur de confianceSignalements priorisés après historique positif
Modérateur juniorTraiter signalements simples (spam, contenu évident)
Modérateur seniorCas complexes, appels, décisions de ban
Admin modérationDéfinir les règles, superviser l'équipe
-

Flux de modération

-
1. Auditeur signale → File d'attente
-2. IA pré-filtre → Cas évidents traités automatiquement
-3. Modérateur junior → Traite 80% des cas restants
-4. Modérateur senior → Cas complexes + recours
-
-

Outils de modération automatique

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OutilFonction
Transcription audioConversion automatique en texte pour analyse
Analyse vocale IADétection de ton agressif, cris, insultes
Empreinte audioDétection de contenus déjà modérés (réupload)
Détection droits d'auteurIdentification automatique de musique protégée
Filtrage mots-clésListe noire de termes inappropriés
-

Modération préventive

-
    -
  • Nouveaux créateurs : validation manuelle des 3 premiers contenus
  • -
  • Score de confiance : évolution selon l'historique du créateur
  • -
  • Publicités : validation manuelle obligatoire avant diffusion
  • -
-

Système de strikes

- - - - - - - - - - - - - - - - - - - - - - - - - -
StrikeSanction
Strike 1Avertissement + formation modération
Strike 2Suspension 7 jours + contenu supprimé
Strike 3Suspension 30 jours
Strike 4Ban définitif
-
    -
  • Réhabilitation : -1 strike tous les 6 mois sans incident
  • -
-

Priorisation des signalements

- - - - - - - - - - - - - - - - - - - - - - - - - -
PrioritéType de contenu
CRITIQUEViolence, suicide, mise en danger immédiate
HAUTEHarcèlement, haine, désinformation
MOYENNESpam, contenu inapproprié
BASSEQualité audio, tags incorrects
-

Transparence et recours

-
    -
  • Notification explicite lors de suppression (raison détaillée)
  • -
  • Processus d'appel : le créateur peut contester une décision
  • -
  • Délai de traitement : 48-72h pour les recours
  • -
  • Historique : tableau de bord des sanctions pour le créateur
  • -
-

Modération communautaire

-
    -
  • Utilisateurs de confiance : signalements priorisés après historique positif
  • -
  • Récompenses : badges, réduction premium pour signalements pertinents
  • -
  • Lutte contre les signalements abusifs (sanctions possibles)
  • -
-
-

Modèle économique

-

Offres

- - - - - - - - - - - - - - - - - -
FormuleDescription
GratuitAccès complet avec publicités entre les contenus
PremiumSans publicité + accès aux contenus exclusifs
-

Monétisation créateurs

-
    -
  • Partage des revenus pub : rémunération basée sur le nombre d'écoutes
  • -
  • Pourboires : les auditeurs peuvent faire des dons aux créateurs
  • -
-
-

Conformité RGPD

-

Données collectées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DonnéeFinalitéBase légale
Position GPSDiffusion de contenu géolocaliséConsentement
Historique d'écoutePersonnalisation des recommandationsIntérêt légitime
Centres d'intérêtAlgorithme de recommandationConsentement
Identité créateurPublication de contenuExécution du contrat
-

Droits des utilisateurs

-
    -
  • Accès : consulter toutes ses données personnelles
  • -
  • Rectification : modifier ses informations
  • -
  • Suppression : supprimer son compte et toutes ses données
  • -
  • Portabilité : exporter ses données dans un format standard
  • -
  • Opposition : désactiver le profilage publicitaire
  • -
-

Mesures techniques

-
    -
  • Consentement explicite requis pour la géolocalisation
  • -
  • Anonymisation des données de localisation après 24h (sauf historique personnel)
  • -
  • Possibilité d'utiliser l'app en mode dégradé (sans géolocalisation précise)
  • -
  • Données hébergées dans l'UE
  • -
-
- -

RoadWave - Architecture Technique

-
-

Les décisions techniques sont documentées dans docs/adr/

-
-

Stack Technologique

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComposantTechnologieADR
BackendGo + FiberADR-001
Architecture BackendMonolithe ModulaireADR-012
AuthentificationZitadelADR-008
StreamingHLSADR-002
CodecOpusADR-003
CDNBunny CDNADR-004
Base de donnéesPostgreSQL + PostGISADR-005
ORM/Accès donnéessqlcADR-013
CacheRedis ClusterADR-005
ChiffrementTLS 1.3ADR-006
LiveWebRTCADR-002
Frontend MobileFlutterADR-014
TestsTestify + Godog (Gherkin)ADR-015, ADR-007
PaiementsMangopayADR-009
Commandes volantLike automatiqueADR-010
Conformité storesCarPlay, Android Auto, App/Play StoreADR-011
-
-

Streaming Audio

-

Protocole : HLS (HTTP Live Streaming)

-
    -
  • Fonctionne à travers firewalls et réseaux mobiles instables
  • -
  • Cache CDN natif (réduction des coûts)
  • -
  • Bitrate adaptatif automatique (tunnels, zones rurales)
  • -
  • Support natif iOS/Android
  • -
-

Codec : Opus

-

Optimisé pour la voix en environnement bruyant (voiture).

- - - - - - - - - - - - - - - - - - - - - - - - - -
QualitéBitrateUsage
Basse24 kbps2G/Edge
Standard48 kbps3G
Haute64 kbps4G/5G
-

Fallback AAC-LC pour appareils legacy.

-

Buffering Adaptatif

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RéseauBuffer minBuffer cibleBuffer max
WiFi5s30s120s
4G/5G10s45s120s
3G30s90s300s
-
-

Sécurité

-

Chiffrement

-
    -
  • TLS 1.3 sur tous les endpoints (overhead ~1-2%)
  • -
  • DTLS-SRTP pour WebRTC (radio live)
  • -
  • Pas de DRM initialement (ajout si licences l'exigent)
  • -
-

Authentification

-
    -
  • Zitadel (self-hosted) pour IAM
  • -
  • JWT validation locale (zitadel-go SDK)
  • -
  • OAuth2 PKCE pour mobile (iOS/Android)
  • -
  • MFA et passkeys disponibles
  • -
  • Rate limiting par IP et par utilisateur (Nginx + Zitadel)
  • -
-
-

Base de Données

-

PostgreSQL + PostGIS

-
-- Requête géolocalisée typique
-SELECT id, ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
-FROM contents
-WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
-ORDER BY distance
-LIMIT 20;
-
-

Redis Geospatial (Cache)

-
GEOADD contents:geo longitude latitude content_id
-GEORADIUS contents:geo user_lon user_lat 50 km WITHDIST COUNT 20 ASC
-
-

TTL cache : 5 minutes (le contenu ne bouge pas).

-
-

Architecture Services

-
┌─────────────────┐
-│   Bunny CDN     │  Cache HLS, distribution globale
-└────────┬────────┘
-         │
-┌────────┴────────┐
-│   Nginx         │  SSL, rate limiting, reverse proxy
-└────────┬────────┘
-         │
-┌────────┴────────┐
-│   API Gateway   │  Go + Fiber
-└────────┬────────┘
-         │
-    ┌────┴────┬─────────────┐
-    │         │             │
-┌───▼───┐ ┌───▼───┐ ┌───────▼───────┐
-│ Auth  │ │ User  │ │ Content/Geo   │
-│Service│ │Service│ │ Service       │
-└───────┘ └───────┘ └───────────────┘
-    │         │             │
-    └─────────┴─────────────┘
-              │
-    ┌─────────┴─────────┐
-    │                   │
-┌───▼───┐         ┌─────▼─────┐
-│ Redis │         │ PostgreSQL│
-│Cluster│         │ + PostGIS │
-└───────┘         └───────────┘
-
-
-

Scaling 10M Utilisateurs

-

Stratégie par phase

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhaseUtilisateursInfraCoût estimé
MVP0-100KMonolithe Go, PostgreSQL managé + Zitadel, Bunny CDN/Storage50-150€/mois
Growth100K-1MKubernetes managé, replicas multi-région2-5K€/mois
Scale1M-10MMulti-région, Nginx origin shield, Bunny CDN20-50K€/mois
-

Métriques cibles

- - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueObjectif
Latence API p99< 100ms
Temps de démarrage audio< 3s
Disponibilité99.9%
Connexions/serveur100K+
-
-

Points de vigilance

-
    -
  1. Buffering mobile : Pré-chargement agressif avant tunnels (détection GPS)
  2. -
  3. Handoff réseau : Buffer suffisant pour survivre aux changements de cellule
  4. -
  5. Mode offline : Téléchargement complet sur WiFi
  6. -
  7. Bande passante : 48 kbps Opus = ~20 MB/heure (faible consommation data)
  8. -
-
-

Pourquoi pas UDP brut ?

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UDPHLS/TCP
Latence minimaleLatence acceptable (5-30s)
Problèmes NAT/firewallPasse partout
Perte de paquets = artefactsRetransmission automatique
Pas de cache CDNCache CDN = économies
Complexité++Standard de l'industrie
-

Pour du contenu non-interactif (podcasts, audio-guides), la latence HLS est acceptable. WebRTC réservé à la radio live uniquement.

-
- -

ADR-001 : Langage Backend

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé.

-

Décision

-

Go avec le framework Fiber.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionPerformanceSimplicitéÉcosystème
Go + Fiber1M+ conn/serveurÉlevéeExcellent cloud-native
Rust + Tokio2M+ conn/serveurFaibleBon
Node.js100-500K connÉlevéeExcellent
Elixir/Phoenix2M+ connMoyenneBon temps réel
-

Justification

-
    -
  • Performance : Go gère 1M+ connexions par serveur avec ~10KB/connexion
  • -
  • Simplicité : Syntaxe claire, compilation rapide, facile à recruter
  • -
  • Écosystème : First-class Kubernetes, tooling natif (profiling, race detection)
  • -
  • Équilibre : Meilleur compromis performance/simplicité pour une startup
  • -
-

Conséquences

-
    -
  • Formation équipe sur Go si nécessaire
  • -
  • Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis
  • -
-
- -

ADR-002 : Protocole de Streaming

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire).

-

Décision

-

HLS (HTTP Live Streaming) pour le contenu à la demande. -WebRTC réservé à la radio live.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionLatenceFiabilité mobileCache CDNComplexité
HLS5-30sExcellenteOuiFaible
DASH5-30sBonneOuiMoyenne
WebRTC<500msMoyenneNonÉlevée
UDP brutMinimaleFaibleNonTrès élevée
-

Justification

-
    -
  • Réseaux mobiles : HLS gère les coupures et changements de cellule nativement
  • -
  • Cache CDN : Segments .ts cachables = réduction des coûts
  • -
  • Compatibilité : Support natif iOS/Android
  • -
  • Bitrate adaptatif : Ajustement automatique selon la qualité réseau
  • -
-

Pourquoi pas UDP ?

-
    -
  • Problèmes NAT/firewall sur réseaux mobiles
  • -
  • Perte de paquets = artefacts audio
  • -
  • Impossible à cacher sur CDN
  • -
  • Complexité sans bénéfice pour du contenu non-interactif
  • -
-

Conséquences

-
    -
  • Latence de 5-30s acceptable pour podcasts/audio-guides
  • -
  • WebRTC à implémenter séparément pour la radio live
  • -
-
- -

ADR-003 : Codec Audio

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire.

-

Décision

-

Opus comme codec principal, AAC-LC en fallback.

-

Profils d'encodage

- - - - - - - - - - - - - - - - - - - - - - - - - -
QualitéBitrateUsage
Basse24 kbps2G/Edge
Standard48 kbps3G
Haute64 kbps4G/5G
-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CodecBitrateQualité voixSupport mobile
Opus24-64 kbpsExcellenteAndroid natif, iOS via libs
AAC-LC64-128 kbpsBonneUniversel
AAC-HE v232-64 kbpsTrès bonneBon
MP3128-320 kbpsCorrecteUniversel (legacy)
-

Justification

-
    -
  • Environnement bruyant : Opus intègre des algorithmes de résilience au bruit
  • -
  • Bande passante : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix
  • -
  • Consommation data : ~20 MB/heure à 48 kbps
  • -
  • Latence : 2.5-60ms, idéal pour streaming adaptatif
  • -
-

Conséquences

-
    -
  • Fallback AAC-LC pour appareils legacy
  • -
  • Pipeline d'encodage à prévoir côté ingestion
  • -
-
- -

ADR-004 : CDN

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et indépendance vis-à-vis des géants du cloud.

-

Décision

-

Bunny CDN comme CDN principal.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SolutionCoût/mois (100TB)SetupPerformanceDépendance
Bunny CDN~1 000€15 minTrès bonFaible
Cloudflare0-5 000€5 minExcellentMoyenne
CloudFront~9 750€1hExcellentForte (AWS)
Fastly~12-20 000€2hExceptionnelMoyenne
Nginx self-hosted~2-5 000€1 jourExcellentAucune
-

Justification

-
    -
  • Coût : 10x moins cher que CloudFront
  • -
  • HLS natif : Support optimisé pour le streaming
  • -
  • Simplicité : Setup en 15 minutes, zéro maintenance
  • -
  • Européen : Conforme RGPD, 114 PoPs
  • -
  • Pas de lock-in : Migration facile si besoin
  • -
-

Évolution prévue

-
    -
  1. Phase 1 (0-1M users) : Bunny CDN seul
  2. -
  3. Phase 2 (1-5M users) : Ajout Nginx origin shield si nécessaire
  4. -
  5. Phase 3 (5M+) : Évaluation multi-CDN
  6. -
-

Conséquences

-
    -
  • Configuration des règles de cache pour .m3u8 (TTL court) et .ts (TTL long)
  • -
  • Token authentication pour protéger les segments
  • -
-
- -

ADR-005 : Base de Données

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute.

-

Décision

-
    -
  • PostgreSQL + PostGIS : Données persistantes et requêtes géospatiales
  • -
  • Redis Cluster : Cache géolocalisation et sessions
  • -
-

Architecture

-
Requête → Redis Cache → [HIT] → Réponse
-              ↓
-           [MISS]
-              ↓
-          PostGIS → Cache → Réponse
-
-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UsageOption choisieAlternatives
Données utilisateursPostgreSQLMySQL, MongoDB
GéolocalisationPostGISMongoDB Geo, Elasticsearch
CacheRedisMemcached, KeyDB
Analytics (futur)ClickHouseTimescaleDB
-

Justification

-

PostgreSQL + PostGIS

-
    -
  • Requêtes géospatiales complexes et précises
  • -
  • Index GIST pour performance
  • -
  • ACID, fiabilité éprouvée
  • -
  • Écosystème mature
  • -
-

Redis

-
    -
  • Cache géo natif (GEORADIUS) : 100K+ requêtes/sec
  • -
  • Sessions utilisateurs
  • -
  • Pub/sub pour temps réel
  • -
-

Exemple de requête

-
SELECT id, name,
-       ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
-FROM contents
-WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
-ORDER BY distance
-LIMIT 20;
-
-

Conséquences

-
    -
  • TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas)
  • -
  • Index GIST sur colonnes géométriques
  • -
  • Réplication read replicas pour scaling lecture
  • -
-
- -

ADR-006 : Chiffrement

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu.

-

Décision

-
    -
  • TLS 1.3 sur tous les endpoints
  • -
  • DTLS-SRTP pour WebRTC (radio live)
  • -
  • Pas de DRM au lancement
  • -
-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MéthodeOverheadUsage
TLS 1.3~1-2% CPUHTTPS streaming
DTLS-SRTP~3-5% CPUWebRTC temps réel
AES-128-CBCMinimalChiffrement segments HLS
Widevine/FairPlayModéréDRM (si licences l'exigent)
-

Justification

-

Pourquoi chiffrer ?

-
    -
  • RGPD : Protection des données utilisateurs obligatoire
  • -
  • Confiance : Standard attendu en 2025
  • -
  • Intégrité : Empêche injection de contenu par opérateurs
  • -
  • Overhead minimal : TLS 1.3 optimisé, impact négligeable
  • -
-

Pourquoi pas de DRM ?

-
    -
  • Contenu généré par utilisateurs (pas de licences)
  • -
  • Complexité et coût d'intégration Widevine/FairPlay
  • -
  • À reconsidérer si partenariats avec labels/éditeurs
  • -
-

Conséquences

-
    -
  • Certificats SSL gérés par Bunny CDN ou Let's Encrypt
  • -
  • Configuration TLS 1.3 sur Nginx/API
  • -
  • DTLS-SRTP à implémenter pour le module radio live
  • -
-
- -

ADR-007 : Tests et Spécifications Exécutables

-

Statut : Accepté -Date : 2025-01-17

-

Contexte

-

RoadWave nécessite une documentation des use cases qui soit à la fois lisible par tous les stakeholders et vérifiable automatiquement. Les scénarios utilisateurs (touriste, routier, commerçant) doivent être validés en continu.

-

Décision

-

Gherkin pour les spécifications avec Godog comme runner de tests.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionLisibilitéIntégration GoMaintenance
Gherkin + GodogExcellenteNativeFaible
Gauge (Markdown)BonnePluginMoyenne
Tests Go natifsFaible (devs only)NativeFaible
ConcordionBonneJava-centricÉlevée
-

Justification

-
    -
  • Living Documentation : Les fichiers .feature servent de documentation ET de tests
  • -
  • Accessibilité : Syntaxe Given/When/Then lisible par PO, devs, testeurs
  • -
  • Cohérence stack : Godog est le standard BDD pour Go
  • -
  • CI/CD : Intégration simple dans les pipelines
  • -
-

Structure

-
features/
-├── recommendation/
-│   ├── geolocalisation.feature
-│   └── interets.feature
-├── streaming/
-│   ├── lecture.feature
-│   └── buffering.feature
-├── moderation/
-│   └── signalement.feature
-└── steps/
-    └── steps.go
-
-

Exemple

-
Feature: Recommandation géolocalisée
-
-  Scenario: Touriste près d'un monument
-    Given un utilisateur avec l'intérêt "tourisme" à 80%
-    And une position GPS à 100m de la Tour Eiffel
-    When le système calcule les recommandations
-    Then l'audio guide "Histoire de la Tour Eiffel" est en première position
-
-

Conséquences

-
    -
  • Dépendance : github.com/cucumber/godog
  • -
  • Les use cases du README doivent être traduits en .feature
  • -
  • CI exécute godog run avant chaque merge
  • -
-
- -

ADR-008 : Authentification et Gestion d'Identité

-

Statut : Accepté -Date : 2025-01-18

-

Contexte

-

RoadWave nécessite un système d'authentification sécurisé pour mobile (iOS/Android), scalable jusqu'à 10M utilisateurs, avec contraintes de coût réduit et conformité RGPD.

-

Décision

-

Zitadel (self-hosted) pour l'IAM avec validation JWT locale côté API Go.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SolutionCoût (10M users)PerformanceSimplicitéIntégration Go
Zitadel200-500€/moisExcellenteÉlevéeSDK natif
Supabase Auth32K€/moisExcellenteÉlevéeREST API
Keycloak200-800€/moisBonneFaibleLib tierce
Auth050K€+/moisExcellenteÉlevéeSDK natif
JWT Custom0€ (dev)ExcellenteMoyenneNatif
-

Justification

-
    -
  • Coût maîtrisé : 100x moins cher que Supabase/Auth0 à 10M users
  • -
  • Performance : JWT validation locale = 0 latence auth sur chaque requête API
  • -
  • Stack alignée : Go + PostgreSQL + Redis (déjà dans RoadWave)
  • -
  • Scalabilité prouvée : Clients avec 2.3M tenants, architecture event-sourced
  • -
  • RGPD natif : Entreprise suisse, data residency EU, DPA fourni
  • -
  • Standards ouverts : OpenID Connect certifié (pas de vendor lock-in)
  • -
-

Architecture

-
┌─────────────────┐
-│  Mobile Apps    │  OAuth2 PKCE + Refresh tokens
-└────────┬────────┘
-         │
-┌────────▼────────┐
-│  Zitadel IdP    │  PostgreSQL + Redis
-│  (self-hosted)  │  MFA, passkeys, SSO
-└────────┬────────┘
-         │ JWT token
-┌────────▼────────┐
-│  Go + Fiber API │  Validation JWT locale
-│  (RoadWave)     │  github.com/zitadel/zitadel-go
-└─────────────────┘
-
-

Exemple d'intégration

-
import "github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
-
-// Validation JWT locale haute performance
-verifier := oauth.WithJWT(config)
-app.Use(verifier.Middleware())
-
-// Accès aux claims
-userID := ctx.Locals("sub").(string)
-
-

Conséquences

-
    -
  • Déploiement Docker Compose pour MVP
  • -
  • Migration vers Kubernetes HA en production
  • -
  • Gestion refresh tokens (rotation automatique)
  • -
  • MFA et passkeys disponibles out-of-the-box
  • -
  • Rate limiting intégré à Zitadel
  • -
-
- -

ADR-009 : Solution de Paiement et Gestion des Abonnements

-

Statut : Accepté -Date : 2025-01-19

-

Contexte

-

RoadWave nécessite une solution de paiement pour gérer les abonnements Premium (4.99€/mois) et reverser 70% des revenus aux créateurs de contenu. Besoin de marketplace natif (split payments), KYC automatique, conformité RGPD, et coûts maîtrisés.

-

Décision

-

Mangopay (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SolutionCoût transactionMarketplaceKYCSouveraineté
Mangopay1.8% + 0.18€✅ Natif✅ Gratuit🇪🇺 France/LU
Stripe Connect2.9% + 0.30€✅ Natif❌ 1.20€🇺🇸 USA
Mollie2.9% + 0.29€❌ Non❌ Non🇪🇺 Pays-Bas
Paddle5% + 0.50€✅ Natif✅ Inclus🇬🇧 UK
-

Justification

-
    -
  • 38% moins cher que Stripe (1.8% vs 2.9%)
  • -
  • Marketplace natif : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits
  • -
  • KYC gratuit : vérification d'identité incluse (vs 1.20€/créateur chez Stripe)
  • -
  • Souveraineté EU : France/Luxembourg, régulé ACPR, RGPD natif
  • -
  • Conformité DAC7 : reporting fiscal automatique
  • -
  • Spécialisé marketplace : utilisé par Vinted, Ulule, ManoMano
  • -
-

Architecture

-
┌────────────────────────┐
-│   Utilisateurs Premium │  4.99€/mois
-└───────────┬────────────┘
-            │
-    ┌───────▼───────┐
-    │   Mangopay    │  - Abonnements récurrents
-    │               │  - KYC créateurs (gratuit)
-    │               │  - E-wallets automatiques
-    └───────┬───────┘  - Payouts SEPA (gratuits)
-            │
-  ┌─────────┼─────────┐
-  │         │         │
-┌─▼───┐  ┌─▼───┐  ┌─▼────┐
-│Créa │  │Créa │  │Plate-│
-│teur │  │teur │  │forme │
-│  A  │  │  B  │  │(30%) │
-│(70%)│  │(70%)│  │      │
-└─────┘  └─────┘  └──────┘
-
-

Exemple intégration

-
// Abonnement récurrent
-POST /v2.01/{ClientId}/recurringpayinregistrations
-{
-  "AuthorId": "{UserId}",
-  "FirstTransactionDebitedFunds": {"Currency": "EUR", "Amount": 499}
-}
-
-// Transfer vers créateur (70%)
-POST /v2.01/{ClientId}/transfers
-{
-  "DebitedWalletId": "{PlatformWalletId}",
-  "CreditedWalletId": "{CreatorWalletId}",
-  "DebitedFunds": {"Currency": "EUR", "Amount": 349}
-}
-
-// Payout SEPA gratuit
-POST /v2.01/{ClientId}/payouts/bankwire
-
-

Conséquences

-
    -
  • Solution tout-en-un : 1 seul prestataire vs 2-3
  • -
  • Économie de 2160€/an sur 1000 abonnés (vs Stripe)
  • -
  • Délai activation compte : 2-5 jours
  • -
  • Intégration Go via REST API (pas de SDK Go officiel)
  • -
  • Apple/Google IAP gérés séparément (comme toute solution de paiement)
  • -
-
- -

ADR-010 : Commandes au volant et likes

-

Statut : Accepté -Date : 2026-01-20

-

Contexte

-

RoadWave est utilisée en conduisant. Les utilisateurs doivent pouvoir liker du contenu pour améliorer les recommandations, mais les commandes au volant ont des limitations : -- 40% des véhicules n'ont que Suivant/Précédent/Mute -- iOS/Android ne supportent pas nativement les appuis longs ou doubles-appuis -- La sécurité impose des interactions minimales

-

Décision

-

Like automatique basé sur le temps d'écoute.

-

Règles : -- ≥80% d'écoute → Like renforcé (+2 points) -- 30-79% d'écoute → Like standard (+1 point) -- <30% d'écoute → Pas de like -- Skip <10s → Signal négatif (-0.5 point)

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionCompatibilitéSécuritéComplexité
Like automatique100%MaximaleFaible
Double-tap Pause~80%MoyenneMoyenne
Appui long Suivant~95%FaibleÉlevée
Configuration paramétrable100%VariableTrès élevée
-

Justification

-
    -
  • Sécurité maximale : Aucune action complexe en conduite
  • -
  • Compatibilité universelle : Fonctionne sur 100% des véhicules
  • -
  • UX intuitive : Comportement standard (Spotify, YouTube Music)
  • -
  • Engagement : Tous les contenus génèrent des signaux
  • -
  • Simplicité : Une seule logique à implémenter et maintenir
  • -
-

Conséquences

-
    -
  • Tracking du temps d'écoute via le player audio
  • -
  • Calcul du score côté backend basé sur completion_rate
  • -
  • Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute"
  • -
  • Possibilité de like manuel depuis l'app (à l'arrêt)
  • -
  • Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs
  • -
-
- -

ADR-011 : Conformité App Stores et Plateformes Auto

-

Statut : Accepté avec actions requises -Date : 2026-01-20

-

Contexte

-

RoadWave est une app audio géolocalisée utilisée en conduite (CarPlay/Android Auto) avec : -- Contenu généré par utilisateurs (UGC) -- Monétisation : publicités géolocalisées + Premium (4.99€ web / 5.99€ IAP) -- GPS en arrière-plan -- Partage de revenus avec créateurs (70/30)

-

Décision

-

Stratégie de conformité multi-plateforme avec : -- Modération UGC robuste (IA + humain) -- Prix différenciés selon région (US/EU/Monde) -- GPS avec disclosure complète -- Paiements créateurs externes (Mangopay)

-

Plateformes analysées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PlateformeConformitéPoints critiques
Android Auto✅ ConformeAPI Level 35+ (Android 15+)
CarPlay✅ ConformeEntitlement audio à demander
Google Play⚠️ Actions requisesDéclaration GPS + UGC modération
App Store⚠️ Actions requisesPrix différenciés US/EU
-

Conformité détaillée

-

Android Auto / CarPlay ✅

-
    -
  • 100% audio (pas de vidéo)
  • -
  • Commandes standard au volant
  • -
  • Aucun achat in-car
  • -
  • Like automatique = sécurité maximale
  • -
  • Notifications géolocalisées : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel)
  • -
  • Action : Demander CarPlay Audio Entitlement (Apple)
  • -
-

Google Play ⚠️

-

UGC (critique) : -- Modération hybride IA + humain ✅ -- 3 premiers contenus validés manuellement ✅ -- Système de strikes (4 = ban) ✅ -- Signalement + blocage utilisateurs ✅

-

GPS Background (critique) : -- Permission "Always Location" = OPTIONNELLE -- Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides) -- Justification Play Console :

-
-

"RoadWave permet aux utilisateurs de recevoir des alertes audio-guides lorsqu'ils passent à pied près de monuments/musées, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée dans les paramètres." -- In-app disclosure obligatoire (écran dédié avant demande permission) -- Si refusée : app fonctionne en mode voiture uniquement -- Action : Remplir formulaire background location Play Console avec justification

-
-

Réponses formulaire Play Console :

- - - - - - - - - - - - - - - - - - - - - - - - - -
QuestionRéponse
Why does your app need background location?"RoadWave offers optional pedestrian mode: users receive push notifications when passing near audio-guide points (museums, monuments) even when app is in background. This feature is opt-in and can be disabled in settings."
Is this feature core to your app?"No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)."
What user value does this provide?"Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app."
Does a less invasive alternative exist?"Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience."
-

App Store ⚠️

-

Prix différenciés (légaux depuis 2025-2026) : -- 🇺🇸 US : Lien externe autorisé (0% commission) -- 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite) -- 🌍 Monde : IAP obligatoire (30% commission)

-

UGC : -- Mode Kids obligatoire (filtrage selon âge) ✅ -- Système de modération + signalement ✅

-

GPS Background (critique) : -- Permission "Always Location" = OPTIONNELLE -- Deux strings Info.plist requises : - - NSLocationWhenInUseUsageDescription : explication mode voiture - - NSLocationAlwaysAndWhenInUseUsageDescription : explication mode piéton (optionnel) -- In-app disclosure obligatoire avant demande "Always" -- Flux two-step : When In Use → Always (si user active mode piéton) -- Si refusée : app fonctionne en mode voiture uniquement -- Action : Voir strings détaillés dans 05-interactions-navigation.md

-

Revenus créateurs

-

Position : Paiements créateurs = "services" (comme YouTube/Uber), pas IAP -- Paiement via Mangopay Connect (externe) -- Commission stores uniquement sur Premium (IAP) -- Comparables : YouTube AdSense, TikTok Creator Fund, Uber

-

Actions bloquantes avant soumission

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionPlateformeDeadlineComplexité
Demander CarPlay Audio EntitlementAppleAvant soumission iOSFaible
Remplir formulaire background location avec justificationGoogle PlayAvant soumission AndroidFaible
Implémenter disclosure GPS (écran dédié mode piéton)iOS + AndroidMVPMoyenne
Rendre permission "Always Location" optionnelleiOS + AndroidMVPMoyenne
Désactiver overlay visuel notification en CarPlay/Android AutoiOS + AndroidMVPMoyenne
Mettre à jour strings Info.plist avec justifications détailléesiOSMVPFaible
Finaliser système modération UGCGoogle + AppleMVPÉlevée
-

Estimation totale : +5 jours développement avant soumission stores

-

Stratégie de lancement

-

Phase 1 - MVP : -- IAP uniquement (5.99€/mois mondial) -- Modération UGC active -- GPS avec disclosure -- CarPlay/Android Auto basique

-

Phase 2 - Post-validation : -- Prix différenciés US (lien externe 4.99€) -- Paiement externe EU (DMA) -- Monétisation créateurs (Mangopay)

-

Conséquences

-
    -
  • Formation équipe sur politiques stores
  • -
  • Suivi des métriques modération (% rejet, SLA)
  • -
  • Migration iOS 26 SDK (Avril 2026)
  • -
  • API Level 35 Android (2026)
  • -
  • Communication transparente GPS/publicités
  • -
-

Sources

- -
- -

ADR-012 : Architecture Backend

-

Statut : Accepté -Date : 2025-01-20

-

Contexte

-

RoadWave nécessite une architecture backend évolutive tout en gardant la simplicité opérationnelle pour un MVP. Le système doit supporter une croissance progressive de 0 à 10M utilisateurs.

-

Décision

-

Monolithe modulaire avec séparation claire en modules internes.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ArchitectureComplexitéCoûts infraTime to marketÉvolutivité
Monolithe modulaireFaibleFaibleRapide0-1M users
MicroservicesÉlevéeÉlevéeLent1M+ users
Hybrid (Mono + Workers)MoyenneMoyenneMoyen100K-5M users
-

Justification

-
    -
  • Simplicité : 1 seul binaire Go, déploiement trivial
  • -
  • Transactions : Communications inter-modules en mémoire (pas de latence réseau)
  • -
  • Debugging : Stack traces complètes, profiling unifié
  • -
  • Coûts : 1 serveur suffit pour 100K users (vs N services)
  • -
  • Refactoring : Modules internes bien séparés facilitent migration vers microservices si nécessaire
  • -
-

Structure modulaire

-
internal/
-├── auth/         # Validation JWT, intégration Zitadel
-├── user/         # Profils, centres d'intérêt
-├── content/      # CRUD contenus, métadonnées
-├── geo/          # Recherche géospatiale, algorithme
-├── streaming/    # Génération HLS, transcoding
-├── moderation/   # Signalements, workflow
-├── payment/      # Intégration Mangopay
-└── analytics/    # Métriques écoute, jauges
-
-

Chaque module suit : handler.goservice.gorepository.go.

-

Conséquences

-
    -
  • Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users)
  • -
  • Transition vers microservices possible en phase 2 (extraction progressive des modules)
  • -
  • Importance de maintenir découplage fort entre modules (interfaces claires)
  • -
-
- -

ADR-013 : ORM et Accès Données

-

Statut : Accepté -Date : 2025-01-20

-

Contexte

-

RoadWave nécessite des requêtes SQL complexes (PostGIS géospatiales) avec performance optimale et type safety. Le choix entre ORM, query builder ou SQL brut impacte maintenabilité et performance.

-

Décision

-

sqlc pour génération de code Go type-safe depuis SQL.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SolutionPerformanceType SafetyContrôle SQLCourbe apprentissage
sqlcExcellenteTrès hauteTotalFaible
GORMMoyenneMoyenneLimitéFaible
pgx + SQL brutExcellenteFaibleTotalMoyenne
sqlxBonneFaibleTotalFaible
-

Justification

-
    -
  • Performance : Génération compile-time, zero overhead runtime
  • -
  • Type safety : Structs Go générées automatiquement, erreurs détectées à la compilation
  • -
  • Contrôle SQL : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM)
  • -
  • Maintenabilité : Modifications SQL → sqlc generate → code mis à jour
  • -
  • Simplicité : Pas de magic, code généré lisible et debuggable
  • -
-

Workflow

-
-- queries/content.sql
--- name: GetContentNearby :many
-SELECT id, title, ST_Distance(location, $1::geography) as distance
-FROM contents
-WHERE ST_DWithin(location, $1::geography, $2)
-ORDER BY distance
-LIMIT $3;
-
-
sqlc generate
-
-
// Code Go type-safe généré automatiquement
-contents, err := q.GetContentNearby(ctx, location, radius, limit)
-
-

Conséquences

-
    -
  • Dépendance : github.com/sqlc-dev/sqlc
  • -
  • Fichier sqlc.yaml à la racine pour configuration
  • -
  • Migrations gérées séparément avec golang-migrate
  • -
  • CI doit exécuter sqlc generate pour valider cohérence SQL/Go
  • -
-
- -

ADR-014 : Frontend Mobile

-

Statut : Accepté -Date : 2025-01-20

-

Contexte

-

RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances.

-

Décision

-

Flutter pour iOS et Android avec codebase unique.

-

Alternatives considérées

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameworkCodebasePerformanceAudio/CarPlayCommunauté
FlutterUniqueNativeExcellenteLarge
React NativeUniqueBonneModules natifs requisTrès large
Native (Swift+Kotlin)DoubleExcellenteNativeLarge
Ionic/CapacitorUniqueMoyenneLimitéeMoyenne
-

Justification

-
    -
  • Codebase unique : iOS + Android maintenus ensemble, vélocité développement x2
  • -
  • Performance : Dart compilé en code natif (pas de bridge JS)
  • -
  • Audio HLS : Package just_audio mature avec support HLS, buffering adaptatif
  • -
  • CarPlay/Android Auto : Support via packages communautaires (flutter_carplay, android_auto_flutter)
  • -
  • Géolocalisation : geolocator robuste avec gestion permissions
  • -
  • Écosystème : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod)
  • -
-

Packages clés

-
dependencies:
-  flutter_bloc: ^8.1.3        # State management
-  just_audio: ^0.9.36         # Lecture audio HLS
-  geolocator: ^11.0.0         # GPS temps réel (mode voiture)
-  geofence_service: ^5.2.0    # Geofencing arrière-plan (mode piéton)
-  flutter_local_notifications: ^17.0.0  # Notifications géolocalisées
-  dio: ^5.4.0                 # HTTP client
-  flutter_secure_storage: ^9.0.0  # Tokens JWT
-  cached_network_image: ^3.3.1    # Cache images
-
-

Nouveaux packages (contenus géolocalisés) :

-
    -
  • geofence_service : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton)
  • -
  • Geofencing natif iOS/Android
  • -
  • Minimise consommation batterie
  • -
  • -

    Supporte notifications push même app fermée

    -
  • -
  • -

    flutter_local_notifications : Notifications locales avec compteur dynamique

    -
  • -
  • Notification avec compteur décroissant (7→1) en mode voiture
  • -
  • Icônes personnalisées selon type contenu
  • -
  • Désactivation overlay en mode CarPlay/Android Auto (conformité)
  • -
-

Structure application

-
lib/
-├── core/           # Config, DI, routes
-├── data/           # Repositories, API clients
-├── domain/         # Models, business logic
-├── presentation/   # UI (screens, widgets, blocs)
-└── main.dart
-
-

Conséquences

-
    -
  • Équipe doit apprendre Dart (syntaxe proche Java/TypeScript)
  • -
  • Taille binaire : 8-15 MB (acceptable)
  • -
  • Tests : flutter_test pour widgets, integration_test pour E2E
  • -
  • CI/CD : Fastlane pour déploiement stores
  • -
-
- -

ADR-015 : Stratégie Tests

-

Statut : Accepté -Date : 2025-01-20

-

Contexte

-

RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité.

-

Décision

-

Approche multi-niveaux : unitaires, intégration, BDD (Gherkin), E2E, load testing.

-

Stratégie par type

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeFrameworkCibleFréquence
UnitairesTestify80%+ couvertureChaque commit
Intégration DBTestify + TestcontainersRepositories critiquesAvant merge PR
BDD (Gherkin)GodogUser storiesAvant release
E2E MobileFlutter integration_testParcours critiquesNightly
Loadk6N/AAvant mise en prod
-

Tests unitaires (Testify)

-
// internal/user/service_test.go
-func TestGetUserByID(t *testing.T) {
-    mockRepo := new(MockRepository)
-    service := NewService(mockRepo)
-
-    mockRepo.On("FindByID", "123").Return(&User{ID: "123"}, nil)
-
-    user, err := service.GetByID("123")
-
-    assert.NoError(t, err)
-    assert.Equal(t, "123", user.ID)
-    mockRepo.AssertExpectations(t)
-}
-
-

Couverture minimale : 80% sur packages internal/*/service.go

-

Tests BDD (Gherkin + Godog)

-

Voir ADR-007 pour contexte complet.

-
# features/recommendation.feature
-Feature: Recommandation géolocalisée
-
-  Scenario: Contenu proche prioritaire
-    Given je suis à Paris (48.8566, 2.3522)
-    And un contenu existe à 500m avec tag "tourisme"
-    And mon intérêt "tourisme" est à 85%
-    When je demande des recommandations
-    Then le contenu est en première position
-    And le score de pertinence est supérieur à 0.8
-
-

Couverture : Tous les cas d'usage du README.md traduits en .feature.

-

Tests intégration (Testcontainers)

-
// internal/geo/repository_integration_test.go
-func TestFindContentNearby(t *testing.T) {
-    container := testcontainers.RunPostGISContainer(t)
-    defer container.Terminate()
-
-    repo := NewRepository(container.DB())
-
-    // Insert test data
-    repo.CreateContent(testContent)
-
-    // Query
-    results := repo.FindNearby(48.8566, 2.3522, 5000)
-
-    assert.Len(t, results, 1)
-}
-
-

Tests E2E Mobile (Flutter)

-
// integration_test/player_test.dart
-testWidgets('Play audio and skip', (tester) async {
-  await tester.pumpWidget(MyApp());
-
-  await tester.tap(find.byIcon(Icons.play_arrow));
-  await tester.pumpAndSettle();
-
-  expect(find.text('Now Playing'), findsOneWidget);
-
-  await tester.tap(find.byIcon(Icons.skip_next));
-  expect(find.text('Next Content'), findsOneWidget);
-});
-
-

Load testing (k6)

-
// tests/load/streaming.js
-import http from 'k6/http';
-import { check } from 'k6';
-
-export let options = {
-  stages: [
-    { duration: '2m', target: 1000 },
-    { duration: '5m', target: 10000 },
-  ],
-};
-
-export default function () {
-  let res = http.get('https://api.roadwave.com/v1/content/nearby');
-  check(res, { 'status is 200': (r) => r.status === 200 });
-}
-
-

Objectif : API p99 < 100ms à 10K RPS.

-

CI/CD Pipeline

-
# .github/workflows/ci.yml
-- name: Unit tests
-  run: go test -race -coverprofile=coverage.out ./...
-
-- name: BDD tests
-  run: godog run features/
-
-- name: Integration tests
-  run: go test -tags=integration ./...
-
-- name: Coverage gate
-  run: |
-    coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
-    if (( $(echo "$coverage < 80" | bc -l) )); then
-      echo "Coverage $coverage% < 80%"
-      exit 1
-    fi
-
-

Conséquences

-
    -
  • Dépendances :
  • -
  • github.com/stretchr/testify
  • -
  • github.com/cucumber/godog
  • -
  • github.com/testcontainers/testcontainers-go
  • -
  • grafana/k6
  • -
  • Temps CI : ~3-5 min (tests unitaires + BDD)
  • -
  • Tests intégration/E2E : nightly builds (15-30 min)
  • -
  • Load tests : avant chaque release majeure
  • -
-
- -

Règles métier RoadWave

-
-

Documentation complète des règles métier validées pour l'application RoadWave. -Chaque section détaille les comportements, flux et décisions techniques.

-
-
-

📋 Table des matières

-

01. Authentification & Inscription

-

Contenu : Inscription, connexion, récupération de compte

-
    -
  • Inscription : email/password uniquement (pas d'OAuth tiers)
  • -
  • Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j)
  • -
  • Connexion : 5 tentatives max, blocage 15 min, refresh token 30j
  • -
  • Récupération mot de passe : email, lien expire 1h
  • -
-
-

02. Algorithme de recommandation

-

Contenu : Scoring, géolocalisation, orientation politique, mode Kids

-
    -
  • Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%)
  • -
  • Engagement : 20%, Aléatoire : 10%
  • -
  • Orientation politique : 5 niveaux, équilibre imposé (40/40/20)
  • -
  • Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans
  • -
  • Historique : >80% jamais reproposer, <10s ne pas reproposer
  • -
-
-

03. Centres d'intérêt et jauges

-

Contenu : Évolution jauges, valeurs initiales

-
    -
  • Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1%
  • -
  • Like explicite (manuel) : +2% (cumulable avec auto)
  • -
  • Abonnement : +5%
  • -
  • Skip rapide (<10s) : -0.5%
  • -
  • Valeur initiale : 50% (neutre)
  • -
  • Limites : 0-100% stricte, pas de dégradation temporelle
  • -
-
-

04. Création et publication de contenu

-

Contenu : Upload, métadonnées, validation, modification

-
    -
  • Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h
  • -
  • Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge
  • -
  • Validation 3 premiers contenus : 24-48h (modération RoadWave)
  • -
  • Modification : métadonnées uniquement, pas audio/zone/classification
  • -
-
-

05. Interactions et navigation

-

Contenu : Commandes Suivant/Précédent, interactions volant, lecture en boucle

-
    -
  • Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min
  • -
  • Précédent : <10s → contenu avant, ≥10s → replay début
  • -
  • Commandes volant : Suivant, Précédent, Play/Pause uniquement
  • -
  • Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point
  • -
  • Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto)
  • -
  • Passage auto après 2s (1s mode Kids)
  • -
-
-

06. Publicités

-

Contenu : Campagnes, fréquence, insertion, facturation

-
    -
  • Interface self-service, budget min 50€, étalement paramétrable
  • -
  • Fréquence : 1/5 contenus (gratuits uniquement)
  • -
  • Durée : 10-60s (recommandé 15-30s), skippable après 5s
  • -
  • Validation manuelle 24-48h, prépaiement Mangopay
  • -
  • Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€
  • -
-
-

07. Radio live

-

Contenu : Démarrage, arrêt, comportement auditeur

-
    -
  • Buffer 15s avant diffusion publique, durée max 8h
  • -
  • Notification push abonnés dans zone géo uniquement
  • -
  • Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s
  • -
  • Enregistrement auto MP3 256 kbps → replay sous 5-10 min
  • -
  • Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat
  • -
-
-

08. Abonnements et notifications

-

Contenu : Impact algorithme, notifications, audio-guides, limites

-
    -
  • Boost +30% au score final (pas priorité absolue)
  • -
  • Détection contexte : <5 km/h piéton, >10 km/h voiture
  • -
  • Voiture : in-app uniquement, Piéton : push actives
  • -
  • Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h
  • -
  • Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle
  • -
  • Max 200 abonnements, +5% jauges tous tags créateur
  • -
-
-

09. Monétisation créateurs

-

Contenu : Activation, KYC, sources revenus, paiement

-
    -
  • Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j
  • -
  • KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois
  • -
  • Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub)
  • -
  • Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute)
  • -
  • Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay
  • -
-
-

10. Premium

-

Contenu : Offre, multi-devices, avantages, gestion abonnement

-
    -
  • Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif)
  • -
  • Pas d'essai gratuit, pas de partage familial (MVP)
  • -
  • Multi-devices : 1 seul stream actif, détection connexion simultanée
  • -
  • Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité
  • -
  • Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission)
  • -
-
-

11. Mode offline

-

Contenu : Téléchargement, validité, synchronisation

-
    -
  • Zone géographique : choix manuel (autour de moi / ville / département / région)
  • -
  • Nombre contenus : gratuit 50 max, Premium illimité
  • -
  • WiFi par défaut, mobile avec confirmation + estimation volume
  • -
  • Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours)
  • -
  • Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max
  • -
-
-

12. Gestion des erreurs

-

Contenu : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé

-
    -
  • Aucun contenu : élargissement auto 50km → 100km → département → région → national
  • -
  • Contenu supprimé : laisser terminer, passage auto suivant après 2s
  • -
  • Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6×
  • -
  • GPS désactivé : mode dégradé (contenu national + neutre + téléchargé)
  • -
-
-

13. Conformité RGPD

-

Contenu : Consentements, anonymisation, export, suppression

-
    -
  • Consentement : Tarteaucitron.js + PostgreSQL versioning
  • -
  • GPS précis : 24h puis geohash 5 (~5km²)
  • -
  • Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j
  • -
  • Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé")
  • -
  • Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers
  • -
  • DPO : fondateur formé CNIL (non obligatoire <250 employés)
  • -
-
-

14. Modération - Flows opérationnels

-

Contenu : Signalement, traitement, sanctions

-
    -
  • Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre)
  • -
  • IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min)
  • -
  • SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h
  • -
  • Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription)
  • -
  • Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard)
  • -
-
-

15. Autres comportements

-

Contenu : Partage, profil créateur, recherche

-
    -
  • Partage : bouton partout, lien roadwave.fr/share/c/[id], web player + deep link
  • -
  • Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓
  • -
  • Badge vérifié : KYC validé OU célébrité OU >10K abonnés
  • -
  • Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM)
  • -
  • Filtres : type, durée, âge, géo, tags, date, premium (combinables)
  • -
  • Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet
  • -
-
-

16. Audio-guides multi-séquences

-

Contenu : Modes déplacement, navigation, déclenchement GPS, publicités

-
    -
  • 4 modes : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport
  • -
  • Mode Piéton : pause auto après chaque séquence, user clique Suivant, navigation libre
  • -
  • Mode Voiture : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h
  • -
  • Affichage voiture : distance temps réel + ETA + direction (flèche) + vitesse
  • -
  • Rayons : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m)
  • -
  • Publicités : 1/5 séquences tous modes, auto-play, skippable 5s
  • -
  • Reprise : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud)
  • -
-
-

🗂️ Organisation

-

Chaque fichier de règles métier suit la structure :

-
    -
  1. Décisions : choix validés avec justifications
  2. -
  3. Comportements détaillés : flux utilisateur, cas limites
  4. -
  5. Paramètres : valeurs exactes, seuils, durées
  6. -
  7. Points d'attention Gherkin : éléments à tester
  8. -
-
-

🚀 Utilisation

-

Ces documents servent de référence unique pour :

-
    -
  • ✅ Développement backend/frontend
  • -
  • ✅ Écriture des tests Gherkin (BDD)
  • -
  • ✅ Validation QA
  • -
  • ✅ Documentation produit
  • -
-

Prochaine étape : Création des fichiers .feature Gherkin dans features/ basés sur ces règles.

-
-

📊 Statistiques

-
    -
  • 16 sections validées
  • -
  • ~12 000 lignes de spécifications détaillées
  • -
  • Coût infrastructure MVP : ~50-250€/mois (hors salaires)
  • -
  • Technologies : 100% open source (sauf Mangopay paiements)
  • -
-
-

Dernière mise à jour : Janvier 2026 -Statut : ✅ Toutes sections validées

-
- -

1. Authentification & Inscription

-

1.1 Méthodes d'inscription

-

Décision : Email/Password uniquement (pas d'OAuth tiers)

-
    -
  • ❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine)
  • -
  • ✅ Email + mot de passe
  • -
  • ✅ 2FA (Two-Factor Authentication) disponible
  • -
  • ✅ Option "Appareil de confiance" (skip 2FA pour 30 jours)
  • -
-

Justification : -- Souveraineté : pas de dépendance externe -- RGPD : données 100% contrôlées -- Coût : 0€ (Zitadel intégré)

-
-

1.2 Vérification email

-

Décision : Différenciée selon le rôle utilisateur

-

Pour les auditeurs (écoute uniquement)

- - - - - - - - - - - - - - - - - -
ÉtatCapacités
Email non vérifiéLecture illimitée + création max 5 contenus
Email vérifiéToutes fonctionnalités débloquées
-

Paramètres : -- Lien de vérification expire après 7 jours -- Possibilité de renvoyer le lien (max 3 fois/jour) -- Rappel in-app après création du 3ème contenu

-

Justification : -- Friction minimale à l'inscription -- Anti-spam sans bloquer l'essai du produit -- Incitation naturelle à vérifier (déblocage)

-

Pour les créateurs (monétisation)

-

Vérification obligatoire sous 7 jours pour : -- Accès au programme de monétisation -- KYC et reversement des revenus (conformité Mangopay) -- Publication illimitée de contenus

-

Justification : -- Conformité légale : KYC obligatoire pour transferts financiers -- Anti-fraude : Vérification identité réelle pour paiements -- Responsabilité : RoadWave doit pouvoir prouver identité créateurs monétisés

-
-

1.3 Données requises à l'inscription

-

Obligatoires : -- ✅ Email (format validé) -- ✅ Mot de passe (voir règles ci-dessous) -- ✅ Pseudo (3-30 caractères, alphanumérique + underscore) -- ✅ Date de naissance (vérification âge minimum)

-

Optionnelles : -- ❌ Nom complet (privacy by design) -- ❌ Photo de profil (avatar par défaut généré) -- ❌ Bio (ajout ultérieur)

-

Âge minimum : -- 13 ans minimum (conformité réglementation réseaux sociaux EU) -- Vérification à l'inscription via date de naissance -- Blocage inscription si <13 ans avec message explicite

-

Justification : -- RGPD minimal data -- Friction réduite (4 champs max) -- Protection mineurs (obligation légale)

-
-

1.4 Tranches d'âge des contenus

-

Décision : Classification obligatoire des contenus

-

Catégories : -- 🟢 Tout public (défaut) -- 🟡 13+ : contenu mature léger (débats, actualité sensible) -- 🟠 16+ : contenu mature (violence verbale, sujets sensibles) -- 🔴 18+ : contenu adulte (langage explicite, sujets réservés)

-

Règles de diffusion : -- Utilisateur 13-15 ans → contenus 🟢 uniquement -- Utilisateur 16-17 ans → contenus 🟢 🟡 -- Utilisateur 18+ → tous contenus

-

Modération : -- Vérification obligatoire de la classification lors de la validation -- Reclassification possible par modérateurs -- Strike si classification volontairement incorrecte

-

Justification : -- Protection mineurs (obligation légale) -- Responsabilité plateforme -- Coût : champ supplémentaire + règle algo

-
-

1.5 Validation mot de passe

-

Règles : -- ✅ Minimum 8 caractères -- ✅ Au moins 1 majuscule -- ✅ Au moins 1 chiffre -- ❌ Pas de symbole obligatoire (simplicité)

-

Validation : -- Côté client (feedback temps réel) -- Côté backend (sécurité) -- Message d'erreur explicite par règle non respectée

-

Justification : -- Standard industrie -- Bloque 95% des mots de passe faibles -- UX acceptable (pas trop restrictif)

-
-

1.6 Two-Factor Authentication (2FA)

-

Décision : Optionnel mais recommandé

-

Méthodes disponibles : -- ✅ TOTP (Time-based One-Time Password) via app (Google Authenticator, Authy) -- ✅ Email (code 6 chiffres, expire 10 min) -- ❌ SMS (coût élevé ~0.05€/SMS)

-

Appareil de confiance : -- Option "Ne plus demander sur cet appareil" → bypass 2FA pendant 30 jours -- Révocable depuis paramètres compte -- Liste des appareils de confiance visible

-

Justification : -- Sécurité renforcée sans coût SMS -- UX : appareil de confiance évite friction quotidienne -- Zitadel natif (0€)

-
-

1.7 Tentatives de connexion

-

Règles : -- Maximum 5 tentatives par période de 15 minutes -- Blocage temporaire après 5 échecs -- Compteur reset automatique après 15 min -- Notification email si blocage (tentative suspecte)

-

Déblocage : -- Automatique après 15 min -- Ou via lien "Mot de passe oublié"

-

Justification : -- Anti brute-force -- Standard industrie (équilibre sécurité/UX) -- Zitadel natif (0€)

-
-

1.8 Sessions et refresh tokens

-

Durée de vie : -- Access token : 15 minutes -- Refresh token : 30 jours

-

Rotation : -- Refresh token rotatif (nouveau token à chaque refresh) -- Ancien token invalidé immédiatement -- Détection token replay attack

-

Extension automatique : -- Si app utilisée, session prolongée automatiquement -- Inactivité 30 jours → déconnexion

-

Justification : -- Sécurité (token court-vie) -- UX (pas de reconnexion fréquente) -- Standard OAuth2/OIDC

-
-

1.9 Multi-device

-

Décision : Sessions simultanées illimitées

-

Gestion : -- Liste des devices connectés visible (OS, navigateur, dernière connexion, IP/ville) -- Révocation individuelle possible -- Révocation globale "Déconnecter tous les appareils"

-

Alertes : -- Notification push + email si connexion depuis nouveau device -- Détection localisation suspecte (IP pays différent)

-

Justification : -- UX maximale (écoute voiture + tablette maison + web) -- Sécurité via transparence (utilisateur voit tout) -- Coût : table sessions PostgreSQL

-
-

1.10 Récupération de compte

-

Méthode : Email uniquement

-

Processus : -1. Utilisateur clique "Mot de passe oublié" -2. Email avec lien de reset envoyé -3. Lien expire après 1 heure -4. Page de reset : nouveau mot de passe (validation règles) -5. Confirmation + déconnexion tous devices (sauf celui en cours)

-

Notifications : -- Email immédiat si changement mot de passe -- Push si changement depuis appareil non reconnu

-

Limite : -- Maximum 3 demandes/heure (anti-spam)

-

Justification : -- Standard sécurité -- Pas de coût SMS -- Protection contre attaque sociale

-
-

Récapitulatif Section 1

-
- -

2. Algorithme de recommandation

-

2.1 Classification de géo-pertinence

-

Décision : 3 types de contenus selon leur pertinence géographique

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeDescriptionExemplePondération géo
Géo-ancréContenu lié à un lieu précisAudio-guide monument, pub restaurant local70%
Géo-contextuelPertinent dans une zoneActualité régionale, événement local50%
Géo-neutreUniversel, pas de lien géoPodcast philosophie, musique20%
-

Qui décide : -- ✅ Créateur choisit le type à la publication -- ✅ Modération peut reclassifier après validation -- ✅ Modification possible après publication (tout le monde a le droit de se tromper)

-

Justification : -- Différencie audio-guide (hyper-local) des podcasts génériques -- Algorithme adapte automatiquement la pondération -- Coût : champ supplémentaire en DB + règle algo

-
-

2.2 Formule de scoring

-

Décision : Score combiné dynamique selon type de contenu

-
score_final = (score_geo * poids_geo_type)
-            + (score_interets * poids_interets_type)
-            + (score_engagement * 0.2)
-            + (bonus_aleatoire)
-
-où :
-- score_geo = 1 - (distance_km / distance_max_km)
-- score_interets = moyenne des jauges utilisateur pour les tags du contenu
-- score_engagement = (taux_completion * 0.5) + (ratio_likes * 0.3) + (ratio_abonnements * 0.2)
-- bonus_aleatoire = 10% des recommandations tirées aléatoirement
-
-

Pondérations par type :

- - - - - - - - - - - - - - - - - - - - - - - - - -
TypePoids géoPoids intérêts
Géo-ancré0.70.1
Géo-contextuel0.50.3
Géo-neutre0.20.6
-

Paramètres : -- Distance max recommandée : 200 km -- Dégradation : linéaire (1 - distance/200km) -- Rayon point GPS : 500m (adapté au volume de contenu local)

-

Tous ces paramètres sont configurables à chaud via interface admin.

-

Justification : -- Flexibilité totale selon type de contenu -- Linéaire = rattrapage naturel du contenu viral ancien -- Auditable via métriques engagement (moyenne/médiane)

-
-

2.3 Score d'engagement et popularité

-

Décision : Intégration popularité avec poids 0.2

-

Métriques : -- Taux de complétion : écoutes >80% / total écoutes (poids 0.5) -- Ratio likes : likes / écoutes (poids 0.3) -- Ratio abonnements : nouveaux abonnés après écoute / écoutes (poids 0.2)

-

Seuil minimum : -- Minimum 50 écoutes avant de considérer l'engagement -- Contenu <50 écoutes : score engagement = 0.5 (neutre)

-

Contenu viral : -- Un contenu viral à Paris peut être proposé à Marseille -- Score géo faible compensé par score engagement élevé -- Paramétrable admin

-

Dépréciation temporelle : -- Pas de dépréciation automatique -- Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent

-

Justification : -- Équilibre découverte / qualité -- Pas de pénalisation arbitraire des contenus anciens -- Coût : calculs sur métriques existantes

-
-

2.4 Part d'aléatoire (exploration)

-

Décision : 10% par défaut, paramétrable utilisateur

-

Fonctionnement : -- 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté) -- Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max)

-

Curseur utilisateur : -- 🎯 0% : Personnalisé max (recommandations strictes) -- ⚖️ 10% : Équilibré (défaut) -- 🎲 30% : Découverte élevée -- 🌍 50% : Découverte max (équivaut à national = découverte)

-

Justification : -- Évite la bulle de filtre -- Laisse l'utilisateur maître de son expérience -- Coût : variable aléatoire en algo

-
-

2.5 Contenu politique (version MVP simplifiée)

-
-

⚠️ Note : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir ANNEXE-POST-MVP.md pour la version complète.

-
-

Décision MVP : Tag simple "Politique" sans classification idéologique

-

Tagging : -- Créateur peut taguer son contenu comme "Politique" (optionnel) -- Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc. -- Pas de classification gauche/droite -- Pas d'équilibrage imposé

-

Filtrage utilisateur : -- Option paramètres : "Masquer contenu politique" -- Si activé → 0% de contenus tagués "Politique" dans le feed -- Par défaut : désactivé (tous contenus visibles)

-

Justification MVP : -- Simplicité : Pas de modération politique coûteuse (~2000€/mois économisés) -- Neutralité technique : Aucun jugement éditorial sur orientation -- Risque minimal : Évite controverses et contentieux DSA au lancement -- Fonctionnel : Utilisateurs peuvent filtrer si souhaité

-

Post-MVP : -- Classification avancée possible si forte demande utilisateurs -- Nécessite ressources modération dédiées et audit DSA

-
-

2.6 Mode Kids (13-15 ans)

-

Décision : Mode optionnel pour adolescents 13-15 ans uniquement

-
-

⚠️ Note : Âge minimum d'inscription = 13 ans (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme.

-
-

Tranche concernée :

- - - - - - - - - - - - - - - - - -
TrancheDescriptionContenus autorisésRestrictions
13-15 ansCollègeContenus "Tous publics" uniquementFiltrage 16+ et 18+
-

Activation : -- ❌ Pas d'activation automatique (tous les utilisateurs ont ≥13 ans) -- ✅ Activation manuelle via toggle paramètres -- ✅ Parents peuvent activer pour leurs enfants 13-15 ans -- ✅ Utilisateur peut désactiver à tout moment

-

Filtrage quand Mode Kids activé : -- ✅ Contenus "Tous publics" uniquement -- ❌ Exclusion contenus 16+ et 18+ -- ❌ Pas de contenu politique (automatiquement filtré) -- ❌ Pas de publicité (ou uniquement pub validée manuellement)

-

Interface : -- Interface standard (pas d'interface dédiée enfants pour MVP) -- Filtrage algorithmique des contenus inappropriés

-

Justification : -- Conformité légale : Âge minimum 13 ans (RGPD, DSA) -- Simplicité MVP : Un seul mode optionnel vs 4 tranches d'âge -- Protection mineurs : Filtrage contenus adultes pour 13-15 ans -- Flexibilité : Parents décident d'activer ou non

-
-

2.7 Déclenchement géographique

-

Décision : Notification au passage, pas d'anticipation

-

Fonctionnement : -1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré) -2. Notification sonore (bip court) + visuelle (logo selon type) -3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement -4. Délai réaction utilisateur : 5 secondes pour accepter (bouton volant ou commande vocale) -5. Si accepté → lecture immédiate -6. Si ignoré → contenu proposé normalement en file d'attente

-

Publicités : -- ⚠️ Jamais d'interruption de contenu en cours -- Pub s'intercale entre deux séquences uniquement -- Notification pub : son différent (facultatif selon paramètres)

-

Gestion demi-tour : -- Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé) -- Réinitialisation après 24h

-

Justification : -- Respect écoute en cours (pas de coupure brutale) -- UX fluide (utilisateur garde contrôle) -- Simplicité technique (pas de prédiction trajectoire)

-
-

2.8 Historique et repropositon

-

Décision : Pas de reproposition sauf contenu partiel

-

Règles :

- - - - - - - - - - - - - - - - - - - - - - - - - -
État écouteCompletionAction
Écouté complètement>80%❌ Ne jamais reproposer (sauf flag replayable = true pour audio-guides)
Skippé rapidement<10s❌ Ne pas reproposer
Partiellement écouté10-80%✅ Reproposer avec reprise position (last_position_seconds)
-

Stockage historique : -- Table user_content_history (user_id, content_id, completion_rate, last_position, listened_at) -- Historique illimité (PostgreSQL) -- Algorithme considère les 100 derniers pour optimisation requêtes -- Export complet disponible (RGPD)

-

Justification : -- Découverte maximale (pas de redites) -- Respect erreurs de clic (contenu partiel = 2nde chance) -- Coût stockage négligeable (PostgreSQL scalable)

-
-

2.9 Paramétrabilité admin (interface dashboard)

-

Décision : Tous paramètres scoring exposés + A/B testing

-

Paramètres configurables à chaud :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamètrePlageDéfautUnité
poids_geo_ancre0.5 - 1.00.7%
poids_geo_contextuel0.3 - 0.70.5%
poids_geo_neutre0.0 - 0.40.2%
poids_engagement0.0 - 0.50.2%
part_aleatoire_global0.0 - 0.30.1%
distance_max_km50 - 500200km
rayon_gps_point_m100 - 2000500m
seuil_min_ecoutes_engagement10 - 20050nb
-

Application changements : -- Immédiat : nouveaux calculs utilisent nouvelle config -- Aucun recalcul batch (coût CPU) -- Version config trackée (git-like) -- Rollback 1 clic

-

A/B Testing : -- Création variantes (Config A vs Config B) -- Split utilisateurs 50/50 aléatoire -- Métriques comparatives : taux complétion, engagement, session duration -- Dashboard graphique temps réel

-

Audit engagement : -- Métriques clés : moyenne/médiane temps d'écoute par session -- Graphiques : évolution engagement selon config -- Export CSV pour analyse externe

-

Justification : -- Optimisation continue sans redéploiement -- Data-driven decisions (métriques objectives) -- Coût : dashboard admin à développer (one-time)

-
-

2.10 Paramétrabilité utilisateur

-

Décision : Curseurs avancés avec profils sauvegardables

-

Niveaux de personnalisation :

-

Curseurs disponibles : -- 📍 Géolocalisation : Local ← slider → National (découverte = national) -- 🎲 Découverte : 0% ← slider → 50% (part aléatoire) -- ⚖️ Politique : Masquer / Équilibré / Mes préférences

-

Profils sauvegardables : -- 🚗 Trajet quotidien (boulot) : géo local, découverte 5%, politique masqué -- 🛣️ Road trip : géo régional, découverte 30%, politique équilibré -- 👶 Enfants : Mode Kids activé

-

Synchronisation : -- ✅ Sync profils entre devices (cloud PostgreSQL) -- ❌ Pas de partage profils entre utilisateurs (famille) -- Auto-switch selon context (détection trajet récurrent via GPS)

-

Sécurité conduite : -- ⚠️ Blocage modification si vitesse GPS >10 km/h -- Warning au lancement app : "Configurez avant de prendre la route" -- Modifications uniquement app arrêtée/passager

-

Justification : -- Utilisateur maître de son expérience -- Contextes d'usage différents (quotidien vs voyage) -- Sécurité routière (pas de distraction)

-
-

2.11 Médias traditionnels

-

Décision : Ouverture aux médias établis

-

Médias autorisés : -- Presse nationale : Le Monde, Le Parisien, Libération, Le Figaro, etc. -- Radios : France Inter, RTL, Europe 1, etc. -- Médias régionaux : Ouest-France, Sud-Ouest, etc.

-

Format contenus : -- Flashs info géolocalisés (actualité régionale) -- Chroniques thématiques (culture, économie, sport) -- Éditos et débats (classification politique appliquée)

-

Validation : -- Compte média vérifié (badge ✓) -- Pas de validation 3 premiers contenus (confiance établie) -- Modération a posteriori uniquement

-

Monétisation : -- Partage revenus pub standard (même conditions créateurs) -- Possibilité sponsoring direct (pas via plateforme)

-

Justification : -- Crédibilité plateforme (contenus professionnels) -- Diversité éditoriale -- Attractivité grand public (noms reconnus)

-
-

Récapitulatif Section 2

-
- -

3. Centres d'intérêt et jauges

-

3.1 Évolution des jauges

-

Décision : Système simple avec valeurs fixes

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionImpact jaugeJustification
Like automatique renforcé (≥80% écoute)+2%Signal fort d'intérêt (écoute quasi-complète)
Like automatique standard (30-79% écoute)+1%Signal modéré d'intérêt
Like explicite (manuel)+2%Signal fort, cumulable avec auto
Abonnement créateur+5% sur tous ses tagsSignal très fort d'affinité
Skip rapide (<10s)-0.5%Désintérêt marqué
Skip tardif (≥30%)0%Neutre (contenu essayé suffisamment)
-

Paramètres techniques : -- Les jauges sont bornées strictement entre 0% et 100% -- Calcul immédiat à chaque action (pas de batch différé) -- Les tags du contenu sont définis par le créateur à la publication -- Si un contenu a plusieurs tags, chaque jauge correspondante est impactée

-

Exemple de calcul :

-
Contenu de 5 minutes tagué "Automobile" + "Voyage"
-
-Scénario 1 : Écoute 4min30 (90%)
-→ Like automatique renforcé (+2%)
-→ Jauge Automobile : 45% → 47%
-→ Jauge Voyage : 60% → 62%
-
-Scénario 2 : Écoute 2min30 (50%)
-→ Like automatique standard (+1%)
-→ Jauge Automobile : 45% → 46%
-→ Jauge Voyage : 60% → 61%
-
-Scénario 3 : Écoute 2min30 (50%) + Like manuel
-→ Like auto +1% puis like manuel +2% = +3% total
-→ Jauge Automobile : 45% → 48%
-→ Jauge Voyage : 60% → 63%
-
-Scénario 4 : Skip après 5s
-→ Signal négatif (-0.5%)
-→ Jauge Automobile : 45% → 44.5%
-→ Jauge Voyage : 60% → 59.5%
-
-

Justification : -- Like automatique : Reflète l'engagement réel (voir ADR-010) -- Sécurité routière : Pas d'action complexe en conduite -- Prévisibilité : Règles claires et déterministes -- Coût minimal : Calculs simples en backend -- Fiabilité : Pas d'edge cases complexes -- Ajustable : Valeurs modifiables via dashboard admin si besoin

-
-

3.2 Jauge initiale

-

Décision : Démarrage neutre à 50%, pas de questionnaire

-

À l'inscription : -- Toutes les jauges d'intérêt sont initialisées à 50% -- Pas de questionnaire onboarding (friction zéro) -- L'algorithme apprend naturellement via les premières écoutes

-

Catégories disponibles : -- Automobile -- Voyage -- Famille -- Amour -- Musique -- Économie -- Cryptomonnaie -- Politique -- Culture générale -- Sport -- Technologie -- Santé -- ... (extensible)

-

Cold start (premiers jours) : -1. Nouvel utilisateur s'inscrit → toutes jauges à 50% -2. Écoute premier podcast "Automobile" → jauge Auto monte à 51% -3. Skip un contenu "Économie" → jauge Éco descend à 48% -4. Après 10-15 écoutes, profil commence à se dessiner clairement

-

Alternative optionnelle (post-MVP) : -- Questionnaire optionnel proposé après 3 écoutes (in-app) -- Message : "Améliorez vos recommandations en sélectionnant vos centres d'intérêt" -- Si rempli : jauges sélectionnées passent à 70%, non sélectionnées à 30% -- Si skip : conserve 50% partout

-

Justification : -- Inscription ultra-rapide : pas de questionnaire = moins de churn -- Découverte naturelle : l'algorithme apprend en quelques écoutes -- Équitable : pas de biais initial vers certains créateurs -- Comportement déterministe : facile à tester et débugger -- Cold start acceptable : à 50%, tous les contenus ont une chance égale initialement

-
-

3.3 Dégradation temporelle

-

Décision : Pas de dégradation automatique

-

Les jauges ne diminuent jamais avec le temps de manière automatique.

-

Règle : -- Une jauge ne change que par les actions utilisateur (like, écoute, skip) -- Pas de cron job de dégradation périodique -- Pas de "rafraîchissement" artificiel

-

Scénario illustratif :

-
Utilisateur aimait "Économie" (jauge 80%) il y a 1 an
-→ Depuis, skip tous les contenus Éco
-→ Jauge descend naturellement à 40% via les skips
-→ Pas besoin de dégradation temporelle
-
-

Si utilisateur inactif longtemps : -- Utilisateur part en vacances 6 mois → jauges conservées -- Au retour : ses jauges reflètent toujours ses goûts d'avant -- Comportement cohérent et prévisible

-

Alternative utilisateur (contrôle explicite) : -- Bouton "Réinitialiser mes centres d'intérêt" dans paramètres -- Action manuelle : remet toutes les jauges à 50% -- Permet nouveau départ si souhaité (changement de vie, etc.)

-

Justification : -- Principe KISS (Keep It Simple, Stupid) -- Coût 0 : pas de batch nocturne, pas de calculs temporels -- Fiabilité maximale : pas de bugs de fuseaux horaires, dates, etc. -- UX prévisible : jauge = reflet des actions, pas d'automatisme caché -- Respect historique : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ? -- Évolution naturelle : les actions récentes suffisent à faire évoluer les jauges

-
-

Récapitulatif Section 3

-
- -

4. Création et publication de contenu

-

4.1 Upload et encodage

-

Décision : Formats universels avec encodage asynchrone

-

Formats acceptés : -- ✅ MP3 (.mp3) -- ✅ AAC (.aac, .m4a) -- ❌ WAV, FLAC (trop lourds, inutiles en voiture)

-

Limites :

- - - - - - - - - - - - - - - - - - - - - - - - - -
ParamètreValeurJustification
Taille maximale200 MB~4h de podcast à 128 kbps
Durée maximale4 heuresSuffisant pour podcasts longs
Validation formatClient + backendDouble sécurité
-

Pipeline d'encodage :

-
1. Upload fichier (MP3/AAC) → Bunny Storage temporaire
-2. Job asynchrone (worker Go + FFmpeg) :
-   - Validation format et intégrité
-   - Réencodage Opus 3 profils (24/48/64 kbps)
-   - Génération segments HLS (.m3u8 + .ts)
-   - Génération image couverture par défaut
-3. Suppression fichier original (économie stockage)
-4. Notification créateur : "Contenu prêt à publier"
-
-

Temps d'encodage estimé : -- Contenu 5 min → ~30 secondes -- Podcast 1h → ~5 minutes -- Podcast 4h → ~20 minutes

-

Profils Opus générés :

- - - - - - - - - - - - - - - - - - - - - - - - - -
QualitéBitrateUsage
Basse24 kbps2G/Edge
Standard48 kbps3G (défaut)
Haute64 kbps4G/5G
-

Écoute accélérée :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VitesseUsage
0.75xCompréhension difficile (accent, technique)
1.0xNormal (défaut)
1.25xGain léger
1.5xPodcasts longs
2.0xSurvol rapide (modérateurs)
-

Disponible pour : -- ✅ Modérateurs (validation rapide : 30s → 15s à 2x) -- ✅ Auditeurs (tous les contenus) -- ✅ Standard industrie (YouTube, Spotify, Apple Podcasts)

-

Justification : -- Simplicité : 2 formats couvrent 95% des cas d'usage -- Coût optimisé : pas de conversion WAV/FLAC lourds -- Stockage réduit : suppression original après encodage -- Scalabilité : workers horizontalement (Kubernetes jobs) -- Productivité : écoute accélérée = double productivité modération

-
-

4.2 Métadonnées obligatoires

-

Décision : Minimaliste pour réduire friction

-

Champs obligatoires :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChampFormatValidation
Titre5-100 caractèresAlphanumérique + ponctuation basique
Type géoEnumAncré / Contextuel / Neutre
Zone diffusionCompositeVoir détails ci-dessous
TagsEnum1 à 3 parmi liste prédéfinie
Classification âgeEnumTout public / 13+ / 16+ / 18+
-

Zone de diffusion (obligatoire) :

-

Options mutuellement exclusives : -- Point GPS : latitude + longitude + rayon (100m à 10km) -- Ville : sélection dans référentiel INSEE -- Département : sélection liste -- Région : sélection liste -- National : France entière

-

Tags disponibles (1 à 3 obligatoires) : -- Automobile -- Voyage -- Famille -- Amour -- Musique -- Économie -- Cryptomonnaie -- Politique -- Culture générale -- Sport -- Technologie -- Santé

-

Champs optionnels : -- ❌ Description (ajout ultérieur) -- ❌ Image couverture (génération auto)

-

Image de couverture par défaut :

-

Génération automatique selon règles : -- Icône selon type géo : 📍 Ancré / 🌍 Contextuel / 🎧 Neutre -- Couleur selon tag principal : bleu (Auto), vert (Voyage), rouge (Musique), etc. -- Format 800×800px, PNG -- Personnalisable ultérieurement (post-MVP)

-

Exemple de publication :

-
Titre : "Histoire de la Tour Eiffel"
-Type géo : Ancré
-Zone : Point GPS (48.8584, 2.2945, rayon 500m)
-Tags : Voyage, Culture générale
-Classification : Tout public
-→ Image auto : 📍 fond bleu-vert (Voyage)
-
-

Justification : -- Friction minimale : 5 champs max = 2 min de publication -- Publication rapide : pas de blocage sur description/image -- Coût 0 : pas de génération IA au MVP -- Évolutif : champs optionnels ajoutables ultérieurement

-
-

4.3 Validation des 3 premiers contenus

-

Décision : Validation manuelle par équipe modération RoadWave

-

Processus nouveau créateur :

-
    -
  1. Créateur upload ses 3 premiers contenus
  2. -
  3. Contenus passent en file d'attente modération
  4. -
  5. Modérateur junior RoadWave :
  6. -
  7. Écoute 30 secondes (ou 15s à 2x)
  8. -
  9. Vérifie métadonnées
  10. -
  11. Valide ou rejette avec raison
  12. -
  13. Si accepté : contenu publié + notification créateur
  14. -
  15. Si refusé : notification avec raison détaillée + lien vers règles
  16. -
  17. Après 3 contenus validés : créateur passe en statut vérifié
  18. -
-

Critères de validation :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CritèreDétails
Qualité audioCompréhensible (pas de grésillement excessif)
Respect règlesPas de contenu prohibé évident (haine, spam, illégal)
Classification âgeCohérente avec contenu écouté
Tags pertinentsCorrespondance minimale avec contenu
Zone diffusionCohérente (pas "Tour Eiffel" avec zone "National")
-

Délai de validation : -- Objectif : 24-48h (jours ouvrés) -- Priorité : FIFO (First In First Out) -- Weekend : délai peut atteindre 72h -- Message au créateur : "Validation en cours, délai estimé 24-48h"

-

Notification créateur :

-

Si accepté : -- Email + push : "✅ Votre contenu '[Titre]' est en ligne !" -- Lien direct vers le contenu -- Compteur : "2/3 contenus validés pour devenir créateur vérifié"

-

Si refusé : -- Email + push : "❌ Contenu '[Titre]' refusé" -- Raison détaillée : "Qualité audio insuffisante" / "Tags non pertinents" / "Classification incorrecte" / etc. -- Lien vers règles de publication -- Possibilité de correction + resoumission

-

Après 3 validations :

-

Créateur obtient statut "Vérifié" : -- Badge ✓ visible sur profil -- Contenus futurs publiés immédiatement (modération a posteriori uniquement) -- Modération seulement si signalé par utilisateurs

-

Outils modérateur : -- Écoute accélérée (1.5x ou 2x) = double productivité -- Interface dédiée : queue de contenus à valider -- Raccourcis clavier : A (Accepter), R (Rejeter), Espace (Pause) -- Historique créateur visible (si déjà 1-2 contenus validés)

-

Modération communautaire (post-MVP) :

-

⚠️ Non implémenté au MVP (complexité juridique)

-

Vision future (envisageable) : -- Créateurs établis peuvent opt-in "Modérateur communautaire" -- Formation obligatoire (30 min) + quiz (80%) -- Pré-validation uniquement (validation finale toujours par équipe RoadWave) -- Compensation : badges, premium offert -- Attribution aléatoire (pas de collusion)

-

Justification décision MVP : -- Responsabilité juridique : plateforme reste responsable (DSA EU) -- Qualité garantie : modérateurs formés et mandatés -- Anti-spam efficace : bloque 95% des abus dès le début -- Coût raisonnable : 30s × 3 contenus = 1.5 min/créateur -- UX acceptable : délai 24-48h expliqué clairement -- Pas de validation par pairs au MVP = évite risques juridiques (collusion, compétence, conflits)

-
-

4.4 Modification et suppression

-

Décision : Modification métadonnées uniquement, suppression immédiate

-

Modification autorisée :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ÉlémentModifiableJustification
TitreCorrection coquilles
DescriptionSi ajoutée ultérieurement
TagsAjustement pertinence
Image couverturePersonnalisation
AudioIntégrité contenu
Zone diffusionÉvite manipulation algo
Type géoÉvite manipulation algo
Classification âgeSécurité mineurs
-

Raisons restrictions :

-

Audio non modifiable : -- Évite fraude : uploader contenu validé → remplacer par spam -- Intégrité : auditeurs doivent écouter ce qui a été validé

-

Zone/Type non modifiables : -- Évite manipulation : créer "Local Paris" → changer en "National" pour boost visibilité -- Évite abus : créer "Neutre" (faible pondération géo) → changer en "Ancré" (forte pondération)

-

Classification non modifiable : -- Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation -- Sécurité : garantit que classification a été vérifiée

-

Si besoin de changer audio/zone/classification : -- Action : Supprimer contenu + republier -- Si créateur <3 contenus validés : retourne en file validation -- Si créateur ≥3 contenus validés : publication immédiate

-

Suppression de contenu :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AspectComportement
DélaiImmédiat
RéversibilitéNon
Historique auditeursMarqué "Contenu supprimé par créateur"
Analytics plateformeAnonymisé et conservé
Fichiers CDNSupprimés sous 24h
-

Exemple scénario suppression :

-
Créateur supprime podcast écouté par 1000 personnes
-→ CDN : fichiers purgés sous 24h
-→ BDD : entrée marquée "deleted", auteur anonymisé
-→ Historique auditeurs : "Contenu supprimé" (conserve durée écoute pour stats)
-→ Analytics : métriques globales conservées (anonymes, RGPD OK)
-
-

Notifications suppression : -- Pas de notification aux auditeurs (pour éviter effet Streisand) -- Historique reste consultable : "Vous avez écouté ce contenu le [date]" -- Si auditeur tente de réécouter : "Ce contenu n'est plus disponible"

-

Justification : -- Simplicité : règles claires et non-ambiguës -- Sécurité : évite manipulations algorithme et contournements modération -- Contrôle créateur : liberté totale de supprimer (RGPD) -- Traçabilité : historique conservé pour analytics (anonymisé) -- Coût 0 : pas de revalidation métadonnées

-
-

Récapitulatif Section 4

-
- -

5. Interactions et navigation

-

5.1 File d'attente et commande "Suivant"

-

Décision : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques

-

File d'attente : -- 5 contenus pré-calculés en cache (Redis) -- Recalcul automatique si : - - Déplacement >10km - - Toutes les 10 minutes (rafraîchissement contenu) - - File d'attente <3 contenus restants

-

Insertion prioritaire géo-ancrée (mode voiture uniquement) :

-

Détection : -- Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android -- Notification déclenchée 7 secondes avant d'arriver au point GPS -- Si vitesse < 5 km/h ET distance < 50m → notification immédiate -- ⚠️ App doit être ouverte (pas de détection en arrière-plan en mode voiture)

-

Notification : -- Sonore uniquement : bip court ou son personnalisé RoadWave -- Visuelle minimale : icône selon type de contenu (🏛️ culture, 👨‍👩‍👧 famille, 🎵 musique, etc.) -- Compteur visible : 7...6...5...4...3...2...1 (décompte des secondes) -- Pas de texte affiché (éviter distraction conducteur) -- Pas de bouton "Annuler" : seul le bouton "Suivant" permet validation

-

Actions utilisateur : -1. User entend notification sonore + voit icône et compteur -2. User appuie "Suivant" dans les 7 secondes → décompte 5s démarre -3. Pendant décompte : contenu actuel continue, compteur visible (5...4...3...2...1) -4. Si contenu actuel se termine pendant décompte → contenu suivant du buffer démarre -5. À la fin du décompte → contenu géolocalisé démarre (fade out/in 0.3s)

-

Si user n'appuie pas sur "Suivant" : -- Notification disparaît après 7 secondes -- Contenu géolocalisé est perdu (pas d'insertion dans file) -- Pas de nouveau contenu géolocalisé pendant 10 minutes (éviter spam)

-

Limitation anti-spam : -- Maximum 6 contenus géolocalisés par heure -- Timer reset toutes les heures (rolling window) -- Exception : séquences d'un même audio-guide multi-séquences (comptent comme 1) -- Si quota atteint : notifications suivantes ignorées jusqu'à libération du quota

-

Invalidation immédiate : -- Utilisateur change ses préférences (curseurs géo/découverte/politique) - - ⚠️ Modification bloquée si vitesse GPS >10 km/h (sécurité routière) -- Live démarre d'un créateur suivi dans la zone

-

Implémentation :

-
Redis cache :
-  - Clé : user:{user_id}:queue
-  - Structure : [content_1, content_2, ..., content_5]
-  - Métadonnées : {last_lat, last_lon, computed_at, mode: "voiture"|"pieton"}
-  - TTL : 15 minutes
-
-Tracking GPS temps réel (mobile) :
-  - Vérification toutes les 1 seconde
-  - Calcul ETA vers points géolocalisés proches (rayon 500m)
-  - Si ETA ≤ 7s → trigger notification
-  - Historique GPS : 30 derniers points pour calcul vitesse moyenne
-
-Quota anti-spam (Redis) :
-  - Clé : user:{user_id}:geo_quota
-  - Structure : sorted set avec timestamps des 6 derniers contenus
-  - TTL : 1 heure
-  - Vérification avant notification : ZCOUNT pour compter contenus dernière heure
-
-Cooldown après ignorance (Redis) :
-  - Clé : user:{user_id}:geo_cooldown
-  - TTL : 10 minutes
-  - Set après notification ignorée
-
-

Justification : -- Expérience fluide : pas de latence au clic "Suivant" -- Réactivité géo : contenu local inséré immédiatement -- Coût optimisé : recalcul uniquement si nécessaire -- Sécurité : pas de modification en conduite

-
-

5.1.2 Mode piéton (audio-guides)

-

Décision : Notifications push en arrière-plan avec rayon large

-

Contexte : -- Mode piéton détecté automatiquement si vitesse moyenne < 5 km/h -- Cas d'usage : visites à pied, musées, monuments, quartiers historiques -- User n'a pas besoin d'avoir l'app ouverte -- ⚠️ Fonctionnalité optionnelle : requiert permission "localisation en arrière-plan" (activée par user)

-

Détection : -- App peut être en arrière-plan (si permission accordée) -- Rayon de détection : 200 mètres autour du point GPS -- Geofencing iOS/Android pour minimiser consommation batterie -- Permission demandée uniquement si user active "Notifications audio-guides piéton" dans settings

-

Notification push système :

-

Format :

-
Titre : "Audio-guide à proximité"
-Body : "[Nom du contenu] - [Nom créateur]"
-Action : Tap → ouvre app sur le contenu
-
-

Exemple :

-
Audio-guide à proximité
-Musée du Louvre : La Joconde - @paris_museum
-
-

Permissions requises :

-

⚠️ Important : Permission "Always Location" est optionnelle et demandée uniquement si user active le mode piéton dans settings.

-

iOS (Info.plist) :

-
<key>NSLocationWhenInUseUsageDescription</key>
-<string>RoadWave utilise votre position pour vous proposer des contenus audio géolocalisés adaptés à votre trajet en temps réel.</string>
-
-<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
-<string>Si vous activez les notifications audio-guides piéton, RoadWave peut vous alerter lorsque vous passez près d'un monument ou musée, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée à tout moment dans les réglages.</string>
-
-

Android (AndroidManifest.xml) :

-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
-
-

Disclosure avant demande permission (Android requis, iOS recommandé) :

-

Écran affiché avant demande permission "Always Location" :

-
┌────────────────────────────────────────┐
-│ 📍 Notifications audio-guides piéton   │
-├────────────────────────────────────────┤
-│ Pour vous alerter d'audio-guides à     │
-│ proximité même quand vous marchez avec │
-│ l'app fermée, RoadWave a besoin de     │
-│ votre position en arrière-plan.        │
-│                                        │
-│ Votre position sera utilisée pour :   │
-│ ✅ Détecter monuments à 200m           │
-│ ✅ Vous envoyer une notification       │
-│                                        │
-│ Votre position ne sera jamais :        │
-│ ❌ Vendue à des tiers                  │
-│ ❌ Utilisée pour de la publicité       │
-│                                        │
-│ Cette fonctionnalité est optionnelle.  │
-│ Vous pouvez utiliser RoadWave sans     │
-│ cette permission.                      │
-│                                        │
-│ [Continuer] [Non merci]                │
-│                                        │
-│ Plus d'infos : Politique confidentialité│
-└────────────────────────────────────────┘
-
-

Si user refuse : -- Mode piéton désactivé (uniquement mode voiture disponible) -- App fonctionne normalement avec permission "When In Use" -- Audio-guides accessibles en mode manuel (user ouvre app, sélectionne contenu)

-

Comportement après tap sur notification : -1. User tap notification push -2. App s'ouvre sur la page du contenu -3. User peut démarrer la lecture manuellement -4. Navigation libre (voir section 16.2 pour audio-guides piéton)

-

Basculement automatique voiture ↔ piéton :

-

Détection par vitesse GPS moyenne sur 30 secondes : -- Vitesse < 5 km/h (stable 10s) → mode piéton -- Vitesse ≥ 5 km/h (stable 10s) → mode voiture

-

Changements de mode :

- - - - - - - - - - - - - - - - - - - - - - - -
Mode actuelVitesse détectéeNouveau modeEffet
Piéton≥ 5 km/hVoitureNotifications push → sonores + icône (app ouverte requise)
Voiture< 5 km/hPiétonNotifications sonores → push arrière-plan
-

Pas de popup confirmation : -- Basculement transparent et automatique -- User n'a rien à faire -- Hysteresis (10s) pour éviter basculements intempestifs

-

Quota anti-spam mode piéton : -- Même limitation que mode voiture : 6 contenus/heure -- Cooldown 10 min si notification ignorée (app pas ouverte après tap)

-

Justification : -- ✅ Expérience adaptée aux visites à pied (rayon large, pas de timing précis) -- ✅ Économie batterie (geofencing natif iOS/Android) -- ✅ User peut garder téléphone en poche -- ✅ Basculement automatique = pas de friction

-
-

5.2 Commande "Précédent"

-

Décision : Comportement smart selon progression écoute

-

Règles :

- - - - - - - - - - - - - - - - - - - - - - - - - -
SituationTemps écoutéAction "Précédent"
Début de contenu<10 secondesRetour au contenu précédent (position exacte)
Milieu/fin≥10 secondesReplay contenu actuel depuis le début
Premier de sessionN/AReplay depuis début (rien avant)
-

Historique de navigation : -- 10 contenus maximum en mémoire (Redis List) -- Structure : [{content_id, position_seconds, listened_at}, ...] -- FIFO : au-delà de 10, suppression du plus ancien

-

Exemple scénario :

-
Utilisateur écoute :
-1. Contenu A → écoute 5s → "Suivant"
-2. Contenu B → écoute 2min30 → "Suivant"
-3. Contenu C → écoute 5s → "Précédent"
-   → Retour Contenu B à 2min30 (car >10s)
-4. Sur Contenu B → "Précédent"
-   → Retour Contenu A à 5s (position exacte)
-
-

Interface (responsabilité front) : -- ❌ Pas de message UI -- ✅ Progress bar revient au début ou à position exacte -- ✅ Animation fluide (transition 0.3s)

-

Justification : -- UX intuitive : comportement standard Spotify/YouTube -- Pas de frustration : si début, vraiment revenir en arrière -- Simplicité : règle unique (seuil 10s)

-
-

5.3 Interactions au volant : Like automatique et engagement

-
-

⚠️ Architecture Decision Record : Voir ADR-010 pour les détails techniques complets

-
-

Décision : Like automatique basé sur le temps d'écoute

-

Problème technique identifié : -- iOS et Android ne supportent pas nativement les appuis longs ou doubles-appuis sur les commandes média -- Les commandes physiques au volant varient selon les véhicules (pas de bouton "Pause" dédié sur beaucoup de modèles) -- Système de double-appui/appui long = non-intuitif et risques sécurité (regarder écran pour feedback)

-
-

Commandes au volant simplifiées

-

Actions disponibles (100% compatibles tous véhicules) :

- - - - - - - - - - - - - - - - - - - - - -
Commande physiqueAction RoadWave
SuivantPasser au contenu suivant
PrécédentRevenir au contenu précédent (règle 10s, voir section 5.2)
Play/PausePause/reprise lecture (fade out 0.3s)
-

Aucune action complexe au volant → Sécurité routière maximale.

-
-

Like automatique implicite

-

Principe : Le système détecte automatiquement l'intérêt utilisateur selon le temps d'écoute.

-

Règles d'attribution :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Durée écoutéeAction automatiquePoints jaugeJustification
≥ 80% du contenuLike renforcé+2.0Écoute quasi-complète = fort intérêt
30-79% du contenuLike standard+1.0Écoute significative = intérêt
< 30% du contenuPas de like0Écoute trop courte
Skip après <10sSignal négatif-0.5Désintérêt marqué
-

Exemples concrets :

-
Contenu de 3 minutes (180s) :
-- Écoute 2min30 (83%) → Like renforcé (+2 points)
-- Écoute 1min15 (42%) → Like standard (+1 point)
-- Écoute 30s (17%) puis skip → Pas de like
-- Skip après 5s → Signal négatif (-0.5 point)
-
-Contenu de 15 minutes (900s) :
-- Écoute 13min (87%) → Like renforcé (+2 points)
-- Écoute 6min (40%) → Like standard (+1 point)
-
-
-

Actions complémentaires (app à l'arrêt)

-

Interface mobile (véhicule arrêté uniquement) :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionMoyenEffet
Like expliciteBouton cœur+2 points jauge (même si déjà liké auto)
UnlikeRe-clic cœur (toggle)-2 points jauge
AbonnementBouton "S'abonner" profil créateur+5 points toutes jauges tags créateur
DésabonnementBouton "Se désabonner"-5 points
SignalementMenu contextuel "⋮"Ouverture flux modération
-

Feedback visuel : -- Like automatique : Badge discret "♥ Ajouté à vos favoris" (2s, bas de l'écran) -- Like explicite : Animation cœur rouge + vibration courte -- Abonnement : Animation étoile dorée + badge "Abonné ✓"

-
-

Commandes vocales (optionnel, si CarPlay/Android Auto)

-

Disponible uniquement avec : -- Apple CarPlay (Siri) -- Android Auto (Google Assistant) -- ~30-40% du parc automobile EU (2026)

-

Exemples de commandes :

-
"Hey Siri, like ce podcast"
-"OK Google, abonne-moi à ce créateur"
-"Hey Siri, passe au contenu suivant"
-"OK Google, signale ce contenu"
-
-

Implémentation : Intents iOS/Android personnalisés (Sprint 5, post-MVP)

-
-

Gestion impacts jauges (algorithme)

-

Like automatique : -- Like renforcé (≥80%) → +2% jauges de tous les tags du contenu -- Like standard (30-79%) → +1% jauges des tags du contenu -- Signal négatif (skip <10s) → -0.5% jauges des tags du contenu

-

Actions explicites : -- Like manuel → +2% jauges (cumulable avec like auto) -- Unlike → -2% jauges -- Abonnement → +5% toutes jauges tags créateur -- Désabonnement → -5% toutes jauges

-

Persistance : -- Événements stockés en base (table listen_events) -- Mise à jour jauges : immédiate (Redis) + async batch (PostgreSQL)

-
-

Implémentation technique

-

Backend (Go) :

-
type ListenEvent struct {
-    UserID       string
-    ContentID    string
-    StartedAt    time.Time
-    StoppedAt    time.Time
-    Duration     int     // secondes écoutées
-    ContentTotal int     // durée totale contenu
-    Percentage   float64 // duration / contentTotal * 100
-    Action       string  // "completed", "skipped", "paused"
-}
-
-func ProcessListenEvent(event ListenEvent) {
-    percentage := event.Percentage
-
-    // Signal négatif fort
-    if event.Action == "skipped" && event.Duration < 10 {
-        UpdateJauges(event.UserID, event.ContentID, -0.5)
-        return
-    }
-
-    // Like automatique
-    if percentage >= 80 {
-        AutoLike(event.UserID, event.ContentID, 2.0) // Renforcé
-    } else if percentage >= 30 {
-        AutoLike(event.UserID, event.ContentID, 1.0) // Standard
-    }
-    // < 30% : pas de like
-}
-
-

Mobile (iOS/Android) :

-
// iOS - Tracking écoute
-class AudioPlayerManager {
-    var startTime: Date?
-    let contentDuration: TimeInterval
-
-    func onPlay() {
-        startTime = Date()
-    }
-
-    func onStop(action: String) { // "completed" | "skipped" | "paused"
-        guard let start = startTime else { return }
-        let duration = Date().timeIntervalSince(start)
-        let percentage = (duration / contentDuration) * 100
-
-        // API call
-        API.track(ListenEvent(
-            contentId: currentContentId,
-            duration: Int(duration),
-            percentage: percentage,
-            action: action
-        ))
-    }
-}
-
-
-

Justification

-

Avantages : -- ✅ Sécurité routière maximale : aucune action complexe au volant -- ✅ UX intuitive : comportement standard industrie (Spotify, YouTube Music, Deezer) -- ✅ Compatibilité 100% : fonctionne sur tous véhicules, tous OS -- ✅ Engagement amélioré : tous les contenus écoutés génèrent des signaux -- ✅ Algorithme plus précis : données granulaires (30%, 50%, 80%, 100%) -- ✅ Simplicité développement : pas de workarounds complexes iOS/Android

-

Inconvénients mitigés : -- ⚠️ Pas de like explicite en conduite → Mitigation : like automatique + vocal (CarPlay/Android Auto) -- ⚠️ Pas d'abonnement en conduite → Mitigation : liste "Créateurs à découvrir" dans app -- ⚠️ Like automatique peut surprendre → Mitigation : onboarding clair + unlike possible

-
-

Communication utilisateurs (onboarding)

-

Écran onboarding 1 :

-
🚗 Conduite sécurisée
-
-RoadWave détecte automatiquement vos goûts
-selon vos écoutes.
-
-Plus vous écoutez longtemps, plus
-l'algorithme s'améliore !
-
-[Suivant]
-
-

Écran onboarding 2 :

-
❤️ Likes automatiques
-
-Pas besoin de liker manuellement :
-si vous écoutez >50% d'un contenu,
-on comprend que vous aimez !
-
-[Suivant]
-
-

Écran onboarding 3 :

-
⏸️ Commandes simples
-
-Utilisez les boutons au volant :
-• Suivant → Prochain contenu
-• Précédent → Contenu d'avant
-• Pause → Mettre en pause
-
-[Commencer]
-
-
-

5.4 Lecture en boucle et enchaînement

-

Décision : Passage automatique après 2s + insertion pub paramétrable

-

Fin de contenu : -1. Audio termine → Timer 2 secondes démarre -2. UI overlay : "Contenu suivant dans 2s..." + barre décompte -3. Possibilité annuler : bouton "Rester sur ce contenu" (optionnel) -4. Timer atteint 0 → passage automatique au contenu suivant

-

Délai selon contexte :

- - - - - - - - - - - - - - - - - - - - - - - - - -
ModeDélaiJustification
Standard2 secondesTemps réaction confortable
Mode Kids1 secondeAttention courte enfants
Live0 secondeEnchaînement immédiat
-

Insertion publicité : -- Pub s'insère pendant le délai de 2s (transition naturelle) -- Fréquence : paramétrable admin (défaut : 1 pub / 5 contenus) -- Message : "Publicité (15s)" puis lecture pub -- ⚠️ Jamais d'interruption d'un contenu en cours

-

Publicité skippable : -- Durée minimale visionnage : paramétrable (défaut : 5 secondes) -- Bouton "Passer" apparaît après délai -- Métriques engagement : taux skip, durée écoute moyenne -- Like et abonnement autorisés sur pub (engagement créateur pub)

-

Si aucun contenu disponible : -1. Message : "Aucun contenu disponible dans cette zone" -2. Proposition : "Élargir la zone de recherche ?" (bouton) -3. Si accepté → relance algo avec rayon +50km -4. Sinon → lecture en pause, attente action utilisateur

-

Gestion erreurs : -- Échec chargement contenu suivant → retry 3× avec backoff exponentiel -- Si 3 échecs → message "Connexion instable, basculement mode offline" -- Mode offline → lecture contenus téléchargés uniquement

-

Justification : -- Fluidité : enchaînement naturel sans action utilisateur -- Contrôle : possibilité annuler pendant délai -- Paramétrabilité pub : évite frustration excès publicité -- Engagement pub : like/abonnement autorisé = monétisation créateurs pub

-
-

Récapitulatif Section 5

-
- -

6. Publicités

-

6.1 Système de campagnes publicitaires

-

Décision : Interface self-service avec maîtrise budget et métriques détaillées

-

Fonctionnalités publicitaire :

-

Création de campagne

-

Paramètres configurables :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamètreOptionsJustification
Budget totalMontant libre (min 50€)Maîtrise coût total
Durée campagneDate début/fin + étalementEx: 300€ sur 2 semaines
Ciblage géographiquePoint GPS / Ville / Département / Région / NationalPrécision selon besoin
Ciblage horairePlages horaires (ex: 7h-9h, 17h-19h)Optimisation trajet domicile-travail
Centres d'intérêtTags (ex: Automobile, Voyage)Ciblage thématique
Tranche d'âgeTout public / 13+ / 16+ / 18+Respect classifications
-

Étalement budget :

-
Exemple campagne :
-- Budget : 300€
-- Durée : 14 jours
-- Zone : Département du Var
-- Horaires : 7h-9h + 17h-19h (rush)
-
-Calcul automatique :
-→ Budget/jour = 300€ / 14 = 21.43€/jour
-→ Diffusions/jour estimées : ~430 (0.05€/écoute)
-→ Alerte si budget épuisé avant fin (réajustement possible)
-
-

Mode de paiement : -- ✅ Prépaiement obligatoire (évite impayés) -- ✅ Carte bancaire uniquement (Mangopay) -- ✅ Recharge automatique optionnelle (si budget <10%)

-

Validation et modération

-

Processus : -1. Publicitaire upload audio pub (formats : MP3, AAC) -2. Validation manuelle obligatoire (modérateur RoadWave) - - Délai : 24-48h ouvrées - - Critères : respect réglementation, qualité audio, classification correcte -3. Si accepté → campagne démarre à la date choisie -4. Si refusé → email avec raison + remboursement automatique

-

Contenus interdits en pub : -- ❌ Alcool, tabac (réglementation française) -- ❌ Jeux d'argent -- ❌ Contenu politique (pendant campagnes électorales) -- ❌ Contenu sexuel ou violence -- ✅ Tous commerces/services légaux

-

Dashboard métriques engagement

-

Indicateurs temps réel :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueDescriptionUtilité
ImpressionsNombre de diffusionsVolume exposition
Écoutes complètesPub écoutée >80%Engagement réel
Taux de skip% skip après délai minQualité contenu
Durée moyenne écouteSecondes écoutéesRétention attention
LikesNombre de likesAppréciation contenu
AbonnementsAbonnements au créateur pubConversion forte
Coût par écouteBudget / écoutes complètesROI campagne
Répartition géographiqueHeatmap diffusionsValidation ciblage
Répartition horaireGraphique par heureOptimisation horaires
-

Métriques engagement avancées : -- Taux complétion par tranche d'âge : identifier audience réceptive -- Carte de chaleur GPS : visualiser zones forte écoute -- Comparatif campagnes : A/B testing créatifs publicitaires

-

Export données : -- ✅ CSV/Excel pour analyse externe -- ✅ Graphiques interactifs (Chart.js) -- ✅ Rapport PDF automatique fin de campagne

-

Gestion budget et alertes

-

Suivi temps réel : -- Dashboard : Budget restant, % consommé, jours restants -- Projection : "À ce rythme, budget épuisé dans X jours" -- Alerte email/push si : - - Budget consommé à 80% - - Budget consommé à 90% - - Budget épuisé - - Campagne terminée (rapport final)

-

Ajustements en cours : -- ✅ Pause campagne (budget conservé) -- ✅ Prolonger campagne (recharge budget) -- ✅ Modifier ciblage horaire/géo (si <50% budget consommé) -- ❌ Modifier audio (nécessite nouvelle validation)

-

Système d'enchères (post-MVP)

-

Optionnel future : -- Enchère au CPM (coût pour 1000 impressions) -- Priorité selon prix : pub prix élevé → diffusion privilégiée -- Floor price : 2€ CPM minimum -- Évite surcharge pub : max 1 pub / 5 contenus stricte

-

Justification décision MVP : -- Tarif fixe simple : 0.05€/écoute complète -- Pas de complexité enchères immédiatement -- Scalable : passage enchères ultérieur si demande forte

-
-

6.2 Insertion et fréquence

-

Décision : Paramétrable admin + respect expérience utilisateur

-

Fréquence d'insertion : -- Défaut : 1 pub / 5 contenus (utilisateurs gratuits) -- Paramétrable admin : curseur 1/3 à 1/10 -- Utilisateurs Premium : 0 pub (modèle sans publicité)

-

Règles strictes : -- ⚠️ Jamais d'interruption contenu en cours -- Pub s'insère uniquement entre deux contenus (pendant délai 2s) -- Rotation : même pub max 3 fois/jour par utilisateur (évite saturation) -- Limite : max 6 pubs/heure par utilisateur (évite spam)

-

Ciblage intelligent : -- Géolocalisation prioritaire (point GPS > ville > département > région > national) -- Centres d'intérêt secondaires (tags utilisateur) -- Horaire (campagne 7h-9h → diffusion uniquement pendant plage)

-

Volume audio normalisé : -- Pub normalisée à -14 LUFS (standard broadcast) -- Évite effet "pub trop forte" (frustration utilisateur) -- Validation automatique via FFmpeg lors encodage

-
-

6.3 Caractéristiques publicités

-

Durée : -- Minimum : 10 secondes -- Maximum : 60 secondes -- Recommandé : 15-30 secondes (sweet spot engagement)

-

Skippable : -- Délai minimum obligatoire : 5 secondes (paramétrable admin : 3-10s) -- Bouton "Passer la publicité" apparaît après délai -- Durée minimale comptabilisée pour facturation

-

Facturation : -- Écoute complète (>80%) : 0.05€ facturé publicitaire -- Skip après délai min : 0.02€ (exposition partielle) -- Skip immédiat (<5s) : 0€ (pas d'engagement)

-

Justification modèle tarif : -- Incitatif qualité : pub engageante = coût réduit -- Équitable : publicitaire paie pour attention réelle -- Transparent : dashboard montre écoutes complètes vs skips

-
-

Récapitulatif Section 6

-
- -

7. Radio live

-

7.1 Démarrage d'un live

-

Décision : Buffer 15s + notification abonnés + limite 8h

-

Processus de démarrage :

-
    -
  1. Créateur appuie "Démarrer live" dans l'app
  2. -
  3. Vérification pré-live :
  4. -
  5. Connexion ≥1 Mbps upload (warning si insuffisant)
  6. -
  7. Micro autorisé
  8. -
  9. Zone diffusion déjà définie (ville, département, région, national)
  10. -
  11. Buffer initial 15 secondes avant diffusion publique
  12. -
  13. Créateur parle pendant 15s → accumulation buffer serveur
  14. -
  15. Message créateur : "Live démarre dans 15s... Testez votre micro"
  16. -
  17. Permet vérifier qualité audio avant diffusion
  18. -
  19. Après 15s → Live public, auditeurs peuvent rejoindre
  20. -
-

Notification abonnés : -- ✅ Push notification immédiate à tous les abonnés dans la zone géographique -- Message : "🔴 [Nom créateur] est en direct : [Titre live]" -- Tap notification → ouverture app + lecture live immédiate -- Filtrage géographique : si abonné hors zone, pas de notif (évite frustration)

-

Limite de durée : -- Maximum 8 heures par session live -- Warning créateur à 7h30 : "Votre live se terminera dans 30 min" -- Si besoin continuer → arrêt + redémarrage nouveau live (évite abus ressources serveur)

-

Métadonnées obligatoires :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChampFormatValidation
Titre5-100 caractèresEx: "Discussion politique en direct"
Tags1-3 centres d'intérêtSélection liste prédéfinie
Classification âgeEnumTout public / 13+ / 16+ / 18+
Zone diffusionGeoVille / Département / Région / National
-

Contenus interdits en live :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeDescriptionSanction
Concert/spectacleDiffusion concert en direct depuis la salleStrike 2 immédiat + ban temporaire
Événement sportif payantMatch, compétition avec droits TVStrike 2 immédiat
Œuvre protégéeFilm, série, musique en fond sans droitsStrike 1 + suppression live
Contenu violentAgression, violence physiqueBan immédiat
Contenu illégalApologie terrorisme, pédopornographieBan définitif + signalement autorités
-

Exemple usecase interdit :

-
❌ Utilisateur dans salle de concert diffuse live performance
-→ Violation droits d'auteur + droits de diffusion
-→ Détection : modération réactive (signalements) + IA audio fingerprint
-→ Sanction : Strike 2 (suspension 7 jours) + suppression live + suppression replay
-
-

Détection violations : -- Signalement utilisateurs : bouton "Signaler" accessible pendant live -- IA audio fingerprint : détection musique protégée en arrière-plan (post-MVP) -- Modération réactive : modérateurs peuvent écouter lives signalés en temps réel -- Coupure immédiate : modérateur peut arrêter live si contenu illégal évident

-

Justification : -- Buffer 15s : équilibre entre test qualité et friction minimale -- Notification abonnés : engagement maximal, valeur ajoutée live -- 8h max : couvre 99% cas usage (podcasts longs, émissions radio) sans abus -- Interdictions strictes : protection juridique plateforme (DSA EU, droits d'auteur) -- Coût : WebRTC ingestion + HLS distribution (réutilise infra existante)

-
-

7.2 Arrêt du live

-

Décision : Compte à rebours 5s + tolérance déconnexion 60s + enregistrement auto

-

Fin manuelle créateur :

-
    -
  1. Créateur appuie "Arrêter live"
  2. -
  3. Compte à rebours 5 secondes affiché
  4. -
  5. Message audio : "Ce live se termine dans 5... 4... 3... 2... 1"
  6. -
  7. Permet au créateur de faire un outro propre
  8. -
  9. Annulable pendant décompte (bouton "Annuler")
  10. -
  11. Timer atteint 0 → arrêt diffusion
  12. -
  13. Traitement post-live automatique démarre (voir ci-dessous)
  14. -
-

Fin automatique si déconnexion :

- - - - - - - - - - - - - - - - - -
Durée coupureComportement
<60 secondesMessage auditeurs : "Connexion créateur perdue, reconnexion en cours..."
≥60 secondesArrêt automatique live + message : "Le live est terminé suite à une coupure de connexion"
-

Enregistrement automatique :

-

Obligatoire et automatique (valeur ajoutée énorme)

-

Processus : -1. Pendant live : enregistrement continu serveur (format Opus raw) -2. Fin live → job asynchrone (worker Go + FFmpeg) : - - Conversion MP3 256 kbps (qualité optimale) - - Génération segments HLS (comme contenu classique) - - Normalisation volume -14 LUFS - - Détection silences prolongés (nettoyage) -3. Publication automatique du replay : - - Titre : "[REPLAY] [Titre live original]" - - Même zone diffusion, tags, classification - - Disponible sous 5-10 minutes après fin live - - Type géo : automatiquement "Géo-neutre" (replay = contenu pérenne)

-

Options créateur :

- - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDéfautDescription
Publier replay automatiquement✅ OUIDésactivable avant démarrage live
Supprimer replay après coup✅ PossibleSuppression standard contenu
Modifier replay❌ NonIntégrité enregistrement
-

Conservation fichier source : -- Opus raw conservé 7 jours après fin live (backup) -- Suppression automatique après 7j (économie stockage) -- Si replay supprimé par créateur → fichier raw supprimé immédiatement

-

Justification : -- Compte à rebours 5s : outro propre, pas de coupure brutale -- Tolérance 60s : évite arrêts intempestifs (tunnel, changement cellule) -- Enregistrement auto : valorisation contenu éphémère, génération contenu pérenne -- MP3 256 kbps : qualité optimale pour replay (vs 48 kbps live) -- Coût : stockage minimal (Opus → MP3 1× par live, puis suppression raw après 7j)

-
-

7.3 Comportement auditeur

-

Décision : Buffer 15s + continuation hors zone + reconnexion au live actuel + écoute passive uniquement

-

Buffer de synchronisation :

-
    -
  • 15 secondes entre créateur et auditeurs
  • -
  • Raisons :
  • -
  • Stabilité réseau mobile (3G/4G fluctuant)
  • -
  • Synchronisation approximative acceptable (pas besoin temps réel strict)
  • -
  • Permet buffering anticiper coupures courtes (tunnels)
  • -
-

Comparaison buffers :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BufferAvantagesInconvénientsDécision
5sQuasi temps réelInstable 3G, coupures fréquentes
10sBon compromisLégèrement juste pour 3G
15sStabilité optimale 3G/4GLéger décalage acceptable
20s+Très stableDécalage trop perceptible
-

Zone géographique pendant live :

-
    -
  • Continuation si sortie de zone
  • -
  • Scénario : auditeur écoute live régional → sort du département → live continue
  • -
  • Raisons :
  • -
  • Pas de coupure brutale (mauvaise UX)
  • -
  • Écoute engagée = terminer naturellement
  • -
  • Après fin live → algo normal (pas de contenus hors zone)
  • -
-

Reconnexion après coupure réseau :

- - - - - - - - - - - - - - - - - -
Durée coupureComportement
<90 secondesReprend au live actuel (pas au buffer ancien) + saut temporel transparent
≥90 secondesMessage : "Live en cours perdu, passage au contenu suivant" + algo propose contenu normal
-

Interactions disponibles :

-

Décision ferme : ❌ Aucun chat en direct, ni maintenant ni dans le futur

-

Raisons : -- Sécurité routière : pas de distraction en voiture (focus UX) -- Harcèlement : évite contenu haineux, insultes, trolling -- Modération : pas de coût modération temps réel (impossible à scale) -- Simplicité : écoute passive = expérience uniforme

-

Actions autorisées pendant live :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionDisponibleEffet
LikeBouton cœur interface mobile (véhicule arrêté)
Abonnement créateurBouton profil créateur (interface mobile)
SkipPasse au contenu suivant, sort du live
PrécédentPas de sens sur live (flux temps réel)
ChatJamais implémenté (décision définitive)
Réactions emojiJamais implémenté (décision définitive)
-

Messages utilisateur : -- "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement."

-

Justification décision définitive : -- UX cohérente : RoadWave = écoute en conduisant, pas réseau social interactif -- Bien-être : évite toxicité, harcèlement, haine (fléau réseaux sociaux) -- Juridique : pas de risque contentieux modération chat (DSA EU) -- Coût : 0€ infra chat, 0€ modération temps réel -- Différenciation : positionnement "audio safe" vs plateformes toxiques

-
-

7.4 Architecture technique

-

Stack :

-
Créateur (App mobile)
-    ↓ WebRTC (OPUS 48 kbps)
-Serveur Ingestion (Go + Pion WebRTC)
-    ↓ Conversion temps réel
-Serveur HLS (segments .ts)
-    ↓ CDN (Bunny)
-Auditeurs (App mobile, HLS natif)
-
-

Flux détaillé : -1. Créateur → WebRTC OPUS 48 kbps vers serveur Go -2. Serveur Go → Conversion temps réel OPUS → segments HLS (.m3u8 + .ts) -3. Bunny CDN → Distribution HLS avec cache -4. Auditeurs → Lecture HLS native iOS/Android (buffer 15s) -5. Enregistrement parallèle → Opus raw stocké temporairement -6. Post-live → Job async : Opus → MP3 256 kbps → Publication replay

-

Dépendances : -- ✅ Pion WebRTC (Go library, open source, MIT license) -- ✅ FFmpeg (conversion audio, LGPL/GPL) -- ✅ Bunny CDN (distribution HLS, pas Google/Cloudflare) -- ✅ PostgreSQL + Redis (métadonnées live + cache)

-

Avantages : -- ✅ Pas de dépendance Google/Facebook/Cloudflare (souveraineté) -- ✅ WebRTC standard ouvert (Pion = lib Go pure) -- ✅ Réutilise infra HLS existante (pas de doublon) -- ✅ CDN cache les segments (coût réduit) -- ✅ Scalable horizontalement (workers Go)

-

Coût estimé :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhaseUtilisateursInfra liveCoût/mois
MVP0-100K1 instance Go (ingestion 100 lives simultanés)+50€ (serveur) + bande passante CDN
Growth100K-1M3-5 instances Go (500 lives simultanés)+200€ + bande passante
Scale1M-10MKubernetes auto-scale (2000+ lives)+1K€ + bande passante
-

Bande passante : -- Live : 48 kbps × nb_auditeurs (via CDN, cache segments) -- Exemple : 100 auditeurs = 4.8 Mbps = ~2 Go/heure via CDN -- Coût Bunny : ~0.01€/GB = 0.02€/heure pour 100 auditeurs

-
-

Récapitulatif Section 7

-
- -

8. Abonnements et notifications

-

8.1 Impact sur l'algorithme

-

Décision : Boost +30% au score + reste dans le mix

-

Boost de score abonnements : -- +30% au score final pour contenus d'un créateur suivi -- Application : multiplicateur sur le score calculé

-
score_final_avec_boost = score_final × 1.3
-
-

Reste dans le mix : -- ❌ Pas de priorité absolue (pas de file dédiée abonnements) -- ✅ Contenu suivi entre en compétition avec autres contenus -- ✅ Si créateur suivi publie contenu faible engagement → peut être battu par contenu viral non-suivi

-

Exemple concret :

-
Utilisateur à Paris, 2 contenus disponibles :
-
-Contenu A (créateur NON suivi) :
-- Score géo : 0.9 (très proche)
-- Score intérêts : 0.8
-- Score engagement : 0.7
-→ Score final : 0.80
-
-Contenu B (créateur suivi) :
-- Score géo : 0.5 (moyennement proche)
-- Score intérêts : 0.6
-- Score engagement : 0.5
-→ Score final : 0.53
-→ Score avec boost : 0.53 × 1.3 = 0.69
-
-→ Contenu A proposé en premier (0.80 > 0.69)
-
-

Cas où abonnement fait la différence :

-
Contenu A (non suivi) : score 0.70
-Contenu B (suivi) : score 0.60 → avec boost 0.78
-→ Contenu B proposé (boost fait pencher la balance)
-
-

Justification : -- Équilibre : valorise abonnements sans enfermer utilisateur -- Découverte : contenus viraux/locaux peuvent toujours émerger -- Prévisible : boost fixe, pas de logique opaque -- Coût 0 : multiplicateur simple dans l'algo

-
-

8.2 Notifications contextuelles

-

Décision : Push adapté selon contexte (voiture vs à pied) + limite 10/jour

-

Détection contexte utilisateur :

- - - - - - - - - - - - - - - - - - - - -
ContexteDétectionComportement
En voitureVitesse GPS >10 km/hNotifications silencieuses (in-app uniquement) + commandes volant
À piedVitesse GPS <5 km/hNotifications push actives + interface tactile/vocale
-

Notifications activées :

-

En voiture (mode conduite)

- - - - - - - - - - - - - - - - - - - - - - - - - -
ÉvénementNotificationComportement
Nouveau contenu créateur suiviIn-app uniquementBadge compteur, pas de push (sécurité)
Live créateur suiviIn-app uniquementBadge compteur, pas de push
Point d'intérêt procheAudio notificationBip + annonce vocale : "Audio-guide disponible"
-

À pied (mode piéton)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ÉvénementNotificationComportement
Nouveau contenu créateur suivi✅ PushSi utilisateur dans zone géo du contenu
Live créateur suivi✅ PushSi utilisateur dans zone géo
Audio-guide disponible✅ Push"📍 Audio-guide disponible : [Lieu]"
Séquence suivante suggéréeAudio notificationAnnonce vocale : "Pièce suivante disponible"
-

Format notifications :

-

Nouveau contenu :

-
🎧 [Nom créateur] a publié : "[Titre contenu]"
-Tap pour écouter
-
-

Live en direct :

-
🔴 [Nom créateur] est en direct : "[Titre live]"
-Tap pour rejoindre
-
-

Audio-guide à pied :

-
📍 Audio-guide disponible : [Nom du lieu]
-Choisissez parmi 3 guides pour [Musée du Louvre]
-Tap pour explorer
-
-

Filtrage géographique : -- Si contenu/live hors zone utilisateur → pas de notification -- Évite frustration : "notification pour contenu que je ne peux pas écouter" -- Exception : contenu national → notifie tous les abonnés

-

Fréquence maximale : -- Maximum 10 notifications push/jour par utilisateur (tous types confondus) -- Si dépassement : notifications regroupées -- Message groupé : "🎧 3 nouveaux contenus de créateurs suivis"

-

Plages horaires : -- Mode silencieux : 22h-8h (pas de push, sauf live) -- Paramétrable utilisateur (désactivation totale possible) -- Option "Notifications importantes uniquement" (lives uniquement)

-

Gestion préférences :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PréférenceDéfautDescription
Nouveaux contenus✅ ActivéPush à chaque nouveau contenu (à pied uniquement)
Lives✅ ActivéPush au démarrage live (à pied uniquement)
Audio-guides proximité✅ ActivéPush quand audio-guide détecté à <100m
Mode silencieux✅ Activé (22h-8h)Pas de push nocturne
Limite quotidienne10Modifiable 5-20
-

Justification : -- Sécurité routière : pas de push en conduite (distraction) -- Engagement piéton : push actifs pour audio-guides (valeur ajoutée tourisme) -- Pas de spam : limite 10/jour + mode silencieux -- Filtrage géo : pertinence maximale (pas de notif inutiles) -- Coût : Firebase Cloud Messaging (gratuit jusqu'à volume élevé)

-
-

8.3 Mode Audio-guide (piéton)

-

Décision : Navigation manuelle multiséquence + choix parmi plusieurs guides

-

Fonctionnement :

-

Détection et proposition

-
    -
  1. Utilisateur à pied (<5 km/h) passe à <100m d'un lieu avec audio-guides
  2. -
  3. Notification push : "📍 Audio-guide disponible : [Musée du Louvre]"
  4. -
  5. Tap notification → Page de sélection audio-guides
  6. -
-

Page de sélection

-

Affichage :

-
📍 Musée du Louvre
-
-Choisissez votre guide :
-
-┌─────────────────────────────────┐
-│ 🎨 Visite complète (45 min)    │
-│ Par [Créateur A] • 12 séquences│
-│ ⭐ 4.8 • 1.2K écoutes           │
-└─────────────────────────────────┘
-
-┌─────────────────────────────────┐
-│ 🏛️ Œuvres majeures (20 min)    │
-│ Par [Créateur B] • 5 séquences │
-│ ⭐ 4.9 • 3.5K écoutes           │
-└─────────────────────────────────┘
-
-┌─────────────────────────────────┐
-│ 👶 Visite famille (30 min)     │
-│ Par [Créateur C] • 8 séquences │
-│ ⭐ 4.7 • 850 écoutes            │
-└─────────────────────────────────┘
-
-

Interface audio-guide

-

Après sélection :

-
🎨 Visite complète • Musée du Louvre
-
-Piste actuelle : 2/12
-"La Joconde - Histoire et mystères"
-[████████────────────] 3:24 / 6:50
-
-Liste des séquences :
-✅ 1. Introduction et architecture
-▶️ 2. La Joconde - Histoire et mystères
-⏸️ 3. Vénus de Milo
-⏸️ 4. Victoire de Samothrace
-⏸️ 5. Peintures Renaissance
-...
-⏸️ 12. Conclusion et boutique
-
-

Navigation :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ActionGesteEffet
Séquence suivanteTap "Suivant" ou commande vocale "Suivant"Passe à séquence N+1
Séquence précédenteTap "Précédent" ou commande vocale "Précédent"Revient à séquence N-1
Saut directTap séquence dans listeLecture séquence choisie
PauseTap bouton pauseMet en pause, reprise position exacte
QuitterTap "×"Sauvegarde progression, sortie guide
-

Guidage vocal automatique : -- Entre 2 séquences : "Vous avez terminé la séquence 2. Dirigez-vous vers la Vénus de Milo pour la séquence 3." -- Si utilisateur s'éloigne (>50m de la prochaine pièce) : "Vous vous éloignez de la prochaine étape. Consultez le plan."

-

Sauvegarde progression : -- Position dans guide sauvegardée automatiquement -- Retour ultérieur : "Reprendre à la séquence 5 ?" ou "Recommencer depuis le début" -- Historique : guide marqué "Terminé" si toutes séquences écoutées

-

Création audio-guide multiséquence :

-

Processus créateur : -1. Créateur upload plusieurs fichiers audio (1 par séquence) -2. Numérote les séquences : "Séquence 1", "Séquence 2", etc. -3. Titre chaque séquence : "Introduction", "La Joconde", etc. -4. Définit point GPS unique pour tout le guide (centre du lieu) -5. Métadonnées : durée totale calculée automatiquement

-

Format stockage :

-
{
-  "guide_id": "abc123",
-  "title": "Visite complète Musée du Louvre",
-  "location": {"lat": 48.8606, "lon": 2.3376, "radius": 200},
-  "sequences": [
-    {
-      "sequence_number": 1,
-      "title": "Introduction et architecture",
-      "audio_url": "https://cdn.../seq1.mp3",
-      "duration_seconds": 180
-    },
-    {
-      "sequence_number": 2,
-      "title": "La Joconde - Histoire et mystères",
-      "audio_url": "https://cdn.../seq2.mp3",
-      "duration_seconds": 410
-    },
-    ...
-  ],
-  "total_duration_seconds": 2700,
-  "creator_id": "creator_xyz"
-}
-
-

Justification : -- UX piéton : navigation tactile adaptée (pas de commandes volant) -- Autonomie : utilisateur maître de son rythme (pas d'enchaînement forcé) -- Choix : plusieurs guides = diversité styles (famille, expert, rapide) -- Engagement : sauvegarde progression = incitation terminer -- Coût : réutilise infra contenu standard (juste métadonnées séquences)

-
-

8.4 Limites et désabonnement

-

Décision : 200 abonnements max + désabonnement -5% jauges

-

Nombre maximum d'abonnements : -- 200 créateurs maximum par utilisateur -- Raisons : - - Évite spam : au-delà de 200, notifications ingérables - - Usage réaliste : 200 créateurs = déjà énorme (vs 100-150 sur YouTube/Twitter) - - Performance : requêtes SQL optimisées (index sur 200 max)

-

Si limite atteinte : -- Message : "Vous suivez déjà 200 créateurs. Désabonnez-vous d'un créateur pour en suivre un nouveau." -- Liste triable : par date abonnement, nb contenus écoutés, dernière activité -- Suggestion : "Vous n'avez pas écouté [Créateur X] depuis 6 mois, le désabonner ?"

-

Abonnement initial : -- Impact : +5% toutes jauges tags du créateur (défini en ADR-010) -- Action : Bouton "S'abonner" dans profil créateur (interface mobile) -- Immédiat à l'action

-

Désabonnement : -- Impact : -5% toutes jauges tags du créateur (symétrique) -- Action : Bouton "Se désabonner" dans profil créateur -- Immédiat à l'action -- Pas de confirmation (action réversible)

-

Exemple :

-
Créateur tague ses contenus : Automobile, Voyage
-
-Abonnement :
-→ Jauge Automobile : 60% → 65% (+5%)
-→ Jauge Voyage : 55% → 60% (+5%)
-
-3 mois plus tard, désabonnement :
-→ Jauge Automobile : 65% → 60% (-5%)
-→ Jauge Voyage : 60% → 55% (-5%)
-
-

Gestion multi-tags : -- Si créateur a 3 tags → +5% sur chacun des 3 tags -- Logique : abonnement = signal fort d'affinité à TOUS les sujets du créateur

-

Abonnements réciproques : -- ❌ Pas d'abonnement mutuel visible -- Créateur ne voit pas qui est abonné (privacy) -- Créateur voit uniquement : nombre total abonnés (métrique globale)

-

Justification : -- Limite 200 : équilibre entre liberté et gestion spam -- Symétrie +5%/-5% : cohérence mathématique, prévisibilité -- Privacy : pas de liste publique abonnés (évite stalking) -- Coût : table abonnements PostgreSQL standard

-
-

Récapitulatif Section 8

-
- -

9. Monétisation créateurs

-

9.1 Pourboires

-

Décision : ❌ Fonctionnalité abandonnée pour le MVP

-

Raisons : -- Complexité juridique (collecte pour compte de tiers, TVA variable) -- Frais de transaction élevés sur petits montants (Mangopay ~1.8% + 0.18€) -- UX additionnelle à développer (wallet, transactions, confirmations) -- Charge comptable importante pour la plateforme

-

Post-MVP : Possible réintégration avec crypto (Bitcoin/Lightning Network) si législation UE l'autorise clairement (régulation MiCA en cours).

-
-

9.2 Conditions d'activation de la monétisation

-

Décision : 5 critères cumulatifs obligatoires

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CritèreSeuilJustification
AnciennetéCompte créé depuis ≥ 3 moisAnti-fraude : temps de détecter comportements suspects
Popularité≥ 500 abonnésGarantit audience réelle et engagée
Engagement≥ 10 000 écoutes complètes cumuléesCréateurs produisant du contenu de qualité
FiabilitéAucun strike actif, 0 contenu modéré dans les 6 derniers moisHistorique propre requis
Régularité≥ 5 contenus publiés dans les 90 derniers joursActivité constante
-

Vérification : Automatique via requêtes SQL lors de la demande d'activation

-

Affichage : -- Bouton "Demander la monétisation" dans profil créateur -- Si critères non remplis → affichage progression vers objectifs -- Si critères remplis → redirection vers KYC Mangopay

-

Justification : -- Anti-fraude : Le délai de 3 mois permet de détecter les comptes suspects -- Qualité : Seuls les créateurs sérieux avec audience réelle sont monétisés -- Coût administratif : Réduit le nombre de comptes à gérer (KYC, comptabilité, virements) -- Légitimité : Audience organique prouvée

-
-

9.3 KYC (Know Your Customer) et inscription

-

Décision : Statut juridique professionnel obligatoire

-

Statuts acceptés : -- Auto-entrepreneur (micro-BNC pour artistes/créateurs de contenu) -- SARL/SAS/SASU (sociétés)

-

Documents requis :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DocumentObligatoireFormatValidité
SIRET14 chiffresPermanent
RIB professionnelIBAN FRPermanent
Pièce d'identitéCNI/PasseportEn cours de validité
Numéro TVA intracommunautaire⚠️ Si applicableFR + 11 chiffresPermanent
Kbis <3 mois⚠️ Si sociétéPDF<3 mois
-

Vérification : Via Mangopay (KYC intégré + vérification bancaire)

-

Délai : 24-72h si documents conformes

-

Rejet possible si : -- Documents invalides/illisibles -- Identité ne correspond pas au compte RoadWave -- Liste noire anti-blanchiment (vérification automatique Mangopay) -- RIB non professionnel (particulier)

-

Base légale : -- Conformité fiscale : L'État français impose déclaration revenus >1200€/an (DAS2) -- Anti-blanchiment : Directive EU 2018/843 (5ème directive LCB-FT) -- RGPD : Données hébergées EU via Mangopay (conforme)

-

Justification : -- Responsabilité légale : RoadWave doit pouvoir prouver identité réelle créateurs monétisés -- Automatisation : Mangopay gère tout (KYC, vérifications, conformité, e-wallets) -- KYC gratuit : inclus dans l'offre Mangopay (vs 1.20€ chez Stripe) -- Souveraineté EU : Mangopay est européen (France/Luxembourg), régulé ACPR

-
-

9.4 Sources de revenus créateurs

-

A) Publicités (utilisateurs gratuits)

-

Formule : 3€ / 1000 écoutes complètes (CPM créateur)

-

Répartition économique :

-
Publicité facturée par RoadWave : 0.05€/écoute complète = 50€ CPM
-├─ Créateur touche : 3€ (6% du CA pub)
-└─ Plateforme garde : 47€ (94%)
-    ├─ CDN + infrastructure : ~10-15€
-    ├─ Modération + support : ~5-10€
-    ├─ Développement + R&D : ~10-15€
-    └─ Marge opérationnelle : ~10-15€
-
-

Exemple concret : -- 10 000 écoutes/mois → créateur touche 30€ -- 50 000 écoutes/mois → créateur touche 150€ -- 100 000 écoutes/mois → créateur touche 300€

-

Comparaison industrie : -- YouTube : 3-5€/1000 vues -- Spotify : 3-4€/1000 écoutes -- RoadWave : 3€/1000 écoutes (aligné)

-

Règles comptabilisation : -- ✅ Écoute complète = ≥80% du contenu écouté -- ✅ Utilisateur gratuit uniquement -- ❌ Écoutes Premium ne comptent pas ici (autre système) -- ❌ Bots détectés exclus (rate limiting + analyse patterns)

-
-

B) Abonnés Premium

-

Formule : 70% au créateur, 30% à la plateforme

-

Répartition proportionnelle au temps d'écoute effectif :

-
Utilisateur Premium = 4.99€/mois
-├─ 3.49€ reversés aux créateurs (70%)
-└─ 1.50€ gardés par plateforme (30%)
-
-Si l'utilisateur écoute 3 créateurs ce mois :
-- Créateur A : 10h d'écoute (50%) → 1.75€
-- Créateur B : 6h d'écoute (30%) → 1.05€
-- Créateur C : 4h d'écoute (20%) → 0.70€
-
-

Calcul technique :

-
-- Pour chaque utilisateur Premium
-SELECT
-    creator_id,
-    SUM(listen_duration_seconds) AS total_seconds,
-    (SUM(listen_duration_seconds) / total_user_seconds) AS ratio,
-    (4.99 * 0.70 * ratio) AS revenue_euros
-FROM premium_listens
-WHERE user_id = :user_id
-  AND month = :current_month
-GROUP BY creator_id;
-
-

Comparaison industrie : -- YouTube Premium : 70/30 -- Spotify : 70/30 -- Apple Music : 52/48 (moins avantageux) -- RoadWave : 70/30 (standard)

-

Justification : -- Standard industrie : ratio équitable éprouvé -- Incitation qualité : créateurs les plus écoutés gagnent plus -- Équité : pas de "winner takes all", chaque créateur écouté reçoit sa part -- Marge plateforme : 30% couvre absence revenus pub sur Premium

-
-

9.5 Paiement des créateurs

-

Seuil minimum : 50€

-
    -
  • En dessous → solde reporté mois suivant
  • -
  • Évite frais bancaires sur micro-sommes
  • -
  • Standard industrie (YouTube/Twitch/Spotify = 50-100€)
  • -
-

Fréquence : Mensuelle

- - - - - - - - - - - - - - - - - - - - - - - - - -
DateAction
Dernier jour du mois (ex: 31 janvier)Calcul revenus du mois via SQL
1-14 du mois suivantTraitement contestations/fraudes éventuelles
15 du mois suivant (ex: 15 février)Virement SEPA via Mangopay (Payout)
16-18 du mois suivantRéception virement (1-3 jours ouvrés SEPA)
-

Virement via Mangopay : -- SEPA pour comptes EU (gratuit, 1-3 jours) -- Virement international hors EU (frais variables selon pays, rare en pratique) -- E-wallets automatiques : chaque créateur possède un wallet Mangopay où ses revenus sont transférés automatiquement

-

Tableau de bord créateur (temps réel) :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueDescriptionMise à jour
Revenus pubÉcoutes × CPMTemps réel
Revenus premiumAbonnés actifs × ratio écouteTemps réel
Solde disponibleTotal revenus mois en coursTemps réel
Solde en attenteRevenus mois précédent (paiement le 15)Figé fin de mois
Historique virementsListe des paiements reçusPermanent
Export comptable CSVDonnées pour expert-comptableTéléchargement
-

Gestion échecs virement : -1. Tentative 1 (15 du mois) → échec -2. Retry automatique J+3 -3. Retry automatique J+7 -4. Si 3 échecs → suspension monétisation + email créateur (RIB invalide)

-
-

9.6 Contenus Premium exclusifs

-

Décision : Créateur décide individuellement pour chaque contenu

-

Fonctionnement : -- Toggle "Réservé Premium" lors création/édition contenu -- Aucune limite imposée : créateur peut mettre 0%, 50% ou 100% en premium -- Badge 👑 visible sur interface utilisateur

-

Comportement utilisateurs gratuits : -- Contenu premium visible dans liste/algo -- Tentative lecture → overlay bloquant -- Message : "Ce contenu est réservé aux abonnés Premium" -- CTA : "Passez Premium pour 4.99€/mois"

-

Comportement algorithme : -- Contenus premium inclus dans recommandations -- Si user gratuit → contenu skippé automatiquement (ne consomme pas de slot) -- Si user premium → diffusé normalement

-

Métadonnées : -- Champ is_premium (boolean) en base -- Index sur ce champ pour requêtes rapides -- Cache Redis : content:{id}:premium (TTL 1h)

-

Justification : -- Liberté créateur : chaque créateur choisit sa stratégie (freemium, tout gratuit, tout premium) -- Incitation Premium : contenu exclusif = argument fort pour s'abonner -- Équité : un petit créateur peut tout mettre en premium, un gros peut tout offrir gratuitement

-
-

9.7 Obligations fiscales

-

RoadWave génère automatiquement :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DocumentFréquenceDestinataireBase légale
Relevé mensuel PDFChaque moisCréateurTransparence
Export CSV comptableÀ la demandeCréateur + expert-comptableFacilitation déclarations
DAS2 annuelSi >1200€/anImpôts (DGFIP)Obligation légale France
-

Créateur responsable de : -- Déclarer ses revenus à l'URSSAF (cotisations sociales auto-entrepreneur ou IS/IR) -- Déclarer ses revenus aux impôts (IR ou IS selon statut) -- Gérer sa TVA si applicable (franchise en base jusqu'à ~37K€/an en micro-BNC) -- Conserver justificatifs 10 ans (obligation légale comptable)

-

Mangopay transmet automatiquement : -- Données aux autorités fiscales EU via DAC7 (directive 2021/514) -- Justificatif de chaque virement (preuve bancaire pour comptabilité créateur)

-

Exemple DAS2 :

-
Si créateur a touché 2500€ en 2026 :
-→ RoadWave envoie DAS2 aux impôts en janvier 2027
-→ Créateur reçoit copie par email
-→ Créateur doit déclarer ces 2500€ dans sa déclaration annuelle
-
-

Justification : -- Conformité légale : RoadWave doit déclarer revenus versés (DAS2, DAC7) -- Responsabilité fiscale : Le créateur reste responsable de sa déclaration (impossible de gérer pour lui) -- Automatisation : Minimise charge administrative côtés créateur et plateforme

-
-

9.8 Désactivation et suspension monétisation

-

Créateur peut : -- Désactiver temporairement (vacances, pause création) -- Réactiver sans refaire KYC si données à jour (<2 ans) -- Solde conservé pendant désactivation

-

Plateforme suspend automatiquement si :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MotifActionRéversible
Strike 3+ actifSuspension immédiateOui, après résolution strikes
Compte bancaire invalideSuspension après 3 échecs virementOui, après mise à jour RIB
Documents KYC expirésSuspension avec préavis 30jOui, après renouvellement docs
Fraude détectéeSuspension immédiate + enquêteCas par cas
-

Suppression définitive si : -- Demande du créateur (solde versé sous 30 jours) -- Inactivité 24 mois + solde <50€ (purge RGPD) -- Ban définitif compte (Strike 4)

-

Notification : -- Email + in-app pour toute suspension -- Raison explicite fournie -- Procédure de réactivation indiquée

-

Justification : -- Flexibilité : créateur peut faire pause sans perdre statut -- Sécurité : plateforme doit pouvoir suspendre en cas problème légal/technique -- RGPD : suppression auto données inactives après délai raisonnable

-
-

Récapitulatif Section 9

-
- -

10. Premium

-

10.1 Offre et tarification

-

Décision : Deux formules sans essai gratuit

- - - - - - - - - - - - - - - - - - - - - - - -
FormulePrixÉconomiePrix effectif
Mensuel4.99€/mois-4.99€/mois
Annuel49.99€/an2 mois offerts4.16€/mois
-

❌ Pas d'essai gratuit

-

Raisons : -- Anti-abus vacances : évite inscriptions opportunistes (essai 14j avant road trip vacances, puis annulation) -- Protection revenus créateurs : les écoutes Premium rémunèrent créateurs dès jour 1 -- Simplicité : pas de gestion période trial + conversion -- Engagement : utilisateur qui paie dès début = plus engagé

-

❌ Pas de partage familial (MVP)

-

Raisons : -- Complexité technique (gestion invitations, validation liens, limite devices) -- Risque abus ("familles" de 6 inconnus) -- Coût dev/support élevé pour ROI incertain -- La plupart des users RoadWave sont individuels (conducteurs) -- Post-MVP : Si forte demande, offre "Famille" à 9.99€/mois pour 5 comptes

-

Justification tarif : -- Aligné marché bas : Spotify = 10.99€, YouTube Premium = 11.99€, Apple Music = 10.99€ -- Prix accessible : cible conducteurs quotidiens (budget raisonnable) -- Incitation annuel : 2 mois offerts = engagement long terme + réduction churn

-
-

10.2 Multi-devices et détection simultanée

-

Décision : 1 seul stream actif par compte à tout moment

-

Détection connexion simultanée :

-
User A écoute sur iPhone
-→ User A lance sur iPad
-→ Détection : session active iPhone existe
-→ Action : Arrêt lecture iPhone (WebSocket close)
-→ Message iPhone : "Lecture interrompue : votre compte est utilisé sur un autre appareil"
-→ Lecture démarre iPad
-
-

Implémentation technique :

-
Redis : active_streams:{user_id} → {device_id, started_at}
-TTL : 5 minutes (refresh à chaque heartbeat)
-
-Heartbeat toutes les 30s depuis app :
-→ Si autre device détecté : kill session actuelle
-→ Si pas de heartbeat pendant 5 min : considérer session morte
-
-

Exceptions : -- Contenus téléchargés (offline) ne comptent pas comme stream actif -- Transition rapide device (<10s) tolérée (changement voiture → maison)

-

Justification : -- Anti-partage compte : empêche 2 personnes d'utiliser même compte Premium -- Protection revenus créateurs : 1 abonnement = 1 personne = 1 écoute -- UX claire : message explicite, pas de coupure brutale

-
-

10.3 Contenus exclusifs Premium

-

Décision : Créateur décide (déjà couvert section 9.6)

-

Rappel règles : -- Toggle "Réservé Premium" par contenu -- Aucune limite de ratio gratuit/premium -- Badge 👑 visible -- Users gratuits : lecture bloquée avec CTA "Passez Premium"

-

Impact algorithme : -- Contenus premium inclus dans recommandations -- Si user gratuit → skip automatique (ne consomme pas slot) -- Si user premium → diffusé normalement selon score

-
-

10.4 Avantages Premium

-

Inclus dans l'abonnement :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AvantageGratuitPremium
Publicités1/5 contenus0 (aucune)
Contenus exclusifs❌ Bloqués✅ Accès complet
Qualité audio48 kbps Opus64 kbps Opus
Mode offline50 contenus maxIllimité
Historique écoute100 derniersIllimité
-

Qualité audio : -- Gratuit : 48 kbps Opus (~20 MB/h) = très correct pour voix -- Premium : 64 kbps Opus (~30 MB/h) = excellente qualité

-

Justification différences : -- 0 pub = argument principal (confort écoute) -- Qualité audio = avantage tangible audiophiles -- Offline illimité = use case road trips longs -- Pas d'over-engineering : pas de badges cosmétiques, fonctionnalités sociales, etc. (focus essentiel)

-
-

10.5 Gestion abonnement

-

Souscription :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CanalPrestatairePrixCommission
Web (desktop/mobile)Mangopay4.99€1.8% + 0.18€ = 0.27€
iOS AppApple In-App Purchase5.99€30% (Apple)
Android AppGoogle Play Billing5.99€30% (Google)
-

Majoration mobile (5.99€) : -- Apple/Google prennent 30% de commission -- RoadWave majore prix de 20% pour compenser -- Incitation web : Email aux users "Abonnez-vous sur roadwave.com pour 4.99€/mois" (38% moins cher en frais !)

-

Renouvellement automatique : -- Email rappel 7 jours avant renouvellement -- Email confirmation après renouvellement réussi -- Retry automatique si échec paiement (3 tentatives sur 7 jours) -- Annulation automatique après 3 échecs

-

Annulation : -- Self-service dans Settings app : "Abonnement > Annuler" -- Accès Premium maintenu jusqu'à fin période payée -- Pas de remboursement prorata (standard industrie) -- Email confirmation annulation avec date fin d'accès

-

Réabonnement : -- Possibilité immédiate -- ❌ Pas de nouvelle période d'essai (pas d'essai du tout)

-

Architecture données :

-
CREATE TABLE subscriptions (
-    id UUID PRIMARY KEY,
-    user_id UUID NOT NULL REFERENCES users(id) UNIQUE,
-    mangopay_recurring_payin_id VARCHAR(255), -- Null si IAP
-    mangopay_user_id VARCHAR(255),            -- Null si IAP
-    apple_transaction_id VARCHAR(255),        -- Null si Mangopay
-    google_purchase_token VARCHAR(255),       -- Null si Mangopay
-    status VARCHAR(50) NOT NULL, -- 'active', 'cancelled', 'expired', 'past_due'
-    plan VARCHAR(50) NOT NULL, -- 'monthly', 'yearly'
-    current_period_start TIMESTAMP NOT NULL,
-    current_period_end TIMESTAMP NOT NULL,
-    cancelled_at TIMESTAMP,
-    created_at TIMESTAMP NOT NULL DEFAULT NOW()
-);
-
-

Vérification Premium en temps réel :

-
Cache Redis : premium:{user_id} → boolean (TTL 1h)
-Refresh via webhooks :
-- Mangopay : PAYIN_NORMAL_SUCCEEDED, PAYIN_NORMAL_FAILED
-- Apple : App Store Server Notifications
-- Google : Real-time Developer Notifications
-
-
-

Récapitulatif Section 10

-
- -

11. Mode offline

-

11.1 Téléchargement

-

Zone géographique : Choix manuel utilisateur

-

Options prédéfinies : -- "Autour de moi" (rayon 50 km position actuelle) -- "Ma ville" (limite administrative détectée) -- "Mon département" (sélection liste) -- "Ma région" (sélection liste) -- Recherche manuelle : "Paris", "Lyon", "Marseille", etc.

-

Nombre de contenus téléchargeables :

- - - - - - - - - - - - - - - - - - - - -
StatutLimiteAffichage
Gratuit50 contenus max"12/50 contenus téléchargés"
PremiumIllimité"245 contenus (3.2 GB)"
-

Calcul temps disponible : -- 50 contenus × 5 min moyenne = 250 min = 4h d'écoute (suffisant pour gratuits) -- Premium illimité = limité uniquement par espace disque device

-

Connexion WiFi/Mobile :

-

Par défaut : WiFi uniquement

-

Sur données mobiles : -1. User clique "Télécharger" -2. Détection : pas de WiFi -3. Popup : "Vous n'êtes pas connecté en WiFi. Télécharger via données mobiles consommera environ X MB. Continuer ?" -4. Boutons : "Attendre WiFi" / "Continuer"

-

Calcul estimation :

-
Nombre contenus × durée moyenne × bitrate qualité
-Exemple : 20 contenus × 5 min × 48 kbps = ~72 MB
-
-

Qualité audio téléchargement :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QualitéBitrateTailleDisponibilité
Basse24 kbps~10 MB/hGratuit + Premium
Standard48 kbps~20 MB/hGratuit + Premium (défaut)
Haute64 kbps~30 MB/hPremium uniquement
-

Justification : -- Standard = bon compromis qualité/taille (Opus 48 kbps = très correct pour voix) -- Haute réservée Premium = incitation upgrade -- User peut réduire à "basse" si espace limité

-
-

11.2 Validité et renouvellement

-

Durée de validité : 30 jours après téléchargement

-

Standard industrie : -- Spotify : 30 jours -- YouTube Music : 30 jours -- Deezer : 30 jours

-

Renouvellement automatique :

-
App détecte WiFi + contenus >25 jours
-→ Requête API : GET /offline/contents/refresh
-→ Backend vérifie pour chaque contenu :
-    - Abonnement Premium toujours actif ?
-    - Contenu pas modéré/supprimé ?
-    - Métadonnées à jour ?
-→ Renouvelle validité à 30 jours supplémentaires
-→ Mise à jour métadonnées (titre, créateur, statut)
-→ Pas de re-téléchargement audio (sauf si fichier corrompu)
-
-

Notification avant expiration : -- J-3 : "X contenus expirent dans 3 jours. Connectez-vous en WiFi pour les renouveler" -- J-0 : Suppression automatique -- J+0 : Toast "15 contenus expirés ont été supprimés"

-

Justification : -- Force reconnexion : vérifier abonnement actif, contenus légaux -- Évite stockage obsolète : contenus supprimés/modérés ne restent pas -- UX transparente : renouvellement silencieux si WiFi régulier

-
-

11.3 Synchronisation actions offline

-

Actions stockées localement (SQLite) : -- Likes/unlikes -- Abonnements/désabonnements -- Signalements -- Progression audio-guides

-

Sync automatique à la reconnexion :

-
1. App détecte reconnexion Internet
-2. Récupération queue locale : SELECT * FROM pending_actions ORDER BY created_at
-3. Envoi batch API : POST /sync/actions
-4. Backend traite chaque action
-5. Confirmation réception : DELETE FROM pending_actions WHERE id IN (...)
-6. Toast : "3 likes et 1 abonnement synchronisés"
-
-

Gestion erreurs sync : -- Si échec après 3 tentatives → notification : "Impossible de synchroniser. Réessayez plus tard" -- Actions conservées jusqu'à sync réussie (pas de perte) -- Rétention max 7 jours : après = purge (évite queue infinie)

-

Conflits contenus supprimés :

-
Backend retourne : {deleted_content_ids: [123, 456]}
-→ App supprime fichiers locaux
-→ Si contenu 123 en cours d'écoute :
-    - Attendre fin lecture actuelle
-    - Passage auto suivant après 2s
-→ Toast : "1 contenu téléchargé a été retiré (violation règles)"
-
-

Justification : -- Pas de conflit possible : actions unilatérales user (likes/abonnements) -- UX fluide : pas de blocage offline -- Batch = économie : requêtes HTTP groupées -- Conformité modération : contenu illégal disparaît même offline

-
-

Récapitulatif Section 11

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AspectDécisionValeur
Zone téléchargementChoixManuel (autour/ville/département/région/recherche)
Limite gratuitContenus50 max
Limite PremiumContenusIllimité (espace disque)
ConnexionPar défautWiFi (mobile avec confirmation)
Qualité StandardBitrate48 kbps Opus
Qualité HauteBitrate64 kbps (Premium uniquement)
ValiditéDurée30 jours
RenouvellementModeAutomatique si WiFi
Notification expirationDélaiJ-3
Sync actionsModeBatch automatique reconnexion
Rétention queueDurée7 jours max
-
-
- -

12. Gestion des erreurs

-

12.1 Aucun contenu disponible

-

Stratégie : Élargissement automatique progressif

-

Flow :

-
1. Recherche rayon 50 km → aucun résultat
-2. Élargissement auto 100 km
-3. Si toujours rien → département
-4. Si toujours rien → région
-5. Dernier recours → contenu national (toujours disponible)
-
-

Messages adaptatifs :

- - - - - - - - - - - - - - - - - - - - - -
CasMessage
Trouvé à 100 km"Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)"
Trouvé département"Aucun contenu local disponible. Voici du contenu dans votre département"
Contenu national"Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser"
-

Justification : -- UX fluide : pas de message d'erreur bloquant "Aucun contenu" -- User ne reste jamais sans contenu -- Contenu national = filet de sécurité : actualités Le Monde, podcasts génériques

-
-

12.2 Contenu signalé/supprimé pendant l'écoute

-

Décision : Pas d'interruption brutale

-

Flow :

-
1. Contenu supprimé côté backend (modération)
-2. Si contenu en écoute → laisser terminer lecture en cours
-3. Après fin lecture → désactiver bouton "Précédent" pour ce contenu
-4. Passage automatique suivant après 2s
-5. Toast notification discrète : "Contenu précédent retiré (violation règles)"
-
-

Si tentative "Précédent" manuellement : -- Message : "Ce contenu n'est plus disponible" -- Retour au contenu actuel

-

Justification : -- Sécurité routière : pas d'interruption brutale pendant conduite -- User informé mais pas alarmé : message discret -- Empêche réécoute : contenu modéré inaccessible

-
-

12.3 Perte de réseau

-

Buffer adaptatif (cf. TECHNICAL.md) :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RéseauBuffer minBuffer cibleBuffer max
WiFi5s30s120s
4G/5G10s45s120s
3G30s90s300s
-

Comportement détaillé :

-

Phase 1 : Connexion instable (latence élevée, paquets perdus) -- Aucun message immédiat -- Lecture continue sur buffer -- Si > 10s latence : toast discret "Connexion instable"

-

Phase 2 : Perte totale réseau -- Lecture continue jusqu'à épuisement buffer -- Toast : "Hors ligne, lecture sur buffer (30s restantes)" -- Compte à rebours visible

-

Phase 3 : Buffer épuisé sans reconnexion -- Pause automatique -- Overlay : "Connexion perdue. Reconnexion en cours..." -- Retry automatique toutes les 5s (max 6 tentatives = 30s)

-

Phase 4 : Basculement mode offline (après 30s échec) -- Popup : "Voulez-vous continuer avec vos contenus téléchargés ?" -- Boutons : "Réessayer" / "Mode offline" -- Si "Mode offline" → lecture contenus téléchargés

-

Reconnexion réussie : -- Reprise automatique lecture au point d'arrêt exact -- Toast : "Connexion rétablie"

-

Justification : -- Expérience fluide zones blanches (tunnels, campagne) -- Buffer généreux : absorbe fluctuations réseau mobile -- Mode offline secours : si coupure prolongée

-
-

12.4 Géolocalisation désactivée

-

Mode dégradé automatique

-

Contenu disponible :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Type contenuDisponible
Contenu national (podcasts, actualités)
Contenu téléchargé (offline)
Contenus "Neutre" géographiquement
Contenu géolocalisé (Ancré/Contextuel)
Audio-guides
Notifications push géo-déclenchées
-

Popup au lancement : -- Apparition : Premier lancement après refus géolocalisation -- Message : "RoadWave fonctionne mieux avec la géolocalisation activée. Sans elle, seul le contenu national sera disponible." -- Boutons : - - "Activer" → Redirection paramètres OS - - "Continuer sans" → Mode dégradé -- Checkbox : "Ne plus me demander"

-

Banner permanent si refus : -- Bandeau haut écran : "Mode limité : géolocalisation désactivée. [Activer]" -- Pas intrusif mais rappel constant -- Disparaît si géolocalisation réactivée

-

Justification : -- App reste fonctionnelle sans GPS (pas de blocage) -- Incitation forte à activer (meilleure UX) -- Respecte choix user (RGPD : consentement libre)

-
-

Récapitulatif Section 12

-
- -

13. Conformité RGPD

-

13.1 Gestion du consentement

-

Décision : Tarteaucitron.js + PostgreSQL backend

-

Implémentation web : -- ✅ Tarteaucitron.js (opensource, self-hosted) -- ✅ Banner RGPD français, customisable -- ✅ Granularité : fonctionnel / analytique / marketing

-

Implémentation backend : -- Table user_consents avec versioning -- Champs : user_id, consent_type, version, accepted, timestamp -- Historique complet conservé (preuve légale)

-

Consentements requis : -- Géolocalisation précise : obligatoire (banner + permission OS) -- Analytics : optionnel (Matomo) -- Notifications push : optionnel (permission OS)

-

Justification : -- Opensource, 0€, conformité RGPD garantie -- Historique backend = preuve légale en cas de contrôle -- Granularité conforme recommandations CNIL

-
-

13.2 Anonymisation des données GPS

-

Décision : Geohash après 24h

-

Processus : -1. Données précises conservées 24h (recommandation personnalisée) -2. Après 24h : conversion en geohash précision 5 (~5km²) -3. Coordonnées originales supprimées définitivement

-

Implémentation PostGIS :

-
-- Job quotidien
-UPDATE location_history
-SET location = ST_SetSRID(ST_GeomFromGeoHash(ST_GeoHash(location::geography, 5)), 4326)::geography,
-    anonymized = true
-WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false;
-
-

Exceptions : -- ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif -- ❌ Analytics globales : uniquement geohash anonyme

-

Justification : -- Vraie anonymisation RGPD (CNIL compliant) -- Permet analytics agrégées (heatmaps trafic) -- PostGIS natif, 0€

-
-

13.3 Export des données (portabilité)

-

Décision : JSON + HTML + ZIP, génération asynchrone

-

Contenu de l'export :

-
export-roadwave-[user_id]-[date].zip
-├── export.json              # Machine-readable
-├── index.html              # Human-readable (stylé)
-├── audio/
-│   ├── content-123.opus
-│   ├── content-456.opus
-│   └── ...
-└── README.txt              # Instructions
-
-

Données exportées : -- Profil utilisateur (email, pseudo, date inscription, bio) -- Historique d'écoute (titres, dates, durées) -- Contenus créés (audio + métadonnées) -- Abonnements et likes -- Centres d'intérêt (jauges) -- Historique consentements

-

Processus : -1. Demande via paramètres compte -2. Génération asynchrone (worker background) -3. Email avec lien download (expire 7 jours) -4. Délai : 48h maximum (conformité RGPD)

-

Limite : -- Maximum 1 export/mois (anti-abus)

-

Justification : -- Conformité article 20 RGPD (portabilité) -- Double format (human + machine) -- Worker asynchrone évite timeout

-
-

13.4 Suppression du compte

-

Décision : Grace period 30j + anonymisation contenus

-

Processus : -1. Utilisateur clique "Supprimer mon compte" -2. Compte désactivé immédiatement (login impossible) -3. Contenus cachés pendant 30 jours (non diffusés) -4. Email confirmation + lien annulation (valide 30j) -5. Après 30j sans annulation : suppression effective

-

Suppression effective : -- ✅ Compte utilisateur supprimé (données personnelles) -- ✅ Historique d'écoute supprimé -- ✅ GPS historique supprimé -- ✅ Sessions et tokens révoqués -- ⚠️ Contenus créés anonymisés (créateur = "Utilisateur supprimé") -- ⚠️ Likes et abonnements supprimés (mais compteurs préservés)

-

Contenus conservés anonymement : -- Audio files (CDN) -- Métadonnées (titre, description, tags, géolocalisation) -- Statistiques d'écoute

-

Justification : -- Grace period évite suppressions impulsives -- Anonymisation contenus = intérêt légitime communauté -- Conforme RGPD si créateur = donnée supprimée

-
-

13.5 Mode dégradé (sans GPS précis)

-

Décision : GeoIP par défaut, GPS optionnel

-

Niveaux de précision :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NiveauTechnologieContenus accessiblesConsentement
PaysAucune géolocContenus nationaux uniquement❌ Non requis
VilleGeoIP (MaxMind)Contenus régionaux/ville❌ Non requis
PrécisGPSTous contenus (hyperlocaux inclus)✅ Requis
-

Implémentation : -- Démarrage app : GeoIP automatique (IP → ville) -- Banner in-app : "Activez la géolocalisation pour découvrir du contenu près de chez vous" -- Upgrade volontaire vers GPS

-

API GeoIP : -- MaxMind GeoLite2 (gratuit, self-hosted) -- Update DB mensuelle automatique -- Précision ~80% au niveau ville

-

Justification : -- RGPD : pas de consentement requis pour GeoIP (pas de donnée personnelle) -- UX dégradée acceptable (contenus disponibles) -- Progressive disclosure (upgrade optionnel)

-
-

13.6 Durée de conservation des données

-

Décision : 5 ans inactivité → purge automatique

-

Règles :

- - - - - - - - - - - - - - - - - - - - - - - - - -
Type de compteSeuil inactivitéAction
Auditeur uniquement5 ans sans connexionSuppression automatique
Créateur avec contenus actifsJamais (tant qu'écoutes)Conservation indéfinie
Créateur inactif5 ans sans connexion + 2 ans sans écouteSuppression automatique
-

Notifications avant suppression : -- Email + push : 90 jours avant -- Email + push : 30 jours avant -- Email + push : 7 jours avant -- Toute connexion = reset compteur inactivité

-

Contenu conservé : -- Contenus créés par comptes supprimés (anonymisés) : conservation indéfinie

-

Justification : -- Conformité principe minimisation RGPD -- 5 ans = équilibre raisonnable (standard industrie) -- Exception créateurs actifs = intérêt légitime plateforme

-
-

13.7 Cookies et trackers web

-

Décision : Matomo self-hosted, zéro cookie tiers

-

Cookies utilisés :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CookieTypeDuréeFinalitéConsentement
sessionTechnique30jAuthentification❌ Non requis
refresh_tokenTechnique30jSession persistante❌ Non requis
_pk_idAnalytique13 moisMatomo (IP anonyme)✅ Requis
-

Analytics : Matomo self-hosted : -- Hébergé sur nos serveurs (Docker) -- IP anonymisées automatiquement (2 derniers octets) -- Pas de cookie si consentement refusé -- Alternative : Plausible (SaaS EU, 9€/mois)

-

Trackers interdits : -- ❌ Google Analytics -- ❌ Facebook Pixel -- ❌ Hotjar, Mixpanel, etc.

-

Justification : -- Souveraineté données (pas de transfert US) -- Conformité RGPD max (CNIL compatible) -- Matomo = opensource, 0€ infra

-
-

13.8 Registre des traitements

-

Décision : Document Markdown versionné Git (MVP)

-

Emplacement : -- docs/rgpd/registre-traitements.md -- Versionné Git (historique modifications)

-

Contenu obligatoire par traitement : -- Nom et finalité du traitement -- Catégories de données collectées -- Base légale (consentement / contrat / intérêt légitime) -- Durée de conservation -- Destinataires (sous-traitants, CDN, etc.) -- Transferts hors UE (aucun prévu)

-

Responsable : -- DPO / Fondateur -- Review trimestrielle obligatoire -- Update immédiate si nouveau traitement

-

Migration future : -- Si > 100K utilisateurs : interface admin PostgreSQL

-

Justification : -- Obligation RGPD Article 30 -- Markdown = simple, versionné, auditable -- 0€

-
-

13.9 Notification violations de données (breach)

-

Décision : Monitoring + alertes + runbook

-

Détection automatique :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ÉvénementOutilAlerte
Erreurs backend critiquesSentryDiscord/Slack immédiat
Pic requêtes anormalGrafanaEmail équipe
Accès non autorisé DBPostgreSQL logsSMS fondateur
Authentification suspecteZitadel alertsEmail équipe
-

Procédure breach : -- Runbook : docs/rgpd/procedure-breach.md -- Checklist 72h CNIL : - 1. H+0 : Détection et confinement - 2. H+24 : Évaluation gravité (données concernées, utilisateurs impactés) - 3. H+48 : Notification CNIL si risque pour utilisateurs - 4. H+72 : Notification utilisateurs si risque élevé

-

Contact CNIL : -- Email pré-rédigé (template) -- Formulaire en ligne (account CNIL créé)

-

Justification : -- Obligation RGPD Article 33 (notification 72h) -- Monitoring proactif évite découverte tardive -- Sentry gratuit < 5K events/mois

-
-

13.10 DPO (Délégué à la Protection des Données)

-

Décision : Fondateur = DPO temporaire (MVP)

-

Raison légale : -- Non obligatoire si : - - < 250 employés - - Pas de traitement à grande échelle de données sensibles - - RoadWave : données localisation = sensible MAIS échelle MVP

-

Formation : -- CNIL : formation gratuite en ligne (4h) -- Certification CNIL "Atelier RGPD" (gratuit)

-

Contact : -- Email : dpo@roadwave.fr -- Publié dans CGU et mentions légales -- Délai réponse : 1 mois (RGPD)

-

Migration future : -- Si > 100K utilisateurs : DPO externe mutualisé (~200€/mois) -- Ou recrutement DPO interne si > 10 employés

-

Justification : -- Conforme RGPD (non obligatoire en phase MVP) -- 0€, contrôle total -- Bonne pratique : avoir un contact identifié

-
-

Récapitulatif Section 13

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MesureImplémentationCoût
ConsentementTarteaucitron.js + PostgreSQL0€
Anonymisation GPSGeohash PostGIS (24h)0€
Export donnéesJSON+HTML+ZIP asynchrone0€
Suppression compteGrace period 30j + anonymisation0€
Mode dégradéGeoIP MaxMind + GPS optionnel0€
ConservationPurge auto 5 ans inactivité0€
AnalyticsMatomo self-hosted~5€/mois
Registre traitementsMarkdown Git0€
Breach detectionSentry + Grafana + runbook0€
DPOFondateur formé CNIL0€
-

Coût total RGPD : ~5€/mois

-
-

Points d'attention pour Gherkin

-
    -
  • Tester consentement géolocalisation (accept/refuse → contenus différents)
  • -
  • Tester anonymisation GPS après 24h (job cron)
  • -
  • Tester export données (génération complète + vérification contenu)
  • -
  • Tester grace period suppression (annulation possible)
  • -
  • Tester mode GeoIP (ville détectée correctement)
  • -
  • Tester purge automatique (5 ans inactivité)
  • -
  • Tester notifications avant purge (90j/30j/7j)
  • -
-
- -

14. Modération - Flows opérationnels

-

14.1 Signalement

-

Décision : Formulaire simple avec 7 catégories prédéfinies

-

14.1.1 Catégories de signalement

-

Liste déroulante avec 7 options :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CatégorieDescription
🚫 Haine & violenceIncitation à la haine, discrimination, menaces
🔞 Contenu sexuelPornographie, contenu explicite
⚖️ IllégalitéTerrorisme, apologie de crimes
🎵 Droits d'auteurMusique/contenu protégé non autorisé
📧 SpamPublicité non sollicitée, répétition
Fausse informationDésinformation sur santé, sécurité routière
🔧 AutreChamp texte obligatoire si sélectionné
-

Justification : -- Équilibre entre simplicité (pas trop de choix) et précision (aide les modérateurs) -- Coût : 0€ (liste déroulante standard)

-
-

14.1.2 Commentaire du signaleur

-

Décision : Optionnel avec incitation

-
    -
  • Champ texte libre (0-500 caractères)
  • -
  • Placeholder : "Décrivez le problème (optionnel mais recommandé)"
  • -
  • Non bloquant : le signalement peut être envoyé sans commentaire
  • -
-

Justification : -- Encourage la qualité des signalements sans créer de friction -- Aide les modérateurs à comprendre le contexte -- Pas de risque d'abandon du processus

-
-

14.1.3 Confirmation après signalement

-

Décision : Toast in-app avec lien historique

-

Affichage : -- Toast notification : "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." -- Durée affichage : 5 secondes -- Bouton optionnel "Voir mes signalements" (accès historique)

-

Historique personnel : -- Liste des signalements envoyés par l'utilisateur -- Statut : En cours / Traité / Rejeté -- Notification in-app si action prise (contenu retiré, signalement rejeté)

-

Justification : -- Transparence maximale -- Coût : 0€ (aucun email automatique) -- Bonne UX

-
-

14.2 Traitement des signalements

-

14.2.1 IA pré-filtre (transcription + analyse)

-

Décision : OpenAI Whisper open source + NLP

-

Stack technique :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComposantTechnologieHébergement
TranscriptionWhisper large-v3Self-hosted (CPU MVP, GPU scale)
Analyse sentimentdistilbert-base-uncasedSelf-hosted
Détection hainefacebook/roberta-hate-speechSelf-hosted
Mots-clésListe noire FR/EN + regexPostgreSQL
-

Processus : -1. Signalement reçu → ajout file d'attente asynchrone -2. Transcription audio (1-10 minutes selon durée) -3. Analyse automatique : - - Score de confiance : 0-100% - - Catégorie détectée - - Timestamps des passages problématiques -4. Priorisation automatique selon score

-

Délais : -- Audio <5 min : 1-3 minutes -- Audio 5-30 min : 3-10 minutes -- Audio >30 min : 10-20 minutes

-

Coût : -- MVP : 0€ (CPU standard, processing asynchrone) -- Scale : 50-200€/mois (GPU VPS si >1000 signalements/jour)

-

Justification : -- 100% open source, pas de dépendance GAFAM -- Coût maîtrisé (scaling progressif) -- Gain productivité modérateurs ×3-5

-
-

14.2.2 Délais de traitement (SLA)

-

Décision : SLA progressif selon priorité

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PrioritéDélai cibleTraitement
CRITIQUE<2h (24/7)Violence, suicide, mise en danger → Astreinte modérateur senior
HAUTE<24h (jours ouvrés)Haine, harcèlement, désinformation → Modérateur junior/senior
MOYENNE<24h (jours ouvrés)Spam, contenu inapproprié → Modérateur junior
BASSE<72h (jours ouvrés)Qualité audio, tags incorrects → Modérateur junior
-

Traitement automatique : -- Score IA >95% + catégorie évidente (ex: spam répété) → Action automatique immédiate -- Notification créateur + possibilité d'appel

-

Justification : -- Réaliste et conforme DSA (Digital Services Act) -- Scalable : priorisation automatique -- Ressources humaines optimisées

-
-

14.2.3 Priorisation automatique

-

Décision : File d'attente intelligente basée sur score IA

-

Calcul de priorité :

-
Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1)
-
-

Détails : -- Score_IA : 0-100% (confiance analyse automatique) -- Signalements_cumulés : nombre de signalements du même contenu (boost priorité) -- Fiabilité_signaleur : score utilisateur (historique signalements pertinents)

-

Classification résultante : -- Priorité ≥90 → CRITIQUE (traitement immédiat) -- Priorité 70-89 → HAUTE (file prioritaire) -- Priorité 40-69 → MOYENNE (file normale) -- Priorité <40 → BASSE (file différée)

-

Justification : -- Optimise le temps des modérateurs -- Traite les cas graves en priorité -- Coût : 0€ (algorithme simple)

-
-

14.3 Sanctions

-

14.3.1 Notification au créateur

-

Décision : Multi-canal (email + push + in-app)

-

Canaux utilisés :

- - - - - - - - - - - - - - - - - - - - - - - - - -
CanalTimingContenu
Push notificationImmédiatAlerte courte : "Votre contenu a été modéré"
In-appAu prochain lancementPopup détaillée avec bouton "Voir détails"
EmailDans l'heureNotification complète avec lien vers formulaire d'appel
-

Contenu email :

-
Objet : Modération de votre contenu "[Titre du contenu]"
-
-Bonjour [Pseudo],
-
-Votre contenu "[Titre]" publié le [Date] a été modéré.
-
-Catégorie violée : [Catégorie]
-Raison : [Explication détaillée]
-Sanction : [Strike X / Suspension X jours / Suppression contenu]
-
-Extrait audio concerné : [Timestamp]
-Transcription : "[Passage problématique surligné]"
-
-Vous pouvez contester cette décision sous 7 jours :
-[Lien formulaire d'appel]
-
-L'équipe RoadWave
-
-

Coût : -- Email : ~0.001€/notification (Brevo, Resend) -- Push : 0€ (Firebase Cloud Messaging / APNs) -- In-app : 0€

-

Justification : -- Conformité DSA (transparence obligatoire) -- Multi-canal garantit réception -- Coût négligeable

-
-

14.3.2 Détail de la sanction

-

Décision : Notification complète avec preuves

-

Éléments inclus obligatoirement :

-
    -
  1. Catégorie violée : référence précise CGU (ex: "Article 3.2 - Haine & violence")
  2. -
  3. Raison détaillée : explication en langage clair (non juridique)
  4. -
  5. Extrait audio : timestamp exact du passage problématique (ex: "3:42-4:15")
  6. -
  7. Transcription : texte problématique surligné en rouge
  8. -
  9. Gravité : Strike actuel + conséquences (ex: "Strike 2/4 - Suspension 7 jours")
  10. -
  11. Recours : lien direct vers formulaire d'appel + délai (7 jours)
  12. -
-

Exemple visuel in-app :

-
┌─────────────────────────────────────┐
-│ ⚠️ Contenu modéré                   │
-├─────────────────────────────────────┤
-│ Titre : "Mon podcast #42"           │
-│ Publié le : 15/01/2026              │
-│                                     │
-│ Catégorie violée :                  │
-│ 🚫 Haine & violence (Article 3.2)   │
-│                                     │
-│ Passage problématique : 3:42-4:15   │
-│ "[Transcription surlignée]"         │
-│                                     │
-│ Sanction : Strike 2/4               │
-│ Suspension : 7 jours                │
-│                                     │
-│ [Contester cette décision]          │
-└─────────────────────────────────────┘
-
-

Justification : -- Transparence maximale (obligation DSA) -- Créateur comprend l'erreur → amélioration future -- Réduit les appels non fondés

-
-

14.3.3 Processus d'appel

-

Décision : Formulaire in-app structuré

-

Accès : -- Bouton "Contester cette décision" dans notification -- Section "Mes sanctions" dans profil créateur

-

Formulaire d'appel :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChampTypeObligatoire
Sanction contestéePré-rempli (non modifiable)
Raison de l'appelTexte libre (50-1000 caractères)
ArgumentsZone texte enrichie
PreuvesUpload fichiers (max 5, 10 MB total)
-

Après soumission : -- Génération numéro de ticket unique (ex: #MOD-2026-00142) -- Email confirmation : "Votre appel sera traité sous 72h" -- Statut visible dans l'app : "En cours d'examen"

-

Délai de soumission : -- Maximum 7 jours après notification de sanction -- Après 7 jours : appel automatiquement refusé

-

Justification : -- Professionnel et traçable -- Intégration complète avec système modération -- Coût : 0€ (formulaire custom backend)

-
-

14.3.4 Délai de réponse pour appel

-

Décision : SLA 72h garanti

-

Délais :

- - - - - - - - - - - - - - - - - - - - - - - - - -
Type d'appelDélaiResponsable
Standard72h max (3 jours ouvrés)Modérateur senior
Complexe5 jours ouvrés + notification intermédiaire J+3Modérateur senior + Admin modération
Critique24h (cas suspension longue/ban)Admin modération
-

Notification intermédiaire (si délai >72h) : -- Email J+3 : "Votre appel #MOD-XXX est en cours d'examen approfondi. Réponse sous 2 jours."

-

Réponse finale :

-

Email détaillé avec : -1. Décision : Maintien / Annulation / Réduction de sanction -2. Justification : explication de la décision d'appel -3. Actions : Strike retiré / Suspension annulée / Contenu rétabli (si applicable) -4. Définitif : mention "Cette décision est définitive" (pas de second appel)

-

Suivi in-app : -- Mise à jour statut : "Appel accepté ✓" ou "Appel rejeté ✗" -- Badge notification

-

Justification : -- Équilibre entre rapidité et qualité de traitement -- Conforme pratiques industrie (YouTube, TikTok : 5-7 jours) -- Ressources humaines réalistes

-
-

14.4 Outils modérateurs

-

Stack technique complète :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OutilTechnologieFonction
DashboardReact + TanStack TableInterface modération
File signalementsPostgreSQL + RedisPriorisation temps réel
Player audioWavesurfer.jsLecture avec waveform + annotations
TranscriptionWhisper large-v3Conversion audio → texte
Historique créateurVue 360°Contenus, strikes, appels, métriques
Actions rapidesShortcuts clavierApprouver (A), Rejeter (R), Escalade (E)
Logs auditPostgreSQL + exportTraçabilité complète (DSA)
CollaborationSystème de commentairesModérateurs peuvent s'entraider sur cas complexes
-

Fonctionnalités clés :

-
    -
  1. Lecture accélérée : 0.75x à 2x (gain productivité)
  2. -
  3. Marqueurs temporels : annotation directe sur waveform
  4. -
  5. Historique créateur : vue rapide contenus précédents + strikes
  6. -
  7. Statistiques : signalements traités/jour, temps moyen, précision
  8. -
  9. Fil d'activité : actions récentes équipe (temps réel)
  10. -
-

Coût infrastructure : -- MVP : 0-50€/mois (serveur CPU) -- Scale : 50-200€/mois (GPU + Redis Cluster)

-
-

14.5 Modération préventive (rappel)

-

Nouveaux créateurs : -- Validation manuelle des 3 premiers contenus -- Délai : 24-48h (jours ouvrés) -- Transcription automatique pour aide modérateur

-

Score de confiance : -- Évolution dynamique selon historique -- Créateur fiable (0 strike depuis 6 mois) → validation automatique -- Créateur suspect (strikes récents) → validation manuelle systématique

-

Publicités : -- Validation manuelle obligatoire 24-48h (responsabilité juridique) -- Transcription + analyse métadonnées (ciblage, durée, volume)

-

Justification : -- Prévention > réaction (économie modération) -- Qualité plateforme préservée dès le début

-
-

Récapitulatif Section 14

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PointDécisionCoût
Catégories signalement7 catégories prédéfinies + champ libre0€
Commentaire signaleurOptionnel avec incitation0€
ConfirmationToast in-app + historique personnel0€
IA pré-filtreWhisper (CPU MVP, GPU scale) + NLP open source0-200€/mois
Délais traitementSLA progressif : 2h/24h/72h selon prioritéDépend équipe
PriorisationFile intelligente basée score IA0€
Notification sanctionEmail + push + in-app (multi-canal)~0.001€/notif
Détail sanctionComplet : raison + extrait + transcription0€
Processus appelFormulaire in-app structuré0€
Délai appel72h garanti (standard)Dépend équipe
Outils modérateursDashboard React + Whisper + Wavesurfer.js0-200€/mois
-

Coût total MVP : 0-200€/mois (infrastructure IA optionnelle)

-

Conformité : -- ✅ DSA (Digital Services Act) : transparence, traçabilité, délais -- ✅ RGPD : données modération anonymisées après 3 ans -- ✅ Logs audit : toutes actions tracées (obligation légale plateforme)

-

Scalabilité : -- 0-1000 signalements/mois : équipe 1-2 modérateurs junior + 1 senior -- 1000-10K signalements/mois : équipe 5-10 modérateurs + IA GPU -- 10K+ signalements/mois : équipe dédiée + IA optimisée + modération communautaire

-
-

Prochaine section à clarifier : Section 11 (Mode offline) ou Section 12 (Gestion des erreurs)

-
- -

15. Autres comportements

-

15.1 Partage de contenu

-

Décision : Système de partage complet avec web player

-

15.1.1 Bouton "Partager"

-

Disponibilité : Partout dans l'application

-

Emplacements : -- Player en lecture (bouton dans contrôles) -- Page profil créateur (sur chaque contenu) -- Liste de recherche (menu contextuel) -- Historique personnel

-

Icône : ⬆️ (universelle iOS/Android)

-

Menu options : -- Copier le lien -- WhatsApp -- Email -- SMS -- Plus... (sheet natif OS)

-

Justification : -- Viralité = croissance organique gratuite -- Aucune friction, partage universel

-
-

15.1.2 Comportement du lien partagé

-

Format URL : https://roadwave.fr/share/c/[content_id]

-

Comportement multi-plateforme :

-
User clique lien partagé
-    ↓
-Page web responsive
-    ↓
-┌─────────────────────────────────┐
-│ Si app installée                │
-│ → Deep link (ouverture directe) │
-└─────────────────────────────────┘
-    ↓
-┌─────────────────────────────────┐
-│ Si app non installée             │
-│ → Web player + CTA téléchargement│
-└─────────────────────────────────┘
-
-

Contenu de la page web :

-
┌───────────────────────────────────────┐
-│ RoadWave                              │
-├───────────────────────────────────────┤
-│ [Image cover 16:9]                    │
-│                                       │
-│ 📻 Titre du contenu                   │
-│ Par @créateur · 12 min · 🎧 2.3K      │
-│                                       │
-│ 📍 Paris 5e · Ancré                   │
-│ 🏷️ #Voyage #Histoire                  │
-│                                       │
-│ Description : Lorem ipsum...          │
-│                                       │
-│ [▶️ Écouter maintenant]               │
-│ (Player HTML5 si contenu public)      │
-│                                       │
-│ ──────────────────────────────────    │
-│                                       │
-│ 📱 Télécharger l'app RoadWave         │
-│ [App Store] [Google Play]             │
-│                                       │
-│ [Voir le profil de @créateur]         │
-└───────────────────────────────────────┘
-
-

Métadonnées Open Graph (SEO) :

-
<meta property="og:title" content="[Titre contenu] - RoadWave">
-<meta property="og:description" content="[Description ou extrait]">
-<meta property="og:image" content="[URL cover image]">
-<meta property="og:audio" content="[URL audio si public]">
-<meta property="og:type" content="music.song">
-<meta property="og:site_name" content="RoadWave">
-<meta name="twitter:card" content="player">
-<meta name="twitter:player" content="https://roadwave.fr/player/[content_id]">
-
-

Deep linking : -- iOS : Universal Links (configuration apple-app-site-association) -- Android : App Links (configuration assetlinks.json) -- URL scheme : roadwave://content/[content_id]

-

Justification : -- Meilleure viralité (partage social optimisé) -- SEO (contenus indexés Google) -- UX optimale (web + app) -- Coût : 0€ (backend simple + CDN existant)

-
-

15.1.3 Contenus Premium partagés

-

Décision : Preview 30 secondes + paywall

-

Comportement :

-
    -
  1. User clique lien contenu Premium partagé
  2. -
  3. Page web affiche badge "👑 Contenu Premium"
  4. -
  5. Player démarre automatiquement
  6. -
  7. Après 30 secondes exactement :
  8. -
  9. Fade out audio (2 secondes)
  10. -
  11. Overlay apparaît :
  12. -
-
┌─────────────────────────────────┐
-│ 👑 Contenu réservé Premium      │
-│                                 │
-│ Profitez de ce contenu complet  │
-│ et de milliers d'autres         │
-│ sans publicité                  │
-│                                 │
-│ [Passer Premium - 4.99€/mois]   │
-│ [Télécharger l'app]             │
-└─────────────────────────────────┘
-
-
    -
  1. Utilisateur peut :
  2. -
  3. S'abonner Premium (redirection web Mangopay)
  4. -
  5. Télécharger l'app (redirection stores)
  6. -
  7. Rejouer les 30 premières secondes (illimité)
  8. -
-

Tracking : -- Métriques créateur : "Partages Premium" + "Conversions Premium" -- Créateur touche sa part si conversion (70%)

-

Justification : -- Équilibre viralité / monétisation -- 30s = assez pour donner envie, pas assez pour satisfaire -- Protège revenus créateurs

-
-

15.2 Profil créateur

-

Décision : Profil public complet et transparent

-

15.2.1 Structure de la page profil

-

URL : https://roadwave.fr/@[pseudo]

-

Layout :

-
┌────────────────────────────────────────┐
-│ [Photo profil 120×120]                 │
-│ @pseudo ✓                              │
-│ [Badge vérifié si applicable]          │
-│                                        │
-│ Bio : Lorem ipsum dolor sit amet...    │
-│ (300 caractères max)                   │
-│                                        │
-│ 🎧 1.2K abonnés                        │
-│ 📻 42 contenus                         │
-│ ⏱️ 18h de contenu créé                 │
-│ 🔊 54K écoutes totales                 │
-│                                        │
-│ [S'abonner] [Partager profil] [•••]   │
-│                                        │
-│ ────────────────────────────────────   │
-│                                        │
-│ Contenus ▼ [Plus récents ▼]           │
-│                                        │
-│ ┌──────────────────────────────────┐  │
-│ │ [Cover] Titre contenu 1           │  │
-│ │ 12 min · 🎧 2.3K · 📍 Paris       │  │
-│ │ [▶️]                              │  │
-│ └──────────────────────────────────┘  │
-│                                        │
-│ ┌──────────────────────────────────┐  │
-│ │ [Cover] Titre contenu 2           │  │
-│ │ 8 min · 🎧 5.1K · 📍 Lyon         │  │
-│ │ [▶️]                              │  │
-│ └──────────────────────────────────┘  │
-│                                        │
-│ [Charger plus]                         │
-└────────────────────────────────────────┘
-
-

Informations affichées :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ÉlémentVisibilitéDétails
Photo + pseudo✅ PublicIdentité visuelle
Badge vérifié ✓✅ Public (si applicable)Compte authentique
Bio✅ Public0-300 caractères, markdown basique (gras, italique, liens)
Nombre abonnés✅ PublicArrondi si >1000 (ex: 1.2K, 54K)
Nombre contenus✅ PublicExact
Durée totale créée✅ PublicArrondi en heures (ex: 18h, 142h)
Écoutes totales✅ PublicArrondi (ex: 54K, 1.2M)
Liste abonnés❌ PrivéProtection vie privée (RGPD)
Revenus❌ PrivéConfidentialité financière
Localisation précise❌ PrivéSécurité
Email❌ PrivéAnti-spam
-

Tri des contenus :

- - - - - - - - - - - - - - - - - - - - - - - - - -
OptionComportement
Plus récentsDate publication DESC (défaut)
Plus populairesÉcoutes complètes × (1 + (date_publication - now) / 90 jours)
Plus anciensDate publication ASC
Par tagFiltre multi-sélection tags
-

Recherche locale : -- Barre recherche dans profil : "Rechercher dans les contenus de @pseudo" -- Recherche full-text sur titres + descriptions

-

Actions menu [•••] : -- Partager profil -- Signaler profil (spam, usurpation) -- Bloquer créateur (masque tous ses contenus)

-
-

15.2.2 Statistiques publiques

-

Décision : Stats arrondies et motivantes

-

Affichage public :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueFormat affichageExemple
AbonnésExact si <1000, arrondi sinon342 / 1.2K / 54K / 1.2M
Écoutes totalesArrondi dès 1000842 / 5.4K / 142K / 2.1M
Contenus publiésExact42 contenus
Durée totaleArrondi en heures18h / 142h de contenu
-

Métriques PRIVÉES (créateur uniquement) :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueDisponible dans dashboard créateur
Taux complétion moyen78% (écoutes >80% / écoutes totales)
Évolution abonnésGraphique 30j / 90j / 1 an
Écoutes par contenuTableau détaillé
RevenusDashboard monétisation dédié
Taux conversion PremiumPartages → conversions
DémographieÂge / zone géo (agrégée, anonymisée)
-

Justification : -- Arrondi = évite comparaisons anxiogènes -- Preuve sociale pour nouveaux auditeurs (trust) -- Gamification douce (motivation créateurs) -- Privacy by design

-
-

15.2.3 Badge vérifié

-

Décision : Badge unique ✓ (vérifié officiel)

-

Critères d'attribution (au moins UN des critères) :

-
    -
  1. KYC monétisation validé : identité vérifiée via Mangopay KYC
  2. -
  3. Célébrité / Média officiel : validation manuelle équipe RoadWave
  4. -
  5. Communauté significative : ≥10K abonnés + compte actif >6 mois
  6. -
-

Affichage : -- Badge bleu accolé au pseudo (partout : profil, player, recherche) -- Tooltip au survol/appui long : "Compte vérifié"

-

Processus d'obtention :

- - - - - - - - - - - - - - - - - - - - - -
TypeProcessus
Automatique (KYC)Badge attribué dès validation documents Mangopay
Manuel (célébrité)Formulaire demande → équipe vérifie identité → validation 48-72h
Automatique (10K)Badge attribué automatiquement à 10K abonnés si compte >6 mois
-

Retrait du badge : -- Suspension monétisation → badge retiré temporairement -- Strikes multiples → badge retiré définitivement -- Usurpation identité détectée → ban + retrait

-

Justification : -- Combat usurpations d'identité -- Trust auditeurs (surtout pour médias/personnalités) -- Simplicité (1 seul badge, pas de gamification excessive) -- Coût : 0€ (champ boolean verified en DB)

-
-

15.3 Recherche

-

Décision : Recherche full-text + géo + filtres avancés

-

15.3.1 Recherche par mot-clé

-

Implémentation : PostgreSQL full-text search (français)

-

Configuration technique :

-
-- Index full-text optimisé français
-CREATE INDEX idx_content_search ON contents
-USING GIN(
-    to_tsvector('french',
-        coalesce(title, '') || ' ' ||
-        coalesce(description, '') || ' ' ||
-        coalesce(creator_pseudo, '')
-    )
-);
-
--- Recherche avec ranking
-SELECT
-    c.*,
-    ts_rank(
-        to_tsvector('french', c.title || ' ' || c.description),
-        plainto_tsquery('french', $search_query)
-    ) AS rank
-FROM contents c
-WHERE to_tsvector('french', c.title || ' ' || c.description)
-      @@ plainto_tsquery('french', $search_query)
-ORDER BY rank DESC, listen_count DESC
-LIMIT 20;
-
-

Champs indexés : -- Titre du contenu (poids × 3) -- Description (poids × 1) -- Pseudo créateur (poids × 2) -- Tags (poids × 1.5)

-

Fonctionnalités :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureDescription
Stemming français"voyages" trouve "voyage", "voyager", etc.
Correction autoSuggestion si 0 résultat
Recherches populaires"Essayez plutôt : balade paris, audio-guide louvre"
Historique personnel10 dernières recherches sauvegardées
AutocompleteSuggestions pendant frappe (top 5)
-

Coût : 0€ (PostgreSQL natif)

-

Migration future : -- Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois) -- Si >1M contenus : Elasticsearch cluster

-

Justification : -- PostgreSQL full-text = performant jusqu'à 500K contenus -- Stemming français natif -- 0€, aucune dépendance externe

-
-

15.3.2 Recherche géographique

-

Décision : Recherche lieu + rayon paramétrable

-

Interface utilisateur :

-
┌─────────────────────────────────────┐
-│ 🔍 Recherche contenu...             │
-├─────────────────────────────────────┤
-│ �� Lieu                             │
-│ [Paris, France              ▼]     │
-│ · Autour de moi (GPS actuel)       │
-│ · Entrer une adresse/ville         │
-│                                     │
-│ 📏 Rayon de recherche               │
-│ [●─────────────────] 50 km         │
-│ (curseur 5 km → 500 km)            │
-│                                     │
-│ 🗺️ [Afficher sur carte]            │
-└─────────────────────────────────────┘
-
-

Géocodage :

- - - - - - - - - - - - - - - - - - - - - - - - - -
ServiceUsageCoût
Nominatim (OSM)MVP (API publique)0€ (rate limit 1 req/s)
Nominatim self-hostedScale (Docker)20-50€/mois VPS
Mapbox GeocodingFallback premium0.50€ / 1000 requêtes
-

Processus de recherche géo :

-
    -
  1. User tape "Louvre" ou "Paris"
  2. -
  3. Autocomplete via Nominatim → liste suggestions
  4. -
  5. User sélectionne → récupération coordonnées (lat, lon)
  6. -
  7. Requête PostGIS :
  8. -
-
SELECT c.*,
-       ST_Distance(c.location::geography, ST_Point($lon, $lat)::geography) AS distance
-FROM contents c
-WHERE ST_DWithin(
-    c.location::geography,
-    ST_Point($lon, $lat)::geography,
-    $radius_meters
-)
-ORDER BY distance ASC;
-
-

Affichage résultats : -- Tri par défaut : distance croissante -- Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km" -- Option carte : markers cliquables (clustering si >50 résultats)

-

Coût : -- MVP : 0€ (Nominatim public) -- Scale : 20-50€/mois (Nominatim self-hosted Docker)

-

Justification : -- Essentiel pour tourisme / planification trajet -- OpenStreetMap = pas de dépendance Google -- PostGIS = performant (index GIST natif)

-
-

15.3.3 Filtres avancés

-

Décision : 7 catégories de filtres combinables

-

Interface filtres :

-
┌─────────────────────────────────────┐
-│ Filtres                        [×]  │
-├─────────────────────────────────────┤
-│ Type de contenu                     │
-│ ☐ Contenu court (<5 min)            │
-│ ☐ Podcast (>5 min)                  │
-│ ☐ Radio live                        │
-│ ☐ Audio-guide                       │
-│                                     │
-│ Durée                               │
-│ ○ Toutes durées                     │
-│ ○ <5 min                            │
-│ ○ 5-15 min                          │
-│ ○ 15-30 min                         │
-│ ○ >30 min                           │
-│                                     │
-│ Classification âge                  │
-│ ☐ Tout public                       │
-│ ☐ 13+                               │
-│ ☐ 16+                               │
-│ ☐ 18+                               │
-│                                     │
-│ Géo-pertinence                      │
-│ ☐ Ancré (lieu précis)               │
-│ ☐ Contextuel (zone large)           │
-│ ☐ Neutre (national)                 │
-│                                     │
-│ Tags (multi-sélection)              │
-│ ☐ Automobile   ☐ Voyage             │
-│ ☐ Famille      ☐ Histoire           │
-│ ☐ Économie     ☐ Sciences           │
-│ ... (liste complète tags)           │
-│                                     │
-│ Date de publication                 │
-│ ○ Toutes dates                      │
-│ ○ Dernières 24h                     │
-│ ○ Cette semaine                     │
-│ ○ Ce mois                           │
-│ ○ Cette année                       │
-│                                     │
-│ Abonnement                          │
-│ ○ Tous les contenus                 │
-│ ○ Gratuits uniquement               │
-│ ○ Premium uniquement 👑             │
-│                                     │
-│ ──────────────────────────────      │
-│ [Réinitialiser] [Appliquer]         │
-└─────────────────────────────────────┘
-
-

Options de tri :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TriAlgorithme
PertinenceScore recherche × (1 + log(listen_count + 1))
PopularitéÉcoutes complètes derniers 30j DESC
RécentDate publication DESC
ProximitéDistance GPS ASC (si recherche géo active)
DuréeDurée audio ASC ou DESC
-

Sauvegarde de recherches :

-
    -
  • Bouton "💾 Sauvegarder cette recherche"
  • -
  • Nom personnalisable : "Podcasts voyage Paris"
  • -
  • Maximum 5 recherches sauvegardées
  • -
  • Accès rapide : onglet "Recherches sauvegardées" dans page recherche
  • -
  • Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'"
  • -
-

Performances :

-
-- Index composites pour filtres
-CREATE INDEX idx_content_filters ON contents (
-    content_type,
-    duration,
-    age_rating,
-    geo_type,
-    published_at
-);
-
--- Index GIN pour tags
-CREATE INDEX idx_content_tags ON contents USING GIN(tags);
-
-

Coût : 0€ (PostgreSQL + index standards)

-

Justification : -- Filtres essentiels pour découvrabilité -- Combinables = puissance maximale -- Sauvegarde = gain temps utilisateurs réguliers

-
-

15.3.4 Page de résultats

-

Décision : Liste avec previews enrichies

-

Layout résultats :

-
┌─────────────────────────────────────────┐
-│ 🔍 "voyage paris"                       │
-│ 42 résultats · Tri : Pertinence ▼       │
-│ [Filtres] [Carte]                       │
-├─────────────────────────────────────────┤
-│ ┌─────────────────────────────────────┐ │
-│ │ [Cover     ] Balade à Paris          │ │
-│ │ [16:9      ] @paris_stories ✓        │ │
-│ │ [Image     ] 12 min · 🎧 2.3K        │ │
-│ │              📍 Paris 5e · Ancré     │ │
-│ │              🏷️ #Voyage #Histoire    │ │
-│ │              [▶️ Écouter] [⋮]        │ │
-│ └─────────────────────────────────────┘ │
-│                                         │
-│ ┌─────────────────────────────────────┐ │
-│ │ [Cover     ] Secrets Montmartre      │ │
-│ │ [16:9      ] @explore_paris          │ │
-│ │ [Image     ] 8 min · 🎧 5.1K         │ │
-│ │              📍 Paris 18e · Guide    │ │
-│ │              🏷️ #Voyage #Art         │ │
-│ │              [▶️ Écouter] [⋮]        │ │
-│ └─────────────────────────────────────┘ │
-│                                         │
-│ [Charger plus] (20 suivants)            │
-└─────────────────────────────────────────┘
-
-

Informations par résultat :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ÉlémentAffichage
Cover image16:9, 120×68 px, lazy loading
TitreTronqué 2 lignes max
Créateur@pseudo + badge ✓ si vérifié, cliquable → profil
DuréeFormat : "3 min" / "12 min" / "1h 24 min"
ÉcoutesArrondi : "2.3K" / "54K" / "1.2M"
LocalisationVille + type géo (Ancré/Contextuel/Neutre)
TagsMaximum 3 premiers tags
Badge Premium👑 si contenu premium
DistanceSi recherche géo : "À 2.3 km"
-

Actions contextuelles [⋮] : -- Partager -- Ajouter à une playlist (future feature) -- Télécharger (offline) -- Signaler

-

Pagination : -- 20 résultats par page -- Infinite scroll (charger automatiquement si scroll >80%) -- Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé)

-

Vue carte (alternative) : -- Bouton toggle "Liste / Carte" -- Map Leaflet (OpenStreetMap) -- Markers cliquables → popup avec preview -- Clustering si >50 résultats proches

-

Coût : 0€ (Leaflet open source + OSM tiles gratuit)

-

Justification : -- Équilibre information / compacité -- Lazy loading = performances -- Infinite scroll = UX moderne

-
-

Récapitulatif Section 15

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PointDécisionCoûtComplexité
15.1.1 Bouton partagerDisponible partout (⬆️), menu natif OS0€Faible
15.1.2 Lien partagéWeb player + deep link + Open Graph SEO0€Moyenne
15.1.3 Premium partagéPreview 30s + paywall overlay0€Faible
15.2.1 Page profilProfil public complet (stats + bio + contenus + tri)0€Faible
15.2.2 Stats publiquesArrondies (abonnés, écoutes, durée totale)0€Faible
15.2.3 Badge vérifié✓ si KYC/célébrité/>10K abonnés0€Faible
15.3.1 Recherche textePostgreSQL full-text french + stemming0€Moyenne
15.3.2 Recherche géoLieu + rayon (Nominatim OSM)0-50€/moisMoyenne
15.3.3 Filtres7 catégories combinables + sauvegarde recherches0€Moyenne
15.3.4 Page résultatsListe enrichie + vue carte Leaflet + infinite scroll0€Moyenne
-

Coût total MVP : 0-50€/mois (Nominatim self-hosted optionnel)

-
-

Points d'attention pour Gherkin

-
    -
  • Tester partage contenu public vs Premium (preview 30s)
  • -
  • Tester deep linking iOS/Android (ouverture app si installée)
  • -
  • Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook)
  • -
  • Tester profil public (stats arrondies, badge vérifié)
  • -
  • Tester recherche full-text français (stemming, accents)
  • -
  • Tester recherche géo + rayon (PostGIS distance)
  • -
  • Tester combinaison filtres multiples (AND logic)
  • -
  • Tester sauvegarde recherches (max 5)
  • -
  • Tester pagination infinite scroll + fallback bouton
  • -
  • Tester vue carte Leaflet (clustering, markers cliquables)
  • -
-
- -

16. Audio-guides multi-séquences

-

16.1 Types d'audio-guides et modes de déplacement

-

Décision : 4 modes distincts avec détection automatique

-

16.1.1 Classification par mode

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ModeVitesse détectionDéclenchementUse case
🚶 Piéton<5 km/hManuel (bouton "Suivant")Musées, visites urbaines, monuments
🚗 Voiture>10 km/hAuto GPS + Manuel possibleSafari-parc, routes touristiques, circuits auto
🚴 Vélo5-25 km/hAuto GPS + Manuel possiblePistes cyclables, circuits vélo, parcours nature
🚌 TransportVariableAuto GPS + Manuel possibleBus touristiques, trains panoramiques
-

Détection automatique : -- Vitesse moyenne calculée sur 30 secondes -- Suggestion mode au démarrage : "Détection : 🚗 Voiture. Est-ce correct ? [Oui] [Changer]" -- User peut forcer mode manuellement (settings)

-

Justification : -- Flexibilité maximale créateurs et utilisateurs -- Expériences optimisées par type de déplacement -- Gestion cas limites (vélo lent vs piéton rapide)

-
-

16.1.2 Création d'un audio-guide (côté créateur)

-

Formulaire création :

-
┌────────────────────────────────────────┐
-│ Nouvel audio-guide multi-séquences     │
-├────────────────────────────────────────┤
-│ Titre : [Safari du Paugre]            │
-│ Description : [Découvrez les animaux   │
-│               du parc en voiture...]    │
-│                                        │
-│ Mode de déplacement : *obligatoire     │
-│ ○ 🚶 Piéton (navigation manuelle)      │
-│ ● 🚗 Voiture (GPS auto + manuel)       │
-│ ○ 🚴 Vélo (GPS auto + manuel)          │
-│ ○ 🚌 Transport (GPS auto + manuel)     │
-│                                        │
-│ Vitesse recommandée : 30-50 km/h       │
-│ (si voiture/vélo/transport)            │
-│                                        │
-│ ────────────────────────────────────   │
-│                                        │
-│ Séquences (ordre lecture) :            │
-│                                        │
-│ 1. [📍] Introduction - Point d'accueil │
-│    Lat: 43.1234, Lon: 2.5678          │
-│    Rayon déclenchement : 30m           │
-│    Durée : 2:15                        │
-│    [🎵 Audio uploadé] [✏️] [🗑️]       │
-│                                        │
-│ 2. [📍] Enclos des lions               │
-│    Lat: 43.1245, Lon: 2.5690          │
-│    Rayon déclenchement : 30m           │
-│    Durée : 3:42                        │
-│    [📤 Upload audio] [✏️] [🗑️]        │
-│                                        │
-│ 3. [📍] Enclos des girafes             │
-│    [+ Ajouter point GPS]               │
-│                                        │
-│ [+ Ajouter séquence]                   │
-│                                        │
-│ 📊 Statistiques :                      │
-│ · 2 séquences complètes                │
-│ · 5:57 durée totale                    │
-│ · 320m distance totale                 │
-│                                        │
-│ [🗺️ Aperçu sur carte]                 │
-│ [✅ Publier audio-guide]               │
-└────────────────────────────────────────┘
-
-

Métadonnées obligatoires :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ChampRequisDétails
Titre audio-guide5-100 caractères
Description10-500 caractères
Mode déplacementPiéton / Voiture / Vélo / Transport
Nombre séquencesMinimum 2, maximum 50
Point GPS par séquence✅ (sauf piéton)Latitude, longitude (WGS84)
Rayon déclenchement✅ (sauf piéton)10-100m selon mode
Vitesse recommandéeOptionnel, affichée utilisateur
Tags1-3 parmi liste prédéfinie
Classification âgeTout public / 13+ / 16+ / 18+
Zone diffusionPolygon géographique
-

Wizard de création : -- Étape 1 : Infos générales (titre, description, mode) -- Étape 2 : Ajout séquences une par une -- Étape 3 : Preview carte (trace + points) -- Étape 4 : Validation modération (3 premiers audio-guides)

-

Justification : -- Contrôle total créateur sur expérience -- Carte preview aide visualiser parcours -- Wizard guidé = réduction friction création

-
-

16.2 Mode Piéton (manuel)

-

Décision : Navigation manuelle avec pub auto-play

-

16.2.1 Passage entre séquences

-

Séquence normale (sans pub) :

-
    -
  1. Séquence 1 se termine
  2. -
  3. Player se met en pause automatique
  4. -
  5. Message affiché : "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."
  6. -
  7. User appuie sur [▶|] → Séquence 2 démarre immédiatement
  8. -
-

Séquence avec publicité (1 pub / 5 séquences) :

-
    -
  1. Séquence 2 se termine
  2. -
  3. Publicité s'enchaîne automatiquement (pas d'attente bouton)
  4. -
  5. Pub se lit (skippable après 5s)
  6. -
  7. Pub se termine → Player se met en pause automatique
  8. -
  9. Message : "Séquence 3 prête. Appuyez sur Suivant."
  10. -
  11. User appuie sur [▶|] → Séquence 3 démarre
  12. -
-

Schéma flux :

-
Séquence 1 [fin] → PAUSE → User clique → Séquence 2 [fin] → PUB AUTO-PLAY → PAUSE → User clique → Séquence 3
-
-

Fréquence pub : -- Gratuits : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10) -- Premium : 0 pub

-

Justification : -- Pub s'insère naturellement (pas d'attente utilisateur pour déclencher) -- User garde contrôle rythme visite (pause après pub) -- Monétisation effective créateurs -- Premium reste attractif (0 interruption)

-
-

16.2.2 Navigation et contrôles

-

Décision : Liberté totale utilisateur

-

Contrôles disponibles :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BoutonFonctionComportement
[▶|] SuivantPasse séquence suivanteImmédiat, même si séquence actuelle pas terminée
[|◀] PrécédentRetour séquence précédenteSaut direct séquence avant (pas de logique "replay si >10s")
[⏸️] PausePause temporaireReprend à position exacte
[▶️] PlayReprend lectureContinue position actuelle
Liste séquencesNavigation libreTap séquence → saut direct (même séquences non écoutées)
-

Interface liste séquences :

-
┌────────────────────────────────────────┐
-│ 🚶 Audio-guide Piéton                  │
-│ Musée du Louvre                        │
-├────────────────────────────────────────┤
-│ [Cover image]                          │
-│                                        │
-│ ▶️ 0:00 ──●────────── 3:42            │
-│                                        │
-│ Séquence 3/12 : La Joconde            │
-│                                        │
-│ [|◀] [⏸️] [▶|]                        │
-│                                        │
-│ ────────────────────────────────────   │
-│                                        │
-│ 📋 Liste des séquences                 │
-│                                        │
-│ ✅ 1. Introduction (2:15)              │
-│    Écouté le 15/01/2026                │
-│                                        │
-│ ✅ 2. Pyramide du Louvre (1:48)        │
-│    Écouté le 15/01/2026                │
-│                                        │
-│ ▶️ 3. La Joconde (3:42) - EN COURS    │
-│    ──●──────────── 1:22/3:42          │
-│                                        │
-│ ⭕ 4. Vénus de Milo (2:58)             │
-│                                        │
-│ ⭕ 5. Code d'Hammurabi (4:12)          │
-│                                        │
-│ ⭕ 6. Victoire de Samothrace (3:25)    │
-│                                        │
-│ ... +6 séquences                       │
-│                                        │
-│ [Tout afficher ▼]                      │
-└────────────────────────────────────────┘
-
-

Navigation libre : -- User peut sauter séquences déjà connues -- User peut revenir en arrière à tout moment -- User peut aller directement à séquence 8 (même si 4-7 non écoutées)

-

Sauvegarde progression : -- Checkmarks ✅ sur séquences écoutées >80% -- Position exacte sauvegardée dans séquence en cours

-

Justification : -- Utilisateur contrôle 100% son rythme -- Adapté musées : visitor peut voir physiquement une œuvre lointaine et vouloir écouter sa description -- Pas de frustration (liberté totale)

-
-

16.3 Mode Voiture (GPS automatique)

-

Décision : GPS auto avec navigation manuelle conservée

-

16.3.1 Déclenchement et contrôles

-

Distinction audio-guides vs contenus géolocalisés simples :

-

⚠️ Important : Les audio-guides multi-séquences fonctionnent différemment des contenus géolocalisés simples.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeSéquencesDéclenchementNotificationEnchaînementComptabilité quota
Contenu géolocalisé simple1 séquence uniqueNotification 7s avant (temps ETA)Sonore + icôneFin → retour buffer normal1 contenu = 1 quota
Audio-guide multi-séquences2 à 50 séquencesAu point GPS exact (distance 30m)Ding + toast 2sSéquences s'enchaînent auto1 audio-guide = 1 quota (toutes séquences)
-

Fonctionnement GPS automatique :

-
    -
  1. User démarre audio-guide en voiture (voir section 16.1 pour démarrage)
  2. -
  3. Séquence 1 démarre automatiquement au point GPS défini (rayon 30m)
  4. -
  5. Séquence 1 se termine
  6. -
  7. Affichage progress bar : distance temps réel + ETA jusqu'au prochain point
  8. -
  9. User roule vers point GPS suivant
  10. -
  11. Arrivée au point GPS suivant (rayon 30m) → déclenchement automatique séquence suivante
  12. -
  13. Notification sonore discrète : "Ding" (0.3s) + toast 2s : "Enclos des girafes"
  14. -
  15. Séquence suivante démarre immédiatement (pas de décompte)
  16. -
-

Pas de système "7 secondes avant" pour les audio-guides : -- Contrairement aux contenus géolocalisés simples (voir 05-interactions-navigation.md) -- Les séquences se déclenchent au point GPS exact (rayon 30m) -- Raison : expérience guidée continue, user sait qu'il suit un parcours

-

Navigation manuelle CONSERVÉE :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BoutonÉtatComportement
[▶|] Suivant✅ Toujours actifPasse séquence suivante immédiatement (même hors point GPS)
[|◀] Précédent✅ Toujours actifRetour séquence précédente (même hors point GPS)
[⏸️] PausePause temporaire
Liste séquencesSaut direct possible
-

Use cases navigation manuelle :

- - - - - - - - - - - - - - - - - - - - - - - - - -
SituationSolution manuelle
Embouteillage (séquence finie, point GPS loin)User clique Suivant → avance manuellement
Point GPS inaccessible (route fermée)User clique Suivant → skip point
Envie réécouter séquence précédenteUser clique Précédent → retour
Passager manipule l'appPassager navigue librement
-

Avertissement sécurité :

-
    -
  • Si vitesse >10 km/h ET user clique bouton (Suivant/Précédent) :
  • -
  • Toast 3 secondes : "⚠️ Manipulation en conduite détectée. Pour votre sécurité, demandez à un passager."
  • -
  • Action quand même exécutée (pas de blocage)
  • -
  • Justification : sensibilisation sans bloquer (passager peut légitimement manipuler)
  • -
-

Schéma flux :

-
Point GPS 1 (30m) → Séquence 1 AUTO → User roule → Distance affichée → Point GPS 2 (30m) → Séquence 2 AUTO
-                                                    ↓
-                                          User clique Suivant (manuel) → Séquence 2 immédiate
-
-

Justification : -- Flexibilité maximale : GPS optimise expérience MAIS user garde contrôle -- Gestion cas limites : routes fermées, détours, embouteillages -- Sécurité : warning sensibilise sans bloquer (passager légitime)

-
-

16.3.2 Affichage distance et guidage

-

Décision : Distance + direction (PAS de carte miniature)

-

Interface en conduite :

-
┌────────────────────────────────────────┐
-│ 🚗 Audio-guide Voiture                 │
-│ Safari du Paugre                       │
-├────────────────────────────────────────┤
-│                                        │
-│ ▶️ 0:00 ──●────────── 2:15            │
-│                                        │
-│ Séquence 2/8 : Les lions               │
-│                                        │
-│ ────────────────────────────────────   │
-│                                        │
-│ 📍 Prochain point                      │
-│                                        │
-│    Enclos des girafes                  │
-│                                        │
-│ ┌────────────────────────────────┐    │
-│ │                                │    │
-│ │          ↗️                     │    │
-│ │      (direction)               │    │
-│ │                                │    │
-│ │       320 mètres               │    │
-│ │      ≈ 40 secondes              │    │
-│ │                                │    │
-│ └────────────────────────────────┘    │
-│                                        │
-│ Vitesse actuelle : 28 km/h             │
-│ Vitesse recommandée : 20-30 km/h       │
-│                                        │
-│ [|◀] [⏸️] [▶|] [📋 Liste]            │
-└────────────────────────────────────────┘
-
-

Affichage entre deux séquences :

-

Quand une séquence se termine et qu'il reste un point GPS suivant, l'interface bascule en mode "attente prochain point" :

-
┌────────────────────────────────────────┐
-│ 🚗 Audio-guide Voiture                 │
-│ Safari du Paugre                       │
-├────────────────────────────────────────┤
-│                                        │
-│ ✅ Séquence 2/8 terminée               │
-│    Les lions                           │
-│                                        │
-│ ────────────────────────────────────   │
-│                                        │
-│ 📍 Prochain point                      │
-│                                        │
-│    Enclos des girafes                  │
-│                                        │
-│ ┌────────────────────────────────┐    │
-│ │         [Progress bar]         │    │
-│ │     ████████░░░░░░░░░ 65%      │    │
-│ │                                │    │
-│ │          ↗️                     │    │
-│ │      (direction)               │    │
-│ │                                │    │
-│ │       320 mètres               │    │
-│ │      ≈ 40 secondes              │    │
-│ │                                │    │
-│ └────────────────────────────────┘    │
-│                                        │
-│ Vitesse actuelle : 28 km/h             │
-│                                        │
-│ [|◀] [▶️ Rejouer séq.] [▶|]           │
-└────────────────────────────────────────┘
-
-

Progress bar dynamique : -- Se remplit au fur et à mesure qu'on se rapproche du point -- Calcul : progress = 100 - (distance_actuelle / distance_initiale * 100) -- Exemple : distance initiale 500m, distance actuelle 175m → progress = 65% -- Couleur : vert (#4CAF50) pour la partie remplie, gris (#E0E0E0) pour le reste

-

Bouton "Rejouer séq." : -- Permet de réécouter la séquence qui vient de se terminer -- User clique → séquence actuelle redémarre depuis 0:00 -- Utile si distraction pendant l'écoute

-
-

Informations affichées :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
InfoMise à jourFormat
DistanceChaque seconde"320 m" / "1.2 km"
ETAChaque seconde"≈ 40 secondes" / "≈ 2 minutes"
DirectionChaque 5sFlèche indique direction (8 directions : ↑ ↗ → ↘ ↓ ↙ ← ↖)
Vitesse actuelleChaque seconde"28 km/h"
Vitesse recommandéeStatique"20-30 km/h" (définie par créateur)
Progress barChaque secondePourcentage parcouru vers prochain point
-

Calcul direction :

-
// Calcul angle entre position actuelle et prochain point
-const currentGPS = getCurrentLocation();
-const nextPoint = audioGuide.sequences[currentIndex + 1].location;
-
-const angle = calculateBearing(currentGPS, nextPoint); // 0-360°
-
-// Conversion en flèche (8 directions)
-const arrows = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'];
-const index = Math.round(angle / 45) % 8;
-const direction = arrows[index];
-
-

Calcul ETA :

-
const distance = calculateDistance(currentGPS, nextPoint); // mètres
-const currentSpeed = getCurrentSpeed(); // km/h
-
-if (currentSpeed > 5) {
-  const eta = (distance / 1000) / currentSpeed * 3600; // secondes
-  return formatETA(eta); // "≈ 40 secondes" ou "≈ 2 minutes"
-} else {
-  return "En attente de déplacement";
-}
-
-

Justification : -- Distance + ETA = info essentielle sans surcharge visuelle -- Direction (flèche) = aide se repérer sans carte complexe -- Simplicité = moins distraction conducteur -- Économie batterie (pas de rendu carte)

-
-

16.3.3 Rayon de déclenchement et tolérance

-

Décision : Rayon configurable créateur avec défauts intelligents

-

Rayons par défaut :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ModeRayon déclenchementRayon "point manqué"Justification
🚗 Voiture30 mètres100 mètresVitesse élevée = anticipation
🚴 Vélo50 mètres75 mètresVitesse variable, arrêts fréquents
🚌 Transport100 mètres150 mètresArrêts bus/train, moins précis
-

Configuration créateur :

-
    -
  • Curseur rayon : 10m → 200m
  • -
  • Défaut pré-sélectionné selon mode choisi
  • -
  • Preview visuel : cercle sur carte (lors création)
  • -
  • Suggestion auto : "Recommandé : 30m pour voiture à 30 km/h"
  • -
-

Gestion point manqué :

-
User passe à 110m du point GPS
-(hors rayon déclenchement 30m MAIS dans rayon tolérance 100m)
-    ↓
-Toast : "⚠️ Point manqué : Enclos des girafes"
-    ↓
-Popup 5 secondes :
-┌────────────────────────────────────┐
-│ Point manqué                       │
-│                                    │
-│ "Enclos des girafes"               │
-│ Vous êtes passé à 110m du point    │
-│                                    │
-│ [🔊 Écouter quand même]            │
-│ [⏭️ Passer au suivant]             │
-│ [🔙 Faire demi-tour]               │
-└────────────────────────────────────┘
-
-

Actions popup :

- - - - - - - - - - - - - - - - - - - - - -
BoutonComportement
Écouter quand mêmeLance séquence immédiatement (même hors zone)
Passer au suivantSkip séquence, continue vers prochain point
Faire demi-tourLance navigation GPS externe (Google Maps / Waze) vers point manqué
-

Si user au-delà rayon tolérance (>100m) : -- Aucun popup (point trop loin, probablement hors itinéraire) -- User peut naviguer manuellement (bouton Suivant)

-

Justification : -- Flexibilité créateur (ajuste selon terrain, vitesse prévue) -- Gestion intelligente imprévus (détours, routes fermées) -- User pas bloqué (toujours moyen avancer)

-
-

16.4 Modes Vélo et Transport

-

Décision : Même logique voiture avec tolérances ajustées

-

Différences par rapport à mode voiture :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamètreVoitureVéloTransport
Rayon déclenchement30m50m100m
Rayon tolérance "point manqué"100m75m150m
Vitesse recommandée affichée20-50 km/h10-25 km/hVariable (selon ligne)
Warning sécurité>10 km/h>5 km/hDésactivé
-

Mode Vélo spécificités :

-
    -
  • Rayon plus large : vitesse variable, nombreux arrêts (feux, piétons)
  • -
  • Warning sécurité dès 5 km/h (vélo en mouvement)
  • -
  • Tolérance GPS moins stricte (tracé moins prévisible qu'auto)
  • -
-

Mode Transport spécificités :

-
    -
  • Rayon très large : arrêts fréquents (bus, train), ligne fixe
  • -
  • Pas de warning sécurité (user = passager, pas conducteur)
  • -
  • Vitesse recommandée = "Selon ligne" (pas de valeur fixe)
  • -
  • Tolérance horaire : si bus en retard, point peut se déclencher avec 2-3 min de délai
  • -
-

Comportement identique voiture :

-
    -
  • Navigation manuelle conservée (boutons actifs)
  • -
  • Affichage distance + ETA + direction
  • -
  • Gestion point manqué
  • -
  • Pub entre séquences
  • -
-

Justification : -- Vélo : moins de contrôle qu'auto (obstacles, arrêts), nécessite tolérance -- Transport : moins de contrôle utilisateur (suit ligne fixe), rayon large compense -- Même UX globale = cohérence

-
-

16.5 Publicités dans audio-guides

-

Décision : Pub auto-play entre séquences TOUS modes

-

16.5.1 Règles universelles

-

Insertion publicité :

-
    -
  • Fréquence : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10)
  • -
  • Gratuits uniquement, Premium 0 pub
  • -
  • Pub s'enchaîne automatiquement après séquence
  • -
  • Skippable après 5 secondes (règle standard RoadWave)
  • -
  • Volume normalisé -14 LUFS (comme pubs normales)
  • -
-

Comportement MODE PIÉTON :

-
Séquence 2 [fin]
-    → Pub AUTO-PLAY
-    → Pub se termine
-    → PAUSE AUTO
-    → Message "Séquence 3 prête. Appuyez sur Suivant."
-    → User clique [▶|]
-    → Séquence 3 démarre
-
-

Comportement MODE VOITURE/VÉLO/TRANSPORT :

-
Séquence 2 [fin]
-    → Pub AUTO-PLAY
-    → Pub se termine
-    → ATTENTE point GPS suivant OU user clique Suivant
-    → Séquence 3 démarre
-
-

Schéma complet :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ModeAprès séquence normaleAprès pub
PiétonPause + attente userPause + attente user
VoitureAttente GPS OU user clique SuivantAttente GPS OU user clique Suivant
VéloAttente GPS OU user clique SuivantAttente GPS OU user clique Suivant
TransportAttente GPS OU user clique SuivantAttente GPS OU user clique Suivant
-

Justification : -- Monétisation équitable créateurs (tous modes participent) -- Pub s'insère naturellement (auto-play, pas d'attente utilisateur) -- User garde contrôle : piéton clique Suivant, voiture peut skip manuel -- Premium reste attractif (expérience 0 interruption) -- Modèle économique viable

-
-

16.5.2 Métriques pub audio-guides

-

Dashboard créateur :

- - - - - - - - - - - - - - - - - - - - - - - - - -
MétriqueAffichage
Impressions pubNombre de pubs insérées dans audio-guides
Écoutes complètes pubNombre de pubs écoutées >80%
Taux skip pub% pubs skippées avant 5s vs après
Revenus pub audio-guides3€ / 1000 écoutes complètes (6% CA pub)
-

Distinction contenus normaux vs audio-guides : -- Dashboard sépare : "Revenus contenus classiques" / "Revenus audio-guides" -- Permet créateur voir performance par type

-

Justification : -- Transparence créateur (comprend revenus) -- Incite création audio-guides (nouvelle source revenus)

-
-

16.6 Reprise et sauvegarde progression

-

Décision : Sauvegarde complète automatique avec popup intelligente

-

16.6.1 Sauvegarde automatique

-

Données sauvegardées :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
InfoDétailUtilité
Audio-guide IDIdentifiant uniqueRetrouver audio-guide
Séquence actuelleIndex (ex: 3/12)Reprise position
Position dans séquenceTimestamp exact (ex: 1:42/3:20)Reprise exacte
Séquences écoutéesListe avec checkmarks ✅Historique progression
Date dernière écouteTimestampProposer reprise si <30j
GPS dernière positionCoordonnées optionnellesInfo contextuelle (non utilisée pour reprise)
-

Stockage :

- - - - - - - - - - - - - - - - - - - - -
EnvironnementTechnologieUtilité
LocalSQLite mobileFonctionnement offline
CloudPostgreSQL (sync auto)Multi-device (reprendre sur autre appareil)
-

Synchronisation : -- Sauvegarde locale : chaque fin de séquence + chaque 30s -- Sync cloud : à la reconnexion réseau (batch)

-

Justification : -- Expérience fluide (pas de perte progression) -- Multi-device (démarrer sur iPhone, continuer sur iPad) -- Offline-first (fonctionne sans réseau)

-
-

16.6.2 Interface de reprise

-

Conditions popup : -- Dernière écoute <30 jours -- Progression >0% et <100% (pas terminé)

-

Popup reprise :

-
┌────────────────────────────────────────┐
-│ Reprendre l'audio-guide ?              │
-├────────────────────────────────────────┤
-│ 🚗 Safari du Paugre                    │
-│ @safari_createur                       │
-│                                        │
-│ Progression : 3/8 séquences écoutées   │
-│ Dernière écoute : il y a 2 jours       │
-│                                        │
-│ Vous étiez à :                         │
-│ "Les lions" (1:42/3:20)                │
-│                                        │
-│ [▶️ Reprendre] [🔄 Recommencer]        │
-│ [📋 Voir toutes les séquences]         │
-└────────────────────────────────────────┘
-
-

Actions :

- - - - - - - - - - - - - - - - - - - - - -
BoutonComportement
ReprendreContinue séquence 3 à position 1:42 exacte
RecommencerReset progression, démarre séquence 1 depuis 0:00
Voir séquencesAffiche liste complète, user choisit séquence départ
-

Expiration progression : -- Progression conservée 30 jours -- Après 30j : popup "Audio-guide expiré. Recommencez depuis le début ?" -- Suppression données progression (mais historique "écouté" préservé)

-

Justification : -- Contexte clair : user sait exactement où il en est -- Flexibilité : reprendre OU recommencer (choix utilisateur) -- 30 jours = raisonnable pour tourisme multi-jours ou retour ultérieur

-
-

16.6.3 Multi-device

-

Scénario :

-
    -
  1. User démarre audio-guide sur iPhone (séquences 1-3)
  2. -
  3. Progression sync cloud
  4. -
  5. Lendemain : user ouvre app sur iPad
  6. -
  7. Popup : "Reprendre Safari du Paugre sur cet appareil ?"
  8. -
  9. User clique Reprendre → continue séquence 4
  10. -
-

Conflit de version : -- Si modifications simultanées 2 appareils (rare) : dernière modification gagne -- Toast : "Progression mise à jour depuis votre autre appareil"

-

Justification : -- Confort utilisateur (change d'appareil librement) -- Use case réel : planning trajet sur tablette, écoute sur smartphone en voiture

-
-

Récapitulatif Section 16

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PointDécisionCoûtComplexité
16.1 Types audio-guides4 modes (piéton/voiture/vélo/transport) avec détection auto0€Moyenne
16.1.2 CréationFormulaire séquences + GPS + rayon + wizard guidé0€Moyenne
16.2.1 Piéton - PassagesManuel AVEC pub auto-play entre séquences, pause après0€Faible
16.2.2 Piéton - NavigationLiberté totale (skip, retour, saut direct liste)0€Faible
16.3.1 Voiture - DéclenchementGPS auto + boutons manuels actifs (warning sécurité si >10 km/h)0€Moyenne
16.3.2 Voiture - AffichageDistance + ETA + direction (flèche) + vitesse (PAS de carte)0€Faible
16.3.3 Voiture - RayonConfigurable créateur (défauts 30m/50m/100m selon mode)0€Faible
16.4 Vélo & TransportMêmes règles avec tolérances ajustées + warning adapté0€Faible
16.5 Publicités1/5 séquences, auto-play TOUS modes, skippable 5s0€Faible
16.6.1 SauvegardeComplète (séquence + position + historique) local + cloud0€Faible
16.6.2 ReprisePopup intelligente avec choix (reprendre/recommencer), expiration 30j0€Faible
16.6.3 Multi-deviceSync cloud PostgreSQL (reprendre sur autre appareil)0€Faible
-

Coût total MVP : 0€ (GPS natif, calcul distance PostGIS)

-
-

Points d'attention pour Gherkin

-
    -
  • Tester 4 modes audio-guides (détection vitesse auto)
  • -
  • Tester création séquences avec points GPS + rayon configurable
  • -
  • Tester mode piéton : pause après séquence + pub auto-play + pause après pub + clic Suivant
  • -
  • Tester navigation libre piéton (skip, retour, saut direct liste)
  • -
  • Tester mode voiture : déclenchement GPS auto rayon 30m
  • -
  • Tester navigation manuelle voiture : boutons actifs + warning si vitesse >10 km/h
  • -
  • Tester affichage distance + ETA + direction (flèche 8 directions)
  • -
  • Tester rayon tolérance "point manqué" (popup 3 actions)
  • -
  • Tester mode vélo (rayon 50m) et transport (rayon 100m)
  • -
  • Tester insertion pub 1/5 séquences tous modes avec auto-play
  • -
  • Tester sauvegarde progression locale + sync cloud
  • -
  • Tester popup reprise (3 boutons : reprendre/recommencer/voir liste)
  • -
  • Tester expiration progression 30 jours
  • -
  • Tester multi-device : démarrer iPhone, continuer iPad
  • -
  • Tester gestion conflit progression simultanée 2 appareils
  • -
-
- -

Annexe : Fonctionnalités reportées Post-MVP

-

Date : 2026-01-19 -Statut : Fonctionnalités validées mais reportées après le MVP

-
-

Sommaire

-
    -
  1. Classification politique et équilibre éditorial
  2. -
  3. Système de pourboires créateurs
  4. -
-
-

1. Classification politique et équilibre éditorial

-
-

⚠️ Reporté post-MVP pour raisons de coût, complexité et risques juridiques.

-
-

Contexte du report

-

Raisons : -- Coût modération : Classification manuelle humaine très coûteuse (~2000€/mois pour 1-2 modérateurs senior full-time) -- Risque juridique : Accusations de biais éditorial, contentieux DSA -- Complexité technique : Dashboard audit, logs 3 ans, alertes déséquilibre -- Controverse : Peut créer polémique dès le lancement -- Pas essentiel MVP : L'application fonctionne sans ce système

-

Version MVP (actuelle) : -- Tag "Politique" simple (comme "Économie", "Sport") -- Pas de classification gauche/droite -- Pas d'équilibrage imposé -- Option utilisateur "Masquer politique" → 0% contenus politiques

-
-

Spécifications complètes (future implémentation)

-

Échelle de classification (5 niveaux) : -- 🔴 Extrême gauche (anticapitalisme radical, révolution) -- 🟠 Gauche (écologie, social, critique capitalisme modérée) -- ⚪ Centre/Neutre (pas de positionnement politique clair) -- 🔵 Droite (sécurité, tradition, économie libérale) -- 🟣 Extrême droite (nationalisme radical, conservatisme extrême) -- 🟢 Non politique (enfants, musique, fiction, culture générale)

-

Qui classifie : -- ❌ Pas de classification automatique IA (outil informatif uniquement, jamais décisionnaire) -- ✅ Modérateurs senior après transcription -- ✅ Créateur peut contester via processus d'appel

-

Affichage : -- Badge politique visible : au choix de l'utilisateur (paramètre "Afficher orientation politique") -- Par défaut : badges masqués (UX neutre)

-

Règles de diffusion (équilibre imposé) :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Préférence utilisateurRépartitionJustification
Équilibré (défaut)35% gauche / 35% droite / 30% centre-neutreNeutralité plateforme
Plutôt gauche50% gauche / 20% droite / 30% centre-neutrePréférence respectée avec minimum opposition
Plutôt droite50% droite / 20% gauche / 30% centre-neutrePréférence respectée avec minimum opposition
Masquer politique0% gauche / 0% droite / 100% centre-neutre + non politiqueOption apolitique
-

Audit et conformité DSA : -- Rapport hebdomadaire automatique : % gauche/droite/centre diffusé par utilisateur -- Alerte si déséquilibre global plateforme (>55% d'un bord) -- Logs conservés 3 ans (exigence Digital Services Act EU) -- Dashboard admin : visualisation répartition temps réel

-

Sanctions mauvaise classification : -- Classification volontairement incorrecte = Strike 1 -- Récidive = Strike 2 (suspension 7j) -- Détection via signalements utilisateurs + audit modération

-

Justification : -- Conformité juridique DSA (obligation neutralité plateforme EU) -- Protection contre accusations de biais éditorial -- Transparence auditable -- Coût : temps modération humaine (incompressible)

-
-

Conditions de réintégration

-

Prérequis : -1. Base utilisateurs stable et revenus suffisants pour financer modération -2. Équipe modération dédiée (2+ modérateurs senior formés) -3. Dashboard admin audit DSA opérationnel -4. Système de logs et archivage 3 ans en place -5. Validation juridique du processus de classification

-

Chronologie estimée : -- Phase 1 (Post-MVP+3 mois) : Validation demande utilisateurs via sondages -- Phase 2 (Post-MVP+6 mois) : Recrutement modérateurs + développement dashboard -- Phase 3 (Post-MVP+9 mois) : Tests bêta avec utilisateurs volontaires -- Phase 4 (Post-MVP+12 mois) : Déploiement progressif si résultats positifs

-
-

2. Système de pourboires créateurs

-
-

⚠️ Reporté post-MVP - Fonctionnalité crypto (Lightning Network) prévue ultérieurement.

-
-

Contexte du report

-

Raisons : -- Complexité technique : Intégration Lightning Network, gestion wallets crypto -- Réglementation : Incertitude juridique crypto en EU (MiCA 2025) -- Focus MVP : Priorité sur monétisation via abonnements Premium et publicités -- Adoption utilisateurs : Nécessite éducation et adoption crypto préalables

-

Version MVP (actuelle) : -- Monétisation créateurs via : - - Partage revenus publicités (3€ CPM) - - 70% revenus abonnements Premium

-
-

Spécifications complètes (future implémentation)

-

Système prévu : Micro-dons via Lightning Network (Bitcoin Layer 2)

-

Fonctionnement : -1. Auditeur peut envoyer pourboire pendant ou après écoute -2. Montants suggérés : 0.10€, 0.50€, 1€, 5€ (personnalisable) -3. Transaction instantanée via Lightning Network (frais <0.01€) -4. Créateur reçoit directement dans wallet Lightning -5. Conversion EUR/BTC automatique (optionnelle)

-

Avantages Lightning Network : -- ✅ Frais quasi-nuls (<1%) vs 1.8% Mangopay -- ✅ Transactions instantanées (<1 seconde) -- ✅ Micropaiements possibles (dès 0.01€) -- ✅ International sans frais supplémentaires -- ✅ Pas d'intermédiaire (peer-to-peer)

-

Contraintes : -- ❌ Adoption crypto limitée (2-5% population EU en 2026) -- ❌ Volatilité BTC (nécessite conversion EUR immédiate) -- ❌ UX complexe pour utilisateurs non-crypto -- ❌ Réglementation MiCA en évolution

-

Alternatives étudiées : -- Ko-fi / Buy Me a Coffee : simple mais frais 5% -- PayPal/Stripe : frais 2.9% + 0.30€ (non viable pour micropaiements) -- Mangopay : déjà utilisé, mais frais élevés pour petits montants

-
-

Conditions de réintégration

-

Prérequis : -1. Réglementation MiCA stabilisée et conforme -2. Adoption crypto suffisante dans la base utilisateurs (>10%) -3. Intégration Lightning Network validée techniquement -4. UX simplifiée pour utilisateurs non-crypto (onboarding dédié) -5. Demande créateurs confirmée via sondages

-

Chronologie estimée : -- Phase 1 (Post-MVP+6 mois) : Étude de marché et demande utilisateurs -- Phase 2 (Post-MVP+12 mois) : Développement intégration Lightning -- Phase 3 (Post-MVP+15 mois) : Tests bêta avec créateurs volontaires -- Phase 4 (Post-MVP+18 mois) : Déploiement public si résultats positifs

-
-

Autres fonctionnalités candidates Post-MVP

-

Liste non exhaustive de fonctionnalités évoquées mais non encore spécifiées :

-
    -
  • Mode offline avancé : Téléchargement automatique zones fréquentes
  • -
  • Playlists collaboratives : Co-création de playlists géolocalisées
  • -
  • API publique créateurs : Intégration RSS, podcasts existants
  • -
  • Gamification : Badges, défis géolocalisés, leaderboards
  • -
  • Mode nuit : Interface sombre automatique
  • -
  • Statistiques avancées créateurs : Démographie, retention, heatmaps GPS
  • -
-

Ces fonctionnalités seront spécifiées et priorisées selon les retours utilisateurs MVP.

-
-

Suivi et validation

-

Responsable : Product Owner -Révision : Trimestrielle -Critères de priorisation : -1. Demande utilisateurs (votes, sondages) -2. Impact business (revenus, rétention) -3. Faisabilité technique (complexité, ressources) -4. Conformité légale (RGPD, DSA, MiCA) -5. Différenciation concurrentielle

-
- -

Audio-guides multi-séquences pour piétons

-
-

En tant qu'auditeur à pied -Je veux profiter d'audio-guides structurés lors de mes visites -Afin de découvrir des lieux de manière autonome et à mon rythme

-
-

29 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'auditeur -Et que je suis en mode piéton (vitesse <5 km/h)

-
-

1. Détection d'audio-guide à proximité

-

Étant donné que je me trouve à 80 mètres du Musée du Louvre -Et que 3 audio-guides sont disponibles pour ce lieu

-

Quand le système détecte ma position

-

Alors je reçois une notification push:

-
-

2. Rayon de détection de 100m

-

Étant donné qu'un audio-guide est centré aux coordonnées GPS du Louvre

-

Quand je suis à exactement 100m du centre

-

Alors la notification est déclenchée -Et quand je suis à 101m, aucune notification n'est envoyée

-
-

3. Page de sélection des audio-guides

-

Étant donné que j'ai tapé sur la notification audio-guide

-

Quand la page de sélection s'affiche

-

Alors je vois une liste de guides disponibles:

-
| titre | créateur | nb_sequences | durée | note | écoutes |
-|---|---|---|---|---|---|
-| Visite complète | Créateur A | 12 | 45 min | 4.8 | 1.2K |
-| Œuvres majeures | Créateur B | 5 | 20 min | 4.9 | 3.5K |
-| Visite famille | Créateur C | 8 | 30 min | 4.7 | 850 |
-
-
-

4. Sélection d'un audio-guide

-

Étant donné que je suis sur la page de sélection

-

Quand je tape sur "Visite complète (45 min)"

-

Alors l'interface de lecture d'audio-guide s'ouvre -Et la séquence 1 commence automatiquement -Et je vois la liste complète des 12 séquences

-
-

5. Interface de lecture audio-guide

-

Étant donné que j'ai sélectionné un audio-guide de 12 séquences

-

Quand l'interface s'affiche

-

Alors je vois:

-
| élément | exemple |
-|---|---|
-| Titre guide | 🎨 Visite complète • Musée du Louvre |
-| Piste actuelle | Piste 2/12 |
-| Titre séquence | "La Joconde - Histoire et mystères" |
-| Barre de progression | 3:24 / 6:50 |
-| Liste séquences | ✅ 1. Intro, ▶️ 2. Joconde, ⏸️ 3. Vénus... |
-| Boutons navigation | Précédent, Play/Pause, Suivant |
-
-
-

6. Navigation vers séquence suivante

-

Étant donné que j'écoute la séquence 2

-

Quand je tape sur "Suivant"

-

Alors la séquence 3 commence immédiatement -Et le titre de la séquence s'affiche: "Vénus de Milo" -Et la barre de progression se réinitialise

-
-

7. Navigation vers séquence précédente

-

Étant donné que j'écoute la séquence 5

-

Quand je tape sur "Précédent"

-

Alors la séquence 4 recommence depuis le début -Et je peux réécouter cette séquence

-
-

8. Saut direct à une séquence spécifique

-

Étant donné que j'écoute la séquence 2 -Et que la liste des séquences est affichée

-

Quand je tape sur "7. Peintures Renaissance"

-

Alors la séquence 7 démarre immédiatement -Et je passe directement de la séquence 2 à la 7

-
-

9. Commande vocale "Suivant"

-

Étant donné que j'écoute la séquence 3

-

Quand je dis "Suivant" via la commande vocale

-

Alors la séquence 4 démarre -Et la commande vocale fonctionne même si l'écran est verrouillé

-
-

10. Commande vocale "Précédent"

-

Étant donné que j'écoute la séquence 6

-

Quand je dis "Précédent" via la commande vocale

-

Alors la séquence 5 démarre depuis le début

-
-

11. Pause et reprise à la position exacte

-

Étant donné que j'écoute la séquence 4 à la position 2:30

-

Quand je mets en pause -Et que j'attends 5 minutes -Et que je reprends la lecture

-

Alors la séquence reprend exactement à 2:30 -Et aucune donnée n'est perdue

-
-

12. Guidage vocal automatique entre séquences

-

Étant donné que la séquence 2 se termine

-

Quand la transition vers la séquence 3 se produit

-

Alors j'entends un message vocal: -Et la séquence 3 ne démarre pas automatiquement (navigation manuelle)

-
-

13. Avertissement si éloignement du point d'intérêt

-

Étant donné que je suis dans le guide du Louvre -Et que je devrais être devant la Vénus de Milo (séquence 3)

-

Quand je m'éloigne de plus de 50m de ce point

-

Alors j'entends un message vocal: -Et un bouton "Voir le plan" apparaît dans l'interface

-
-

14. Sauvegarde automatique de la progression

-

Étant donné que j'écoute la séquence 5 à la position 1:45

-

Quand je ferme l'application brutalement -Et que je la rouvre 10 minutes plus tard

-

Alors je vois une popup "Reprendre la visite du Musée du Louvre ?" -Et si je choisis "Reprendre", je retourne à la séquence 5 à 1:45

-
-

15. Option de recommencer depuis le début

-

Étant donné que j'ai une progression sauvegardée à la séquence 7

-

Quand je rouvre le guide

-

Alors je vois 2 options:

-
| option | action |
-|---|---|
-| Reprendre à la séquence 7 | Reprend à la position exacte |
-| Recommencer depuis le début | Retourne à la séquence 1 |
-
-
-

16. Expiration de la sauvegarde après 30 jours

-

Étant donné que j'ai une progression sauvegardée depuis 30 jours

-

Quand j'essaie de reprendre le guide

-

Alors la sauvegarde est considérée comme expirée -Et je recommence depuis la séquence 1 -Et je vois le message "Votre précédente visite date de plus de 30 jours. Recommençons depuis le début."

-
-

17. Synchronisation multi-device de la progression

-

Étant donné que j'écoute un guide sur mon iPhone à la séquence 4

-

Quand je ferme l'app et ouvre sur mon iPad

-

Alors je vois la progression synchronisée -Et je peux reprendre à la séquence 4 sur l'iPad

-
-

18. Marquage "Terminé" après toutes les séquences

-

Étant donné que j'écoute la dernière séquence (12/12)

-

Quand cette séquence se termine

-

Alors le guide est marqué "✅ Terminé" dans mon historique -Et je vois un message de félicitation: -Et le créateur gagne les statistiques d'écoute complète

-
-

19. Création d'audio-guide par un créateur

-

Étant donné que je suis un créateur

-

Quand je crée un nouvel audio-guide

-

Alors je dois:

-
| étape | détail |
-|---|---|
-| Uploader plusieurs fichiers | 1 fichier MP3 par séquence |
-| Numéroter les séquences | Séquence 1, Séquence 2, etc. |
-| Titrer chaque séquence | "Introduction", "La Joconde", etc. |
-| Définir point GPS unique | Centre du lieu (ex: Louvre) |
-| Définir rayon de détection | Par défaut 100m |
-
-

Et la durée totale est calculée automatiquement

-
-

20. Structure JSON de stockage audio-guide

-

Étant donné qu'un créateur publie un audio-guide du Louvre

-

Quand les métadonnées sont stockées en base

-

Alors le format JSON contient:

-
-

21. Limitation du nombre de séquences

-

Étant donné que je crée un audio-guide

-

Quand j'essaie d'ajouter plus de 50 séquences

-

Alors je vois le message "Maximum 50 séquences par audio-guide" -Et je dois structurer mon contenu différemment ou créer plusieurs guides

-
-

22. Quitter le guide et sauvegarder

-

Étant donné que j'écoute la séquence 6

-

Quand je tape sur le bouton "×" (fermer)

-

Alors je vois une confirmation: -Et si je confirme, la progression est enregistrée -Et je retourne à l'écran principal

-
-

23. Statistiques créateur pour audio-guides

-

Étant donné que je suis créateur d'un audio-guide

-

Quand je consulte mes statistiques

-

Alors je vois:

-
| métrique | exemple valeur |
-|---|---|
-| Nombre de démarrages | 1250 |
-| Nombre de complétions (100%) | 387 (31%) |
-| Séquence la plus skippée | Séquence 8 |
-| Durée moyenne d'écoute | 28 min (sur 45) |
-
-
-

24. Audio-guide multilingue (post-MVP)

-

Étant donné qu'un créateur peut publier plusieurs versions linguistiques

-

Quand un touriste anglophone visite le Louvre

-

Alors il voit les guides disponibles en anglais -Et peut choisir parmi les guides traduits -Mais cette fonctionnalité n'est pas disponible en MVP

-
-

25. Publicité entre séquences d'audio-guide

-

Étant donné que je suis un utilisateur gratuit -Et que j'écoute un audio-guide

-

Quand je passe de la séquence 5 à la séquence 6

-

Alors une publicité peut être insérée (1 pub toutes les 5 séquences) -Et la publicité est skippable après 5 secondes -Et les utilisateurs Premium ne voient pas de publicité

-
-

26. Audio-guide en mode offline

-

Étant donné que j'ai téléchargé un audio-guide complet

-

Quand je visite le lieu sans connexion internet

-

Alors toutes les séquences sont disponibles hors ligne -Et la navigation fonctionne normalement -Et seule la sauvegarde cloud est différée jusqu'à reconnexion

-
-

27. Notation d'un audio-guide après écoute

-

Étant donné que j'ai terminé un audio-guide

-

Quand je ferme l'interface

-

Alors je vois une popup "Notez cette visite" -Et je peux donner une note de 1 à 5 étoiles -Et cette note contribue à la note globale visible par les autres utilisateurs

-
-

28. Filtrage par langue dans la page de sélection

-

Étant donné que plusieurs audio-guides sont disponibles en différentes langues

-

Quand j'accède à la page de sélection

-

Alors je peux filtrer par langue -Et par défaut, les guides dans ma langue système sont affichés en premier

-
-

29. Réutilisation de l'infrastructure existante

-

Étant donné qu'un audio-guide est techniquement un contenu structuré

-

Alors il réutilise:

-
| composant | usage |
-|---|---|
-| Stockage Bunny | Hébergement fichiers MP3 séquences |
-| Streaming HLS | Diffusion audio adaptative |
-| Cache Redis | Métadonnées guides + progressions |
-| PostgreSQL | Stockage structure JSON guides |
-
-

Et aucune infrastructure dédiée n'est nécessaire

-
-
- -

Impact des abonnements sur l'algorithme

-
-

En tant qu'auditeur -Je veux que les contenus de mes créateurs suivis soient favorisés -Afin de ne pas rater leurs publications tout en découvrant de nouveaux contenus

-
-

16 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'auditeur -Et que je suis abonné au créateur "JeanDupont"

-
-

1. Boost de +30% appliqué au score final

-

Étant donné un contenu du créateur "JeanDupont" avec:

-
| score_geo | 0.5 |
-|---|---|
-| score_interet | 0.6 |
-| score_engage | 0.5 |
-
-

Quand le score final est calculé

-

Alors le score de base est 0.53 -Et le boost abonnement de +30% est appliqué -Et le score final avec boost est 0.69

-
-

2. Contenu non-suivi peut battre contenu suivi

-

Étant donné que je suis à Paris -Et que 2 contenus sont disponibles:

-
| contenu | createur_suivi | score_geo | score_interet | score_engage | score_final_base | score_avec_boost |
-|---|---|---|---|---|---|---|
-| Contenu A | Non | 0.9 | 0.8 | 0.7 | 0.80 | 0.80 |
-| Contenu B | Oui | 0.5 | 0.6 | 0.5 | 0.53 | 0.69 |
-
-

Quand l'algorithme sélectionne le prochain contenu

-

Alors le Contenu A est proposé en premier

-
-

3. Contenu suivi remporte grâce au boost

-

Étant donné que je suis à Paris -Et que 2 contenus sont disponibles:

-
| contenu | createur_suivi | score_final_base | score_avec_boost |
-|---|---|---|---|
-| Contenu A | Non | 0.70 | 0.70 |
-| Contenu B | Oui | 0.60 | 0.78 |
-
-

Quand l'algorithme sélectionne le prochain contenu

-

Alors le Contenu B est proposé en premier

-
-

4. Contenu suivi avec faible engagement ne domine pas

-

Étant donné que je suis abonné au créateur "CreateurMoyen" -Et qu'il publie un contenu avec très faible engagement (score 0.30) -Et qu'un contenu viral d'un créateur non-suivi a un score de 0.85

-

Quand l'algorithme sélectionne le prochain contenu

-

Alors le contenu viral est proposé en premier (0.85)

-
-

5. Pas de file dédiée aux abonnements

-

Étant donné que je suis abonné à 50 créateurs

-

Quand l'algorithme génère ma file d'attente de 5 contenus

-

Alors les contenus suivis et non-suivis sont mélangés -Et tous entrent en compétition selon leurs scores (avec boost si abonnement) -Et il n'y a pas de section séparée "Contenus de vos abonnements"

-
-

6. Vérification du calcul du boost

-

Étant donné un contenu d'un créateur suivi -Et que le score final de base est calculé à 0.65

-

Quand le boost abonnement est appliqué

-

Alors le multiplicateur utilisé est exactement 1.3 -Et le score final avec boost est 0.845 (0.65 × 1.3) -Et le résultat est arrondi à 2 décimales: 0.85

-
-

7. Boost appliqué à tous les contenus du créateur suivi

-

Étant donné que je suis abonné au créateur "JeanDupont" -Et qu'il a publié 10 contenus différents

-

Quand l'algorithme évalue chacun de ces contenus

-

Alors le boost de +30% est appliqué à tous les 10 contenus -Et chaque contenu bénéficie du même multiplicateur 1.3

-
-

8. Plusieurs créateurs suivis en compétition

-

Étant donné que je suis abonné à "Créateur A" et "Créateur B" -Et que les 2 ont des contenus disponibles dans ma zone:

-
| createur | score_base | score_avec_boost |
-|---|---|---|
-| Créateur A | 0.70 | 0.91 |
-| Créateur B | 0.65 | 0.85 |
-
-

Quand l'algorithme sélectionne le prochain contenu

-

Alors le contenu du Créateur A est proposé en premier (0.91 > 0.85) -Et les 2 bénéficient du boost, mais le meilleur score gagne

-
-

9. Contenu national d'un créateur suivi

-

Étant donné que je suis abonné à "MediaNational" -Et qu'il publie un contenu de type "National" (score_geo 0.2)

-

Quand le score est calculé avec:

-
| score_geo | score_interet | score_engage |
-|---|---|---|
-| 0.2 | 0.7 | 0.6 |
-
-

Alors le score de base est environ 0.50 -Et avec le boost abonnement, le score devient 0.65 -Et le contenu peut être proposé malgré son score géo faible

-
-

10. Transparence du boost dans les paramètres

-

Quand j'accède aux paramètres de l'algorithme de recommandation

-

Alors je vois l'information: "Les contenus de vos créateurs suivis bénéficient d'un boost de +30%" -Et je comprends que ce n'est pas une priorité absolue -Et que la découverte de nouveaux contenus reste possible

-
-

11. Boost désactivé si désabonnement

-

Étant donné que je suis abonné au créateur "JeanDupont" -Et qu'un de ses contenus bénéficiait du boost +30%

-

Quand je me désabonne de "JeanDupont"

-

Alors ses contenus n'ont plus le boost -Et leur score revient au score de base sans multiplicateur

-
-

12. Contenu d'un créateur nouvellement suivi

-

Étant donné que je viens de m'abonner à "NouveauCreateur" -Et qu'il a publié un contenu il y a 2 jours

-

Quand l'algorithme recalcule les scores

-

Alors le boost de +30% est immédiatement appliqué à ce contenu -Et il peut apparaître dans ma prochaine file d'attente

-
-

13. Impact sur le taux de contenu suivi dans le feed

-

Étant donné que je suis abonné à 30 créateurs -Et que j'écoute 100 contenus sur une semaine

-

Quand j'analyse la répartition

-

Alors environ 40-50% des contenus proviennent de créateurs suivis -Et 50-60% proviennent de créateurs non-suivis (découverte)

-
-

14. Contenu suivi hors zone géographique

-

Étant donné que je suis à Paris -Et que je suis abonné à un créateur de Marseille -Et qu'il publie un contenu ancré à Marseille (hors de portée)

-

Quand l'algorithme évalue ce contenu

-

Alors le score géo est quasi nul (0.05) -Et même avec boost +30%, le score reste très faible -Et le contenu n'est probablement pas proposé

-
-

15. Performance de calcul du boost

-

Étant donné que je suis abonné à 100 créateurs -Et que l'algorithme évalue 1000 contenus potentiels

-

Quand le calcul des scores avec boost est effectué

-

Alors le temps de calcul reste inférieur à 50ms -Et la requête SQL utilise un JOIN sur la table abonnements

-
-

16. Boost combiné avec d'autres facteurs

-

Étant donné un contenu d'un créateur suivi -Et que le contenu bénéficie aussi de:

-
| facteur | impact |
-|---|---|
-| Score d'engagement élevé | +20% |
-| Contenu récent (<24h) | +10% |
-| Boost abonnement | +30% |
-
-

Quand le score final est calculé

-

Alors le boost abonnement s'applique au score final (après tous les autres calculs) -Et les boosts ne s'additionnent pas, le boost abonnement est un multiplicateur final

-
-
- -

Limites d'abonnements et désabonnement

-
-

En tant qu'auditeur -Je veux gérer mes abonnements de manière équilibrée -Afin de suivre mes créateurs préférés sans être submergé

-
-

27 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'auditeur

-
-

1. Limite maximale de 200 abonnements

-

Étant donné que je suis abonné à 199 créateurs

-

Quand j'essaie de m'abonner à un 200ème créateur

-

Alors l'abonnement réussit -Et je suis maintenant abonné à 200 créateurs

-
-

2. Impossible de dépasser 200 abonnements

-

Étant donné que je suis déjà abonné à 200 créateurs

-

Quand j'essaie de m'abonner à un nouveau créateur

-

Alors l'action échoue -Et je vois le message:

-
-

3. Suggestion de désabonnement de créateurs inactifs

-

Étant donné que je suis abonné à 200 créateurs -Et que j'essaie de m'abonner à un nouveau créateur

-

Quand je vois le message de limite atteinte

-

Alors je vois aussi une suggestion: -Et un bouton "Désabonner" est proposé pour ce créateur

-
-

4. Liste triable des abonnements

-

Étant donné que je suis abonné à 150 créateurs

-

Quand j'accède à ma liste d'abonnements

-

Alors je peux trier par:

-
| critère | ordre |
-|---|---|
-| Date d'abonnement | Plus récent / Plus ancien |
-| Nombre de contenus écoutés | Plus écoutés / Moins écoutés |
-| Dernière activité créateur | Plus récent / Plus ancien |
-| Ordre alphabétique | A-Z / Z-A |
-
-
-

5. Abonnement initial augmente les jauges de +5%

-

Étant donné que mes jauges d'intérêt sont:

-
| catégorie | valeur initiale |
-|---|---|
-| Automobile | 60% |
-| Voyage | 55% |
-
-

Et qu'un créateur tague ses contenus "Automobile" et "Voyage"

-

Quand je m'abonne à ce créateur

-

Alors mes jauges évoluent:

-
| catégorie | nouvelle valeur |
-|---|---|
-| Automobile | 65% (+5%) |
-| Voyage | 60% (+5%) |
-
-
-

6. Abonnement avec créateur ayant 3 tags

-

Étant donné qu'un créateur tague ses contenus:

-
| tags |
-|---|
-| Automobile, Voyage, Technologie |
-
-

Et que mes jauges sont toutes à 50%

-

Quand je m'abonne à ce créateur

-

Alors les 3 jauges augmentent de +5%:

-
| catégorie | nouvelle valeur |
-|---|---|
-| Automobile | 55% |
-| Voyage | 55% |
-| Technologie | 55% |
-
-
-

7. Désabonnement diminue les jauges de -5%

-

Étant donné que je suis abonné à un créateur avec tags "Politique" et "Économie" -Et que mes jauges sont:

-
| catégorie | valeur actuelle |
-|---|---|
-| Politique | 70% |
-| Économie | 65% |
-
-

Quand je me désabonne de ce créateur

-

Alors mes jauges évoluent:

-
| catégorie | nouvelle valeur |
-|---|---|
-| Politique | 65% (-5%) |
-| Économie | 60% (-5%) |
-
-
-

8. Désabonnement sans confirmation

-

Étant donné que je consulte le profil d'un créateur suivi

-

Quand je clique sur "Se désabonner"

-

Alors le désabonnement est immédiat -Et aucune popup de confirmation n'apparaît

-
-

9. Réabonnement possible immédiatement

-

Étant donné que je viens de me désabonner d'un créateur

-

Quand je consulte à nouveau son profil

-

Alors le bouton "S'abonner" est affiché -Et je peux me réabonner immédiatement -Et mes jauges augmentent à nouveau de +5%

-
-

10. Effet symétrique abonnement/désabonnement

-

Étant donné qu'un créateur a les tags "Musique" et "Culture" -Et que ma jauge Musique est à 50%

-

Quand je m'abonne puis me désabonne immédiatement

-

Alors ma jauge revient exactement à 50% -Et il n'y a pas de perte ou gain net

-
-

11. Abonnement ne dépasse pas 100% de jauge

-

Étant donné que ma jauge Automobile est à 97% -Et qu'un créateur tague ses contenus "Automobile"

-

Quand je m'abonne à ce créateur

-

Alors ma jauge Automobile passe à 100% (limite max) -Et l'augmentation effective est de +3% seulement

-
-

12. Désabonnement ne descend pas sous 0%

-

Étant donné que ma jauge Politique est à 3% -Et que je suis abonné à un créateur avec tag "Politique"

-

Quand je me désabonne de ce créateur

-

Alors ma jauge Politique passe à 0% (limite min) -Et la diminution effective est de -3% seulement

-
-

13. Créateur ne voit pas qui est abonné (privacy)

-

Étant donné que je suis abonné au créateur "JeanDupont"

-

Quand "JeanDupont" consulte ses statistiques

-

Alors il voit le nombre total d'abonnés (ex: "1,247 abonnés") -Mais il ne voit pas la liste des utilisateurs abonnés -Et mon identité reste privée

-
-

14. Créateur voit uniquement le nombre total d'abonnés

-

Étant donné que je suis créateur -Et que j'ai 523 abonnés

-

Quand je consulte mes statistiques

-

Alors je vois "523 abonnés" -Mais je ne peux pas:

-
| action interdite |
-|---|
-| Voir la liste des abonnés |
-| Contacter mes abonnés individuellement |
-| Voir leurs profils |
-
-
-

15. Pas d'abonnement mutuel visible

-

Étant donné que je suis abonné au créateur "Alice" -Et qu'"Alice" est abonnée à mon compte créateur

-

Quand je consulte le profil d'"Alice"

-

Alors je ne vois pas d'indication qu'elle est abonnée à moi -Et il n'y a pas de badge "Abonné mutuellement"

-
-

16. Performance avec 200 abonnements

-

Étant donné que je suis abonné à 200 créateurs

-

Quand l'algorithme calcule ma recommandation

-

Alors la requête SQL utilise un JOIN sur la table abonnements -Et la table est indexée sur user_id et creator_id -Et le temps de calcul reste inférieur à 50ms

-
-

17. Impact sur la recommandation avec beaucoup d'abonnements

-

Étant donné que je suis abonné à 150 créateurs très actifs -Et qu'ils publient collectivement 100 contenus par jour

-

Quand l'algorithme génère ma file de 5 contenus

-

Alors environ 60-70% des contenus proviennent de créateurs suivis (grâce au boost +30%) -Mais 30-40% proviennent de nouveaux créateurs (découverte)

-
-

18. Notification de désabonnement au créateur (non implémenté)

-

Étant donné que je me désabonne d'un créateur

-

Alors le créateur ne reçoit aucune notification -Et il ne peut pas savoir qui s'est désabonné

-
-

19. Statistiques d'abonnements pour l'utilisateur

-

Étant donné que je suis abonné à 87 créateurs

-

Quand j'accède à mes statistiques d'abonnements

-

Alors je vois:

-
| métrique | exemple valeur |
-|---|---|
-| Nombre total d'abonnements | 87 / 200 |
-| Créateurs les plus écoutés | Top 10 avec % écoute |
-| Créateurs non écoutés depuis 6 mois | 12 créateurs |
-| Nouveaux contenus non écoutés | 23 contenus |
-
-
-

20. Recherche dans la liste d'abonnements

-

Étant donné que je suis abonné à 120 créateurs

-

Quand j'accède à ma liste d'abonnements

-

Alors je peux chercher par nom de créateur -Et les résultats sont filtrés en temps réel -Et je trouve rapidement un créateur spécifique

-
-

21. Export de la liste d'abonnements (RGPD)

-

Étant donné que je demande l'export de mes données

-

Quand l'export est généré

-

Alors la liste de mes abonnements est incluse:

-
-

22. Suppression compte utilisateur et impact sur abonnements

-

Étant donné que je suis abonné à 50 créateurs

-

Quand je supprime définitivement mon compte

-

Alors tous mes abonnements sont supprimés -Et le compteur d'abonnés de chaque créateur est décrémenté de -1 -Et les jauges n'existent plus (données supprimées)

-
-

23. Suppression compte créateur et impact sur abonnés

-

Étant donné que je suis abonné au créateur "Bob"

-

Quand "Bob" supprime son compte créateur

-

Alors je suis automatiquement désabonné -Et mes jauges diminuent de -5% pour les tags de "Bob" -Et je ne vois plus "Bob" dans ma liste d'abonnements

-
-

24. Limite 200 justifiée par usage réaliste

-

Étant donné que la moyenne d'abonnements sur YouTube est de ~50-100 chaînes -Et que Twitter limite à 5000 follows (mais moyenne ~150)

-

Quand RoadWave fixe la limite à 200

-

Alors cela couvre largement 99% des utilisateurs -Et évite les abus (comptes spam suivant tout le monde)

-
-

25. Table PostgreSQL optimisée pour abonnements

-

Étant donné la structure de table subscriptions:

-

Alors les requêtes d'abonnements sont O(1) avec index -Et le count d'abonnés par créateur est rapide -Et la vérification "est abonné ?" est instantanée

-
-

26. Détection d'abonnements abusifs

-

Étant donné qu'un utilisateur s'abonne à 200 créateurs en moins de 5 minutes

-

Quand le système détecte cette activité suspecte

-

Alors un rate limiting est appliqué (max 10 abonnements/minute) -Et l'utilisateur voit "Trop d'actions rapides. Veuillez réessayer dans 1 minute" -Et cela prévient les bots de spam

-
-

27. Badge créateur vérifié visible dans abonnements

-

Étant donné que je suis abonné à 3 créateurs dont 1 vérifié

-

Quand je consulte ma liste d'abonnements

-

Alors le créateur vérifié a un badge ✓ bleu -Et les créateurs non vérifiés n'ont pas de badge

-
-
- -

Notifications contextuelles selon le mode de déplacement

-
-

En tant qu'auditeur -Je veux recevoir des notifications adaptées à mon contexte -Afin d'être informé sans être distrait en conduisant

-
-

28 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'auditeur -Et que j'ai activé les notifications

-
-

1. Détection automatique du contexte en voiture

-

Étant donné que ma vitesse GPS est de 50 km/h

-

Quand le système détecte mon contexte

-

Alors je suis identifié comme "En voiture" -Et les notifications push sont désactivées -Et seules les notifications in-app sont actives

-
-

2. Détection automatique du contexte à pied

-

Étant donné que ma vitesse GPS est de 3 km/h

-

Quand le système détecte mon contexte

-

Alors je suis identifié comme "À pied" -Et les notifications push sont activées -Et l'interface tactile et vocale sont disponibles

-
-

3. Zone de transition 5-10 km/h

-

Étant donné que ma vitesse GPS varie entre 5 et 10 km/h

-

Quand le système détecte mon contexte

-

Alors un algorithme de lissage est appliqué sur 30 secondes -Et le mode est déterminé selon la vitesse moyenne -Et les changements de mode ne sont pas trop fréquents

-
-

4. Nouveau contenu créateur suivi - Mode voiture

-

Étant donné que je suis en voiture (vitesse >10 km/h) -Et que je suis abonné au créateur "JeanDupont"

-

Quand "JeanDupont" publie un nouveau contenu dans ma zone

-

Alors je ne reçois pas de notification push -Mais je vois un badge compteur in-app -Et le contenu apparaît dans ma file avec boost +30%

-
-

5. Nouveau contenu créateur suivi - Mode piéton

-

Étant donné que je suis à pied (vitesse <5 km/h) -Et que je suis abonné au créateur "JeanDupont" -Et que je suis situé en Île-de-France

-

Quand "JeanDupont" publie un contenu géolocalisé en Île-de-France

-

Alors je reçois une notification push:

-
-

6. Live créateur suivi - Mode voiture

-

Étant donné que je suis en voiture -Et que je suis abonné au créateur "RadioLive"

-

Quand "RadioLive" démarre un live dans ma zone

-

Alors je ne reçois pas de notification push -Mais je vois un badge compteur in-app -Et le live peut apparaître dans ma recommandation automatiquement

-
-

7. Live créateur suivi - Mode piéton

-

Étant donné que je suis à pied -Et que je suis abonné au créateur "RadioLive" -Et que je suis situé dans la zone du live

-

Quand "RadioLive" démarre un live

-

Alors je reçois une notification push:

-
-

8. Audio-guide disponible à proximité - Mode piéton

-

Étant donné que je suis à pied

-

Quand je passe à moins de 100m d'un lieu avec audio-guides

-

Alors je reçois une notification push:

-
-

9. Audio-guide disponible à proximité - Mode voiture

-

Étant donné que je suis en voiture

-

Quand je passe à moins de 100m d'un lieu avec audio-guides

-

Alors je reçois une notification audio (bip) -Et une annonce vocale: "Audio-guide disponible" -Mais pas de notification push (sécurité)

-
-

10. Filtrage géographique des notifications

-

Étant donné que je suis abonné au créateur "CreateurMarseille" -Et que je suis situé à Paris

-

Quand "CreateurMarseille" publie un contenu ancré à Marseille

-

Alors je ne reçois pas de notification -Et cela évite la frustration de contenus non écoutables

-
-

11. Contenu national notifie tous les abonnés

-

Étant donné que je suis abonné au créateur "MediaNational" -Et que je suis situé n'importe où en France

-

Quand "MediaNational" publie un contenu de type "National"

-

Alors je reçois une notification (si mode piéton)

-
-

12. Limite de 10 notifications push par jour

-

Étant donné que je suis abonné à 50 créateurs actifs -Et que j'ai déjà reçu 10 notifications push aujourd'hui

-

Quand un 11ème contenu est publié

-

Alors je ne reçois pas de notification push individuelle -Mais une notification groupée: "🎧 3 nouveaux contenus de créateurs suivis"

-
-

13. Paramétrage de la limite quotidienne

-

Étant donné que la limite par défaut est de 10 notifications/jour

-

Quand j'accède aux paramètres de notifications

-

Alors je peux modifier la limite entre 5 et 20 -Et si je choisis 15, je recevrai jusqu'à 15 notifications/jour

-
-

14. Mode silencieux nocturne par défaut

-

Étant donné que le mode silencieux est activé de 22h à 8h par défaut -Et qu'il est 23h30

-

Quand un créateur suivi publie un contenu

-

Alors je ne reçois pas de notification push -Mais les notifications sont empilées -Et je les vois le lendemain matin à 8h01

-
-

15. Exception du mode silencieux pour les lives

-

Étant donné que le mode silencieux est activé (22h-8h) -Et qu'il est 23h00 -Et que j'ai activé "Notifications importantes uniquement" (lives uniquement)

-

Quand un créateur suivi démarre un live

-

Alors je reçois quand même la notification push du live

-
-

16. Désactivation complète des notifications

-

Étant donné que j'accède aux paramètres de notifications

-

Quand je désactive toutes les notifications

-

Alors je ne reçois plus aucune notification push -Et les badges in-app sont également désactivés -Et seule la recommandation algorithmique reste active

-
-

17. Notification "Nouveaux contenus" activée par défaut

-

Étant donné que je crée un nouveau compte -Et que je m'abonne à mon premier créateur

-

Quand je consulte les préférences de notifications

-

Alors "Nouveaux contenus" est activé par défaut -Et "Lives" est activé par défaut -Et "Audio-guides proximité" est activé par défaut

-
-

18. Désactivation sélective par type de notification

-

Étant donné que j'ai activé toutes les notifications

-

Quand je désactive uniquement "Nouveaux contenus"

-

Alors je ne reçois plus de notifications pour nouveaux contenus -Mais je reçois toujours les notifications de lives -Et les notifications d'audio-guides restent actives

-
-

19. Notification groupée après limite dépassée

-

Étant donné que j'ai reçu 10 notifications push aujourd'hui -Et que 5 nouveaux contenus sont publiés dans l'heure suivante

-

Quand la 11ème notification devrait être envoyée

-

Alors les 5 contenus sont regroupés en une seule notification:

-
-

20. Détail de la notification groupée

-

Étant donné que j'ai reçu une notification groupée "3 nouveaux contenus"

-

Quand je tape sur la notification

-

Alors l'app s'ouvre sur une liste des 3 contenus:

-
| créateur | titre |
-|---|---|
-| JeanDupont | "Actualité du jour" |
-| MarieDurand | "Podcast économie" |
-| PaulMartin | "Anecdote historique" |
-
-

Et je peux choisir lequel écouter en premier

-
-

21. Personnalisation des plages horaires du mode silencieux

-

Étant donné que le mode silencieux est 22h-8h par défaut

-

Quand j'accède aux paramètres

-

Alors je peux modifier les heures: par exemple 23h-7h -Et le mode silencieux s'applique dans la nouvelle plage horaire

-
-

22. Format notification nouveau contenu complet

-

Étant donné que je suis à pied -Et qu'un créateur suivi publie un contenu

-

Quand je reçois la notification push

-

Alors elle contient:

-
| élément | exemple |
-|---|---|
-| Emoji | 🎧 |
-| Créateur | JeanDupont |
-| Action | a publié |
-| Titre | "Les secrets du Louvre" |
-| CTA | Tap pour écouter |
-
-
-

23. Format notification live complet

-

Étant donné que je suis à pied -Et qu'un créateur suivi démarre un live

-

Quand je reçois la notification push

-

Alors elle contient:

-
| élément | exemple |
-|---|---|
-| Emoji | 🔴 |
-| Créateur | RadioLive |
-| Action | est en direct |
-| Titre | "Débat politique ce soir" |
-| CTA | Tap pour rejoindre |
-
-
-

24. Notification disparaît si contenu supprimé

-

Étant donné que j'ai reçu une notification pour un contenu -Et que je n'ai pas encore tapé dessus

-

Quand le créateur supprime le contenu

-

Alors la notification est automatiquement retirée de mon centre de notifications -Et si je tape dessus par erreur, je vois "Contenu non disponible"

-
-

25. Badge compteur in-app en mode voiture

-

Étant donné que je suis en voiture -Et que 5 créateurs suivis publient des contenus

-

Quand j'ouvre l'application

-

Alors je vois un badge "5" sur l'onglet "Nouveautés" -Et en consultant l'onglet, je vois les 5 nouveaux contenus -Et le badge disparaît après consultation

-
-

26. Coût des notifications push Firebase

-

Étant donné que je reçois 10 notifications push par jour -Et que je suis actif 365 jours par an

-

Quand le système calcule le coût

-

Alors 3650 notifications/an sont envoyées -Et Firebase Cloud Messaging est gratuit jusqu'à plusieurs millions de notifications -Et le coût reste 0€ pour le volume MVP/Growth

-
- -

Étant donné que je reçois une notification push pour un contenu

-

Quand je tape sur la notification

-

Alors l'app s'ouvre directement sur le contenu -Et la lecture démarre automatiquement (si j'étais à pied)

-
-

28. Notification refusée si permissions désactivées au niveau OS

-

Étant donné que j'ai désactivé les notifications dans les paramètres iOS/Android

-

Quand un créateur suivi publie un contenu

-

Alors aucune notification push n'est envoyée -Et l'app propose de réactiver les permissions dans les paramètres -Mais les badges in-app continuent de fonctionner

-
-
- -

Création d'audio-guide multi-séquences

-
-

En tant que créateur de contenu -Je veux créer des audio-guides avec plusieurs séquences géolocalisées -Afin d'offrir des expériences guidées adaptées aux différents modes de déplacement

-
-

35 scénarios (32 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que le créateur "guide@example.com" est connecté -Et que son compte est vérifié

-
-

1. 📋 Plan: Détection automatique du mode selon la vitesse

-

Étant donné que l'utilisateur se déplace à km/h

-

Quand la vitesse est calculée sur 30 secondes

-

Alors le mode est suggéré automatiquement

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
vitessemode
3Piéton
15Vélo
35Voiture
50Voiture
-
-

2. Suggestion de mode au démarrage avec confirmation

-

Étant donné qu'un audio-guide "Safari du Paugre" est disponible -Et que l'utilisateur se déplace à 35 km/h

-

Quand l'audio-guide démarre

-

Alors une popup s'affiche:

-
-

3. Changement manuel du mode détecté

-

Étant donné que le mode "Voiture" est suggéré automatiquement

-

Quand l'utilisateur clique sur "Changer"

-

Alors les 4 modes sont proposés:

-
| mode | emoji |
-|---|---|
-| Piéton | 🚶 |
-| Voiture | 🚗 |
-| Vélo | 🚴 |
-| Transport | 🚌 |
-
-
-

4. 📋 Plan: Caractéristiques par mode de déplacement

-

Étant donné un audio-guide configuré en mode

-

Alors les paramètres suivants sont appliqués:

-
| paramètre | valeur |
-|---|---|
-| Vitesse détection | <vitesse_detection> |
-| Déclenchement | <declenchement> |
-
-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
modevitesse_detectiondeclenchement
Piéton<5 km/hManuel (bouton Suivant)
Voiture>10 km/hAuto GPS + Manuel
Vélo5-25 km/hAuto GPS + Manuel
TransportVariableAuto GPS + Manuel
-
-

5. Accès au formulaire de création d'audio-guide

-

Étant donné que le créateur est sur son dashboard

-

Quand il clique sur "Créer un audio-guide"

-

Alors le formulaire de création s'affiche -Et le wizard guidé en 4 étapes est visible:

-
| étape | description |
-|---|---|
-| 1 | Infos générales |
-| 2 | Ajout séquences |
-| 3 | Preview carte |
-| 4 | Validation modération |
-
-
-

6. Étape 1 - Informations générales obligatoires

-

Étant donné que le créateur est sur l'étape 1 du wizard

-

Quand il complète le formulaire

-

Alors les champs suivants sont obligatoires:

-
| champ | contrainte |
-|---|---|
-| Titre | 5-100 caractères |
-| Description | 10-500 caractères |
-| Mode de déplacement | Choix parmi 4 |
-| Tags | 1-3 tags |
-| Classification âge | Tout public/13+/16+/18+ |
-
-
-

7. Sélection du mode de déplacement

-

Étant donné que le créateur crée un audio-guide

-

Quand il sélectionne le mode "🚗 Voiture (GPS auto + manuel)"

-

Alors le champ "Vitesse recommandée" s'affiche -Et la plage suggérée est "30-50 km/h"

-
-

8. Validation du titre

-

Étant donné que le créateur entre un titre

-

Quand le titre contient moins de 5 caractères

-

Alors un message d'erreur "Minimum 5 caractères" s'affiche -Et le bouton "Suivant" est désactivé

-
-

9. Validation de la description

-

Étant donné que le créateur entre une description

-

Quand la description contient 520 caractères

-

Alors un message d'erreur "Maximum 500 caractères" s'affiche -Et les 20 caractères en trop sont surlignés en rouge

-
-

10. Étape 2 - Ajout de la première séquence

-

Étant donné que le créateur est sur l'étape 2 "Ajout séquences"

-

Quand il clique sur "Ajouter séquence"

-

Alors le formulaire de séquence s'affiche avec:

-
| champ | requis | note |
-|---|---|---|
-| Titre séquence | ✅ | 5-80 caractères |
-| Audio | ✅ | Upload MP3/AAC, max 200 MB |
-| Point GPS | ✅* | *Sauf mode piéton |
-| Rayon déclenchement | ✅* | *Sauf mode piéton, 10-200m |
-
-
-

11. Ajout du point GPS pour une séquence

-

Étant donné que le créateur ajoute une séquence en mode "Voiture"

-

Quand il clique sur "📍 Ajouter point GPS"

-

Alors une carte s'affiche -Et il peut:

-
| action |
-|---|
-| Cliquer sur la carte |
-| Entrer coordonnées manuelles |
-| Utiliser sa position actuelle |
-
-
-

12. Configuration du rayon de déclenchement avec preview

-

Étant donné qu'un point GPS est défini à (43.1234, 2.5678)

-

Quand le créateur ajuste le curseur de rayon

-

Alors le rayon varie de 10m à 200m -Et un cercle visuel est affiché sur la carte -Et la valeur actuelle s'affiche "30m"

-
-

13. 📋 Plan: Rayon par défaut selon le mode

-

Étant donné un audio-guide en mode

-

Quand le créateur ajoute un point GPS

-

Alors le rayon par défaut est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
moderayon_defaut
Voiture30m
Vélo50m
Transport100m
-
-

14. Suggestion intelligente du rayon

-

Étant donné un audio-guide en mode "Voiture" avec vitesse recommandée 30 km/h

-

Quand le créateur ajoute un point GPS

-

Alors une suggestion s'affiche: "Recommandé : 30m pour voiture à 30 km/h"

-
-

15. Upload audio pour une séquence

-

Étant donné que le créateur crée une séquence "Introduction"

-

Quand il upload un fichier audio de 5 MB

-

Alors le fichier est vérifié:

-
| vérification | règle |
-|---|---|
-| Format | MP3, AAC, M4A |
-| Taille max | 200 MB |
-| Durée max | 15 minutes |
-
-
-

16. Ordre des séquences modifiable

-

Étant donné un audio-guide avec 5 séquences:

-
| ordre | titre |
-|---|---|
-| 1 | Introduction |
-| 2 | Les lions |
-| 3 | Les girafes |
-| 4 | Les éléphants |
-| 5 | Conclusion |
-
-

Quand le créateur glisse "Les éléphants" en position 2

-

Alors l'ordre devient:

-
| ordre | titre |
-|---|---|
-| 1 | Introduction |
-| 2 | Les éléphants |
-| 3 | Les lions |
-| 4 | Les girafes |
-| 5 | Conclusion |
-
-
-

17. Nombre minimum de séquences requis

-

Étant donné un audio-guide avec seulement 1 séquence

-

Quand le créateur tente de passer à l'étape suivante

-

Alors un message d'erreur s'affiche: "Minimum 2 séquences requis" -Et le bouton "Suivant" est désactivé

-
-

18. Nombre maximum de séquences

-

Étant donné un audio-guide avec 50 séquences

-

Quand le créateur tente d'ajouter une 51ème séquence

-

Alors un message d'erreur s'affiche: "Maximum 50 séquences par audio-guide" -Et le bouton "+ Ajouter séquence" est désactivé

-
-

19. Étape 3 - Preview carte avec tracé et points

-

Étant donné un audio-guide avec 5 séquences géolocalisées

-

Quand le créateur accède à l'étape 3 "Preview carte"

-

Alors une carte Leaflet s'affiche -Et les éléments suivants sont visibles:

-
| élément | description |
-|---|---|
-| Markers numérotés | 1, 2, 3, 4, 5 sur chaque point |
-| Tracé entre points | Ligne pointillée connectant les points |
-| Cercles de déclenchement | Rayon visuel autour de chaque point |
-
-
-

20. Statistiques du parcours

-

Étant donné un audio-guide avec les séquences suivantes:

-
| séquence | durée | distance_au_suivant |
-|---|---|---|
-| 1 | 2:15 | 150m |
-| 2 | 3:42 | 200m |
-| 3 | 4:10 | 320m |
-
-

Quand les statistiques sont calculées

-

Alors le résumé suivant est affiché:

-
| métrique | valeur |
-|---|---|
-| Séquences | 3 complètes |
-| Durée totale | 10:07 |
-| Distance totale | 670m |
-
-
-

21. Modification d'une séquence depuis la carte

-

Étant donné que la preview carte est affichée

-

Quand le créateur clique sur le marker "2"

-

Alors une popup s'affiche avec:

-
| information |
-|---|
-| Titre: "Les lions" |
-| Durée: 3:42 |
-| Rayon: 30m |
-| [✏️ Modifier] |
-| [🗑️ Supprimer] |
-
-
-

22. Zone de diffusion géographique

-

Étant donné un audio-guide avec des points dans Paris

-

Quand le créateur définit la zone de diffusion

-

Alors il peut choisir:

-
| type | exemple |
-|---|---|
-| Polygon | Tracé manuel sur carte |
-| Ville | Paris (API Nominatim) |
-| Département | 75 - Paris |
-| Région | Île-de-France |
-
-
-

23. Étape 4 - Publication et validation modération

-

Étant donné un créateur qui publie ses 3 premiers audio-guides

-

Quand il clique sur "✅ Publier audio-guide"

-

Alors un message s'affiche:

-
-

24. Publication directe pour créateurs expérimentés

-

Étant donné un créateur ayant publié 5 audio-guides validés -Et aucun strike actif

-

Quand il publie un nouvel audio-guide

-

Alors l'audio-guide est publié immédiatement -Et il devient visible pour les utilisateurs -Et aucune validation manuelle n'est requise

-
-

25. Mode piéton sans points GPS obligatoires

-

Étant donné un audio-guide en mode "🚶 Piéton"

-

Quand le créateur ajoute une séquence

-

Alors le champ "Point GPS" est optionnel -Et le champ "Rayon déclenchement" est masqué -Et un message info s'affiche: "Mode manuel : les séquences se déclenchent au clic utilisateur"

-
-

26. Sauvegarde brouillon automatique

-

Étant donné que le créateur édite un audio-guide depuis 5 minutes

-

Quand il ajoute une nouvelle séquence

-

Alors l'audio-guide est sauvegardé en brouillon automatiquement -Et un toast "Brouillon sauvegardé" s'affiche brièvement

-
-

27. Reprise d'un brouillon

-

Étant donné un audio-guide en brouillon "Safari du Paugre" -Et qu'il contient 3 séquences complètes

-

Quand le créateur retourne sur son dashboard

-

Alors le brouillon est visible avec le statut "📝 Brouillon" -Et un bouton "Continuer" est disponible -Et la progression "3/5 séquences" est affichée

-
-

28. Suppression d'un brouillon

-

Étant donné un audio-guide en brouillon

-

Quand le créateur clique sur "🗑️ Supprimer"

-

Alors une confirmation s'affiche:

-
-

29. Modification d'un audio-guide publié

-

Étant donné un audio-guide publié "Safari du Paugre"

-

Quand le créateur clique sur "✏️ Modifier"

-

Alors il peut modifier:

-
| élément modifiable | élément non modifiable |
-|---|---|
-| Titre | Mode de déplacement |
-| Description | Points GPS |
-| Tags | Rayons déclenchement |
-| Séquences (ordre) |  |
-
-

Et un avertissement s'affiche: "Les modifications structurelles nécessitent une nouvelle publication"

-
-

30. Duplication d'un audio-guide existant

-

Étant donné un audio-guide publié "Visite Paris"

-

Quand le créateur clique sur "📋 Dupliquer"

-

Alors une copie est créée avec le titre "Visite Paris (copie)" -Et toutes les séquences sont copiées -Et le statut est "📝 Brouillon" -Et le créateur peut modifier avant publication

-
-

31. Upload audio échoue (format non supporté)

-

Étant donné que le créateur upload un fichier "audio.wav"

-

Quand le format est vérifié

-

Alors un message d'erreur s'affiche: "Format non supporté. Utilisez MP3, AAC ou M4A" -Et le fichier est rejeté

-
-

32. Upload audio échoue (taille trop grande)

-

Étant donné que le créateur upload un fichier de 250 MB

-

Quand la taille est vérifiée

-

Alors un message d'erreur s'affiche: "Fichier trop volumineux. Maximum 200 MB" -Et le fichier est rejeté

-
-

33. Points GPS trop éloignés (alerte cohérence)

-

Étant donné un audio-guide en mode "Piéton" -Et une séquence au Louvre (Paris)

-

Quand le créateur ajoute une séquence à Lyon

-

Alors un avertissement s'affiche:

-
-

34. Pas de connexion lors de la sauvegarde

-

Étant donné que le créateur édite un audio-guide -Et que la connexion réseau est perdue

-

Quand il tente de sauvegarder

-

Alors le brouillon est sauvegardé localement -Et un message s'affiche: "Sauvegarde locale. Sera synchronisée à la reconnexion" -Et une icône "☁️ Hors ligne" s'affiche

-
-

35. Reprise après perte de connexion

-

Étant donné un brouillon sauvegardé localement

-

Quand la connexion réseau est rétablie

-

Alors le brouillon est synchronisé automatiquement -Et un toast "✅ Audio-guide synchronisé" s'affiche

-
-
- -

Intégration audio-guides avec autres fonctionnalités

-
-

En tant qu'utilisateur -Je veux utiliser les audio-guides avec toutes les fonctionnalités de l'app -Afin d'avoir une expérience complète et cohérente

-
-

39 scénarios (38 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté

-
-

1. Téléchargement complet d'un audio-guide

-

Étant donné un audio-guide "Visite du Louvre" avec 12 séquences

-

Quand l'utilisateur clique sur "⬇️ Télécharger pour écouter hors ligne"

-

Alors toutes les 12 séquences sont téléchargées -Et les métadonnées (titres, descriptions, GPS) sont sauvegardées -Et les images (cover, miniatures) sont mises en cache

-
-

2. Affichage de la progression du téléchargement

-

Étant donné qu'un téléchargement d'audio-guide est en cours

-

Quand l'utilisateur consulte l'état

-

Alors la progression s'affiche:

-
-

3. Téléchargement uniquement en WiFi (par défaut)

-

Étant donné que l'option "Télécharger uniquement en WiFi" est activée

-

Quand l'utilisateur lance un téléchargement sur réseau mobile

-

Alors un avertissement s'affiche:

-
-

4. Gestion de l'espace de stockage

-

Étant donné que l'appareil a 500 MB d'espace libre -Et qu'un audio-guide pèse 380 MB

-

Quand l'utilisateur lance le téléchargement

-

Alors un avertissement s'affiche:

-
-

5. Liste des audio-guides téléchargés

-

Étant donné que l'utilisateur a téléchargé 3 audio-guides

-

Quand il accède à "Bibliothèque > Téléchargés"

-

Alors il voit:

-
| audio_guide | taille | date_telechargement |
-|---|---|---|
-| Visite du Louvre | 380 MB | 2026-01-20 |
-| Safari du Paugre | 245 MB | 2026-01-18 |
-| Circuit Loire à Vélo | 520 MB | 2026-01-15 |
-
-
-

6. Lecture hors connexion complète

-

Étant donné qu'un audio-guide est téléchargé -Et que l'utilisateur active le mode avion

-

Quand il lance l'audio-guide

-

Alors toutes les séquences sont lisibles -Et les métadonnées sont accessibles -Et les images s'affichent normalement -Et la progression est sauvegardée localement

-
-

7. GPS fonctionne en mode avion (mode voiture)

-

Étant donné qu'un audio-guide voiture est téléchargé -Et que le mode avion est activé (avec GPS actif)

-

Quand l'utilisateur se déplace

-

Alors les déclenchements GPS fonctionnent normalement -Et la distance/ETA sont calculés

-
-

8. Suppression d'audio-guide téléchargé

-

Étant donné qu'un audio-guide téléchargé pèse 380 MB

-

Quand l'utilisateur clique sur "🗑️ Supprimer téléchargement"

-

Alors une confirmation s'affiche -Et si confirmé, les 380 MB sont libérés -Et l'audio-guide reste accessible en streaming

-
-

9. Mise à jour automatique si nouvelle version

-

Étant donné qu'un audio-guide téléchargé a été mis à jour par le créateur

-

Quand l'utilisateur se connecte en WiFi

-

Alors une notification s'affiche:

-
-

10. Ajout d'audio-guide à une playlist

-

Étant donné que l'utilisateur consulte un audio-guide

-

Quand il clique sur "➕ Ajouter à une playlist"

-

Alors ses playlists s'affichent:

-
| playlist |
-|---|
-| 🗺️ Voyages en France |
-| 🏛️ Musées parisiens |
-| + Créer nouvelle playlist |
-
-
-

11. Comportement audio-guide dans une playlist

-

Étant donné une playlist contenant 2 audio-guides et 1 podcast

-

Quand la lecture atteint un audio-guide

-

Alors l'audio-guide démarre à la séquence 1 (ou progression sauvegardée) -Et les séquences se jouent normalement

-

Quand l'audio-guide se termine (dernière séquence)

-

Alors le contenu suivant de la playlist démarre

-
-

12. Audio-guide marqué comme "Favori"

-

Étant donné qu'un utilisateur aime un audio-guide

-

Quand il clique sur "⭐ Ajouter aux favoris"

-

Alors l'audio-guide est ajouté à la section "Favoris" -Et il est facilement accessible depuis le menu principal

-
-

13. Collections thématiques d'audio-guides

-

Étant donné que RoadWave propose des collections éditoriales

-

Quand l'utilisateur accède à "Collections"

-

Alors il voit des collections comme:

-
| collection | nombre_audio_guides |
-|---|---|
-| 🏛️ Musées de France | 12 |
-| 🦁 Parcs animaliers | 8 |
-| 🚴 Circuits vélo | 15 |
-| 🚗 Routes touristiques | 10 |
-
-
-

14. Bouton partager sur page audio-guide

-

Étant donné qu'un utilisateur consulte un audio-guide

-

Quand il clique sur "⬆️ Partager"

-

Alors le menu de partage natif s'ouvre -Et le lien généré est "https://roadwave.fr/share/ag/louvre_123"

-
-

15. Page web de partage pour audio-guide

-

Étant donné qu'un lien d'audio-guide partagé est ouvert sur le web

-

Quand la page se charge

-

Alors elle affiche:

-
| élément | exemple |
-|---|---|
-| Cover image 16:9 | Photo du Louvre |
-| Titre | "Visite du Louvre" |
-| Créateur | "@art_guide ✓" |
-| Badge type | "🎧 Audio-guide • 12 séquences" |
-| Durée totale | "45 minutes" |
-| Mode | "🚶 Piéton" |
-| Description | Texte complet |
-| Preview séquence 1 | Player HTML5 (séquence intro) |
-| Carte avec points GPS | Leaflet avec 12 markers |
-| CTA téléchargement | Boutons App Store / Google Play |
-
-
- -

Étant donné que l'app est installée -Et qu'un lien "https://roadwave.fr/share/ag/louvre_123" est cliqué

-

Quand le système détecte l'app

-

Alors l'app s'ouvre directement sur l'audio-guide -Et l'utilisateur peut démarrer immédiatement

-
-

17. Partage avec séquence spécifique

-

Étant donné qu'un utilisateur est sur la séquence 5 "La Joconde"

-

Quand il partage l'audio-guide

-

Alors le lien généré est "https://roadwave.fr/share/ag/louvre_123?seq=5" -Et le destinataire est dirigé vers la séquence 5 directement

-
-

18. Note globale de l'audio-guide

-

Étant donné qu'un utilisateur termine un audio-guide

-

Quand la dernière séquence se termine

-

Alors une popup de notation s'affiche:

-
-

19. Note moyenne affichée sur la page

-

Étant donné qu'un audio-guide a reçu 150 notes -Et que la moyenne est 4.3/5

-

Quand la page est affichée

-

Alors la note "⭐ 4.3 (150 avis)" est visible

-
-

20. Commentaires triés par pertinence

-

Étant donné qu'un audio-guide a 50 commentaires

-

Quand l'utilisateur consulte les avis

-

Alors les commentaires sont triés par défaut selon:

-
| critère | poids |
-|---|---|
-| Note élevée | 30% |
-| Récent | 30% |
-| Likes reçus | 40% |
-
-
-

21. Réponse du créateur aux commentaires

-

Étant donné qu'un utilisateur laisse un commentaire négatif

-

Quand le créateur consulte son dashboard

-

Alors il peut répondre au commentaire -Et sa réponse apparaît en dessous avec badge "Créateur"

-
-

22. Audio-guides similaires recommandés

-

Étant donné qu'un utilisateur termine "Visite du Louvre"

-

Quand il consulte les recommandations

-

Alors l'algorithme suggère des audio-guides basés sur:

-
| critère | exemple |
-|---|---|
-| Tags similaires | #Art #Histoire #Musée |
-| Créateur identique | Autres audio-guides de @art_guide |
-| Localisation proche | Autres musées parisiens |
-| Mode de déplacement | Autres audio-guides piéton |
-
-
-

23. Suggestion géographique contextuelle

-

Étant donné qu'un utilisateur est à Paris (GPS détecté)

-

Quand il ouvre l'onglet "Audio-guides"

-

Alors les audio-guides parisiens sont mis en avant -Et un filtre "🗺️ Autour de moi" est pré-appliqué

-
-

24. Badge "Populaire dans votre région"

-

Étant donné qu'un audio-guide a >100 écoutes dans la région Île-de-France -Et que l'utilisateur est en Île-de-France

-

Quand l'audio-guide est affiché

-

Alors un badge "🔥 Populaire près de chez vous" est visible

-
-

25. Préchargement de la séquence suivante

-

Étant donné que la séquence 3 est en cours à 2:30/3:42

-

Quand il reste 60 secondes de lecture

-

Alors la séquence 4 est préchargée en arrière-plan -Et la transition est instantanée (0 latence)

-
-

26. Buffer adaptatif selon connexion

-

Étant donné qu'un utilisateur est sur réseau 4G

-

Quand la séquence démarre

-

Alors 30 secondes d'audio sont bufferisées initialement -Et le buffering continue en arrière-plan

-
-

27. 📋 Plan: Buffer selon qualité réseau

-

Étant donné qu'un utilisateur est sur réseau

-

Quand une séquence démarre

-

Alors secondes sont bufferisées

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
reseaubuffer_secondes
WiFi60
5G45
4G30
3G20
-
-

28. Compression audio adaptative

-

Étant donné qu'un utilisateur est sur connexion lente (3G)

-

Quand une séquence est streamée

-

Alors le CDN sert la version 64 kbps (au lieu de 128 kbps) -Et la qualité reste acceptable pour la voix

-
-

29. Cache intelligent des séquences jouées

-

Étant donné qu'un utilisateur a écouté les séquences 1-5

-

Quand il clique sur "Précédent" pour réécouter la séquence 4

-

Alors la séquence 4 est chargée depuis le cache local -Et le chargement est instantané (pas de stream)

-
-

30. Nettoyage automatique du cache

-

Étant donné que le cache audio occupe 500 MB -Et que la limite configurée est 300 MB

-

Quand le nettoyage automatique s'exécute

-

Alors les séquences les plus anciennes (non téléchargées) sont supprimées -Et le cache revient à 280 MB

-
-

31. Tracking des événements clés

-

Étant donné qu'un utilisateur écoute un audio-guide

-

Quand il interagit avec l'application

-

Alors les événements suivants sont trackés:

-
| événement | données |
-|---|---|
-| audio_guide_started | audio_guide_id, mode, user_id |
-| sequence_completed | sequence_id, completion_rate, duration |
-| audio_guide_completed | audio_guide_id, total_time, sequences_count |
-| point_gps_triggered | point_id, distance, auto_or_manual |
-| point_gps_missed | point_id, distance, action_taken |
-| paywall_displayed | audio_guide_id, sequence_number |
-| premium_conversion | source: audio_guide_paywall |
-
-
-

32. Heatmap des abandons par séquence

-

Étant donné qu'un audio-guide a été écouté 1000 fois

-

Quand le créateur consulte la heatmap

-

Alors il voit pour chaque séquence:

-
| sequence | starts | completions | abandon_rate |
-|---|---|---|---|
-| 1 | 1000 | 950 | 5% |
-| 2 | 950 | 920 | 3% |
-| 3 | 920 | 850 | 8% |
-| ... | ... | ... | ... |
-| 12 | 650 | 580 | 11% |
-
-
-

33. Attribution GPS auto vs manuel

-

Étant donné un audio-guide voiture avec 8 points GPS

-

Quand les statistiques sont calculées

-

Alors le créateur voit:

-
| mode_declenchement | nombre |
-|---|---|
-| GPS automatique | 542 |
-| Manuel | 123 |
-| Point manqué | 89 |
-
-
-

34. Audio-guide avec une seule séquence (edge case)

-

Étant donné un audio-guide avec seulement 1 séquence

-

Quand il est publié

-

Alors un avertissement s'affiche:

-
-

35. Séquence manquante ou corrompue

-

Étant donné qu'une séquence 5 a un fichier audio corrompu

-

Quand l'utilisateur tente de la lire

-

Alors un message d'erreur s'affiche -Et un bouton "⏭️ Passer à la suivante" est disponible -Et le créateur reçoit une notification de l'erreur

-
-

36. GPS désactivé puis réactivé en cours de route

-

Étant donné un audio-guide voiture en cours -Et que l'utilisateur désactive le GPS

-

Quand il le réactive 10 minutes plus tard

-

Alors le déclenchement automatique reprend -Et les points GPS manqués entre-temps ne déclenchent pas de popup

-
-

37. Modification d'audio-guide avec utilisateurs en cours

-

Étant donné qu'un audio-guide a 50 utilisateurs en cours d'écoute

-

Quand le créateur modifie une séquence

-

Alors les utilisateurs actuels conservent l'ancienne version -Et les nouveaux utilisateurs obtiennent la nouvelle version -Et un message informe les utilisateurs lors de la prochaine ouverture

-
-

38. Suppression d'audio-guide par le créateur

-

Étant donné qu'un audio-guide a 20 utilisateurs avec progression

-

Quand le créateur supprime l'audio-guide

-

Alors une confirmation stricte est demandée -Et si confirmé, les progressions utilisateurs sont archivées (30 jours) -Et l'audio-guide devient inaccessible

-
-

39. Signalement d'audio-guide pour contenu inapproprié

-

Étant donné qu'un utilisateur signale un audio-guide

-

Quand le signalement est modéré -Et jugé valide

-

Alors l'audio-guide est dépublié temporairement -Et le créateur reçoit une notification d'explication -Et il peut corriger puis republier

-
-
- -

Audio-guide mode piéton (navigation manuelle)

-
-

En tant qu'utilisateur à pied -Je veux naviguer manuellement entre les séquences d'un audio-guide -Afin de contrôler mon rythme de visite

-
-

29 scénarios (28 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté (gratuit) -Et qu'un audio-guide piéton "Visite du Louvre" est disponible avec 12 séquences

-
-

1. Fin de séquence normale avec pause automatique

-

Étant donné que la séquence 1 "Introduction" est en cours de lecture

-

Quand la séquence se termine à 2:15

-

Alors le player se met en pause automatiquement -Et le message suivant s'affiche: "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt." -Et la barre de progression indique "1/12 complétée"

-
-

2. Passage manuel à la séquence suivante

-

Étant donné que la séquence 1 est terminée et le player en pause

-

Quand l'utilisateur appuie sur le bouton [▶|] "Suivant"

-

Alors la séquence 2 "Pyramide du Louvre" démarre immédiatement -Et aucune latence n'est observée

-
-

3. Séquence avec publicité (1/5 séquences)

-

Étant donné que la séquence 5 se termine -Et que c'est la 5ème séquence (1 pub toutes les 5)

-

Quand la séquence se termine

-

Alors la publicité s'enchaîne automatiquement (sans attente bouton) -Et la publicité se lit normalement -Et elle est skippable après 5 secondes

-
-

4. Fin de publicité avec pause automatique

-

Étant donné qu'une publicité est en cours de lecture

-

Quand la publicité se termine

-

Alors le player se met en pause automatiquement -Et le message suivant s'affiche: "Séquence 6 prête. Appuyez sur Suivant." -Et l'utilisateur doit cliquer sur [▶|] pour continuer

-
-

5. Flux complet séquence → pub → séquence

-

Étant donné que la séquence 5 démarre

-

Quand la séquence 5 se termine

-

Alors la publicité démarre automatiquement

-

Quand la publicité se termine

-

Alors le player se met en pause

-

Quand l'utilisateur clique sur [▶|]

-

Alors la séquence 6 démarre

-
-

6. 📋 Plan: Fréquence de publicité configurable

-

Étant donné que l'utilisateur gratuit écoute un audio-guide -Et que la fréquence pub est configurée à

-

Quand il termine la séquence

-

Alors une publicité est insérée :

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
frequencenumero_sequencepub_inseree
1/55Oui
1/510Oui
1/54Non
1/33Oui
1/36Oui
-
-

7. Utilisateur Premium sans publicités

-

Étant donné que l'utilisateur "premium@example.com" est abonné Premium -Et qu'il écoute un audio-guide piéton

-

Quand il termine la séquence 5

-

Alors aucune publicité n'est insérée -Et le player se met en pause immédiatement -Et le message "Séquence 6 prête. Appuyez sur Suivant." s'affiche

-
-

8. Boutons de contrôle disponibles en mode piéton

-

Étant donné qu'un audio-guide piéton est en lecture

-

Quand l'utilisateur consulte les contrôles

-

Alors les boutons suivants sont visibles:

-
| bouton | fonction |
-|---|---|
-| [▶\ | ] Suivant | Passe à la séquence suivante |
-| [\ | ◀] Précédent | Retour à la séquence précédente |
-| [⏸️] Pause | Pause temporaire |
-| [▶️] Play | Reprend la lecture |
-| [📋] Liste | Affiche toutes les séquences |
-
-
-

9. Passage à la séquence suivante pendant la lecture

-

Étant donné que la séquence 3 "La Joconde" est en cours à 1:42/3:42

-

Quand l'utilisateur clique sur [▶|] "Suivant"

-

Alors la séquence 4 "Vénus de Milo" démarre immédiatement -Et la séquence 3 n'est pas marquée comme écoutée (car <80%)

-
-

10. Retour à la séquence précédente (saut direct)

-

Étant donné que la séquence 5 est en cours de lecture

-

Quand l'utilisateur clique sur [|◀] "Précédent"

-

Alors la séquence 4 démarre depuis le début (0:00) -Et il n'y a pas de logique "replay si >10s" (contrairement au contenu classique)

-
-

11. Pause et reprise pendant une séquence

-

Étant donné que la séquence 2 est en cours à 1:15/1:48

-

Quand l'utilisateur clique sur [⏸️] "Pause"

-

Alors la lecture se met en pause -Et la position 1:15 est conservée

-

Quand l'utilisateur clique sur [▶️] "Play"

-

Alors la lecture reprend exactement à 1:15

-
-

12. Interface liste des séquences

-

Étant donné qu'un audio-guide de 12 séquences est en cours

-

Quand l'utilisateur clique sur [📋] "Liste séquences"

-

Alors une liste complète s'affiche avec:

-
| élément | exemple |
-|---|---|
-| Numéro et titre | "3. La Joconde" |
-| Durée | (3:42) |
-| État | ✅ Écouté / ▶️ En cours / ⭕ À écouter |
-| Date écoute (si écouté) | "Écouté le 15/01/2026" |
-
-
-

13. Séquence en cours dans la liste

-

Étant donné que la séquence 3 est en cours à 1:22/3:42

-

Quand la liste des séquences est affichée

-

Alors la séquence 3 affiche:

-
-

14. Navigation libre vers séquence non encore écoutée

-

Étant donné que l'utilisateur est sur la séquence 3 -Et que les séquences 4 à 12 n'ont pas été écoutées

-

Quand l'utilisateur clique sur "8. Les Appartements de Napoléon"

-

Alors la séquence 8 démarre immédiatement depuis 0:00 -Et les séquences 4 à 7 restent marquées ⭕ "À écouter"

-
-

15. Retour à une séquence déjà écoutée

-

Étant donné que la séquence 2 "Pyramide du Louvre" a été écoutée à 100% -Et qu'elle est marquée ✅ "Écouté"

-

Quand l'utilisateur clique dessus dans la liste

-

Alors la séquence 2 démarre depuis 0:00 -Et le statut ✅ est conservé

-
-

16. Checkmarks sur séquences écoutées >80%

-

Étant donné que l'utilisateur écoute la séquence 2 de durée 1:48

-

Quand il écoute jusqu'à 1:30 (83% de complétion) -Et qu'il passe à la séquence suivante

-

Alors la séquence 2 est marquée ✅ "Écouté" -Et la date d'écoute est enregistrée

-
-

17. Pas de checkmark si séquence écoutée <80%

-

Étant donné que l'utilisateur écoute la séquence 3 de durée 3:42

-

Quand il écoute jusqu'à 1:30 (40% de complétion) -Et qu'il passe à la séquence suivante

-

Alors la séquence 3 reste marquée ⭕ "À écouter"

-
-

18. Bouton "Tout afficher" si plus de 6 séquences

-

Étant donné un audio-guide avec 12 séquences

-

Quand la liste est affichée

-

Alors seules les 6 premières séquences sont visibles initialement -Et un bouton "Tout afficher ▼" est présent

-

Quand l'utilisateur clique sur "Tout afficher ▼"

-

Alors les 6 séquences restantes sont affichées

-
-

19. Saut vers séquence spécifique depuis la barre de progression

-

Étant donné qu'un audio-guide est en cours

-

Quand l'utilisateur clique sur "3/12" dans la barre de progression

-

Alors la liste des séquences s'ouvre -Et la séquence en cours (3) est mise en surbrillance

-
-

20. Position exacte sauvegardée automatiquement

-

Étant donné que la séquence 5 est en cours à 2:34/4:10

-

Quand l'utilisateur quitte l'application

-

Alors la position 2:34 dans la séquence 5 est sauvegardée -Et la sauvegarde est effectuée localement (SQLite) -Et la sauvegarde est synchronisée sur le cloud (PostgreSQL)

-
-

21. Reprise après fermeture de l'application

-

Étant donné que l'utilisateur a quitté l'app à la séquence 5 position 2:34

-

Quand il rouvre l'audio-guide

-

Alors une popup de reprise s'affiche

-

Quand il clique sur "▶️ Reprendre"

-

Alors la lecture reprend à la séquence 5 position 2:34 exacte

-
-

22. Visiteur qui connaît déjà certaines œuvres

-

Étant donné qu'un visiteur du Louvre démarre l'audio-guide -Et qu'il connaît déjà "La Joconde" (séquence 3)

-

Quand il arrive à la séquence 3 -Et qu'il clique sur [▶|] "Suivant" après 10 secondes

-

Alors la séquence 4 démarre immédiatement -Et la séquence 3 n'est pas marquée comme écoutée

-
-

23. Visiteur qui veut voir une œuvre éloignée

-

Étant donné qu'un visiteur est à la séquence 2 -Et qu'il aperçoit "La Victoire de Samothrace" (séquence 8) physiquement

-

Quand il ouvre la liste et clique sur la séquence 8

-

Alors la séquence 8 démarre immédiatement -Et il peut écouter la description même si les séquences 3-7 ne sont pas écoutées

-
-

24. Visiteur qui prend une pause café

-

Étant donné qu'un visiteur écoute la séquence 6

-

Quand il clique sur [⏸️] "Pause" -Et qu'il ferme l'application pendant 30 minutes -Quand il rouvre l'application

-

Alors la séquence 6 reprend à la position exacte où il s'était arrêté

-
-

25. Visiteur qui revient le lendemain

-

Étant donné qu'un visiteur a écouté les séquences 1-5 hier -Et qu'il revient au musée aujourd'hui

-

Quand il ouvre l'audio-guide

-

Alors une popup propose "▶️ Reprendre" (séquence 6) -Et les séquences 1-5 sont marquées ✅ "Écouté"

-
-

26. Séquence audio corrompue ou indisponible

-

Étant donné que la séquence 7 a un fichier audio corrompu

-

Quand l'utilisateur tente de la lire

-

Alors un message d'erreur s'affiche:

-
-

27. Perte de connexion pendant le chargement

-

Étant donné que l'utilisateur lance la séquence 4 -Et que la connexion réseau est perdue

-

Quand le chargement échoue

-

Alors un message s'affiche: "Connexion perdue. Vérifiez votre réseau." -Et un bouton "🔄 Réessayer" est disponible

-
-

28. Batterie faible en cours de visite

-

Étant donné que la batterie de l'appareil est à 5%

-

Quand l'utilisateur écoute une séquence

-

Alors une notification système s'affiche: "Batterie faible. Progression sauvegardée." -Et la position est sauvegardée localement toutes les 10 secondes

-
-

29. Mode piéton sans points GPS (pas d'alerte localisation)

-

Étant donné un audio-guide en mode piéton -Et que le GPS est désactivé

-

Quand l'utilisateur démarre l'audio-guide

-

Alors aucune alerte GPS ne s'affiche -Et l'audio-guide fonctionne normalement (navigation 100% manuelle)

-
-
- -

Audio-guide mode voiture (GPS automatique)

-
-

En tant qu'utilisateur en voiture -Je veux que les séquences se déclenchent automatiquement selon ma position GPS -Afin de profiter d'une expérience guidée hands-free

-
-

45 scénarios (40 standards, 5 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté (gratuit) -Et qu'un audio-guide voiture "Safari du Paugre" est disponible avec 8 séquences -Et que le GPS est activé

-
-

1. Distinction audio-guides vs contenus géolocalisés simples

-

Étant donné que l'utilisateur est en mode voiture

-

Quand il écoute un contenu géolocalisé simple (1 séquence unique)

-

Alors une notification avec compteur 7→1 est affichée 7s avant le point -Et il doit valider avec "Suivant" + décompte 5s -Et ce contenu compte 1/6 dans le quota horaire

-

Quand il démarre un audio-guide multi-séquences

-

Alors les séquences se déclenchent au point GPS exact (rayon 30m) -Et aucun compteur 7s n'est affiché (juste notification "Ding" + toast 2s) -Et l'audio-guide entier compte 1/6 dans le quota

-
-

2. Démarrage automatique au premier point GPS

-

Étant donné que l'utilisateur démarre l'audio-guide "Safari du Paugre" -Et que le point de départ est à (43.1234, 2.5678) avec rayon 30m

-

Quand l'utilisateur entre dans le rayon de 30m

-

Alors la séquence 1 "Introduction - Point d'accueil" démarre automatiquement -Et une notification sonore "Ding" est jouée (non intrusif) -Et un toast s'affiche brièvement pendant 2s: "Introduction - Point d'accueil" -Et aucun compteur 7→1 n'est affiché (contrairement aux contenus géolocalisés simples)

-
-

3. Déclenchement automatique séquence suivante

-

Étant donné que la séquence 1 est terminée -Et que l'utilisateur se déplace vers le point GPS 2 (43.1245, 2.5690)

-

Quand l'utilisateur entre dans le rayon de 30m du point 2

-

Alors la séquence 2 "Enclos des lions" démarre automatiquement -Et une notification "Ding" + toast "Enclos des lions" s'affiche

-
-

4. Navigation manuelle conservée (bouton Suivant actif)

-

Étant donné que la séquence 1 est en cours -Et que l'utilisateur est encore loin du point GPS 2 (distance 500m)

-

Quand l'utilisateur clique sur [▶|] "Suivant"

-

Alors la séquence 2 démarre immédiatement -Et aucune vérification GPS n'est effectuée

-
-

5. Navigation manuelle conservée (bouton Précédent actif)

-

Étant donné que la séquence 3 est en cours

-

Quand l'utilisateur clique sur [|◀] "Précédent"

-

Alors la séquence 2 démarre depuis le début -Et aucune vérification GPS n'est effectuée

-
-

6. Tous les boutons de contrôle restent actifs

-

Étant donné qu'un audio-guide voiture est en cours

-

Quand l'utilisateur consulte les contrôles

-

Alors les boutons suivants sont actifs:

-
| bouton | état | comportement |
-|---|---|---|
-| [▶\ | ] Suivant | ✅ | Passe séquence suivante immédiate |
-| [\ | ◀] Précédent | ✅ | Retour séquence précédente |
-| [⏸️] Pause | ✅ | Pause temporaire |
-| [📋] Liste | ✅ | Saut direct possible |
-
-
-

7. Use case - Embouteillage (séquence finie, point GPS loin)

-

Étant donné que la séquence 3 "Enclos des girafes" est terminée -Et que le point GPS 4 est à 2 km de distance (embouteillage)

-

Quand l'utilisateur clique manuellement sur [▶|] "Suivant"

-

Alors la séquence 4 démarre immédiatement -Et l'utilisateur peut continuer l'expérience sans attendre d'atteindre le point GPS

-
-

8. Use case - Route fermée (point GPS inaccessible)

-

Étant donné que le point GPS 5 est sur une route fermée -Et que l'utilisateur ne peut pas s'en approcher

-

Quand l'utilisateur clique sur [▶|] "Suivant"

-

Alors la séquence 5 démarre quand même -Et l'audio-guide continue normalement

-
-

9. Use case - Passager manipule l'application

-

Étant donné que l'utilisateur est passager (non conducteur) -Et que la vitesse du véhicule est 45 km/h

-

Quand le passager clique sur [▶|] "Suivant"

-

Alors la séquence suivante démarre -Et un avertissement s'affiche pendant 3 secondes

-
-

10. Avertissement sécurité si vitesse >10 km/h

-

Étant donné que la vitesse actuelle est 35 km/h

-

Quand l'utilisateur clique sur un bouton (Suivant ou Précédent)

-

Alors l'action est exécutée immédiatement (pas de blocage) -Et un toast s'affiche pendant 3 secondes:

-
-

11. 📋 Plan: Avertissement selon la vitesse

-

Étant donné que la vitesse actuelle est km/h

-

Quand l'utilisateur clique sur un bouton de navigation

-

Alors l'avertissement est affiché :

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vitesseavertissement
5Non
10Non
11Oui
35Oui
90Oui
-
-

12. Affichage entre deux séquences avec progress bar

-

Étant donné que la séquence 2 "Les lions" vient de se terminer -Et que le prochain point GPS 3 "Enclos des girafes" est à 500m

-

Quand l'interface bascule en mode "attente prochain point"

-

Alors l'écran affiche:

-
| élément | description |
-|---|---|
-| Statut séquence | "✅ Séquence 2/8 terminée" |
-| Nom séquence | "Les lions" |
-| Progress bar | Barre dynamique remplie selon distance (0%) |
-| Distance prochain point | "500 mètres" |
-| ETA | "≈ 1 minute 30" |
-| Direction | ↗️ |
-| Vitesse actuelle | "28 km/h" |
-| Bouton "Rejouer séq." | Permet de réécouter la séquence qui vient de finir |
-
-
-

13. Progress bar dynamique vers le prochain point

-

Étant donné que la distance initiale vers le prochain point était 500m -Et que la séquence précédente est terminée

-

Quand l'utilisateur se rapproche du prochain point -Et que la distance actuelle est 175m

-

Alors la progress bar affiche "65%" remplie -Et le calcul est: 100 - (175 / 500 * 100) = 65% -Et la barre se met à jour chaque seconde

-
-

14. Bouton "Rejouer séq." pour réécouter

-

Étant donné que la séquence 3 vient de se terminer -Et que l'interface "attente prochain point" est affichée

-

Quand l'utilisateur clique sur [▶️ Rejouer séq.]

-

Alors la séquence 3 redémarre depuis 0:00 -Et l'utilisateur peut la réécouter (utile si distraction)

-
-

15. Interface en conduite avec distance et ETA

-

Étant donné que la séquence 2 est en cours -Et que le prochain point GPS 3 "Enclos des girafes" est à 320m -Et que la vitesse actuelle est 28 km/h

-

Quand l'interface est affichée

-

Alors les informations suivantes sont visibles:

-
| information | valeur |
-|---|---|
-| Nom prochain point | "Enclos des girafes" |
-| Distance | "320 mètres" |
-| ETA | "≈ 40 secondes" |
-| Direction | ↗️ (flèche direction) |
-| Vitesse actuelle | "28 km/h" |
-| Vitesse recommandée | "20-30 km/h" |
-
-
-

16. Mise à jour de la distance en temps réel

-

Étant donné que la distance au prochain point est 500m

-

Quand 10 secondes s'écoulent et que l'utilisateur se rapproche

-

Alors la distance est mise à jour chaque seconde -Et la nouvelle distance "450m" s'affiche

-
-

17. Mise à jour de l'ETA en temps réel

-

Étant donné que l'ETA est "≈ 2 minutes" -Et que la vitesse est constante à 30 km/h

-

Quand l'utilisateur se rapproche du point

-

Alors l'ETA est recalculé chaque seconde -Et il diminue progressivement: "≈ 1 minute 50", "≈ 1 minute 40", etc.

-
-

18. 📋 Plan: Format d'affichage de la distance

-

Étant donné que la distance au prochain point est

-

Quand l'interface est mise à jour

-

Alors la distance affichée est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
distance_metresaffichage
5050 m
320320 m
980980 m
12001.2 km
54005.4 km
-
-

19. 📋 Plan: Format d'affichage de l'ETA

-

Étant donné que l'ETA calculé est secondes

-

Quand l'interface est mise à jour

-

Alors l'ETA affiché est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
secondesaffichage
30≈ 30 secondes
75≈ 1 minute
150≈ 2 minutes
400≈ 6 minutes
-
-

20. Calcul de la direction (flèche 8 directions)

-

Étant donné que la position actuelle est (43.1234, 2.5678) -Et que le prochain point est au nord-est (angle 45°)

-

Quand la direction est calculée

-

Alors la flèche "↗" est affichée

-
-

21. 📋 Plan: Flèches de direction selon l'angle

-

Étant donné que l'angle vers le prochain point est °

-

Quand la direction est calculée

-

Alors la flèche "" est affichée

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
anglefleche
0
45
90
135
180
225
270
315
-
-

22. Mise à jour de la direction toutes les 5 secondes

-

Étant donné que la direction actuelle est ↑ (nord) -Et que l'utilisateur tourne vers l'est

-

Quand 5 secondes s'écoulent

-

Alors la direction est recalculée -Et la nouvelle flèche ↗ (nord-est) s'affiche

-
-

23. Message "En attente de déplacement" si vitesse <5 km/h

-

Étant donné que la vitesse actuelle est 2 km/h (arrêté)

-

Quand l'ETA est calculé

-

Alors le message "En attente de déplacement" s'affiche -Et l'ETA n'est pas calculé (car vitesse insuffisante)

-
-

24. Simplicité de l'interface (pas de carte miniature)

-

Étant donné qu'un audio-guide voiture est en cours

-

Quand l'interface est affichée

-

Alors aucune carte miniature n'est présente -Et seuls les éléments essentiels sont affichés:

-
| élément |
-|---|
-| Distance |
-| ETA |
-| Direction (flèche) |
-| Vitesse |
-| Contrôles audio |
-
-
-

25. Rayon de déclenchement par défaut en mode voiture

-

Étant donné un audio-guide voiture

-

Quand un point GPS est défini

-

Alors le rayon de déclenchement est 30 mètres par défaut -Et le rayon de tolérance "point manqué" est 100 mètres

-
-

26. Déclenchement dans le rayon (30m)

-

Étant donné que le point GPS 3 est défini avec rayon 30m

-

Quand l'utilisateur entre à 25m du point

-

Alors la séquence 3 se déclenche automatiquement

-
-

27. Pas de déclenchement hors rayon

-

Étant donné que le point GPS 3 a un rayon de 30m

-

Quand l'utilisateur passe à 45m du point

-

Alors la séquence 3 ne se déclenche pas automatiquement

-
-

28. Point manqué dans rayon de tolérance (100m)

-

Étant donné que l'utilisateur passe à 60m du point GPS 4 (hors rayon 30m) -Et que 60m < 100m (rayon tolérance)

-

Quand le point est détecté comme manqué

-

Alors un toast s'affiche: "⚠️ Point manqué : Enclos des éléphants" -Et une popup s'affiche pendant 5 secondes avec 3 options

-
-

29. Popup "Point manqué" avec 3 actions

-

Étant donné qu'un point GPS a été manqué (distance 60m)

-

Quand la popup s'affiche

-

Alors les options suivantes sont disponibles:

-
| bouton | icône | comportement |
-|---|---|---|
-| Écouter quand même | 🔊 | Lance séquence immédiatement (même hors zone) |
-| Passer au suivant | ⏭️ | Skip séquence, continue vers prochain point |
-| Faire demi-tour | 🔙 | Ouvre GPS externe (Google Maps/Waze) vers point |
-
-
-

30. Action "Écouter quand même"

-

Étant donné qu'un point GPS est manqué

-

Quand l'utilisateur clique sur "🔊 Écouter quand même"

-

Alors la séquence correspondante démarre immédiatement -Et l'utilisateur peut continuer sa route

-
-

31. Action "Passer au suivant"

-

Étant donné qu'un point GPS 5 est manqué

-

Quand l'utilisateur clique sur "⏭️ Passer au suivant"

-

Alors la séquence 5 est ignorée (non écoutée) -Et l'application attend le point GPS 6 -Et la distance vers le point 6 s'affiche

-
-

32. Action "Faire demi-tour"

-

Étant donné qu'un point GPS est manqué à (43.1250, 2.5700)

-

Quand l'utilisateur clique sur "🔙 Faire demi-tour"

-

Alors l'application détecte l'app GPS installée (Google Maps ou Waze) -Et ouvre la navigation GPS externe vers (43.1250, 2.5700)

-
-

33. Point manqué au-delà du rayon de tolérance (>100m)

-

Étant donné que l'utilisateur passe à 150m du point GPS 6

-

Quand la distance est détectée

-

Alors aucune popup ne s'affiche (point trop loin) -Et l'utilisateur peut naviguer manuellement avec [▶|]

-
-

34. 📋 Plan: Gestion selon la distance au point

-

Étant donné un point GPS avec rayon 30m et tolérance 100m

-

Quand l'utilisateur passe à du point

-

Alors le comportement est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
distancecomportement
20mDéclenchement automatique séquence
40mRien (hors rayon, pas encore tolérance)
60mPopup "Point manqué" avec 3 options
110mRien (trop loin, hors tolérance)
-
-

35. Configuration rayon personnalisé par le créateur

-

Étant donné qu'un créateur définit un rayon de 50m (au lieu de 30m)

-

Quand un utilisateur entre à 45m du point

-

Alors la séquence se déclenche automatiquement -Et le rayon personnalisé est respecté

-
-

36. Rayon minimum et maximum configurables

-

Étant donné qu'un créateur configure un rayon

-

Quand il ajuste le curseur

-

Alors les valeurs disponibles sont de 10m à 200m -Et le rayon par défaut suggéré est 30m pour la voiture

-
-

37. Safari-parc avec déclenchement automatique fluide

-

Étant donné qu'un utilisateur roule dans un safari à 20 km/h

-

Quand il passe devant "Enclos des lions" (point GPS 2)

-

Alors la séquence 2 démarre automatiquement sans intervention -Et il peut se concentrer sur la conduite et l'observation

-
-

38. Détour imprévu (travaux sur la route)

-

Étant donné qu'un utilisateur prend un détour à cause de travaux -Et que le point GPS 4 devient inaccessible

-

Quand il est loin du point (>100m) -Et qu'il clique manuellement sur [▶|]

-

Alors la séquence 4 démarre quand même -Et l'expérience continue sans blocage

-
-

39. Passager qui navigue librement

-

Étant donné qu'un passager utilise l'application -Et que le conducteur roule à 50 km/h

-

Quand le passager clique sur "Précédent" pour réécouter

-

Alors l'action est exécutée immédiatement -Et un warning apparaît brièvement (sensibilisation)

-
-

40. Embouteillage prolongé

-

Étant donné que la séquence 3 est terminée depuis 10 minutes -Et que l'utilisateur est bloqué dans un embouteillage -Et que le point GPS 4 est encore à 1.5 km

-

Quand l'utilisateur clique sur [▶|]

-

Alors la séquence 4 démarre immédiatement -Et l'utilisateur peut passer le temps en écoutant

-
-

41. GPS désactivé en mode voiture

-

Étant donné qu'un audio-guide voiture est démarré -Et que le GPS est désactivé

-

Quand l'application détecte l'absence de GPS

-

Alors une alerte s'affiche:

-
-

42. Action "Passer en mode Manuel"

-

Étant donné que le GPS est désactivé

-

Quand l'utilisateur clique sur "Passer en mode Manuel"

-

Alors l'audio-guide bascule en navigation 100% manuelle -Et les boutons [▶|] et [|◀] permettent de naviguer -Et aucun déclenchement GPS n'est tenté

-
-

43. Précision GPS insuffisante

-

Étant donné que le signal GPS a une précision de ±150m

-

Quand l'utilisateur approche d'un point GPS avec rayon 30m

-

Alors un avertissement s'affiche:

-
-

44. Perte signal GPS en cours de route

-

Étant donné qu'un audio-guide voiture est en cours

-

Quand le signal GPS est perdu (tunnel, parking souterrain)

-

Alors un toast s'affiche: "Signal GPS perdu. Navigation manuelle active." -Et les boutons de navigation restent actifs

-

Quand le signal GPS revient

-

Alors un toast s'affiche: "Signal GPS rétabli" -Et le déclenchement automatique est réactivé

-
-

45. Dépassement de la vitesse recommandée

-

Étant donné qu'un audio-guide recommande 20-30 km/h -Et que l'utilisateur roule à 65 km/h

-

Quand la vitesse est détectée

-

Alors l'affichage vitesse est en orange: "⚠️ 65 km/h" -Et un message info s'affiche: "Vitesse élevée. Risque de manquer des points."

-
-
- -

Audio-guides modes vélo et transport

-
-

En tant qu'utilisateur à vélo ou en transport en commun -Je veux profiter d'un guidage GPS adapté à mon mode de déplacement -Afin d'avoir une expérience optimisée avec tolérances appropriées

-
-

27 scénarios (24 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté -Et que le GPS est activé

-
-

1. 📋 Plan: Paramètres par mode de déplacement

-

Étant donné un audio-guide configuré en mode

-

Alors les paramètres suivants sont appliqués:

-
| paramètre | valeur |
-|---|---|
-| Rayon déclenchement | <rayon_declenchement> |
-| Rayon tolérance "point manqué" | <rayon_tolerance> |
-| Vitesse recommandée | <vitesse_recommandee> |
-| Seuil warning sécurité | <seuil_warning> |
-
-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
moderayon_declenchementrayon_tolerancevitesse_recommandeeseuil_warning
Voiture30m100m20-50 km/h>10 km/h
Vélo50m75m10-25 km/h>5 km/h
Transport100m150mVariableDésactivé
-
-

2. Déclenchement automatique avec rayon 50m (mode vélo)

-

Étant donné un audio-guide vélo "Circuit des châteaux de la Loire" -Et que le point GPS 3 a un rayon de 50m

-

Quand l'utilisateur à vélo entre à 45m du point

-

Alors la séquence 3 "Château de Chambord" se déclenche automatiquement

-
-

3. Rayon plus large justifié pour le vélo

-

Étant donné qu'un cycliste roule sur piste cyclable -Et que sa vitesse varie entre 8 et 22 km/h (arrêts fréquents) -Et que le tracé est moins prévisible qu'en voiture

-

Quand un point GPS avec rayon 50m est défini

-

Alors le rayon plus large compense la variabilité de trajectoire

-
-

4. Warning sécurité dès 5 km/h en vélo

-

Étant donné un audio-guide vélo en cours -Et que la vitesse actuelle est 12 km/h

-

Quand l'utilisateur clique sur [▶|] "Suivant"

-

Alors l'action est exécutée -Et un warning s'affiche: "⚠️ Manipulation en déplacement détecté. Pour votre sécurité, arrêtez-vous."

-
-

5. 📋 Plan: Warning vélo selon la vitesse

-

Étant donné que la vitesse actuelle à vélo est km/h

-

Quand l'utilisateur clique sur un bouton de navigation

-

Alors le warning est affiché :

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vitessewarning
0Non
4Non
6Oui
15Oui
25Oui
-
-

6. Tolérance GPS moins stricte en vélo

-

Étant donné qu'un cycliste passe à 65m du point GPS 4 -Et que le rayon de déclenchement est 50m -Et que le rayon de tolérance est 75m

-

Quand la distance est détectée

-

Alors la popup "Point manqué" s'affiche avec 3 options -Et le système tolère l'écart (trajectoire vélo moins prévisible)

-
-

7. Affichage adapté au vélo

-

Étant donné un audio-guide vélo en cours

-

Quand l'interface est affichée

-

Alors les informations suivantes sont visibles:

-
| information | valeur |
-|---|---|
-| Icône mode | 🚴 |
-| Distance prochain point | "450 m" |
-| ETA | "≈ 2 minutes" |
-| Direction | ↗️ |
-| Vitesse actuelle | "18 km/h" |
-| Vitesse recommandée | "10-25 km/h" |
-
-
-

8. Cas d'usage - Piste cyclable avec arrêts fréquents

-

Étant donné qu'un cycliste suit un circuit nature -Et qu'il s'arrête régulièrement (feux, photos, fatigue)

-

Quand il s'arrête à 40m d'un point GPS (rayon 50m)

-

Alors la séquence se déclenche automatiquement -Et le rayon large permet le déclenchement malgré l'arrêt

-
-

9. Cas d'usage - Circulation mixte piétons/vélos

-

Étant donné qu'un cycliste roule sur voie partagée -Et qu'il doit ralentir fréquemment pour éviter les piétons

-

Quand sa vitesse varie entre 5 et 20 km/h

-

Alors le système s'adapte avec le rayon 50m -Et le déclenchement reste fiable

-
-

10. Déclenchement automatique avec rayon 100m (mode transport)

-

Étant donné un audio-guide transport "Ligne touristique Paris" -Et que le point GPS "Tour Eiffel" a un rayon de 100m

-

Quand le bus touristique entre à 85m du point

-

Alors la séquence "Tour Eiffel" se déclenche automatiquement

-
-

11. Rayon très large justifié pour le transport

-

Étant donné qu'un bus touristique suit une ligne fixe -Et qu'il effectue des arrêts fréquents (stations) -Et que l'utilisateur n'a aucun contrôle sur la trajectoire

-

Quand un point GPS avec rayon 100m est défini

-

Alors le rayon large compense les arrêts et la ligne fixe

-
-

12. Pas de warning sécurité en mode transport

-

Étant donné un audio-guide transport en cours -Et que le bus roule à 50 km/h

-

Quand l'utilisateur clique sur [▶|] "Suivant"

-

Alors l'action est exécutée immédiatement -Et aucun warning n'est affiché

-
-

13. Vitesse recommandée "Selon ligne"

-

Étant donné un audio-guide transport

-

Quand l'interface est affichée

-

Alors la vitesse recommandée indique "Selon ligne" -Et aucune valeur fixe n'est affichée (car ligne de transport varie)

-
-

14. Tolérance horaire pour retards

-

Étant donné qu'un bus touristique est en retard de 3 minutes -Et qu'il arrive au point GPS "Musée du Louvre" avec retard

-

Quand il entre dans le rayon de 100m

-

Alors la séquence se déclenche normalement -Et le système tolère le retard (pas de pénalité temporelle)

-
-

15. Tolérance spatiale très large (150m)

-

Étant donné qu'un bus passe à 120m du point GPS "Arc de Triomphe" -Et que le rayon de déclenchement est 100m -Et que le rayon de tolérance est 150m

-

Quand la distance est détectée

-

Alors la popup "Point manqué" s'affiche avec 3 options

-
-

16. Affichage adapté au transport

-

Étant donné un audio-guide transport en cours

-

Quand l'interface est affichée

-

Alors les informations suivantes sont visibles:

-
| information | valeur |
-|---|---|
-| Icône mode | 🚌 |
-| Distance prochain point | "1.2 km" |
-| ETA | "≈ 3 minutes" |
-| Direction | → |
-| Vitesse actuelle | "35 km/h" |
-| Vitesse recommandée | "Selon ligne" |
-
-
-

17. Cas d'usage - Bus touristique hop-on hop-off

-

Étant donné un bus touristique "Paris Open Tour" -Et qu'il suit un circuit fixe avec 15 arrêts

-

Quand il approche de chaque arrêt

-

Alors la séquence correspondante se déclenche automatiquement -Et l'utilisateur n'a rien à faire (expérience passive)

-
-

18. Cas d'usage - Train panoramique

-

Étant donné un train touristique "Ligne des Alpes" -Et qu'il roule à vitesse variable (20-80 km/h)

-

Quand il passe près de points d'intérêt

-

Alors les séquences se déclenchent avec rayon 100m -Et le système compense la vitesse élevée

-
-

19. Navigation manuelle conservée (vélo et transport)

-

Étant donné un audio-guide en mode

-

Quand l'utilisateur clique sur [▶|] ou [|◀]

-

Alors les boutons manuels fonctionnent normalement -Et aucune vérification GPS n'est effectuée

-
-

20. Affichage distance + ETA + direction (tous modes)

-

Étant donné un audio-guide en mode

-

Quand l'interface est affichée

-

Alors les informations distance, ETA et direction sont affichées -Et le format est identique au mode voiture

-
-

21. Gestion "Point manqué" identique

-

Étant donné un audio-guide en mode

-

Quand un point GPS est manqué (dans rayon tolérance)

-

Alors la popup avec 3 options s'affiche:

-
| option |
-|---|
-| 🔊 Écouter quand même |
-| ⏭️ Passer au suivant |
-| 🔙 Faire demi-tour |
-
-
-

22. 📋 Plan: Insertion publicité dans tous les modes

-

Étant donné un utilisateur gratuit écoute un audio-guide en mode

-

Quand la séquence 5 se termine (1 pub / 5 séquences)

-

Alors la publicité s'enchaîne automatiquement -Et elle est skippable après 5 secondes

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - -
mode
Voiture
Vélo
Transport
Piéton
-
-

23. GPS imprécis en forêt (vélo)

-

Étant donné un cycliste dans une forêt dense -Et que la précision GPS est ±80m

-

Quand il approche d'un point GPS avec rayon 50m

-

Alors un avertissement s'affiche:

-
-

24. Bus dévié de son itinéraire (transport)

-

Étant donné un bus touristique avec déviation -Et que plusieurs points GPS deviennent inaccessibles

-

Quand l'utilisateur est informé

-

Alors un message s'affiche:

-
-

25. Changement de mode en cours de route

-

Étant donné un audio-guide démarré en mode "Vélo"

-

Quand l'utilisateur décide de continuer à pied -Et qu'il ouvre les paramètres

-

Alors il peut changer le mode vers "Piéton" -Et les rayons sont reconfigurés automatiquement -Et une confirmation s'affiche:

-
-

26. Détection automatique incohérente

-

Étant donné qu'un utilisateur marche rapidement (7 km/h) -Et que le système détecte "Vélo" par erreur

-

Quand la suggestion s'affiche

-

Alors l'utilisateur peut cliquer sur "Changer" -Et sélectionner manuellement "Piéton"

-
-

27. Batterie en mode vélo longue distance

-

Étant donné un circuit vélo de 50 km avec 20 séquences -Et que l'utilisateur roule pendant 3 heures

-

Quand la batterie atteint 15%

-

Alors une notification suggère:

-
-
- -

Audio-guides Premium et monétisation

-
-

En tant que créateur -Je veux pouvoir proposer des audio-guides Premium -Afin de monétiser mon contenu de qualité

-
-

31 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que le créateur "guide@example.com" est connecté et vérifié

-
-

1. Création d'un audio-guide Premium

-

Étant donné que le créateur crée un audio-guide "Visite VIP Versailles"

-

Quand il accède aux paramètres de monétisation (étape 4)

-

Alors il peut choisir:

-
| option | description |
-|---|---|
-| Gratuit | Accessible à tous (avec pubs) |
-| Premium | Réservé abonnés Premium |
-
-
-

2. Badge Premium visible sur l'audio-guide

-

Étant donné un audio-guide configuré en Premium

-

Quand il est affiché dans les résultats de recherche

-

Alors un badge "👑 Premium" est visible -Et la cover image a un cadre doré subtil

-
-

3. Preview 3 premières séquences pour utilisateurs gratuits

-

Étant donné un audio-guide Premium "Visite VIP Versailles" avec 15 séquences -Et qu'un utilisateur gratuit ouvre l'audio-guide

-

Quand il consulte la liste des séquences

-

Alors les séquences affichent:

-
| séquence | état |
-|---|---|
-| 1 | ✅ Accessible (preview) |
-| 2 | ✅ Accessible (preview) |
-| 3 | ✅ Accessible (preview) |
-| 4 | 🔒 Réservé Premium |
-| 5-15 | 🔒 Réservé Premium |
-
-
-

4. Écoute des 3 premières séquences sans blocage

-

Étant donné un utilisateur gratuit -Et un audio-guide Premium avec preview

-

Quand il écoute les séquences 1, 2 et 3

-

Alors aucune publicité n'est insérée (preview = teasing) -Et l'écoute est fluide

-
-

5. Paywall après la 3ème séquence

-

Étant donné qu'un utilisateur gratuit termine la séquence 3

-

Quand la séquence se termine

-

Alors un overlay paywall s'affiche immédiatement:

-
-

6. Bouton "Passer Premium" vers tunnel d'abonnement

-

Étant donné que l'overlay paywall Premium est affiché

-

Quand l'utilisateur clique sur "Passer Premium"

-

Alors il est redirigé vers la page d'abonnement Mangopay -Et l'audio-guide actuel est marqué en "pending" (reprise après souscription)

-
-

7. Reprise automatique après souscription Premium

-

Étant donné qu'un utilisateur s'est abonné Premium depuis un paywall audio-guide

-

Quand l'abonnement est activé

-

Alors il est redirigé vers l'audio-guide automatiquement -Et la séquence 4 démarre immédiatement -Et un toast de bienvenue s'affiche: "✨ Bienvenue Premium ! Profitez de votre audio-guide"

-
-

8. Utilisateur Premium - Accès complet immédiat

-

Étant donné qu'un utilisateur Premium ouvre un audio-guide Premium

-

Quand il consulte la liste des séquences

-

Alors toutes les 15 séquences sont accessibles -Et aucun paywall ne s'affiche -Et aucune publicité n'est insérée

-
-

9. Pas de preview si l'audio-guide a <3 séquences

-

Étant donné un audio-guide Premium avec seulement 2 séquences

-

Quand un utilisateur gratuit tente de l'ouvrir

-

Alors un paywall s'affiche immédiatement (avant lecture) -Et aucune preview n'est disponible

-
-

10. Rémunération créateur pour audio-guide Premium

-

Étant donné un créateur avec un audio-guide Premium -Et que 50 utilisateurs Premium ont écouté l'audio-guide ce mois

-

Quand la répartition des revenus est calculée

-

Alors le créateur reçoit 70% des revenus proportionnels -Et la formule est: (Écoutes créateur / Total écoutes Premium) × 70% pool Premium

-
-

11. Dashboard revenus par audio-guide

-

Étant donné qu'un créateur a 3 audio-guides Premium publiés

-

Quand il consulte son dashboard revenus

-

Alors il voit pour chaque audio-guide:

-
| audio_guide | ecoutes_mois | revenus_estime |
-|---|---|---|
-| Visite VIP Versailles | 142 | 45.20 € |
-| Secrets du Louvre | 89 | 28.50 € |
-| Châteaux de la Loire | 203 | 64.80 € |
-
-
-

12. Comparaison gratuit vs Premium

-

Étant donné qu'un créateur a publié 2 audio-guides:

-
| titre | type | ecoutes_mois | revenus |
-|---|---|---|---|
-| Tour de Paris | Gratuit | 1200 | 12.50 € |
-| Visite VIP Versailles | Premium | 142 | 45.20 € |
-
-

Quand il consulte son dashboard

-

Alors il peut comparer les performances -Et constater que Premium génère plus de revenus par écoute

-
-

13. Seuil minimum de paiement (20€)

-

Étant donné qu'un créateur a généré 18€ de revenus ce mois

-

Quand le paiement mensuel est traité

-

Alors le montant est reporté au mois suivant -Et un message s'affiche: "Seuil minimum non atteint (20€). Montant reporté."

-
-

14. Paiement automatique mensuel

-

Étant donné qu'un créateur a généré 138.50€ de revenus en janvier

-

Quand le 5 février arrive

-

Alors le paiement est initié automatiquement via Mangopay -Et le créateur reçoit une notification: "Paiement de 138.50€ en cours" -Et les fonds arrivent sous 2-3 jours ouvrés

-
-

15. Insertion publicité toutes les 5 séquences (gratuit)

-

Étant donné un audio-guide gratuit avec 12 séquences -Et un utilisateur gratuit

-

Quand il termine la séquence 5

-

Alors une publicité démarre automatiquement

-

Quand il termine la séquence 10

-

Alors une deuxième publicité démarre

-
-

16. Publicité après séquence en mode piéton (avec pause)

-

Étant donné un audio-guide piéton gratuit

-

Quand la séquence 5 se termine

-

Alors la publicité démarre automatiquement (pas d'attente bouton) -Et la pub est skippable après 5 secondes

-

Quand la publicité se termine

-

Alors le player se met en pause -Et l'utilisateur doit cliquer sur [▶|] pour continuer

-
-

17. Publicité en mode voiture/vélo/transport (automatique)

-

Étant donné un audio-guide voiture gratuit

-

Quand la séquence 5 se termine

-

Alors la publicité démarre automatiquement

-

Quand la publicité se termine

-

Alors la séquence 6 démarre automatiquement (pas de pause)

-
-

18. Publicités géolocalisées dans audio-guides

-

Étant donné un audio-guide dans la région "Île-de-France"

-

Quand une publicité doit être insérée

-

Alors l'API publicitaire filtre par:

-
| critère | valeur |
-|---|---|
-| Géolocalisation | Île-de-France |
-| Catégorie | Tourisme, Culture |
-| Langue | Français |
-
-
-

19. Comptabilisation des impressions pub pour créateur

-

Étant donné qu'un audio-guide gratuit génère 200 écoutes complètes -Et que chaque écoute complète = 2 publicités (séq. 5 et 10)

-

Quand les revenus pub sont calculés

-

Alors 400 impressions sont comptabilisées -Et le créateur reçoit 0.80€ (400 × 0.002€)

-
-

20. CTA Premium après audio-guide gratuit complété

-

Étant donné qu'un utilisateur gratuit complète un audio-guide gratuit

-

Quand il termine la dernière séquence

-

Alors un overlay s'affiche:

-
-

21. Recommandations d'audio-guides Premium après gratuit

-

Étant donné qu'un utilisateur termine un audio-guide gratuit "Tour de Paris"

-

Quand l'overlay de fin s'affiche

-

Alors 3 audio-guides Premium similaires sont suggérés:

-
| titre | type | créateur |
-|---|---|---|
-| Secrets de Montmartre | Premium | @paris_stories |
-| Visite VIP Musée d'Orsay | Premium | @art_guide |
-| Paris hors des sentiers | Premium | @explore_paris |
-
-
-

22. Badge "Premium recommandé" sur audio-guides populaires

-

Étant donné un audio-guide Premium avec >500 écoutes et note >4.5/5

-

Quand il est affiché dans les résultats de recherche

-

Alors un badge "⭐ Premium recommandé" est visible -Et il est mis en avant dans les résultats

-
-

23. Conversion tracking pour attribution créateur

-

Étant donné qu'un utilisateur découvre Premium via un audio-guide créateur

-

Quand il s'abonne

-

Alors la conversion est trackée:

-
| donnée | valeur |
-|---|---|
-| source_conversion | audio_guide_paywall |
-| audio_guide_id | visite_vip_versailles_123 |
-| creator_id | guide_versailles_456 |
-
-

Et le créateur bénéficie d'un bonus de conversion

-
-

24. Essai gratuit 7 jours Premium via audio-guide

-

Étant donné qu'un utilisateur gratuit atteint le paywall d'un audio-guide Premium -Et qu'il n'a jamais essayé Premium

-

Quand l'overlay s'affiche

-

Alors une offre d'essai est proposée:

-
-

25. Activation immédiate après essai gratuit

-

Étant donné qu'un utilisateur démarre un essai gratuit 7 jours

-

Quand l'essai est activé

-

Alors l'audio-guide Premium démarre immédiatement -Et toutes les séquences sont débloquées -Et aucune publicité n'est insérée

-
-

26. Rappel 2 jours avant fin d'essai

-

Étant donné qu'un utilisateur a démarré un essai gratuit le 15/01

-

Quand le 20/01 arrive (J-2)

-

Alors une notification est envoyée:

-
-

27. Créateur mix gratuit + Premium

-

Étant donné qu'un créateur a publié 5 audio-guides:

-
| titre | type |
-|---|---|
-| Découverte de Paris | Gratuit |
-| Visite VIP Louvre | Premium |
-| Balade Montmartre | Gratuit |
-| Secrets Versailles | Premium |
-| Visite express Orsay | Gratuit |
-
-

Quand un utilisateur découvre son profil

-

Alors les audio-guides gratuits servent de teasing -Et les audio-guides Premium sont mis en avant avec badge

-
-

28. Utilisateur hésite à s'abonner

-

Étant donné qu'un utilisateur atteint le paywall d'un audio-guide Premium -Et qu'il clique sur "Découvrir d'autres audio-guides gratuits"

-

Quand il revient 2 jours plus tard sur le même audio-guide

-

Alors le paywall s'affiche à nouveau -Et une réduction temporaire est proposée: "Offre spéciale : -20% premier mois"

-
-

29. Échec du paiement Premium via paywall

-

Étant donné qu'un utilisateur tente de s'abonner Premium

-

Quand le paiement Mangopay échoue

-

Alors un message d'erreur s'affiche:

-
-

30. Abonnement Premium expiré pendant écoute

-

Étant donné qu'un utilisateur Premium écoute un audio-guide Premium -Et que son abonnement expire pendant l'écoute (séquence 8/15)

-

Quand l'expiration est détectée

-

Alors l'écoute continue jusqu'à la fin de la séquence en cours -Et un overlay s'affiche ensuite:

-
-

31. Créateur change audio-guide de gratuit à Premium

-

Étant donné qu'un audio-guide gratuit a 50 utilisateurs avec progression

-

Quand le créateur le passe en Premium

-

Alors les utilisateurs ayant déjà commencé gardent l'accès complet -Et seuls les nouveaux utilisateurs sont soumis au paywall -Et un message de transparence s'affiche:

-
-
- -

Sauvegarde et reprise de progression audio-guide

-
-

En tant qu'utilisateur -Je veux que ma progression soit sauvegardée automatiquement -Afin de pouvoir reprendre mon audio-guide là où je me suis arrêté

-
-

32 scénarios (31 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté

-
-

1. Sauvegarde automatique toutes les 10 secondes

-

Étant donné qu'un audio-guide "Visite du Louvre" est en cours -Et que la séquence 3 est à la position 1:24

-

Quand 10 secondes s'écoulent

-

Alors la progression est sauvegardée automatiquement:

-
| donnée | valeur |
-|---|---|
-| audio_guide_id | louvre_123 |
-| sequence_actuelle | 3 |
-| position_audio | 1:24 |
-| timestamp | 2026-01-22 14:35:42 |
-| sequences_ecoutees | [1, 2] |
-
-
-

2. Sauvegarde locale (SQLite) pour rapidité

-

Étant donné qu'une sauvegarde est déclenchée

-

Quand la progression est enregistrée

-

Alors les données sont écrites en SQLite local -Et l'écriture prend moins de 50ms -Et l'application reste fluide

-
-

3. Synchronisation cloud en arrière-plan

-

Étant donné qu'une sauvegarde locale est effectuée

-

Quand 30 secondes s'écoulent

-

Alors la progression est synchronisée vers PostgreSQL cloud -Et la synchronisation s'effectue en arrière-plan -Et elle n'impacte pas les performances

-
-

4. Sauvegarde immédiate lors de la fermeture

-

Étant donné qu'un audio-guide est en cours à la séquence 4 position 2:15

-

Quand l'utilisateur ferme l'application

-

Alors la progression est sauvegardée immédiatement (local + cloud) -Et les données sont écrites avant la fermeture complète

-
-

5. Sauvegarde des séquences complétées

-

Étant donné qu'un audio-guide de 12 séquences est en cours -Et que les séquences 1, 2, 4, 5 ont été écoutées à >80%

-

Quand la progression est sauvegardée

-

Alors les séquences complétées sont enregistrées:

-
-

6. Historique des écoutes pour statistiques

-

Étant donné qu'un utilisateur a écouté 3 séquences d'un audio-guide

-

Quand les données sont sauvegardées

-

Alors l'historique d'écoute inclut:

-
| sequence_id | started_at | completed_at | completion_rate |
-|---|---|---|---|
-| 1 | 2026-01-22 14:10:00 | 2026-01-22 14:12:15 | 100% |
-| 2 | 2026-01-22 14:12:20 | 2026-01-22 14:14:08 | 100% |
-| 3 | 2026-01-22 14:14:15 | 2026-01-22 14:17:45 | 92% |
-
-
-

7. Popup de reprise au redémarrage

-

Étant donné que l'utilisateur a quitté l'app à la séquence 6 position 2:34

-

Quand il rouvre l'audio-guide "Visite du Louvre"

-

Alors une popup s'affiche:

-
-

8. Action "Reprendre" - Position exacte restaurée

-

Étant donné qu'une popup de reprise est affichée

-

Quand l'utilisateur clique sur "▶️ Reprendre"

-

Alors la séquence 6 "Vénus de Milo" se charge -Et la position exacte 2:34 est restaurée -Et la lecture démarre automatiquement après 1 seconde

-
-

9. Action "Recommencer" - Réinitialisation complète

-

Étant donné qu'une popup de reprise est affichée

-

Quand l'utilisateur clique sur "🔄 Recommencer"

-

Alors l'audio-guide redémarre depuis la séquence 1 position 0:00 -Et toutes les séquences sont marquées ⭕ "À écouter" -Et l'historique d'écoute est réinitialisé pour cette session

-
-

10. Reprise après 7 jours d'inactivité

-

Étant donné qu'un utilisateur a arrêté un audio-guide le 15/01/2026 -Et qu'il le rouvre le 22/01/2026 (7 jours plus tard)

-

Quand l'audio-guide se charge

-

Alors la popup de reprise s'affiche normalement -Et toutes les données de progression sont conservées

-
-

11. Reprise sur un autre appareil (synchronisation cloud)

-

Étant donné qu'un utilisateur écoute un audio-guide sur iPhone -Et qu'il quitte à la séquence 4 position 1:20

-

Quand il ouvre le même audio-guide sur iPad

-

Alors la popup de reprise s'affiche avec la progression iPhone -Et il peut reprendre exactement où il s'était arrêté

-
-

12. Conflit de synchronisation (dernier appareil gagne)

-

Étant donné qu'un utilisateur écoute sur iPhone à la séquence 3 -Et simultanément sur iPad à la séquence 7

-

Quand les deux appareils synchronisent

-

Alors la progression la plus récente (timestamp) est conservée -Et l'appareil avec ancienne progression affiche une notification:

-
-

13. Mode hors-ligne - Sauvegarde locale uniquement

-

Étant donné qu'un utilisateur écoute un audio-guide hors connexion -Et qu'il atteint la séquence 5

-

Quand la progression est sauvegardée

-

Alors les données sont écrites localement (SQLite) -Et une icône "☁️ Non synchronisé" s'affiche discrètement

-
-

14. Synchronisation automatique à la reconnexion

-

Étant donné que l'utilisateur a écouté hors ligne jusqu'à la séquence 8 -Et que 5 progressions locales ne sont pas synchronisées

-

Quand la connexion réseau est rétablie

-

Alors les 5 progressions sont synchronisées automatiquement -Et un toast s'affiche brièvement: "✅ Progression synchronisée"

-
-

15. Suppression de la progression (recommencer proprement)

-

Étant donné qu'un utilisateur est à la séquence 10/12

-

Quand il ouvre les paramètres de l'audio-guide -Et qu'il clique sur "🔄 Réinitialiser progression"

-

Alors une confirmation s'affiche: -Et si confirmé, la progression est effacée

-
-

16. Taux de complétion global de l'audio-guide

-

Étant donné un audio-guide de 12 séquences -Et que l'utilisateur a écouté complètement 8 séquences -Et partiellement 1 séquence (45%)

-

Quand les statistiques sont calculées

-

Alors le taux de complétion affiché est "67%" (8/12)

-
-

17. Badge "Audio-guide complété" à 100%

-

Étant donné un audio-guide de 12 séquences

-

Quand l'utilisateur écoute la 12ème séquence à 100%

-

Alors un badge "✅ Audio-guide complété" s'affiche -Et une notification de félicitations est envoyée -Et le statut "Complété le 22/01/2026" est visible dans l'historique

-
-

18. Temps total passé sur l'audio-guide

-

Étant donné qu'un utilisateur a écouté un audio-guide sur 2 sessions:

-
| session | durée |
-|---|---|
-| 1 | 25 min |
-| 2 | 18 min |
-
-

Quand les statistiques sont calculées

-

Alors le temps total est "43 minutes" -Et il est affiché dans l'historique personnel

-
-

19. Liste des audio-guides "En cours" dans le profil

-

Étant donné qu'un utilisateur a 3 audio-guides en cours:

-
| audio_guide | progression |
-|---|---|
-| Visite du Louvre | 6/12 |
-| Safari du Paugre | 3/8 |
-| Circuit Loire à Vélo | 12/15 |
-
-

Quand il consulte son profil "Audio-guides"

-

Alors la section "📍 En cours" affiche les 3 audio-guides -Et chaque élément montre la progression sous forme de barre

-
-

20. Liste des audio-guides "Complétés" dans le profil

-

Étant donné qu'un utilisateur a complété 2 audio-guides:

-
| audio_guide | date_completion |
-|---|---|
-| Tour de Paris | 2026-01-15 |
-| Découverte de Lyon | 2026-01-20 |
-
-

Quand il consulte son profil "Audio-guides"

-

Alors la section "✅ Complétés" affiche les 2 audio-guides -Et la date de complétion est visible

-
-

21. Badge "Complétiste" pour 10 audio-guides complétés

-

Étant donné qu'un utilisateur complète son 10ème audio-guide

-

Quand la complétion est enregistrée

-

Alors un badge "🏆 Complétiste" est débloqué -Et il apparaît sur son profil -Et une notification est envoyée:

-
-

22. 📋 Plan: Niveaux de badges selon nombre d'audio-guides complétés

-

Étant donné qu'un utilisateur complète audio-guides

-

Quand le badge est attribué

-

Alors il reçoit le badge ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nombrebadge
1🎧 Premier audio-guide
5🗺️ Explorateur
10🏆 Complétiste
25🌟 Expert
50💎 Maître audio-guideur
-
-

23. Dashboard créateur - Statistiques par audio-guide

-

Étant donné qu'un créateur a publié l'audio-guide "Visite du Louvre"

-

Quand il consulte son dashboard

-

Alors les métriques suivantes sont affichées:

-
| métrique | valeur |
-|---|---|
-| Écoutes totales | 1542 |
-| Écoutes complètes (>80%) | 892 |
-| Taux de complétion moyen | 58% |
-| Temps d'écoute total | 423h |
-| Séquence la plus écoutée | Séq. 3 |
-| Séquence la moins écoutée | Séq. 11 |
-
-
-

24. Graphique de complétion par séquence

-

Étant donné un audio-guide de 12 séquences

-

Quand le créateur consulte les statistiques détaillées

-

Alors un graphique en barres affiche:

-
| séquence | taux_completion |
-|---|---|
-| 1 | 100% |
-| 2 | 95% |
-| 3 | 89% |
-| ... | ... |
-| 12 | 58% |
-
-
-

25. Détection des points d'abandon

-

Étant donné qu'un audio-guide a un taux de complétion de 58% -Et que 35% des utilisateurs abandonnent à la séquence 7

-

Quand le créateur consulte les insights

-

Alors un avertissement s'affiche:

-
-

26. Heatmap géographique des écoutes

-

Étant donné un audio-guide géolocalisé

-

Quand le créateur consulte la heatmap

-

Alors une carte affiche:

-
| élément | description |
-|---|---|
-| Densité d'écoutes | Zones rouge/orange/jaune selon écoutes |
-| Points GPS | Marqueurs sur chaque point |
-| Statistiques par point | Nombre d'écoutes par zone |
-
-
-

27. Temps moyen par séquence

-

Étant donné qu'un créateur analyse son audio-guide

-

Quand il consulte les statistiques temporelles

-

Alors il voit pour chaque séquence:

-
| séquence | durée_audio | temps_ecoute_moyen | ecart |
-|---|---|---|---|
-| 1 | 2:15 | 2:10 | -5s |
-| 2 | 1:48 | 1:30 | -18s |
-| 3 | 3:42 | 3:40 | -2s |
-
-
-

28. Notification créateur pour milestone

-

Étant donné qu'un audio-guide atteint 1000 écoutes

-

Quand le seuil est franchi

-

Alors une notification est envoyée au créateur:

-
-

29. Corruption de données de sauvegarde

-

Étant donné qu'une sauvegarde locale (SQLite) est corrompue

-

Quand l'application tente de charger la progression

-

Alors une récupération depuis le cloud est tentée -Et si réussie, les données cloud sont restaurées -Et la base locale est reconstruite

-
-

30. Échec de synchronisation cloud

-

Étant donné que l'API cloud est indisponible

-

Quand une tentative de synchronisation est effectuée

-

Alors l'application continue avec sauvegarde locale uniquement -Et un retry automatique est programmé dans 5 minutes -Et l'icône "☁️ Non synchronisé" reste affichée

-
-

31. Suppression accidentelle de progression (récupération)

-

Étant donné qu'un utilisateur réinitialise un audio-guide par erreur

-

Quand il contacte le support dans les 7 jours

-

Alors l'équipe peut restaurer la progression depuis les backups -Et les données sont récupérables (backup quotidien conservé 30 jours)

-
-

32. Nettoyage automatique des vieilles progressions

-

Étant donné qu'une progression n'a pas été mise à jour depuis 6 mois

-

Quand le nettoyage automatique s'exécute

-

Alors la progression est archivée (mais pas supprimée) -Et l'utilisateur peut la restaurer via l'historique

-
-
- -

Classification des contenus par âge

-
-

En tant que plateforme responsable -Je veux classifier les contenus par tranche d'âge -Afin de protéger les mineurs et respecter les obligations légales

-
-

13 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Créateur doit classifier son contenu à la publication

-

Étant donné que je suis un créateur connecté

-

Quand je crée un nouveau contenu audio

-

Alors je dois obligatoirement choisir une classification d'âge parmi:

-
| classification | description |
-|---|---|
-| Tout public | Contenu adapté à tous les âges |
-| 13+ | Contenu mature léger |
-| 16+ | Contenu mature |
-| 18+ | Contenu adulte |
-
-
-

2. Publication impossible sans classification

-

Étant donné que je crée un contenu audio

-

Quand j'essaie de publier sans sélectionner de classification

-

Alors la publication échoue -Et je vois le message "Vous devez sélectionner une classification d'âge"

-
-

3. Utilisateur 13-15 ans voit uniquement du contenu "Tout public"

-

Étant donné que je suis un utilisateur de 14 ans -Et qu'il existe des contenus avec les classifications suivantes:

-
| classification | nombre |
-|---|---|
-| Tout public | 20 |
-| 13+ | 15 |
-| 16+ | 10 |
-| 18+ | 5 |
-
-

Quand je demande des recommandations

-

Alors je vois uniquement les 20 contenus "Tout public" -Et les autres contenus ne sont jamais proposés

-
-

4. Utilisateur 16-17 ans voit "Tout public" et "13+"

-

Étant donné que je suis un utilisateur de 17 ans -Et qu'il existe des contenus avec les classifications suivantes:

-
| classification | nombre |
-|---|---|
-| Tout public | 20 |
-| 13+ | 15 |
-| 16+ | 10 |
-| 18+ | 5 |
-
-

Quand je demande des recommandations

-

Alors je vois 35 contenus (Tout public + 13+) -Et les contenus 16+ et 18+ ne sont pas proposés

-
-

5. Utilisateur 18+ voit tous les contenus

-

Étant donné que je suis un utilisateur de 25 ans -Et qu'il existe des contenus avec toutes les classifications

-

Quand je demande des recommandations

-

Alors je vois tous les contenus sans restriction -Et aucun filtre d'âge n'est appliqué

-
-

6. Mode Kids activé automatiquement pour les moins de 13 ans

-

Étant donné que je m'inscris avec une date de naissance "2013-01-21"

-

Alors le mode Kids est activé automatiquement -Et je vois uniquement du contenu "Tout public" -Et des protections supplémentaires sont appliquées

-
-

7. Modérateur reclassifie un contenu mal catégorisé

-

Étant donné qu'un contenu est publié avec la classification "Tout public" -Et que ce contenu contient du langage inapproprié détecté en modération

-

Quand le modérateur reclassifie ce contenu en "16+"

-

Alors la nouvelle classification est appliquée immédiatement -Et le contenu n'est plus visible pour les utilisateurs de moins de 16 ans -Et le créateur reçoit une notification de reclassification

-
-

8. Strike si classification volontairement incorrecte

-

Étant donné qu'un créateur a publié un contenu "18+" classifié comme "Tout public" -Et que ce contenu a été signalé

-

Quand le modérateur confirme la mauvaise classification volontaire

-

Alors le créateur reçoit 1 strike -Et le contenu est reclassifié en "18+" -Et le créateur reçoit une notification explicative

-
-

9. Créateur peut voir la distribution d'âge de son audience

-

Étant donné que je suis un créateur -Et que j'ai publié des contenus avec différentes classifications

-

Quand je consulte mes statistiques

-

Alors je vois la répartition des âges de mes auditeurs:

-
| tranche_age | pourcentage |
-|---|---|
-| 13-15 ans | 15% |
-| 16-17 ans | 20% |
-| 18+ ans | 65% |
-
-
-

10. Recherche filtrée par classification d'âge

-

Étant donné que je suis un utilisateur de 16 ans

-

Quand je recherche des contenus

-

Alors les résultats incluent uniquement:

-
| classification |
-|---|
-| Tout public |
-| 13+ |
-
-

Et je ne vois pas les contenus 16+ et 18+ dans les résultats

-
-

11. Notification si tentative d'accès à contenu non autorisé

-

Étant donné que je suis un utilisateur de 14 ans -Et qu'un contenu "16+" est partagé avec moi via un lien direct

-

Quand j'essaie d'accéder au contenu

-

Alors l'accès est refusé -Et je vois le message "Ce contenu est réservé aux utilisateurs de 16 ans et plus"

-
-

12. Validation obligatoire des 3 premiers contenus inclut la classification

-

Étant donné que je suis un nouveau créateur -Et que je publie mon premier contenu classifié "18+"

-

Quand le modérateur valide mon contenu

-

Alors il vérifie que la classification "18+" est appropriée -Et peut la modifier si nécessaire avant validation

-
-

13. Statistiques de classification dans l'interface créateur

-

Étant donné que je suis un créateur

-

Quand je consulte mes contenus publiés

-

Alors je vois pour chaque contenu:

-
| information | exemple |
-|---|---|
-| Classification actuelle | 13+ |
-| Nombre de signalements | 2 |
-| Reclassifications | Aucune / 1× par modérateur |
-
-
-
- -

Connexion utilisateur

-
-

En tant qu'utilisateur existant -Je veux me connecter à mon compte -Afin d'accéder à mes contenus et paramètres

-
-

11 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur existe avec: - | email | mot_de_passe | - |---|---| - | user@test.fr | Password123 |

-
-

1. Connexion réussie avec identifiants valides

-

Quand je me connecte avec:

-
| email | mot_de_passe |
-|---|---|
-| user@test.fr | Password123 |
-
-

Alors je suis connecté avec succès -Et je reçois un access token valide pour 15 minutes -Et je reçois un refresh token valide pour 30 jours

-
-

2. Connexion échouée avec email inexistant

-

Quand je me connecte avec l'email "inexistant@test.fr"

-

Alors la connexion échoue -Et je vois le message "Email ou mot de passe incorrect"

-
-

3. Connexion échouée avec mot de passe incorrect

-

Quand je me connecte avec:

-
| email | mot_de_passe |
-|---|---|
-| user@test.fr | MauvaisPass1 |
-
-

Alors la connexion échoue -Et je vois le message "Email ou mot de passe incorrect"

-
-

4. Blocage après 5 tentatives échouées

-

Étant donné que j'ai échoué 4 tentatives de connexion

-

Quand j'échoue une 5ème tentative de connexion

-

Alors mon compte est temporairement bloqué -Et je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées" -Et je reçois un email de notification de blocage

-
-

5. Tentative de connexion pendant le blocage

-

Étant donné que mon compte est bloqué suite à 5 tentatives échouées -Et que seulement 5 minutes se sont écoulées

-

Quand j'essaie de me connecter avec les bons identifiants

-

Alors la connexion échoue -Et je vois le message "Compte bloqué. Réessayez dans 10 minutes"

-
-

6. Déblocage automatique après 15 minutes

-

Étant donné que mon compte est bloqué suite à 5 tentatives échouées -Et que 15 minutes se sont écoulées

-

Quand je me connecte avec les bons identifiants

-

Alors je suis connecté avec succès -Et le compteur de tentatives est réinitialisé

-
-

7. Reset du compteur après connexion réussie

-

Étant donné que j'ai échoué 3 tentatives de connexion

-

Quand je me connecte avec les bons identifiants

-

Alors je suis connecté avec succès -Et le compteur de tentatives est remis à 0

-
-

8. Reset automatique du compteur après 15 minutes sans blocage

-

Étant donné que j'ai échoué 3 tentatives de connexion -Et que 15 minutes se sont écoulées sans nouvelle tentative

-

Quand je consulte mon compteur de tentatives

-

Alors le compteur est réinitialisé à 0

-
-

9. Déblocage via lien "Mot de passe oublié"

-

Étant donné que mon compte est bloqué suite à 5 tentatives échouées

-

Quand j'utilise la fonction "Mot de passe oublié" -Et que je réinitialise mon mot de passe

-

Alors le blocage est levé immédiatement -Et je peux me connecter avec le nouveau mot de passe

-
-

10. Email de notification lors d'un blocage

-

Étant donné que j'ai échoué 5 tentatives de connexion

-

Alors je reçois un email avec:

-
| sujet | Tentatives de connexion suspectes détectées |
-|---|---|
-| contenu_contient | Votre compte a été temporairement bloqué |
-| lien_mot_de_passe | présent |
-
-
-

11. Connexion multi-device simultanée autorisée

-

Étant donné que je suis connecté sur un appareil iOS

-

Quand je me connecte également sur un appareil Android

-

Alors les deux sessions sont actives simultanément -Et je peux utiliser l'application sur les deux appareils

-
-
- -

Inscription utilisateur

-
-

En tant que nouvel utilisateur -Je veux créer un compte avec email et mot de passe -Afin d'accéder à l'application RoadWave

-
-

15 scénarios (14 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que Zitadel est configuré

-
-

1. Inscription réussie avec données valides

-

Étant donné que l'email "nouveau@example.com" n'existe pas

-

Quand je m'inscris avec les données suivantes:

-
| champ | valeur |
-|---|---|
-| email | nouveau@example.com |
-| mot_de_passe | Password123 |
-| pseudo | nouveau_user |
-| date_naissance | 1995-06-15 |
-
-

Alors mon compte est créé avec succès -Et je reçois un email de vérification -Et le lien de vérification expire dans 7 jours -Et je suis redirigé vers l'application

-
-

2. Inscription avec email déjà existant

-

Étant donné qu'un utilisateur existe avec l'email "existant@example.com"

-

Quand je m'inscris avec l'email "existant@example.com"

-

Alors l'inscription échoue -Et je vois le message "Cet email est déjà utilisé"

-
-

3. Inscription avec mot de passe invalide - trop court

-

Quand je m'inscris avec un mot de passe de moins de 8 caractères "Pass1"

-

Alors l'inscription échoue -Et je vois le message "Le mot de passe doit contenir au moins 8 caractères"

-
-

4. Inscription avec mot de passe invalide - sans majuscule

-

Quand je m'inscris avec un mot de passe sans majuscule "password123"

-

Alors l'inscription échoue -Et je vois le message "Le mot de passe doit contenir au moins une majuscule"

-
-

5. Inscription avec mot de passe invalide - sans chiffre

-

Quand je m'inscris avec un mot de passe sans chiffre "Password"

-

Alors l'inscription échoue -Et je vois le message "Le mot de passe doit contenir au moins un chiffre"

-
-

6. Inscription avec pseudo invalide - trop court

-

Quand je m'inscris avec un pseudo de 2 caractères "ab"

-

Alors l'inscription échoue -Et je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"

-
-

7. Inscription avec pseudo invalide - caractères spéciaux

-

Quand je m'inscris avec un pseudo contenant des caractères spéciaux "user@123"

-

Alors l'inscription échoue -Et je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"

-
-

8. Inscription avec email invalide

-

Quand je m'inscris avec un email invalide "email.invalide"

-

Alors l'inscription échoue -Et je vois le message "Format d'email invalide"

-
-

9. 📋 Plan: Inscription avec âge minimum non respecté

-

Étant donné la date du jour est "2026-01-21"

-

Quand je m'inscris avec une date de naissance ""

-

Alors l'inscription échoue -Et je vois le message "Vous devez avoir au moins 13 ans pour créer un compte"

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
date_naissanceage
2013-01-2212
2015-06-1510
2020-01-016
-
-

10. Inscription avec âge limite acceptable (13 ans)

-

Étant donné la date du jour est "2026-01-21"

-

Quand je m'inscris avec une date de naissance "2013-01-21"

-

Alors mon compte est créé avec succès -Et le mode Kids est activé automatiquement

-
-

11. Inscription avec âge supérieur à 18 ans

-

Étant donné la date du jour est "2026-01-21"

-

Quand je m'inscris avec une date de naissance "1990-06-15"

-

Alors mon compte est créé avec succès -Et j'ai accès à tous les contenus sans restriction d'âge

-
-

12. Données minimales requises à l'inscription

-

Quand je m'inscris sans fournir de nom complet -Et sans fournir de photo de profil -Et sans fournir de bio

-

Alors mon compte est créé avec succès -Et un avatar par défaut est généré -Et les champs optionnels sont vides

-
-

13. Renvoyer l'email de vérification

-

Étant donné que je me suis inscrit avec l'email "nouveau@example.com" -Et que je n'ai pas vérifié mon email

-

Quand je demande à renvoyer l'email de vérification

-

Alors un nouvel email de vérification est envoyé -Et le précédent lien est invalidé

-
-

14. Limite de renvoi d'email de vérification

-

Étant donné que je me suis inscrit avec l'email "nouveau@example.com" -Et que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui

-

Quand je demande à renvoyer l'email de vérification une 4ème fois

-

Alors la demande échoue -Et je vois le message "Vous avez atteint la limite de 3 renvois par jour"

-
-

15. Expiration du lien de vérification

-

Étant donné que je me suis inscrit il y a 8 jours -Et que je n'ai pas vérifié mon email

-

Quand j'essaie d'utiliser le lien de vérification

-

Alors la vérification échoue -Et je vois le message "Ce lien a expiré" -Et je peux demander un nouveau lien

-
-
- -

Récupération de compte

-
-

En tant qu'utilisateur ayant oublié son mot de passe -Je veux pouvoir réinitialiser mon mot de passe via email -Afin de récupérer l'accès à mon compte

-
-

14 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur existe avec l'email "user@test.fr"

-
-

1. Demander la réinitialisation du mot de passe

-

Quand je clique sur "Mot de passe oublié" -Et que je saisis mon email "user@test.fr"

-

Alors je reçois un email avec un lien de réinitialisation -Et le lien expire dans 1 heure -Et je vois le message "Email de réinitialisation envoyé"

-
-

2. Email inexistant lors de la demande de réinitialisation

-

Quand je demande une réinitialisation pour l'email "inexistant@test.fr"

-

Alors je vois le même message "Email de réinitialisation envoyé" -Mais aucun email n'est envoyé (sécurité - pas d'énumération d'emails)

-
-

3. Réinitialiser le mot de passe avec un lien valide

-

Étant donné que j'ai demandé une réinitialisation de mot de passe -Et que j'ai reçu le lien de réinitialisation

-

Quand je clique sur le lien -Et que je saisis un nouveau mot de passe "NouveauPass123" -Et que je confirme le nouveau mot de passe "NouveauPass123"

-

Alors mon mot de passe est modifié avec succès -Et je suis déconnecté de tous mes appareils sauf celui en cours -Et je reçois un email de confirmation de changement

-
-

4. Lien de réinitialisation expiré

-

Étant donné que j'ai demandé une réinitialisation il y a 2 heures

-

Quand j'essaie d'utiliser le lien

-

Alors je vois le message "Ce lien a expiré" -Et je peux demander un nouveau lien

-
-

5. Nouveau mot de passe ne respecte pas les règles

-

Étant donné que j'ai un lien de réinitialisation valide

-

Quand je saisis un nouveau mot de passe "faible"

-

Alors la réinitialisation échoue -Et je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre"

-
-

6. Confirmation du mot de passe ne correspond pas

-

Étant donné que j'ai un lien de réinitialisation valide

-

Quand je saisis un nouveau mot de passe "NouveauPass123" -Et que je confirme avec un mot de passe différent "AutrePass123"

-

Alors la réinitialisation échoue -Et je vois le message "Les mots de passe ne correspondent pas"

-
-

7. Limite de demandes de réinitialisation

-

Étant donné que j'ai déjà demandé 3 réinitialisations dans la dernière heure

-

Quand je demande une 4ème réinitialisation

-

Alors la demande échoue -Et je vois le message "Maximum 3 demandes par heure. Réessayez plus tard."

-
-

8. Compteur de demandes se réinitialise après 1 heure

-

Étant donné que j'ai demandé 3 réinitialisations -Et que 1 heure s'est écoulée

-

Quand je demande une nouvelle réinitialisation

-

Alors la demande réussit -Et je reçois un email avec un nouveau lien

-
-

9. Email de notification de changement de mot de passe

-

Étant donné que je viens de réinitialiser mon mot de passe

-

Alors je reçois un email de confirmation avec:

-
| sujet | Votre mot de passe a été modifié |
-|---|---|
-| contenu_contient | Votre mot de passe a été modifié |
-| date_heure | présente |
-| appareil | présent |
-| localisation | présente |
-| action_urgence | Lien si ce n'était pas vous |
-
-
-

10. Notification push si changement depuis appareil non reconnu

-

Étant donné que je me suis toujours connecté depuis mon iPhone -Et que je réinitialise mon mot de passe depuis un PC Windows

-

Alors je reçois une notification push sur mon iPhone avec:

-
| titre | Mot de passe modifié |
-|---|---|
-| message | Depuis Windows - Paris, France |
-| action | Sécuriser le compte si ce n'est pas vous |
-
-
-

11. Déconnexion de tous les appareils après réinitialisation

-

Étant donné que je suis connecté sur 4 appareils différents -Et que je réinitialise mon mot de passe depuis un navigateur web

-

Alors les 3 autres appareils sont déconnectés immédiatement -Et seule la session du navigateur web reste active -Et je vois le message "Vous avez été déconnecté des autres appareils par sécurité"

-
-

12. Lien de réinitialisation invalide si déjà utilisé

-

Étant donné que j'ai réinitialisé mon mot de passe avec un lien

-

Quand j'essaie de réutiliser le même lien

-

Alors je vois le message "Ce lien a déjà été utilisé" -Et je peux demander un nouveau lien si nécessaire

-
-

13. Nouveau lien invalide l'ancien

-

Étant donné que j'ai demandé une réinitialisation et reçu un lien

-

Quand je demande une nouvelle réinitialisation

-

Alors l'ancien lien est invalidé -Et seul le nouveau lien fonctionne

-
-

14. Réinitialisation débloque un compte bloqué

-

Étant donné que mon compte est bloqué après 5 tentatives de connexion

-

Quand je réinitialise mon mot de passe via email

-

Alors le blocage est levé immédiatement -Et je peux me connecter avec le nouveau mot de passe -Et le compteur de tentatives est remis à 0

-
-
- -

Gestion des sessions et tokens

-
-

En tant qu'utilisateur connecté -Je veux que mes sessions soient sécurisées et gérées automatiquement -Afin de maintenir l'accès à l'application sans friction

-
-

13 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté avec succès

-
-

1. Access token expire après 15 minutes

-

Étant donné que j'ai reçu un access token -Et que 15 minutes se sont écoulées

-

Quand je fais une requête API avec cet access token

-

Alors la requête échoue avec le code 401 -Et je vois le message "Token expiré"

-
-

2. Refresh automatique du token avec refresh token

-

Étant donné que mon access token a expiré -Et que mon refresh token est valide

-

Quand l'application demande un nouveau access token

-

Alors je reçois un nouvel access token valide pour 15 minutes -Et je reçois un nouveau refresh token (rotation) -Et l'ancien refresh token est invalidé

-
-

3. Refresh token expire après 30 jours d'inactivité

-

Étant donné que je me suis connecté il y a 30 jours -Et que je n'ai pas utilisé l'application depuis

-

Quand j'essaie d'utiliser mon refresh token

-

Alors la requête échoue -Et je dois me reconnecter avec email/password

-
-

4. Prolongation automatique de la session si l'app est utilisée

-

Étant donné que je me suis connecté il y a 25 jours -Et que j'utilise l'application régulièrement

-

Quand je fais une requête API

-

Alors ma session est automatiquement prolongée -Et mon refresh token reste valide

-
-

5. Détection de token replay attack

-

Étant donné que j'ai rafraîchi mon token -Et que j'ai reçu un nouveau refresh token

-

Quand j'essaie de réutiliser l'ancien refresh token

-

Alors la requête échoue -Et je vois le message "Token invalide ou révoqué" -Et toutes mes sessions sont révoquées par sécurité

-
-

6. Voir la liste des appareils connectés

-

Étant donné que je suis connecté sur 3 appareils différents

-

Quand je consulte la liste de mes appareils connectés

-

Alors je vois 3 appareils avec les informations suivantes:

-
| information | exemple |
-|---|---|
-| OS | iOS 17.1 |
-| Navigateur | Safari |
-| Dernière connexion | Il y a 2 heures |
-| Localisation | Paris, France (IP visible) |
-
-
-

7. Révoquer un appareil spécifique

-

Étant donné que je suis connecté sur mon iPhone et mon iPad

-

Quand je révoque la session de mon iPad depuis les paramètres

-

Alors la session iPad est immédiatement déconnectée -Et ma session iPhone reste active

-
-

8. Déconnecter tous les appareils sauf celui en cours

-

Étant donné que je suis connecté sur 4 appareils

-

Quand je clique sur "Déconnecter tous les appareils"

-

Alors les 3 autres appareils sont déconnectés -Et seul l'appareil actuel reste connecté

-
-

9. Alerte de connexion depuis nouveau device

-

Étant donné que je me suis toujours connecté depuis Paris

-

Quand je me connecte depuis un nouvel appareil à Lyon

-

Alors je reçois une notification push sur mes autres appareils -Et je reçois un email avec:

-
| sujet | Nouvelle connexion détectée |
-|---|---|
-| localisation | Lyon, France |
-| appareil | Android 14 - Chrome |
-| action | Lien pour révoquer la session |
-
-
-

10. Alerte de connexion suspecte depuis pays différent

-

Étant donné que je me suis toujours connecté depuis la France

-

Quand je me connecte depuis un appareil aux États-Unis

-

Alors je reçois une notification push immédiate -Et je reçois un email d'alerte de sécurité -Et la nouvelle session nécessite une validation 2FA même si désactivée

-
-

11. Déconnexion après 30 jours d'inactivité totale

-

Étant donné que je ne me suis pas connecté depuis 30 jours

-

Quand j'ouvre l'application

-

Alors je suis automatiquement déconnecté -Et je dois me reconnecter avec email/password -Et je vois le message "Session expirée après 30 jours d'inactivité"

-
-

12. Sessions multiples simultanées autorisées

-

Étant donné que je suis connecté sur:

-
| appareil |
-|---|
-| iPhone |
-| iPad |
-| PC Windows (Web) |
-
-

Quand je fais des actions sur les 3 appareils simultanément

-

Alors toutes les sessions fonctionnent sans conflit -Et chaque appareil maintient sa propre session

-
-

13. Validation de JWT via Zitadel

-

Étant donné que j'ai reçu un access token JWT

-

Quand l'API RoadWave valide le token

-

Alors la validation est faite localement avec la clé publique Zitadel -Et aucune requête externe n'est effectuée (performance) -Et le token contient les claims suivants:

-
| claim | valeur_exemple |
-|---|---|
-| sub | user-id-123 |
-| email | user@test.fr |
-| exp | timestamp + 15 minutes |
-| iss | zitadel.roadwave.com |
-
-
-
- -

Authentification à deux facteurs (2FA)

-
-

En tant qu'utilisateur soucieux de sécurité -Je veux activer la 2FA sur mon compte -Afin de protéger mon accès même si mon mot de passe est compromis

-
-

16 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté à mon compte

-
-

1. Activer la 2FA TOTP (Time-based One-Time Password)

-

Étant donné que la 2FA n'est pas activée sur mon compte

-

Quand je choisis d'activer la 2FA TOTP

-

Alors je vois un QR code à scanner -Et je vois le secret partagé en texte clair (backup) -Et je dois entrer un code de vérification depuis mon app authenticator

-

Quand je saisis un code TOTP valide

-

Alors la 2FA TOTP est activée avec succès -Et je reçois des codes de backup (10 codes)

-
-

2. Connexion avec 2FA TOTP activée

-

Étant donné que la 2FA TOTP est activée sur mon compte

-

Quand je me connecte avec email/password

-

Alors je suis redirigé vers la page de saisie du code 2FA

-

Quand je saisis un code TOTP valide de mon authenticator

-

Alors je suis connecté avec succès

-
-

3. Connexion échouée avec code TOTP invalide

-

Étant donné que la 2FA TOTP est activée

-

Quand je me connecte avec email/password -Et que je saisis un code TOTP invalide "000000"

-

Alors la connexion échoue -Et je vois le message "Code d'authentification invalide" -Et je peux réessayer

-
-

4. Utiliser un code de backup pour 2FA

-

Étant donné que la 2FA TOTP est activée -Et que j'ai perdu l'accès à mon authenticator

-

Quand je me connecte avec email/password -Et que je clique sur "Utiliser un code de backup" -Et que je saisis un code de backup valide

-

Alors je suis connecté avec succès -Et le code de backup utilisé est invalidé -Et il me reste 9 codes de backup

-
-

5. Activer la 2FA par email

-

Étant donné que la 2FA n'est pas activée

-

Quand je choisis d'activer la 2FA par email

-

Alors la 2FA email est activée immédiatement -Et je vois le message "2FA email activée. Vous recevrez un code à chaque connexion"

-
-

6. Connexion avec 2FA email

-

Étant donné que la 2FA email est activée

-

Quand je me connecte avec email/password

-

Alors je reçois un email avec un code à 6 chiffres -Et le code expire dans 10 minutes -Et je dois saisir ce code pour terminer la connexion

-
-

7. Code 2FA email expiré

-

Étant donné que la 2FA email est activée -Et que je me suis connecté avec email/password -Et que j'ai reçu un code 2FA par email il y a 11 minutes

-

Quand je saisis ce code

-

Alors la connexion échoue -Et je vois le message "Code expiré. Demandez un nouveau code."

-
-

8. Renvoyer le code 2FA email

-

Étant donné que la 2FA email est activée -Et que je suis sur la page de saisie du code 2FA

-

Quand je clique sur "Renvoyer le code"

-

Alors je reçois un nouveau code par email -Et l'ancien code est invalidé

-
-

9. Ajouter un appareil de confiance (skip 2FA pendant 30 jours)

-

Étant donné que la 2FA TOTP est activée

-

Quand je me connecte avec email/password et code TOTP -Et que je coche "Ne plus demander sur cet appareil"

-

Alors je suis connecté avec succès -Et cet appareil est enregistré comme "appareil de confiance"

-

Quand je me reconnecte dans les 30 jours suivants sur ce même appareil

-

Alors je ne dois pas saisir de code 2FA

-
-

10. Appareil de confiance expire après 30 jours

-

Étant donné que j'ai enregistré un appareil de confiance il y a 31 jours

-

Quand je me connecte depuis cet appareil

-

Alors je dois saisir un code 2FA -Et je vois le message "Appareil de confiance expiré. Veuillez vous authentifier"

-
-

11. Voir la liste des appareils de confiance

-

Étant donné que j'ai enregistré 3 appareils de confiance

-

Quand je consulte mes paramètres de sécurité

-

Alors je vois la liste de mes 3 appareils de confiance avec:

-
| information | exemple |
-|---|---|
-| Nom | iPhone 13 - Safari |
-| Date ajout | 15 janvier 2026 |
-| Dernière vue | Il y a 2 heures |
-| Expire le | 14 février 2026 |
-
-
-

12. Révoquer un appareil de confiance

-

Étant donné que j'ai un iPhone enregistré comme appareil de confiance

-

Quand je révoque cet appareil depuis les paramètres

-

Alors l'appareil est supprimé de la liste

-

Quand je me reconnecte depuis cet iPhone

-

Alors je dois saisir un code 2FA

-
-

13. Révoquer tous les appareils de confiance

-

Étant donné que j'ai 5 appareils de confiance enregistrés

-

Quand je clique sur "Révoquer tous les appareils de confiance"

-

Alors tous les appareils sont révoqués -Et je vois le message "Tous les appareils de confiance ont été révoqués"

-
-

14. 2FA forcée pour connexion suspecte malgré appareil de confiance

-

Étant donné que j'ai un appareil de confiance enregistré en France -Et que je me connecte depuis ce même appareil mais avec une IP américaine

-

Quand je tente de me connecter

-

Alors la 2FA est requise malgré l'appareil de confiance -Et je vois le message "Connexion suspecte détectée. Authentification requise."

-
-

15. Désactiver la 2FA

-

Étant donné que la 2FA TOTP est activée

-

Quand je désactive la 2FA depuis mes paramètres -Et que je confirme avec mon mot de passe

-

Alors la 2FA est désactivée -Et tous les codes de backup sont invalidés -Et tous les appareils de confiance sont révoqués

-
-

16. Régénérer les codes de backup

-

Étant donné que la 2FA est activée -Et que j'ai utilisé 8 codes de backup sur 10

-

Quand je demande à régénérer les codes de backup

-

Alors je reçois 10 nouveaux codes -Et tous les anciens codes (utilisés ou non) sont invalidés

-
-
- -

Vérification d'email

-
-

En tant qu'utilisateur inscrit -Je veux vérifier mon adresse email -Afin d'accéder à toutes les fonctionnalités selon mon rôle

-
-

10 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Auditeur avec email non vérifié - lecture illimitée

-

Étant donné que je suis un auditeur avec email non vérifié

-

Quand j'essaie d'écouter du contenu

-

Alors je peux écouter tous les contenus sans limite

-
-

2. Auditeur avec email non vérifié - création limitée à 5 contenus

-

Étant donné que je suis un auditeur avec email non vérifié -Et que j'ai créé 4 contenus

-

Quand je crée un 5ème contenu

-

Alors le contenu est créé avec succès -Mais quand j'essaie de créer un 6ème contenu -Alors la création échoue -Et je vois le message "Vérifiez votre email pour créer plus de contenus"

-
-

3. Rappel de vérification après le 3ème contenu créé

-

Étant donné que je suis un auditeur avec email non vérifié -Et que j'ai créé 2 contenus

-

Quand je crée mon 3ème contenu

-

Alors le contenu est créé avec succès -Et je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée"

-
-

4. Auditeur vérifie son email

-

Étant donné que je suis un auditeur avec email non vérifié -Et que j'ai reçu un lien de vérification

-

Quand je clique sur le lien de vérification dans l'email

-

Alors mon email est marqué comme vérifié -Et je vois le message "Email vérifié avec succès" -Et toutes les fonctionnalités sont débloquées

-
-

5. Créateur doit vérifier son email sous 7 jours pour monétisation

-

Étant donné que je suis inscrit comme créateur -Et que mon email n'est pas vérifié -Et que je remplis les conditions de monétisation

-

Quand j'essaie d'accéder au programme de monétisation

-

Alors l'accès est refusé -Et je vois le message "Vérifiez votre email pour accéder à la monétisation"

-
-

6. Créateur ne peut pas publier de contenus illimités sans vérification

-

Étant donné que je suis un créateur avec email non vérifié -Et que j'ai créé 5 contenus

-

Quand j'essaie de créer un 6ème contenu

-

Alors la création échoue -Et je vois le message "Vérifiez votre email pour publier des contenus illimités"

-
-

7. Créateur vérifie son email et déboque tout

-

Étant donné que je suis un créateur avec email non vérifié -Et que j'ai reçu un lien de vérification

-

Quand je clique sur le lien de vérification

-

Alors mon email est marqué comme vérifié -Et je peux publier des contenus illimités -Et je peux accéder au programme de monétisation si j'en remplis les conditions

-
-

8. KYC impossible sans email vérifié

-

Étant donné que je suis un créateur avec email non vérifié

-

Quand j'essaie de compléter le KYC via Mangopay

-

Alors l'accès au KYC est refusé -Et je vois le message "Vérifiez votre email avant de procéder au KYC"

-
-

9. Tentative de vérification avec un lien déjà utilisé

-

Étant donné que j'ai déjà vérifié mon email avec un lien

-

Quand j'essaie de réutiliser le même lien de vérification

-

Alors la vérification échoue -Et je vois le message "Ce lien a déjà été utilisé"

-
-

10. Auditeur vérifié peut créer plus de 5 contenus

-

Étant donné que je suis un auditeur avec email vérifié -Et que j'ai créé 10 contenus

-

Quand je crée un 11ème contenu

-

Alors le contenu est créé avec succès -Et il n'y a pas de limite de création

-
-
- -

Métadonnées et publication de contenu

-
-

En tant que créateur -Je veux remplir les métadonnées de mon contenu -Afin de le publier sur RoadWave

-
-

34 scénarios (32 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis un créateur connecté -Et que mon fichier audio est encodé et prêt

-
-

1. Publication avec toutes les métadonnées obligatoires

-

Quand je remplis les métadonnées suivantes:

-
| champ | valeur |
-|---|---|
-| Titre | Histoire de la Tour Eiffel |
-| Type géo | Ancré |
-| Zone | Point GPS (48.8584, 2.2945, 500m) |
-| Tags | Voyage, Culture générale |
-| Classification âge | Tout public |
-
-

Alors la publication réussit -Et mon contenu est soumis pour validation

-
-

2. Titre valide entre 5 et 100 caractères

-

Quand je saisis un titre de 50 caractères

-

Alors le titre est accepté -Et la validation passe

-
-

3. Titre trop court (<5 caractères)

-

Quand je saisis un titre de 4 caractères "Test"

-

Alors la publication échoue -Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

-
-

4. Titre trop long (>100 caractères)

-

Quand je saisis un titre de 101 caractères

-

Alors la publication échoue -Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

-
-

5. Titre à exactement 5 caractères accepté

-

Quand je saisis un titre de exactement 5 caractères "Titre"

-

Alors le titre est accepté

-
-

6. Titre à exactement 100 caractères accepté

-

Quand je saisis un titre de exactement 100 caractères

-

Alors le titre est accepté

-
-

7. Sélectionner type géo "Ancré"

-

Quand je sélectionne le type géo "Ancré"

-

Alors le système applique une pondération géo de 0.7 -Et je dois définir une zone de diffusion précise

-
-

8. Sélectionner type géo "Contextuel"

-

Quand je sélectionne le type géo "Contextuel"

-

Alors le système applique une pondération géo de 0.5 -Et je peux définir une zone ville/département/région

-
-

9. Sélectionner type géo "Neutre"

-

Quand je sélectionne le type géo "Neutre"

-

Alors le système applique une pondération géo de 0.2 -Et je peux définir une zone nationale

-
-

10. Zone diffusion - Point GPS avec rayon

-

Quand je choisis "Point GPS" -Et que je définis les coordonnées (48.8584, 2.2945) -Et que je définis un rayon de 500 mètres

-

Alors la zone est validée -Et le contenu sera diffusé dans un rayon de 500m autour du point

-
-

11. Zone diffusion - Rayon minimum 100m

-

Quand je définis un rayon de 50 mètres (< 100m)

-

Alors la validation échoue -Et je vois le message "Le rayon doit être entre 100m et 10km"

-
-

12. Zone diffusion - Rayon maximum 10km

-

Quand je définis un rayon de 15 km (> 10km)

-

Alors la validation échoue -Et je vois le message "Le rayon doit être entre 100m et 10km"

-
-

13. Zone diffusion - Ville depuis référentiel INSEE

-

Quand je choisis "Ville"

-

Alors je vois une liste de villes du référentiel INSEE

-

Quand je sélectionne "Paris (75000)"

-

Alors la zone est définie sur toute la ville de Paris

-
-

14. Zone diffusion - Département

-

Quand je choisis "Département" -Et que je sélectionne "Ille-et-Vilaine (35)"

-

Alors la zone couvre tout le département 35

-
-

15. Zone diffusion - Région

-

Quand je choisis "Région" -Et que je sélectionne "Bretagne"

-

Alors la zone couvre toute la région Bretagne

-
-

16. Zone diffusion - National

-

Quand je choisis "National"

-

Alors la zone couvre toute la France -Et aucune restriction géographique n'est appliquée

-
-

17. Zones mutuellement exclusives

-

Étant donné que j'ai sélectionné "Point GPS"

-

Quand j'essaie de sélectionner également "Ville"

-

Alors la première sélection est remplacée -Et seule "Ville" reste active

-
-

18. Sélectionner 1 tag minimum

-

Quand je sélectionne 1 tag "Voyage"

-

Alors la validation passe -Et le contenu est tagué "Voyage"

-
-

19. Sélectionner 3 tags maximum

-

Quand je sélectionne 3 tags "Automobile", "Technologie", "Sport"

-

Alors la validation passe -Et le contenu est tagué avec les 3 tags

-
-

20. Impossible de sélectionner 0 tag

-

Quand j'essaie de publier sans sélectionner de tag

-

Alors la publication échoue -Et je vois le message "Vous devez sélectionner entre 1 et 3 tags"

-
-

21. Impossible de sélectionner 4 tags

-

Quand j'essaie de sélectionner 4 tags

-

Alors le 4ème tag ne peut pas être ajouté -Et je vois le message "Maximum 3 tags"

-
-

22. Tags disponibles dans la liste

-

Quand je consulte la liste des tags

-

Alors je vois les tags suivants:

-
| tag |
-|---|
-| Automobile |
-| Voyage |
-| Famille |
-| Amour |
-| Musique |
-| Économie |
-| Cryptomonnaie |
-| Politique |
-| Culture générale |
-| Sport |
-| Technologie |
-| Santé |
-
-
-

23. Classification âge obligatoire

-

Quand j'essaie de publier sans classification âge

-

Alors la publication échoue -Et je vois le message "Vous devez sélectionner une classification d'âge"

-
-

24. 📋 Plan: Sélectionner classification âge

-

Quand je sélectionne la classification ""

-

Alors le contenu sera visible pour ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
classificationpublic_cible
Tout publicTous les utilisateurs
13+Utilisateurs 13 ans et plus
16+Utilisateurs 16 ans et plus
18+Utilisateurs 18 ans et plus
-
-

25. Image de couverture auto-générée selon type géo

-

Étant donné que je choisis le type géo "Ancré" -Et que mon tag principal est "Voyage"

-

Quand la publication est soumise

-

Alors une image de couverture est générée automatiquement:

-
| paramètre | valeur |
-|---|---|
-| Icône | 📍 (Ancré) |
-| Couleur | Bleu-vert (Voyage) |
-| Format | 800×800px PNG |
-
-
-

26. Image de couverture type Contextuel

-

Étant donné que je choisis "Contextuel"

-

Quand l'image est générée

-

Alors l'icône est 🌍 (Contextuel)

-
-

27. Image de couverture type Neutre

-

Étant donné que je choisis "Neutre"

-

Quand l'image est générée

-

Alors l'icône est 🎧 (Neutre)

-
-

28. 📋 Plan: Couleur selon tag principal

-

Étant donné que mon tag principal est ""

-

Quand l'image est générée

-

Alors la couleur de fond est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tagcouleur
AutomobileBleu
VoyageVert
MusiqueRouge
ÉconomieGris
SportOrange
-
-

29. Champs optionnels non obligatoires

-

Quand je publie sans description -Et sans image de couverture personnalisée

-

Alors la publication réussit -Et les champs optionnels restent vides -Et une image par défaut est générée

-
-

30. Temps de publication estimé 2 minutes

-

Étant donné que mon fichier audio est prêt

-

Quand je commence à remplir les métadonnées

-

Alors je peux publier en environ 2 minutes

-
-

31. Publication rapide sans friction

-

Quand je publie mon premier contenu

-

Alors aucun champ complexe n'est demandé -Et je ne suis pas bloqué sur description ou image -Et la publication est fluide

-
-

32. Prévisualisation avant publication

-

Étant donné que j'ai rempli toutes les métadonnées

-

Quand je clique sur "Prévisualiser"

-

Alors je vois un aperçu de mon contenu:

-
| élément | affiché |
-|---|---|
-| Titre | ✅ |
-| Image couverture | ✅ |
-| Tags | ✅ |
-| Zone diffusion | ✅ |
-| Durée audio | ✅ |
-| Classification | ✅ |
-
-
-

33. Enregistrer brouillon

-

Étant donné que j'ai commencé à remplir les métadonnées

-

Quand je clique sur "Enregistrer brouillon"

-

Alors mes métadonnées sont sauvegardées -Et je peux reprendre la publication plus tard

-
-

34. Reprendre brouillon

-

Étant donné que j'ai un brouillon sauvegardé

-

Quand j'accède à mes contenus

-

Alors je vois le brouillon avec statut "📝 Brouillon" -Et je peux reprendre la publication

-
-
- -

Modification et suppression de contenu

-
-

En tant que créateur -Je veux pouvoir modifier ou supprimer mes contenus -Afin de garder le contrôle sur mes publications

-
-

30 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis un créateur connecté -Et que j'ai publié un contenu

-
-

1. Modifier le titre d'un contenu

-

Étant donné que mon contenu a le titre "Histoire de Paris"

-

Quand je modifie le titre en "Histoire fascinante de Paris"

-

Alors la modification est enregistrée immédiatement -Et je vois le message "Titre modifié avec succès"

-
-

2. Correction de coquilles dans le titre

-

Étant donné que mon titre contient une faute "Histoore de Paris"

-

Quand je corrige en "Histoire de Paris"

-

Alors la modification est acceptée -Et le titre corrigé est affiché

-
-

3. Ajouter une description ultérieurement

-

Étant donné que j'ai publié sans description

-

Quand j'ajoute une description "Découvrez l'histoire de la capitale"

-

Alors la description est enregistrée -Et elle est visible sur la page du contenu

-
-

4. Modifier la description existante

-

Étant donné que mon contenu a déjà une description

-

Quand je modifie la description

-

Alors la nouvelle description remplace l'ancienne -Et la modification est immédiate

-
-

5. Modifier les tags pour ajuster pertinence

-

Étant donné que mon contenu est tagué "Sport", "Musique"

-

Quand je change les tags en "Sport", "Santé"

-

Alors les nouveaux tags sont appliqués -Et l'algorithme utilise les nouveaux tags pour recommandations

-
-

6. Personnaliser l'image de couverture

-

Étant donné que mon contenu a une image auto-générée

-

Quand j'uploade une image personnalisée 800×800px

-

Alors l'image personnalisée remplace l'image par défaut -Et elle est visible sur le contenu

-
-

7. Impossible de modifier l'audio

-

Étant donné que mon contenu audio est publié

-

Quand j'essaie de remplacer le fichier audio

-

Alors la modification est refusée -Et je vois le message "L'audio ne peut pas être modifié après publication"

-
-

8. Raison - Éviter fraude audio

-

Étant donné que je veux changer l'audio après validation

-

Quand j'essaie de modifier

-

Alors le système refuse pour éviter:

-
| risque |
-|---|
-| Uploader contenu validé puis remplacer spam |
-| Fraude sur l'intégrité du contenu |
-
-
-

9. Impossible de modifier la zone de diffusion

-

Étant donné que mon contenu est diffusé à Paris

-

Quand j'essaie de changer la zone en "National"

-

Alors la modification est refusée -Et je vois le message "La zone de diffusion ne peut pas être modifiée"

-
-

10. Raison - Éviter manipulation algorithme

-

Étant donné que je veux changer ma zone

-

Quand j'essaie de modifier

-

Alors le système refuse pour éviter:

-
| manipulation |
-|---|
-| Créer "Local Paris" puis changer en "National" |
-| Boost artificiel de visibilité |
-
-
-

11. Impossible de modifier le type géo

-

Étant donné que mon contenu est type "Neutre" (pondération 0.2)

-

Quand j'essaie de changer en "Ancré" (pondération 0.7)

-

Alors la modification est refusée -Et je vois le message "Le type géographique ne peut pas être modifié"

-
-

12. Raison - Éviter abus de pondération

-

Étant donné que je veux changer le type géo

-

Quand j'essaie de modifier

-

Alors le système refuse pour éviter:

-
| abus |
-|---|
-| Créer "Neutre" puis passer en "Ancré" |
-| Manipulation de la pondération algorithme |
-
-
-

13. Impossible de modifier la classification âge

-

Étant donné que mon contenu est classé "Tout public"

-

Quand j'essaie de changer en "18+"

-

Alors la modification est refusée -Et je vois le message "La classification d'âge ne peut pas être modifiée"

-
-

14. Raison - Sécurité mineurs

-

Étant donné que je veux changer la classification

-

Quand j'essaie de modifier

-

Alors le système refuse pour garantir:

-
| protection |
-|---|
-| Classification vérifiée en modération |
-| Pas de contournement validation |
-| Sécurité des mineurs |
-
-
-

15. Solution si besoin de changer audio/zone/classification

-

Étant donné que je veux absolument changer l'audio

-

Quand je consulte les options

-

Alors je vois "Supprimer et republier le contenu" -Et c'est la seule solution disponible

-
-

16. Republication après suppression - créateur <3 validations

-

Étant donné que je suis un nouveau créateur (2 contenus validés) -Et que je supprime puis republie un contenu

-

Quand je republie avec les modifications

-

Alors le contenu repasse en file de validation -Et une nouvelle validation est effectuée

-
-

17. Republication après suppression - créateur vérifié

-

Étant donné que je suis créateur vérifié (≥3 contenus validés) -Et que je supprime puis republie un contenu

-

Quand je republie avec les modifications

-

Alors le contenu est publié immédiatement -Et aucune validation préalable n'est requise

-
-

18. Suppression de contenu immédiate

-

Quand je clique sur "Supprimer le contenu" -Et que je confirme la suppression

-

Alors le contenu est supprimé immédiatement -Et disparaît de la liste publique

-
-

19. Confirmation avant suppression

-

Quand je clique sur "Supprimer"

-

Alors je vois un message de confirmation:

-
| titre | Êtes-vous sûr ? |
-|---|---|
-| message | Cette action est définitive |
-| warning | Le contenu sera supprimé définitivement |
-| actions | Confirmer / Annuler |
-
-
-

20. Suppression définitive et non réversible

-

Étant donné que j'ai supprimé un contenu

-

Quand j'essaie de le récupérer

-

Alors la récupération est impossible -Et le contenu est définitivement perdu

-
-

21. Suppression BDD + CDN sous 5 minutes

-

Quand je supprime un contenu

-

Alors l'entrée en base de données est marquée "deleted" -Et les fichiers CDN sont marqués pour suppression -Et la suppression effective a lieu sous 5 minutes

-
-

22. Historique auditeurs conservé anonymisé

-

Étant donné que 1000 personnes ont écouté mon contenu

-

Quand je supprime le contenu

-

Alors leur historique est conservé -Mais marqué "Contenu supprimé par créateur" -Et la durée d'écoute est conservée pour leurs stats

-
-

23. Analytics plateforme anonymisées conservées

-

Étant donné que mon contenu a généré 10K écoutes

-

Quand je supprime le contenu

-

Alors les métriques globales sont conservées anonymement:

-
| métrique | conservée |
-|---|---|
-| Total écoutes | ✅ (anonyme) |
-| Durée totale | ✅ (anonyme) |
-| Catégorie | ✅ (anonyme) |
-| Auteur | ❌ (anonymisé) |
-
-

Et c'est conforme RGPD

-
-

24. Fichiers CDN supprimés sous 24h

-

Étant donné que mon contenu est supprimé

-

Quand 24 heures s'écoulent

-

Alors tous les fichiers audio sont purgés du CDN Bunny -Et l'espace de stockage est libéré

-
-

25. Pas de notification aux auditeurs

-

Étant donné que 500 utilisateurs ont écouté mon contenu

-

Quand je supprime le contenu

-

Alors aucune notification n'est envoyée aux auditeurs -Et il n'y a pas d'effet Streisand

-
-

26. Auditeur tente de réécouter contenu supprimé

-

Étant donné qu'un auditeur a écouté mon contenu -Et que j'ai supprimé ce contenu

-

Quand l'auditeur tente de le réécouter depuis son historique

-

Alors il voit le message "Ce contenu n'est plus disponible" -Et la lecture est impossible

-
-

27. Historique auditeur conserve trace

-

Étant donné qu'un auditeur a écouté mon contenu le 15 janvier -Et que je supprime le contenu le 20 janvier

-

Quand l'auditeur consulte son historique

-

Alors il voit "Vous avez écouté ce contenu le 15 janvier 2026" -Et le titre est remplacé par "Contenu supprimé" -Et la date d'écoute est conservée

-
-

28. Statistiques créateur après suppression

-

Étant donné que j'ai publié 10 contenus -Et que je supprime 2 contenus

-

Quand je consulte mes statistiques globales

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Contenus publiés | 8 (actifs) |
-| Total historique | 10 |
-| Suppressions | 2 |
-
-

Et l'historique des suppressions est visible

-
-

29. Limite de modifications par contenu

-

Étant donné que j'ai modifié un titre 10 fois

-

Quand j'essaie de modifier une 11ème fois

-

Alors la modification est acceptée

-
-

30. Historique des modifications visible

-

Étant donné que j'ai modifié un contenu plusieurs fois

-

Quand je consulte l'historique

-

Alors je vois:

-
| date | modification |
-|---|---|
-| 21/01/2026 | Titre changé |
-| 20/01/2026 | Tags modifiés |
-| 19/01/2026 | Description ajoutée |
-
-

Et je peux tracer toutes les modifications

-
-
- -

Upload et encodage de contenu audio

-
-

En tant que créateur -Je veux uploader mon contenu audio -Afin qu'il soit encodé et disponible pour les auditeurs

-
-

29 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis un créateur connecté

-
-

1. Upload fichier MP3 valide

-

Quand j'uploade un fichier MP3 de 50 MB et 30 minutes

-

Alors l'upload réussit -Et le fichier est envoyé vers Bunny Storage temporaire -Et un job d'encodage asynchrone est lancé

-
-

2. Upload fichier AAC valide (.aac)

-

Quand j'uploade un fichier AAC de 80 MB et 1 heure

-

Alors l'upload réussit -Et le fichier est accepté -Et l'encodage démarre

-
-

3. Upload fichier M4A valide

-

Quand j'uploade un fichier M4A de 100 MB et 2 heures

-

Alors l'upload réussit -Et le fichier est traité comme AAC -Et l'encodage démarre

-
-

4. Rejet fichier WAV (non supporté)

-

Quand j'essaie d'uploader un fichier WAV

-

Alors l'upload échoue -Et je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

-
-

5. Rejet fichier FLAC (non supporté)

-

Quand j'essaie d'uploader un fichier FLAC

-

Alors l'upload échoue -Et je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

-
-

6. Validation taille maximale 200 MB

-

Quand j'essaie d'uploader un fichier MP3 de 201 MB

-

Alors l'upload échoue -Et je vois le message "Fichier trop volumineux (max 200 MB)"

-
-

7. Upload à la limite de 200 MB accepté

-

Quand j'uploade un fichier MP3 de exactement 200 MB

-

Alors l'upload réussit -Et le fichier est accepté

-
-

8. Validation durée maximale 4 heures

-

Quand j'essaie d'uploader un fichier de 4h 10min

-

Alors l'upload échoue -Et je vois le message "Durée trop longue (max 4 heures)"

-
-

9. Upload à la limite de 4h accepté

-

Quand j'uploade un fichier de exactement 4 heures

-

Alors l'upload réussit -Et le fichier est accepté

-
-

10. Validation format côté client

-

Quand je sélectionne un fichier dans l'interface

-

Alors la validation du format est faite immédiatement côté client -Et je suis informé avant même de lancer l'upload si le format est invalide

-
-

11. Double validation côté backend

-

Étant donné qu'un fichier a passé la validation client

-

Quand le backend reçoit le fichier

-

Alors une validation supplémentaire est effectuée -Et le format et l'intégrité sont vérifiés

-
-

12. Pipeline d'encodage - étape 1 upload

-

Quand j'uploade un fichier MP3 valide

-

Alors le fichier est stocké temporairement dans Bunny Storage -Et un job d'encodage est mis en file d'attente

-
-

13. Pipeline d'encodage - validation format

-

Étant donné qu'un job d'encodage est lancé

-

Quand le worker Go traite le fichier

-

Alors le format est validé avec FFmpeg -Et l'intégrité du fichier est vérifiée

-
-

14. Pipeline d'encodage - génération 3 profils Opus

-

Étant donné qu'un fichier audio est validé

-

Quand l'encodage démarre

-

Alors 3 profils Opus sont générés:

-
| qualité | bitrate | usage |
-|---|---|---|
-| Basse | 24 kbps | 2G/Edge |
-| Standard | 48 kbps | 3G |
-| Haute | 64 kbps | 4G/5G |
-
-
-

15. Pipeline d'encodage - génération segments HLS

-

Étant donné que les profils Opus sont générés

-

Quand l'encodage continue

-

Alors un fichier manifest .m3u8 est créé -Et des segments .ts sont générés -Et le contenu est prêt pour streaming HLS

-
-

16. Pipeline d'encodage - génération image par défaut

-

Étant donné que l'encodage est en cours

-

Quand les métadonnées sont traitées

-

Alors une image de couverture par défaut est générée -Et l'image fait 800×800px au format PNG

-
-

17. Pipeline d'encodage - suppression fichier original

-

Étant donné que l'encodage est terminé avec succès

-

Quand tous les fichiers de sortie sont générés

-

Alors le fichier original MP3/AAC est supprimé -Et seuls les profils Opus et HLS sont conservés -Et l'espace de stockage est économisé

-
-

18. Temps d'encodage contenu 5 minutes

-

Étant donné qu'un fichier de 5 minutes est uploadé

-

Quand l'encodage démarre

-

Alors l'encodage prend environ 30 secondes -Et je reçois une notification "Contenu prêt à publier"

-
-

19. Temps d'encodage podcast 1 heure

-

Étant donné qu'un fichier de 1 heure est uploadé

-

Quand l'encodage démarre

-

Alors l'encodage prend environ 5 minutes -Et une barre de progression est affichée

-
-

20. Temps d'encodage podcast 4 heures

-

Étant donné qu'un fichier de 4 heures est uploadé

-

Quand l'encodage démarre

-

Alors l'encodage prend environ 20 minutes -Et je peux fermer l'app (traitement asynchrone)

-
-

21. Notification "Contenu prêt à publier"

-

Étant donné que mon contenu est en cours d'encodage

-

Quand l'encodage se termine avec succès

-

Alors je reçois une notification push "✅ Votre contenu est prêt à publier" -Et je peux accéder à l'interface de publication

-
-

22. Échec d'encodage - fichier corrompu

-

Étant donné qu'un fichier MP3 corrompu est uploadé

-

Quand l'encodage démarre

-

Alors l'encodage échoue -Et je reçois une notification "❌ Erreur d'encodage: fichier corrompu" -Et le fichier temporaire est supprimé

-
-

23. Écoute accélérée - vitesses disponibles

-

Étant donné qu'un contenu est publié

-

Quand un auditeur écoute le contenu

-

Alors il peut choisir parmi les vitesses:

-
| vitesse | usage |
-|---|---|
-| 0.75x | Compréhension difficile |
-| 1.0x | Normal (défaut) |
-| 1.25x | Gain léger |
-| 1.5x | Podcasts longs |
-| 2.0x | Survol rapide |
-
-
-

24. Écoute accélérée pour modérateurs

-

Étant donné que je suis un modérateur -Et qu'un contenu de 30 secondes est à valider

-

Quand je l'écoute à 2.0x

-

Alors je termine l'écoute en 15 secondes -Et ma productivité est doublée

-
-

25. Écoute accélérée pour auditeurs

-

Étant donné que je suis un auditeur -Et qu'un podcast de 1 heure est disponible

-

Quand je configure la vitesse à 1.5x

-

Alors j'écoute le podcast en 40 minutes -Et je gagne 20 minutes

-
-

26. Sauvegarde préférence vitesse d'écoute

-

Étant donné que je configure la vitesse à 1.5x

-

Quand j'écoute plusieurs contenus

-

Alors tous les contenus sont lus à 1.5x par défaut -Et ma préférence est sauvegardée

-
-

27. Scalabilité horizontale des workers

-

Étant donné que 100 contenus sont uploadés simultanément

-

Quand les jobs d'encodage sont distribués

-

Alors plusieurs workers Go traitent les jobs en parallèle -Et Kubernetes scale automatiquement les pods -Et tous les contenus sont encodés sans délai excessif

-
-

28. Statut d'encodage visible

-

Étant donné que mon contenu est en cours d'encodage

-

Quand je consulte mes contenus

-

Alors je vois le statut:

-
| état | affichage |
-|---|---|
-| En attente | ⏳ File d'attente |
-| En cours | ⚙️ Encodage en cours (45%) |
-| Terminé | ✅ Prêt à publier |
-| Échec | ❌ Erreur - Réessayer |
-
-
-

29. Réessayer après échec d'encodage

-

Étant donné que l'encodage de mon contenu a échoué

-

Quand je clique sur "Réessayer"

-

Alors un nouveau job d'encodage est lancé -Et je peux tenter à nouveau

-
-
- -

Validation des 3 premiers contenus

-
-

En tant que nouveau créateur -Je veux que mes 3 premiers contenus soient validés -Afin de devenir créateur vérifié

-
-

30 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis un nouveau créateur

-
-

1. Premier contenu passe en file de validation

-

Quand je publie mon premier contenu

-

Alors le contenu passe en file d'attente modération -Et je vois le message "Votre contenu est en cours de validation (24-48h)" -Et le contenu n'est pas encore visible publiquement

-
-

2. Deuxième contenu passe également en validation

-

Étant donné que mon premier contenu a été validé

-

Quand je publie mon deuxième contenu

-

Alors le contenu passe en file d'attente modération -Et le délai estimé est 24-48h

-
-

3. Troisième contenu - dernière validation

-

Étant donné que mes 2 premiers contenus ont été validés

-

Quand je publie mon troisième contenu

-

Alors le contenu passe en file d'attente modération -Et je vois "Dernière validation avant statut vérifié ✓"

-
-

4. Modérateur écoute 30 secondes du contenu

-

Étant donné qu'un contenu est en file de validation

-

Quand le modérateur junior l'examine

-

Alors il écoute les 30 premières secondes -Et il vérifie les métadonnées

-
-

5. Validation - Qualité audio acceptable

-

Étant donné qu'un contenu a une qualité audio claire

-

Quand le modérateur l'écoute

-

Alors il vérifie que l'audio est compréhensible -Et qu'il n'y a pas de grésillement excessif

-
-

6. Rejet - Qualité audio insuffisante

-

Étant donné qu'un contenu a un audio très grésillant

-

Quand le modérateur l'écoute

-

Alors le contenu est rejeté -Et la raison est "Qualité audio insuffisante"

-
-

7. Validation - Respect des règles

-

Étant donné qu'un contenu respecte les règles

-

Quand le modérateur l'examine

-

Alors il vérifie qu'il n'y a pas de contenu prohibé:

-
| type prohibé |
-|---|
-| Haine |
-| Violence |
-| Spam |
-| Illégalité |
-
-
-

8. Rejet - Contenu haineux détecté

-

Étant donné qu'un contenu contient des propos haineux

-

Quand le modérateur l'écoute

-

Alors le contenu est rejeté immédiatement -Et la raison est "Contenu haineux (violation des règles)" -Et le créateur peut recevoir un strike

-
-

9. Validation - Classification âge cohérente

-

Étant donné qu'un contenu familial est classé "Tout public"

-

Quand le modérateur l'écoute

-

Alors il vérifie que la classification correspond au contenu -Et le contenu est accepté

-
-

10. Rejet - Classification incorrecte

-

Étant donné qu'un contenu adulte est classé "Tout public"

-

Quand le modérateur détecte l'incohérence

-

Alors le contenu est rejeté -Et la raison est "Classification d'âge incorrecte"

-
-

11. Validation - Tags pertinents

-

Étant donné qu'un contenu sur l'automobile est tagué "Automobile", "Technologie"

-

Quand le modérateur vérifie les tags

-

Alors il confirme que les tags correspondent au contenu -Et le contenu est accepté

-
-

12. Rejet - Tags non pertinents

-

Étant donné qu'un contenu musical est tagué "Automobile", "Sport"

-

Quand le modérateur détecte l'incohérence

-

Alors le contenu est rejeté -Et la raison est "Tags non pertinents avec le contenu"

-
-

13. Validation - Zone diffusion cohérente

-

Étant donné qu'un audio-guide de la Tour Eiffel est en "Point GPS" Paris

-

Quand le modérateur vérifie la cohérence

-

Alors la zone est appropriée -Et le contenu est accepté

-
-

14. Rejet - Zone incohérente

-

Étant donné qu'un audio-guide de la Tour Eiffel est en zone "National"

-

Quand le modérateur détecte l'incohérence

-

Alors le contenu est rejeté -Et la raison est "Zone de diffusion incohérente (devrait être Point GPS)"

-
-

15. Délai de validation 24-48h jours ouvrés

-

Étant donné que je publie un contenu un lundi

-

Quand le contenu entre en file de validation

-

Alors le délai estimé est 24-48h (mercredi maximum)

-
-

16. Délai étendu le weekend

-

Étant donné que je publie un contenu un vendredi soir

-

Quand le contenu entre en file de validation

-

Alors le délai peut atteindre 72h (lundi) -Et je vois "Validation en cours, délai 24-72h (weekend)"

-
-

17. Priorité FIFO (First In First Out)

-

Étant donné que 10 contenus sont en file de validation

-

Quand les modérateurs traitent la file

-

Alors les contenus sont traités dans l'ordre d'arrivée -Et pas de traitement prioritaire

-
-

18. Notification acceptation

-

Étant donné que mon contenu est validé et accepté

-

Alors je reçois un email "✅ Votre contenu '[Titre]' est en ligne !" -Et je reçois une notification push -Et je vois un lien direct vers le contenu

-
-

19. Compteur de validation

-

Étant donné que mon premier contenu est accepté

-

Alors je vois "1/3 contenus validés pour devenir créateur vérifié"

-

Quand mon deuxième contenu est accepté

-

Alors je vois "2/3 contenus validés pour devenir créateur vérifié"

-
-

20. Notification refus avec raison détaillée

-

Étant donné que mon contenu est rejeté

-

Alors je reçois un email "❌ Contenu '[Titre]' refusé" -Et je reçois une notification push -Et je vois la raison exacte: "Qualité audio insuffisante" -Et je vois un lien vers les règles de publication

-
-

21. Possibilité de correction et resoumission

-

Étant donné que mon contenu a été rejeté pour "Tags non pertinents"

-

Quand je corrige les tags -Et que je resoumets le contenu

-

Alors le contenu repasse en file de validation -Et une nouvelle validation est effectuée

-
-

22. Après 3 validations - Statut vérifié obtenu

-

Étant donné que mes 3 premiers contenus ont été validés

-

Alors j'obtiens le statut "Créateur Vérifié" -Et je reçois une notification "🎉 Vous êtes maintenant créateur vérifié !" -Et un badge ✓ apparaît sur mon profil

-
-

23. Badge vérifié visible publiquement

-

Étant donné que j'ai le statut vérifié

-

Quand un utilisateur consulte mon profil

-

Alors il voit le badge ✓ à côté de mon pseudo -Et une mention "Créateur vérifié"

-
-

24. Contenus futurs publiés immédiatement

-

Étant donné que je suis créateur vérifié

-

Quand je publie un 4ème contenu

-

Alors le contenu est publié immédiatement -Et il n'y a pas de validation préalable -Et je vois "✅ Contenu publié"

-
-

25. Modération a posteriori uniquement

-

Étant donné que je suis créateur vérifié -Et que je publie un contenu

-

Quand le contenu est en ligne

-

Alors il peut être signalé par les utilisateurs -Et sera modéré uniquement si signalé

-
-

26. Interface modérateur - Queue de contenus

-

Étant donné que je suis un modérateur junior

-

Quand j'accède à l'interface de modération

-

Alors je vois la file des contenus à valider -Et je vois le nombre total en attente -Et les contenus sont triés par ordre FIFO

-
-

27. Interface modérateur - Écoute accélérée

-

Étant donné que je suis un modérateur

-

Quand j'écoute un contenu de 30 secondes

-

Alors je peux choisir la vitesse 1.5x ou 2.0x -Et je termine l'écoute en 15 secondes à 2x -Et ma productivité est doublée

-
-

28. Interface modérateur - Raccourcis clavier

-

Étant donné que je modère un contenu

-

Quand j'utilise les raccourcis clavier

-

Alors je peux:

-
| touche | action |
-|---|---|
-| A | Accepter |
-| R | Rejeter |
-| Espace | Play/Pause |
-
-

Et la modération est accélérée

-
-

29. Historique créateur visible

-

Étant donné qu'un créateur soumet son 2ème contenu

-

Quand le modérateur examine le contenu

-

Alors il voit l'historique:

-
| contenu | statut |
-|---|---|
-| Contenu 1 | Validé |
-| Contenu 2 | En cours |
-
-

Et il peut juger la cohérence du créateur

-
-

30. Temps de modération estimé 1.5 min/créateur

-

Étant donné qu'un créateur soumet 3 contenus

-

Quand les modérateurs traitent ces contenus

-

Alors le temps total est environ:

-
| action | temps |
-|---|---|
-| Écoute 30s × 3 | 90s |
-| Vérification metadata | 15s |
-| Décision | 5s |
-| Total | 110s |
-
-
-
- -

Élargissement automatique de zone quand aucun contenu n'est disponible

-

9 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que la géolocalisation est activée -Et que je suis en mode écoute

-
-

1. Aucun contenu dans rayon 50km - élargissement à 100km

-

Étant donné que je suis situé à la position GPS 48.8566, 2.3522 -Et qu'aucun contenu n'existe dans un rayon de 50 km autour de ma position -Mais qu'au moins 1 contenu existe dans un rayon de 100 km

-

Quand le système recherche du contenu à me proposer

-

Alors le système élargit automatiquement la zone de recherche à 100 km -Et je reçois un message "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)" -Et un contenu dans le rayon de 100 km m'est proposé

-
-

2. Aucun contenu dans rayon 100km - élargissement au département

-

Étant donné que je suis situé dans le département "75" (Paris) -Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -Mais qu'au moins 1 contenu existe avec la zone "département" pour "75"

-

Quand le système recherche du contenu à me proposer

-

Alors le système élargit automatiquement la zone de recherche au département -Et je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre département" -Et un contenu départemental m'est proposé

-
-

3. Aucun contenu départemental - élargissement à la région

-

Étant donné que je suis situé dans la région "Île-de-France" -Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -Et qu'aucun contenu départemental n'existe pour mon département -Mais qu'au moins 1 contenu existe avec la zone "région" pour "Île-de-France"

-

Quand le système recherche du contenu à me proposer

-

Alors le système élargit automatiquement la zone de recherche à la région -Et je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre région" -Et un contenu régional m'est proposé

-
-

4. Aucun contenu régional - basculement sur contenu national

-

Étant donné que je suis situé en France -Et qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -Et qu'aucun contenu départemental n'existe pour mon département -Et qu'aucun contenu régional n'existe pour ma région

-

Quand le système recherche du contenu à me proposer

-

Alors le système bascule automatiquement sur du contenu national -Et je reçois un message "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser" -Et un contenu national m'est proposé -Et je ne reste jamais sans contenu disponible

-
-

5. Élargissement progressif avec plusieurs étapes

-

Étant donné que je suis situé dans une zone rurale isolée -Et qu'aucun contenu n'existe dans un rayon de 50 km -Et qu'aucun contenu n'existe dans un rayon de 100 km -Et qu'aucun contenu départemental n'existe -Et qu'aucun contenu régional n'existe

-

Quand le système recherche du contenu à me proposer

-

Alors le système essaie d'abord 50 km -Et tout ce processus se fait de manière transparente et automatique -Et je reçois le message correspondant au dernier niveau trouvé

-
-

6. Message personnalisé selon la distance trouvée

-

Étant donné que je suis situé à la position GPS 43.6047, 1.4442 -Et que contenu(s) est/sont trouvé(s)

-

Quand le système me propose du contenu

-

Alors je reçois le message ""

-
-

7. Le contenu national sert de filet de sécurité

-

Étant donné que le système a épuisé toutes les zones géographiques locales

-

Quand le système bascule sur du contenu national

-

Alors je dois toujours avoir au moins 1 contenu disponible -Et ce contenu peut être:

-
| type_contenu |
-|---|
-| Actualités Le Monde |
-| Podcasts génériques |
-| Contenu éducatif national |
-| Contenu culturel national |
-
-
-

8. Pas d'écran d'erreur "Aucun contenu"

-

Étant donné que je lance l'application -Et qu'aucun contenu local n'est disponible dans ma zone

-

Quand le système recherche du contenu

-

Alors je ne dois jamais voir un message d'erreur "Aucun contenu disponible" -Et je ne dois jamais voir un écran vide -Et un contenu doit toujours m'être proposé, même si c'est du contenu national

-
-

9. Élargissement avec prise en compte des centres d'intérêt

-

Étant donné que je suis situé dans une zone rurale -Et qu'aucun contenu n'existe dans un rayon de 50 km -Et que mes centres d'intérêt incluent "Automobile" à 80% et "Voyage" à 70% -Et qu'un contenu national existe avec le tag "Automobile" -Et qu'un contenu national existe avec le tag "Politique"

-

Quand le système bascule sur du contenu national

-

Alors le contenu national proposé prend en compte mes centres d'intérêt -Et le contenu "Automobile" a un score supérieur au contenu "Politique"

-
-
- -

Gestion d'un contenu supprimé pendant l'écoute

-

11 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que je suis en mode écoute -Et qu'un contenu "C123" est en cours de lecture

-
-

1. Contenu supprimé pendant lecture - fin de lecture sans interruption

-

Étant donné que j'écoute le contenu "C123" depuis 30 secondes -Et que la durée totale du contenu est de 120 secondes

-

Quand le contenu est supprimé par la modération côté backend

-

Alors la lecture du contenu continue sans interruption -Et je peux écouter le contenu jusqu'à la fin -Et aucune interruption brutale ne se produit

-
-

2. Passage automatique après fin du contenu supprimé

-

Étant donné que le contenu "C123" a été supprimé pendant ma lecture -Et que j'ai écouté le contenu jusqu'à la fin

-

Quand le contenu se termine

-

Alors le système attend 2 secondes -Et passe automatiquement au contenu suivant -Et je reçois une notification toast discrète "Contenu précédent retiré (violation règles)"

-
-

3. Bouton Précédent désactivé après suppression

-

Étant donné que le contenu "C123" a été supprimé pendant ma lecture -Et que je suis passé au contenu suivant "C456"

-

Quand j'essaie d'appuyer sur le bouton "Précédent"

-

Alors le bouton "Précédent" ne me ramène pas au contenu supprimé -Et je reçois un message "Ce contenu n'est plus disponible" -Et la lecture du contenu actuel "C456" continue

-
-

4. Tentative de retour manuel au contenu supprimé

-

Étant donné que je suis sur le contenu "C456" -Et que le contenu précédent "C123" a été supprimé

-

Quand j'appuie sur le bouton "Précédent" pour revenir au contenu supprimé

-

Alors je reçois un message "Ce contenu n'est plus disponible" -Et la lecture reste sur le contenu actuel "C456" -Et aucune action n'est effectuée

-
-

5. Notification discrète pendant la conduite

-

Étant donné que je conduis à une vitesse de 60 km/h -Et que le contenu "C123" est supprimé pendant ma lecture

-

Quand le contenu se termine

-

Alors la notification "Contenu précédent retiré (violation règles)" s'affiche en toast discret -Et la notification disparaît automatiquement après 5 secondes -Et aucune popup modale n'interrompt ma conduite -Et le contenu suivant démarre automatiquement après 2 secondes

-
-

6. Message informatif mais non alarmiste

-

Étant donné que le contenu "C123" a été supprimé -Et que je passe au contenu suivant

-

Quand la notification s'affiche

-

Alors le message doit être informatif: "Contenu précédent retiré (violation règles)" -Et le ton ne doit pas être alarmiste -Et le message doit être bref et compréhensible -Et aucun détail technique n'est affiché pendant la conduite

-
-

7. Contenu supprimé retiré de l'historique

-

Étant donné que le contenu "C123" a été supprimé

-

Quand je consulte mon historique d'écoute

-

Alors le contenu "C123" n'apparaît plus dans mon historique -Et je ne peux pas relancer la lecture de ce contenu -Et l'historique affiche "[Contenu retiré]" à la place du titre

-
-

8. Contenu supprimé non accessible via lien direct

-

Étant donné que le contenu "C123" a été supprimé -Et que j'ai un lien de partage "roadwave.fr/share/c/C123"

-

Quand je clique sur le lien de partage

-

Alors je reçois un message "Ce contenu a été retiré pour violation des règles de la communauté" -Et je suis redirigé vers l'accueil de l'application -Et aucune lecture n'est possible

-
-

9. Plusieurs contenus supprimés dans l'historique récent

-

Étant donné que j'ai écouté les contenus suivants:

-
| id | statut |
-|---|---|
-| C123 | supprimé |
-| C456 | actif |
-| C789 | supprimé |
-
-

Et que je suis actuellement sur le contenu "C456"

-

Quand j'appuie plusieurs fois sur "Précédent"

-

Alors je ne peux pas revenir aux contenus "C123" ou "C789" -Et le système saute automatiquement les contenus supprimés -Et je reviens au dernier contenu actif disponible avant "C456"

-
-

10. Consultation détaillée du contenu supprimé à l'arrêt

-

Étant donné que je suis à l'arrêt -Et que le contenu "C123" a été supprimé pendant ma session

-

Quand j'ouvre les détails de la notification de suppression

-

Alors je peux voir les informations suivantes:

-
| information |
-|---|
-| Titre du contenu |
-| Créateur |
-| Raison de suppression |
-| Date de suppression |
-
-

Et je peux signaler une erreur de modération si je pense qu'elle est injustifiée

-
-

11. Pas d'impact sur les jauges d'intérêt lors de la suppression

-

Étant donné que j'ai écouté le contenu "C123" pendant 80 secondes (66%) -Et que mes jauges d'intérêt ont été mises à jour pendant l'écoute

-

Quand le contenu est supprimé après mon écoute

-

Alors les modifications de mes jauges d'intérêt sont conservées -Et l'écoute déjà effectuée reste comptabilisée -Et seules les futures écoutes de ce contenu sont bloquées

-
-
- -

Mode dégradé sans géolocalisation

-

19 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que j'ai refusé ou désactivé l'accès à la géolocalisation

-
-

1. Types de contenu disponibles sans géolocalisation

-

Étant donné que la géolocalisation est désactivée

-

Quand j'ouvre l'application

-

Alors les types de contenu suivants sont disponibles:

-
| type_contenu | disponible |
-|---|---|
-| Contenu national | oui |
-| Contenu téléchargé (offline) | oui |
-| Contenus "Neutre" géographiquement | oui |
-| Contenu géolocalisé Ancré | non |
-| Contenu géolocalisé Contextuel | non |
-| Audio-guides | non |
-| Notifications push géo-déclenchées | non |
-
-
-

2. Popup d'information au premier lancement sans GPS

-

Étant donné que c'est mon premier lancement de l'application -Et que j'ai refusé l'accès à la géolocalisation

-

Quand l'application détecte que le GPS est désactivé

-

Alors une popup s'affiche avec le message: -Et la popup contient les boutons suivants:

-
| bouton | action |
-|---|---|
-| Activer | Redirection vers paramètres OS |
-| Continuer sans | Ferme popup et lance en mode dégradé |
-
-

Et une checkbox "Ne plus me demander" est disponible

-
-

3. Popup non affichée si case "Ne plus me demander" cochée

-

Étant donné que j'ai déjà vu la popup de géolocalisation -Et que j'ai coché "Ne plus me demander"

-

Quand je lance l'application avec le GPS désactivé

-

Alors la popup de géolocalisation ne s'affiche pas -Et l'application démarre directement en mode dégradé -Et le banner permanent de rappel s'affiche

-
-

4. Redirection vers paramètres OS lors du clic sur "Activer"

-

Étant donné que la popup de géolocalisation est affichée

-

Quand je clique sur "Activer"

-

Alors je suis redirigé vers les paramètres de géolocalisation de mon OS -Et sur iOS, j'arrive dans "Réglages > RoadWave > Localisation" -Et sur Android, j'arrive dans "Paramètres > Applications > RoadWave > Autorisations > Position"

-
-

5. Banner de rappel permanent sans GPS

-

Étant donné que j'ai cliqué sur "Continuer sans" géolocalisation

-

Quand l'application s'affiche

-

Alors un bandeau s'affiche en haut de l'écran -Et le bandeau contient le texte: "Mode limité : géolocalisation désactivée. [Activer]" -Et le bandeau a un fond de couleur avertissement (jaune/orange) -Et le bandeau n'est pas intrusif mais reste visible -Et le bandeau reste affiché sur toutes les pages de l'application

-
-

6. Clic sur le bouton "Activer" du banner

-

Étant donné que le banner "Mode limité" est affiché

-

Quand je clique sur le lien "[Activer]" dans le banner

-

Alors je suis redirigé vers les paramètres de géolocalisation de mon OS

-
-

7. Disparition du banner après activation GPS

-

Étant donné que le banner "Mode limité" est affiché -Et que je reviens dans l'application après avoir activé le GPS dans les paramètres

-

Quand l'application détecte que la géolocalisation est maintenant active

-

Alors le banner disparaît automatiquement -Et l'application bascule en mode normal avec contenu géolocalisé -Et un toast de confirmation s'affiche: "Géolocalisation activée"

-
-

8. Lecture de contenu national sans GPS

-

Étant donné que la géolocalisation est désactivée -Et que du contenu national existe (actualités Le Monde, podcasts génériques)

-

Quand je lance la lecture

-

Alors je peux écouter le contenu national sans restriction -Et l'algorithme de recommandation se base uniquement sur:

-
| critère |
-|---|
-| Mes centres d'intérêt |
-| Mon historique d'écoute |
-| Popularité générale |
-
-

Et la proximité géographique n'est pas prise en compte

-
-

9. Lecture de contenu téléchargé sans GPS

-

Étant donné que la géolocalisation est désactivée -Et que j'ai téléchargé 30 contenus quand j'avais le GPS activé

-

Quand j'accède à mes contenus téléchargés

-

Alors je peux lire tous mes contenus téléchargés normalement -Et les contenus géolocalisés téléchargés restent accessibles -Et le filtre géographique n'est pas appliqué pour les contenus offline

-
-

10. Contenu "Neutre" géographiquement disponible

-

Étant donné que la géolocalisation est désactivée -Et qu'un créateur a publié du contenu avec la classification géographique "Neutre"

-

Quand je recherche du contenu

-

Alors les contenus "Neutre" sont inclus dans les résultats -Et ils sont mélangés avec le contenu national -Et l'algorithme les priorise selon mes centres d'intérêt

-
-

11. Audio-guides inaccessibles sans GPS

-

Étant donné que la géolocalisation est désactivée

-

Quand je recherche un audio-guide spécifique

-

Alors les audio-guides apparaissent dans les résultats de recherche -Mais un badge "GPS requis" est affiché sur chaque audio-guide -Et quand je clique sur un audio-guide, un message s'affiche: -Et je peux choisir "Activer" ou "Annuler"

-
-

12. Notifications push géo-déclenchées désactivées

-

Étant donné que la géolocalisation est désactivée -Et que je suis abonné à un créateur qui diffuse du contenu géolocalisé

-

Quand le créateur publie un nouveau contenu géolocalisé

-

Alors je ne reçois pas de notification push géo-déclenchée -Mais je reçois une notification push standard (non géo-déclenchée) si le créateur publie du contenu national -Et la notification précise: "Nouveau contenu national de [Créateur]"

-
-

13. Contenu géolocalisé non proposé dans le feed

-

Étant donné que la géolocalisation est désactivée

-

Quand le système génère mon feed de contenu

-

Alors aucun contenu "Ancré" ou "Contextuel" n'est inclus -Et seuls les contenus "Neutre" et "National" sont proposés -Et mon feed contient au minimum 20 contenus disponibles

-
-

14. Application fonctionnelle sans GPS (pas de blocage)

-

Étant donné que la géolocalisation est désactivée

-

Quand j'utilise l'application

-

Alors je ne suis jamais bloqué par un écran "GPS requis" -Et toutes les fonctionnalités non-géolocalisées restent accessibles:

-
| fonctionnalité |
-|---|
-| Écoute contenu national |
-| Gestion profil |
-| Abonnements créateurs |
-| Recherche textuelle |
-| Historique d'écoute |
-| Paramètres |
-| Mode offline |
-
-

Et je peux créer et publier du contenu national

-
-

15. Respect du choix utilisateur de ne pas activer GPS

-

Étant donné que j'ai coché "Ne plus me demander" pour la géolocalisation

-

Quand j'utilise l'application pendant plusieurs semaines

-

Alors la popup de demande GPS ne s'affiche plus jamais automatiquement -Et seul le banner permanent reste affiché -Et l'application ne force jamais l'activation du GPS

-
-

16. Bascule automatique en mode normal après activation GPS

-

Étant donné que j'utilise l'application en mode dégradé depuis 1 semaine -Et que je décide d'activer la géolocalisation

-

Quand l'application détecte que le GPS est maintenant actif

-

Alors le mode dégradé est désactivé automatiquement -Et le banner "Mode limité" disparaît -Et le contenu géolocalisé devient disponible immédiatement -Et mon feed se rafraîchit avec du contenu local pertinent -Et un toast de confirmation s'affiche: "Géolocalisation activée - Contenu local disponible"

-
-

17. Demande de permission GPS lors de l'utilisation d'une fonctionnalité géo

-

Étant donné que la géolocalisation est désactivée

-

Quand j'essaie d'accéder à une fonctionnalité nécessitant le GPS (ex: audio-guide)

-

Alors une popup contextuelle s'affiche: -Et je peux accepter ou refuser -Et si j'accepte, je suis redirigé vers les paramètres OS -Et si je refuse, je reste en mode dégradé sans message d'erreur répétitif

-
-

18. Statistiques de contenu local disponible non affiché

-

Étant donné que la géolocalisation est désactivée

-

Quand je navigue dans l'application

-

Alors le banner peut afficher occasionnellement: -Et ce message incitatif change tous les 3 jours -Et il reste non intrusif (pas de popup, juste le banner)

-
-

19. Onboarding différent pour utilisateurs sans GPS

-

Étant donné que c'est ma première utilisation de RoadWave -Et que j'ai refusé la géolocalisation

-

Quand l'onboarding se termine

-

Alors un écran explicatif s'affiche: -Et je peux continuer avec un bouton "Compris"

-
-
- -

Gestion de la perte de réseau et buffering adaptatif

-

17 scénarios (16 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que je suis en mode écoute -Et qu'un contenu est en cours de lecture

-
-

1. 📋 Plan: Paramètres de buffer selon le type de réseau

-

Étant donné que je suis connecté en ""

-

Quand le système initialise le buffer audio

-

Alors le buffer minimum est de secondes -Et le buffer cible est de secondes -Et le buffer maximum est de secondes

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
type_reseaubuffer_minbuffer_ciblebuffer_max
WiFi530120
4G1045120
5G1045120
3G3090300
-
-

2. Connexion instable avec latence élevée - aucun message immédiat

-

Étant donné que je suis connecté en 4G -Et que le buffer contient 45 secondes de contenu

-

Quand la latence réseau dépasse 500ms

-

Alors aucun message n'est affiché immédiatement -Et la lecture continue normalement sur le buffer -Et le système tente de continuer le téléchargement en arrière-plan

-
-

3. Connexion instable pendant plus de 10 secondes - toast discret

-

Étant donné que je suis connecté en 4G -Et que la latence réseau dépasse 500ms depuis 10 secondes

-

Quand le système détecte la latence prolongée

-

Alors un toast discret s'affiche: "Connexion instable" -Et le toast disparaît automatiquement après 3 secondes -Et la lecture continue normalement

-
-

4. Perte totale de réseau - lecture sur buffer

-

Étant donné que je suis connecté en WiFi -Et que le buffer contient 30 secondes de contenu

-

Quand je perds totalement la connexion réseau

-

Alors la lecture continue sur le buffer disponible -Et un toast s'affiche: "Hors ligne, lecture sur buffer (30s restantes)" -Et un compte à rebours du temps de buffer restant est visible

-
-

5. Buffer qui s'épuise pendant la perte réseau

-

Étant donné que je suis hors ligne -Et que le buffer contient 30 secondes de contenu

-

Quand le contenu continue de jouer

-

Alors le compte à rebours diminue en temps réel -Et le toast affiche "Hors ligne, lecture sur buffer (15s restantes)" après 15 secondes -Et le toast affiche "Hors ligne, lecture sur buffer (5s restantes)" après 25 secondes

-
-

6. Pause automatique après épuisement du buffer

-

Étant donné que je suis hors ligne depuis 30 secondes -Et que le buffer est complètement épuisé

-

Quand il n'y a plus de contenu audio à lire

-

Alors la lecture se met en pause automatiquement -Et un overlay s'affiche: "Connexion perdue. Reconnexion en cours..." -Et le système tente de se reconnecter automatiquement

-
-

7. Tentatives de reconnexion automatique

-

Étant donné que la lecture est en pause suite à l'épuisement du buffer

-

Quand le système tente de se reconnecter

-

Alors une tentative de reconnexion est effectuée toutes les 5 secondes -Et un maximum de 6 tentatives sont effectuées (30 secondes au total) -Et l'overlay affiche "Tentative de reconnexion... (X/6)"

-
-

8. Proposition du mode offline après 30 secondes d'échec

-

Étant donné que 6 tentatives de reconnexion ont échoué -Et que cela fait 30 secondes que je suis déconnecté

-

Quand la 6ème tentative échoue

-

Alors une popup s'affiche: "Voulez-vous continuer avec vos contenus téléchargés ?" -Et la popup contient deux boutons:

-
| bouton | action |
-|---|---|
-| Réessayer | Nouvelle série de 6 tentatives |
-| Mode offline | Bascule sur contenus téléchargés |
-
-
-

9. Basculement réussi vers le mode offline

-

Étant donné que la popup de mode offline est affichée -Et que j'ai téléchargé 20 contenus dans ma zone géographique

-

Quand je clique sur "Mode offline"

-

Alors le système bascule sur les contenus téléchargés -Et un nouveau contenu téléchargé démarre automatiquement -Et un bandeau permanent indique "Mode hors ligne - Contenus téléchargés"

-
-

10. Aucun contenu téléchargé disponible

-

Étant donné que la popup de mode offline est affichée -Et que je n'ai aucun contenu téléchargé

-

Quand je clique sur "Mode offline"

-

Alors un message s'affiche: "Aucun contenu téléchargé disponible" -Et je suis invité à me connecter en WiFi pour télécharger du contenu -Et le bouton "Réessayer" reste la seule option

-
-

11. Reprise automatique après reconnexion

-

Étant donné que la lecture est en pause depuis 15 secondes -Et que j'étais à 02:35 du contenu en cours

-

Quand la connexion réseau est rétablie

-

Alors la lecture reprend automatiquement au point d'arrêt exact (02:35) -Et un toast s'affiche: "Connexion rétablie" -Et le toast disparaît après 3 secondes -Et le buffer se remplit progressivement selon le type de réseau

-
-

12. Reconnexion avec changement de type de réseau

-

Étant donné que j'étais connecté en WiFi -Et que j'ai perdu la connexion

-

Quand je me reconnecte en 4G

-

Alors le système ajuste automatiquement les paramètres de buffer -Et le buffer minimum passe de 5s à 10s -Et le buffer cible passe de 30s à 45s -Et la lecture reprend normalement

-
-

13. Passage dans un tunnel avec perte de signal

-

Étant donné que je conduis à 90 km/h sur autoroute -Et que je suis connecté en 4G avec un buffer de 45 secondes

-

Quand j'entre dans un tunnel et perds le signal

-

Alors la lecture continue sur le buffer pendant 45 secondes maximum -Et aucune notification n'est affichée pendant les 10 premières secondes -Et un toast discret s'affiche après 10 secondes: "Connexion instable"

-
-

14. Sortie du tunnel avant épuisement du buffer

-

Étant donné que je suis dans un tunnel depuis 30 secondes -Et qu'il reste 15 secondes de buffer

-

Quand je sors du tunnel et récupère le signal 4G

-

Alors la lecture continue sans interruption -Et le buffer se remplit à nouveau -Et un toast s'affiche: "Connexion rétablie"

-
-

15. Changement de cellule 4G pendant la lecture

-

Étant donné que je conduis et change de cellule mobile toutes les 5-10 minutes -Et que le buffer contient 45 secondes de contenu

-

Quand un handoff de cellule se produit

-

Alors la lecture continue sans interruption grâce au buffer -Et la connexion à la nouvelle cellule se fait de manière transparente -Et aucune notification n'est affichée si le handoff réussit en moins de 5 secondes

-
-

16. Téléchargement préventif en WiFi avant trajet

-

Étant donné que je suis connecté en WiFi -Et que j'ai activé le téléchargement automatique

-

Quand le système détecte que je suis à l'arrêt en WiFi

-

Alors le système me propose de télécharger du contenu pour mon trajet -Et je peux sélectionner une zone géographique à télécharger -Et le téléchargement se fait en arrière-plan

-
-

17. Tracking des événements de perte réseau pour amélioration

-

Étant donné que je perds la connexion réseau

-

Quand l'événement de perte est détecté

-

Alors le système enregistre les métriques suivantes:

-
| métrique |
-|---|
-| Type de réseau avant perte |
-| Durée de la coupure |
-| Buffer disponible |
-| Position GPS approximative |
-| Heure de la journée |
-
-

Et ces métriques sont anonymisées et envoyées en batch lors de la prochaine connexion WiFi -Et les données servent à améliorer les paramètres de buffer

-
-
- -

Tests BDD - Documentation des fonctionnalités

-

Cette documentation est générée automatiquement à partir des fichiers Gherkin (.feature).

-

Vue d'ensemble

- - - - - - - - - - - - - - - - - - - - - -
MétriqueValeur
Fonctionnalités83
Scénarios2112
Domaines métier18
-
-

🔔 Abonnements

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Audio-guides multi-séquences pour piétons29
Impact des abonnements sur l'algorithme16
Limites d'abonnements et désabonnement27
Notifications contextuelles selon le mode de déplacement28
-

4 fonctionnalités • 100 scénarios

-

🎧 Audio Guides

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Audio-guide mode piéton (navigation manuelle)29
Audio-guide mode voiture (GPS automatique)45
Audio-guides Premium et monétisation31
Audio-guides modes vélo et transport27
Création d'audio-guide multi-séquences35
Intégration audio-guides avec autres fonctionnalités39
Sauvegarde et reprise de progression audio-guide32
-

7 fonctionnalités • 238 scénarios

-

🔐 Authentication

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Authentification à deux facteurs (2FA)16
Classification des contenus par âge13
Connexion utilisateur11
Gestion des sessions et tokens13
Inscription utilisateur15
Récupération de compte14
Vérification d'email10
-

7 fonctionnalités • 92 scénarios

-

🎨 Content Creation

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Modification et suppression de contenu30
Métadonnées et publication de contenu34
Upload et encodage de contenu audio29
Validation des 3 premiers contenus30
-

4 fonctionnalités • 123 scénarios

-

⚠️ Error Handling

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Gestion d'un contenu supprimé pendant l'écoute11
Gestion de la perte de réseau et buffering adaptatif17
Mode dégradé sans géolocalisation19
Élargissement automatique de zone quand aucun contenu n'est disponible9
-

4 fonctionnalités • 56 scénarios

-

📊 Interest Gauges

- - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Jauge initiale et cold start15
Pas de dégradation temporelle des jauges16
Évolution des jauges d'intérêt21
-

3 fonctionnalités • 52 scénarios

-

📴 Mode Offline

- - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Synchronisation actions offline45
Téléchargement de contenus offline49
Validité et renouvellement contenus offline38
-

3 fonctionnalités • 132 scénarios

-

🛡️ Moderation

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Modération préventive22
Sanctions et notifications de modération27
Signalement de contenu inapproprié23
Traitement des signalements par l'IA et les modérateurs25
-

4 fonctionnalités • 97 scénarios

-

💰 Monetisation

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Conditions d'activation de la monétisation28
Contenus Premium exclusifs34
Désactivation et suspension monétisation35
KYC et inscription à la monétisation37
Obligations fiscales30
Paiement des créateurs35
Sources de revenus créateurs34
-

7 fonctionnalités • 233 scénarios

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Actions complémentaires à l'arrêt23
Commande "Précédent"19
Commandes au volant et interactions simplifiées21
Commandes vocales CarPlay et Android Auto25
File d'attente et commande "Suivant"20
Lecture en boucle et enchaînement automatique27
-

6 fonctionnalités • 135 scénarios

-

🔗 Partage

- - - - - - - - - - - - - -
FonctionnalitéScénarios
Partage de contenu22
-

1 fonctionnalités • 22 scénarios

-

⭐ Premium

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Avantages Premium37
Gestion abonnement Premium41
Multi-devices et détection simultanée30
Offre et tarification Premium31
-

4 fonctionnalités • 139 scénarios

-

👤 Profil

- - - - - - - - - - - - - -
FonctionnalitéScénarios
Profil créateur31
-

1 fonctionnalités • 31 scénarios

-

📢 Publicites

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Caractéristiques et facturation des publicités32
Création de campagnes publicitaires30
Gestion du budget et alertes publicitaires30
Insertion et fréquence des publicités31
Métriques d'engagement et dashboard publicitaire27
Validation et modération des publicités29
-

6 fonctionnalités • 179 scénarios

-

📻 Radio Live

- - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Architecture technique radio live24
Arrêt du live19
Comportement auditeur pendant un live27
Démarrage d'un live20
-

4 fonctionnalités • 90 scénarios

-

🔍 Recherche

- - - - - - - - - - - - - -
FonctionnalitéScénarios
Recherche de contenu55
-

1 fonctionnalités • 55 scénarios

-

🎯 Recommendation

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Classification de géo-pertinence des contenus10
Contenus géolocalisés en mode voiture36
Formule de scoring et recommandation21
Gestion de l'historique et reproposition19
Gestion du contenu politique (MVP simplifié)13
Mode Kids pour utilisateurs 13-15 ans15
Médias traditionnels sur RoadWave21
Paramétrabilité admin et A/B testing20
Paramétrabilité utilisateur et profils25
-

9 fonctionnalités • 180 scénarios

-

🔒 Rgpd Compliance

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FonctionnalitéScénarios
Anonymisation des données GPS après 24h18
Conformité administrative RGPD (Registre, Breach, DPO)22
Cookies et analytics avec Matomo self-hosted20
Durée de conservation des données et purge automatique19
Gestion du consentement RGPD16
Mode dégradé avec GeoIP (sans GPS précis)20
Portabilité des données (Article 20 RGPD)22
Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)21
-

8 fonctionnalités • 158 scénarios

-
- -

Pas de dégradation temporelle des jauges

-
-

En tant que système de recommandation -Je veux que les jauges n'évoluent que par les actions utilisateur -Afin d'avoir un comportement prévisible et fiable

-
-

16 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté

-
-

1. Aucune dégradation automatique avec le temps

-

Étant donné que ma jauge "Économie" est à 80% -Et que je n'écoute aucun contenu pendant 30 jours

-

Quand je me reconnecte après 30 jours

-

Alors ma jauge "Économie" est toujours à 80% -Et aucune dégradation temporelle n'a été appliquée

-
-

2. Jauges conservées après 6 mois d'inactivité

-

Étant donné que mes jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 75% |
-| Voyage | 60% |
-| Musique | 45% |
-
-

Et que je pars en vacances pendant 6 mois sans utiliser l'app

-

Quand je me reconnecte après 6 mois

-

Alors mes jauges sont exactement les mêmes:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 75% |
-| Voyage | 60% |
-| Musique | 45% |
-
-
-

3. Évolution naturelle par les actions

-

Étant donné que j'aimais "Économie" il y a 1 an (jauge 80%) -Et que depuis, je skip tous les contenus "Économie" -Et que j'ai skippé 50 contenus "Économie" en 1 an

-

Alors ma jauge "Économie" descend naturellement via les skips -Et atteint environ 55% (80% - 50 × 0.5% = 55%) -Et la dégradation vient des actions, pas du temps

-
-

4. Pas de cron job de dégradation

-

Étant donné que le système vérifie les jauges quotidiennement

-

Quand un utilisateur n'a pas d'activité depuis 90 jours

-

Alors aucun job de dégradation n'est exécuté -Et les jauges restent inchangées -Et aucune ressource CPU n'est consommée pour la dégradation

-
-

5. Comportement prévisible après absence

-

Étant donné que ma jauge "Sport" était à 70% -Et que je n'utilise pas l'app pendant 1 an

-

Quand je reviens et demande des recommandations

-

Alors mes recommandations reflètent toujours mes goûts d'avant -Et je reçois du contenu "Sport" prioritaire -Et le comportement est cohérent et prévisible

-
-

6. Réinitialiser manuellement mes centres d'intérêt

-

Étant donné que je veux repartir de zéro

-

Quand je vais dans les paramètres -Et que je clique sur "Réinitialiser mes centres d'intérêt" -Et que je confirme l'action

-

Alors toutes mes jauges reviennent à 50% -Et je vois le message "Vos centres d'intérêt ont été réinitialisés"

-
-

7. Confirmation avant réinitialisation

-

Étant donné que je suis dans les paramètres

-

Quand je clique sur "Réinitialiser mes centres d'intérêt"

-

Alors je vois un message de confirmation:

-
| titre | Êtes-vous sûr ? |
-|---|---|
-| message | Cette action remettra toutes vos jauges à 50% |
-| actions | Confirmer / Annuler |
-
-
-

8. Annuler la réinitialisation

-

Étant donné que j'ai cliqué sur "Réinitialiser mes centres d'intérêt" -Et que la confirmation est affichée

-

Quand je clique sur "Annuler"

-

Alors mes jauges ne sont pas modifiées -Et je reviens aux paramètres

-
-

9. Raison de réinitialisation - changement de vie

-

Étant donné que j'utilisais RoadWave pour mes trajets professionnels -Et que mes jauges reflétaient "Économie" (85%) et "Technologie" (75%) -Et que je change de vie et deviens musicien

-

Quand je réinitialise mes centres d'intérêt

-

Alors je peux repartir avec toutes les jauges à 50% -Et découvrir du contenu "Musique" et "Culture" sans biais

-
-

10. Pas de suggestion automatique de réinitialisation

-

Étant donné que je n'ai pas utilisé l'app depuis 1 an

-

Quand je me reconnecte

-

Alors aucune suggestion de réinitialisation n'est affichée -Et mes jauges sont conservées telles quelles -Et je garde le contrôle total

-
-

11. Historique conservé après réinitialisation

-

Étant donné que j'ai écouté 500 contenus

-

Quand je réinitialise mes centres d'intérêt

-

Alors mes jauges reviennent à 50% -Mais mon historique d'écoute est conservé -Et je peux toujours consulter mes anciens contenus écoutés

-
-

12. Évolution future basée sur nouvelles actions

-

Étant donné que j'ai réinitialisé mes jauges à 50%

-

Quand j'écoute 5 contenus "Voyage" à >80%

-

Alors ma jauge "Voyage" monte à 60% (50% + 5 × 2%) -Et l'algorithme recommence à apprendre mes nouvelles préférences

-
-

13. Respect de l'historique utilisateur

-

Étant donné qu'un utilisateur aime "Cryptomonnaie" depuis 2 ans -Et que sa jauge est à 90%

-

Quand 2 ans s'écoulent sans dégradation temporelle

-

Alors sa jauge reste à 90% -Et le système ne fait pas d'"oubli" artificiel

-
-

14. Coût infrastructure zéro

-

Étant donné qu'aucune dégradation temporelle n'existe

-

Quand le système calcule les jauges

-

Alors aucun calcul de date n'est nécessaire -Et aucun batch nocturne ne tourne -Et aucun bug de fuseau horaire ne peut survenir -Et le coût CPU est minimal

-
-

15. UX prévisible - jauge = actions

-

Étant donné qu'un utilisateur consulte sa jauge "Sport" à 65%

-

Quand il se demande pourquoi elle est à 65%

-

Alors il peut retracer ses actions:

-
| action | impact |
-|---|---|
-| 10 likes automatiques | +10% |
-| 3 abonnements Sport | +15% |
-| 5 skips de contenu non-Sport | 0% |
-
-

Et il comprend que c'est le reflet exact de ses actions -Et il n'y a pas de mystère ou automatisme caché

-
-

16. Statistiques affichées sans date

-

Étant donné que je consulte mes centres d'intérêt

-

Quand je vois mes jauges

-

Alors je vois:

-
| information | affiché |
-|---|---|
-| Niveau actuel | ✅ 75% |
-| Évolution depuis début | ✅ +25% |
-| Dernière mise à jour | ❌ |
-
-

Et aucune date n'est affichée car non pertinente -Et seules les actions comptent

-
-
- -

Évolution des jauges d'intérêt

-
-

En tant que système de recommandation -Je veux faire évoluer les jauges d'intérêt selon les actions utilisateur -Afin d'affiner les recommandations personnalisées

-
-

21 scénarios (20 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté

-
-

1. Like automatique renforcé après écoute ≥80%

-

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" -Et que ma jauge "Automobile" est à 45%

-

Quand j'écoute le contenu pendant 4 minutes 30 secondes (90%)

-

Alors je reçois un like automatique renforcé -Et ma jauge "Automobile" augmente de 2% -Et ma jauge "Automobile" est maintenant à 47%

-
-

2. Like automatique renforcé exactement à 80%

-

Étant donné qu'un contenu de 10 minutes est tagué "Voyage" -Et que ma jauge "Voyage" est à 60%

-

Quand j'écoute le contenu pendant exactement 8 minutes (80%)

-

Alors je reçois un like automatique renforcé -Et ma jauge "Voyage" augmente de 2% -Et ma jauge "Voyage" est maintenant à 62%

-
-

3. Like automatique standard après écoute 30-79%

-

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" -Et que ma jauge "Automobile" est à 45%

-

Quand j'écoute le contenu pendant 2 minutes 30 secondes (50%)

-

Alors je reçois un like automatique standard -Et ma jauge "Automobile" augmente de 1% -Et ma jauge "Automobile" est maintenant à 46%

-
-

4. Like automatique standard à 30% exactement

-

Étant donné qu'un contenu de 10 minutes est tagué "Musique" -Et que ma jauge "Musique" est à 40%

-

Quand j'écoute le contenu pendant exactement 3 minutes (30%)

-

Alors je reçois un like automatique standard -Et ma jauge "Musique" augmente de 1%

-
-

5. Like automatique standard à 79%

-

Étant donné qu'un contenu de 10 minutes est tagué "Sport" -Et que ma jauge "Sport" est à 55%

-

Quand j'écoute le contenu pendant 7 minutes 54 secondes (79%)

-

Alors je reçois un like automatique standard -Et ma jauge "Sport" augmente de 1% -Et ma jauge "Sport" est maintenant à 56%

-
-

6. Like explicite (manuel) +2%

-

Étant donné qu'un contenu est tagué "Économie" -Et que ma jauge "Économie" est à 70%

-

Quand j'écoute le contenu partiellement -Et que je clique manuellement sur le bouton "Like"

-

Alors ma jauge "Économie" augmente de 2% -Et ma jauge "Économie" est maintenant à 72%

-
-

7. Like manuel cumulable avec like automatique

-

Étant donné qu'un contenu de 5 minutes est tagué "Automobile" -Et que ma jauge "Automobile" est à 45%

-

Quand j'écoute le contenu pendant 2 minutes 30 secondes (50%)

-

Alors je reçois un like automatique standard (+1%)

-

Quand je clique ensuite sur le bouton "Like"

-

Alors ma jauge augmente encore de 2% (like manuel) -Et ma jauge "Automobile" a augmenté de 3% au total -Et ma jauge "Automobile" est maintenant à 48%

-
-

8. Abonnement créateur impacte tous ses tags

-

Étant donné qu'un créateur publie des contenus tagués "Automobile" et "Technologie" -Et que mes jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 50% |
-| Technologie | 45% |
-
-

Quand je m'abonne à ce créateur

-

Alors ma jauge "Automobile" augmente de 5% -Et ma jauge "Technologie" augmente de 5% -Et mes nouvelles jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 55% |
-| Technologie | 50% |
-
-
-

9. Skip rapide (<10s) diminue la jauge

-

Étant donné qu'un contenu est tagué "Économie" -Et que ma jauge "Économie" est à 45%

-

Quand je skip le contenu après 5 secondes

-

Alors ma jauge "Économie" diminue de 0.5% -Et ma jauge "Économie" est maintenant à 44.5%

-
-

10. Skip à exactement 10s ne diminue pas la jauge

-

Étant donné qu'un contenu est tagué "Politique" -Et que ma jauge "Politique" est à 50%

-

Quand je skip le contenu après exactement 10 secondes

-

Alors ma jauge "Politique" ne change pas -Et reste à 50%

-
-

11. Skip tardif (≥30%) est neutre

-

Étant donné qu'un contenu de 10 minutes est tagué "Musique" -Et que ma jauge "Musique" est à 60%

-

Quand j'écoute pendant 3 minutes (30%) -Et que je skip ensuite

-

Alors ma jauge "Musique" ne diminue pas (signal neutre) -Et ma jauge reste à 60% (plus le +1% de like auto si applicable)

-
-

12. Contenu avec plusieurs tags impacte toutes les jauges

-

Étant donné qu'un contenu est tagué "Automobile" et "Voyage" -Et que mes jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 45% |
-| Voyage | 60% |
-
-

Quand j'écoute le contenu à 90%

-

Alors les deux jauges augmentent de 2% -Et mes nouvelles jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 47% |
-| Voyage | 62% |
-
-
-

13. Contenu avec 3 tags impacte les 3 jauges

-

Étant donné qu'un contenu est tagué "Sport", "Santé" et "Technologie" -Et que mes jauges sont à 50% pour chaque catégorie

-

Quand je skip rapidement après 5 secondes

-

Alors les 3 jauges diminuent de 0.5% -Et toutes passent à 49.5%

-
-

14. Jauges bornées - ne peut pas dépasser 100%

-

Étant donné que ma jauge "Cryptomonnaie" est à 99% -Et qu'un contenu tagué "Cryptomonnaie" est disponible

-

Quand j'écoute le contenu à 95% (like auto renforcé +2%)

-

Alors ma jauge "Cryptomonnaie" passe à 100% (maximum) -Et ne dépasse pas 100%

-
-

15. Jauges bornées - ne peut pas descendre sous 0%

-

Étant donné que ma jauge "Politique" est à 0.3% -Et qu'un contenu tagué "Politique" est disponible

-

Quand je skip rapidement après 3 secondes (-0.5%)

-

Alors ma jauge "Politique" passe à 0% (minimum) -Et ne devient pas négative

-
-

16. Calcul immédiat à chaque action

-

Étant donné que ma jauge "Voyage" est à 50%

-

Quand j'écoute un contenu "Voyage" à 85%

-

Alors la jauge est mise à jour immédiatement (pas de batch) -Et passe à 52%

-

Quand je demande mes recommandations dans la seconde suivante

-

Alors l'algorithme utilise déjà la valeur 52%

-
-

17. Like manuel après écoute <30% (pas de like auto)

-

Étant donné qu'un contenu de 10 minutes est tagué "Culture" -Et que ma jauge "Culture" est à 60%

-

Quand j'écoute pendant 2 minutes (20%)

-

Alors je ne reçois pas de like automatique

-

Quand je clique sur le bouton "Like"

-

Alors ma jauge "Culture" augmente de 2% uniquement -Et ma jauge "Culture" est maintenant à 62%

-
-

18. Unlike retire le like manuel

-

Étant donné que j'ai liké manuellement un contenu "Sport" -Et que ma jauge "Sport" est passée de 55% à 57% (+2%)

-

Quand je clique sur "Unlike"

-

Alors ma jauge "Sport" diminue de 2% -Et ma jauge "Sport" revient à 55%

-
-

19. Unlike ne peut pas retirer un like automatique

-

Étant donné que j'ai écouté un contenu "Musique" à 90% -Et que j'ai reçu un like automatique renforcé (+2%) -Et que ma jauge "Musique" est à 52%

-

Quand j'essaie de faire "Unlike"

-

Alors l'action n'est pas disponible -Et ma jauge reste à 52%

-
-

20. Tags définis par créateur à la publication

-

Étant donné que je suis un créateur

-

Quand je publie un contenu

-

Alors je dois sélectionner 1 à 3 tags -Et ces tags sont fixés après publication -Et impacteront les jauges de tous les auditeurs

-
-

21. 📋 Plan: Calculs avec différentes durées d'écoute

-

Étant donné qu'un contenu de 10 minutes est tagué "Voyage" -Et que ma jauge "Voyage" est à 50%

-

Quand j'écoute pendant ()

-

Alors ma jauge évolue de -Et ma nouvelle jauge est à

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
dureepourcentageimpactnouveau_niveau
1 min10%0%50%
3 min30%+1%51%
5 min50%+1%51%
7.9 min79%+1%51%
8 min80%+2%52%
9.5 min95%+2%52%
5 sec<1%-0.5%49.5%
-
-
- -

Jauge initiale et cold start

-
-

En tant que nouvel utilisateur -Je veux que mes jauges d'intérêt démarrent de manière neutre -Afin de découvrir du contenu sans biais initial

-
-

15 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Inscription - toutes les jauges à 50%

-

Quand je m'inscris sur RoadWave

-

Alors toutes mes jauges d'intérêt sont initialisées à 50% -Et je ne dois pas remplir de questionnaire -Et l'inscription est ultra-rapide

-
-

2. Liste des catégories disponibles

-

Étant donné que je suis un nouvel utilisateur

-

Quand je consulte mes centres d'intérêt

-

Alors je vois les catégories suivantes à 50%:

-
| catégorie |
-|---|
-| Automobile |
-| Voyage |
-| Famille |
-| Amour |
-| Musique |
-| Économie |
-| Cryptomonnaie |
-| Politique |
-| Culture générale |
-| Sport |
-| Technologie |
-| Santé |
-
-
-

3. Cold start - premier contenu écouté

-

Étant donné que je viens de m'inscrire -Et que toutes mes jauges sont à 50%

-

Quand j'écoute mon premier podcast "Automobile" à 90%

-

Alors ma jauge "Automobile" monte à 52% (+2%) -Et toutes les autres jauges restent à 50%

-
-

4. Cold start - premier skip

-

Étant donné que je viens de m'inscrire -Et que toutes mes jauges sont à 50%

-

Quand je skip rapidement un contenu "Économie"

-

Alors ma jauge "Économie" descend à 49.5% (-0.5%) -Et toutes les autres jauges restent à 50%

-
-

5. Après 10 écoutes, profil commence à se dessiner

-

Étant donné que je suis un nouvel utilisateur -Et que j'ai écouté:

-
| contenu | tags | completion |
-|---|---|---|
-| Contenu 1 | Automobile | 90% |
-| Contenu 2 | Automobile, Sport | 85% |
-| Contenu 3 | Voyage | 75% |
-| Contenu 4 | Économie | skip 5s |
-| Contenu 5 | Automobile | 95% |
-| Contenu 6 | Sport | 80% |
-| Contenu 7 | Politique | skip 8s |
-| Contenu 8 | Voyage | 88% |
-| Contenu 9 | Automobile | 92% |
-| Contenu 10 | Technologie | 40% |
-
-

Alors mes jauges reflètent mes préférences:

-
| catégorie | tendance |
-|---|---|
-| Automobile | Forte hausse (>55%) |
-| Voyage | Hausse modérée (~53%) |
-| Sport | Hausse modérée (~53%) |
-| Économie | Baisse légère (~49.5%) |
-| Politique | Baisse légère (~49.5%) |
-| Technologie | Neutre (~51%) |
-
-
-

6. Pas de questionnaire onboarding par défaut

-

Quand je termine l'inscription

-

Alors aucun questionnaire de centres d'intérêt n'est affiché -Et je peux commencer à écouter immédiatement -Et l'algorithme apprend naturellement

-
-

7. Algorithme avec jauges à 50% - chances égales

-

Étant donné que toutes mes jauges sont à 50%

-

Quand l'algorithme calcule les recommandations

-

Alors tous les types de contenus ont une chance égale -Et aucun biais initial n'est appliqué -Et la géolocalisation prime sur les intérêts

-
-

8. Questionnaire optionnel après 3 écoutes (post-MVP)

-

Étant donné que j'ai écouté 3 contenus

-

Quand je termine ma 3ème écoute

-

Alors je vois une notification in-app optionnelle:

-
| titre | Améliorez vos recommandations |
-|---|---|
-| message | Sélectionnez vos centres d'intérêt |
-| actions | Configurer maintenant / Plus tard |
-
-
-

9. Remplir le questionnaire optionnel (post-MVP)

-

Étant donné que le questionnaire optionnel est affiché

-

Quand je sélectionne les centres d'intérêt suivants:

-
| catégorie |
-|---|
-| Automobile |
-| Voyage |
-| Sport |
-
-

Alors les jauges sélectionnées passent à 70% -Et les jauges non sélectionnées passent à 30% -Et je vois le message "Vos préférences ont été enregistrées"

-
-

10. Skipper le questionnaire optionnel (post-MVP)

-

Étant donné que le questionnaire optionnel est affiché

-

Quand je clique sur "Plus tard"

-

Alors toutes mes jauges conservent 50% -Et l'algorithme continue d'apprendre naturellement -Et je ne suis plus sollicité

-
-

11. Comportement déterministe et testable

-

Étant donné deux nouveaux utilisateurs A et B

-

Quand les deux s'inscrivent au même moment

-

Alors leurs jauges sont identiques (toutes à 50%) -Et leurs recommandations initiales sont identiques (basées sur géo uniquement)

-
-

12. Équité entre créateurs au cold start

-

Étant donné qu'un nouvel utilisateur s'inscrit -Et qu'il existe 1000 contenus de catégories variées dans sa zone

-

Quand l'algorithme génère les premières recommandations

-

Alors tous les contenus ont une pondération intérêts identique (50%) -Et seuls la géolocalisation et l'engagement différencient les contenus -Et aucun créateur n'a d'avantage initial

-
-

13. Catégories extensibles

-

Étant donné que RoadWave ajoute une nouvelle catégorie "Gastronomie"

-

Quand je consulte mes centres d'intérêt

-

Alors je vois la nouvelle catégorie "Gastronomie" à 50% -Et je peux commencer à l'explorer normalement

-
-

14. Voir l'évolution de mes jauges

-

Étant donné que je suis un utilisateur avec historique

-

Quand je consulte mes centres d'intérêt dans les paramètres

-

Alors je vois mes jauges actuelles:

-
| catégorie | niveau | evolution |
-|---|---|---|
-| Automobile | 67% | +17% |
-| Voyage | 82% | +32% |
-| Économie | 34% | -16% |
-| Sport | 50% | 0% |
-
-

Et je comprends mes préférences actuelles

-
-

15. Friction zéro à l'inscription

-

Étant donné que je veux m'inscrire rapidement

-

Quand je remplis les 4 champs obligatoires -Et que je clique sur "S'inscrire"

-

Alors mon compte est créé immédiatement -Et je peux commencer à écouter dans les 30 secondes -Et aucune configuration supplémentaire n'est requise

-
-
- -

Synchronisation actions offline

-
-

En tant qu'utilisateur -Je veux que mes actions offline soient synchronisées quand je me reconnecte -Afin de ne perdre aucune interaction même sans connexion

-
-

45 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que j'utilise l'application RoadWave

-
-

1. Like d'un contenu en mode offline

-

Étant donné que je n'ai aucune connexion Internet

-

Quand je like un contenu téléchargé

-

Alors l'action est enregistrée localement dans SQLite: -Et l'UI affiche immédiatement le like (optimistic update)

-
-

2. Unlike d'un contenu en mode offline

-

Étant donné que je n'ai aucune connexion Internet -Et que j'avais liké un contenu

-

Quand je retire mon like

-

Alors l'action est enregistrée localement: -Et l'UI retire immédiatement le like

-
-

3. Abonnement à un créateur en mode offline

-

Étant donné que je n'ai aucune connexion Internet

-

Quand je m'abonne à un créateur

-

Alors l'action est enregistrée localement: -Et l'UI affiche immédiatement "Abonné ✓"

-
-

4. Désabonnement d'un créateur en mode offline

-

Étant donné que je n'ai aucune connexion Internet -Et que j'étais abonné à un créateur

-

Quand je me désabonne

-

Alors l'action est enregistrée localement: -Et l'UI affiche "S'abonner"

-
-

5. Signalement d'un contenu en mode offline

-

Étant donné que je n'ai aucune connexion Internet

-

Quand je signale un contenu pour "Contenu inapproprié"

-

Alors l'action est enregistrée localement: -Et je vois "Signalement enregistré. Sera envoyé à la reconnexion."

-
-

6. Progression audio-guide en mode offline

-

Étant donné que je n'ai aucune connexion Internet -Et que j'écoute un audio-guide multi-séquences

-

Quand je termine la séquence 3/10

-

Alors la progression est enregistrée localement: -Et ma progression est sauvegardée

-
-

7. Multiple actions offline stockées en queue

-

Étant donné que je n'ai aucune connexion Internet pendant 2 jours

-

Quand j'effectue plusieurs actions:

-
| action | cible |
-|---|---|
-| like | contenu A |
-| like | contenu B |
-| subscribe | créateur X |
-| unlike | contenu C |
-| report | contenu D |
-
-

Alors les 5 actions sont stockées dans pending_actions -Et elles seront synchronisées dans l'ordre à la reconnexion

-
-

8. Détection reconnexion Internet

-

Étant donné que j'étais en mode offline

-

Quand l'app détecte une reconnexion Internet

-

Alors le processus de synchronisation démarre automatiquement -Et je vois une notification "Synchronisation en cours..."

-
-

9. Récupération queue locale pendant sync

-

Étant donné que la synchronisation démarre

-

Quand l'app récupère les actions en attente

-

Alors une requête SQL est exécutée: -Et toutes les actions sont récupérées dans l'ordre chronologique

-
-

10. Envoi batch API des actions

-

Étant donné que 15 actions sont en attente

-

Quand le batch est envoyé au backend

-

Alors une requête POST /sync/actions est faite: -Et toutes les actions sont groupées en une seule requête

-
-

11. Backend traite chaque action

-

Étant donné que le backend reçoit le batch d'actions

-

Quand il traite chaque action

-

Alors pour chaque action:

-
| étape | détail |
-|---|---|
-| Validation | Vérifier user_id, content_id valides |
-| Vérification existence | Contenu/créateur existe toujours ? |
-| Application action | INSERT/UPDATE/DELETE en base |
-| Mise à jour compteurs | Likes, abonnés, etc. |
-| Impact sur algorithme | Mise à jour jauges si nécessaire |
-
-
-

12. Confirmation réception et suppression queue locale

-

Étant donné que le backend a traité toutes les actions avec succès

-

Quand la confirmation est reçue par l'app

-

Alors les actions sont supprimées de la queue locale: -Et la table pending_actions est vidée

-
-

13. Toast confirmation synchronisation

-

Étant donné que 15 actions ont été synchronisées

-

Quand la synchronisation se termine

-

Alors je vois un toast:

-
-

14. Synchronisation silencieuse si peu d'actions

-

Étant donné que j'ai seulement 2 actions en attente

-

Quand la synchronisation se termine

-

Alors aucun toast n'est affiché (sync silencieuse) -Et l'expérience reste fluide -Mais je peux voir le détail dans l'historique des syncs

-
-

15. Échec synchronisation - Retry automatique

-

Étant donné que la synchronisation échoue (erreur réseau)

-

Quand l'échec est détecté

-

Alors un retry automatique est programmé dans 30 secondes -Et les actions restent dans pending_actions

-
-

16. 3 tentatives échouées - Notification utilisateur

-

Étant donné que 3 tentatives de synchronisation ont échoué

-

Quand la 3ème tentative échoue

-

Alors je reçois une notification:

-
-

17. Actions conservées jusqu'à sync réussie

-

Étant donné que la synchronisation échoue plusieurs fois

-

Quand les tentatives continuent d'échouer

-

Alors les actions restent dans pending_actions -Et aucune action n'est perdue -Et elles seront envoyées dès que la connexion sera stable

-
-

18. Rétention max 7 jours - Purge automatique

-

Étant donné qu'une action est en attente depuis 7 jours

-

Quand le système détecte cette ancienneté

-

Alors l'action est automatiquement supprimée de la queue -Et je vois "1 action trop ancienne supprimée (>7 jours)" -Et cela évite une queue infinie

-
-

19. Justification rétention 7 jours

-

Étant donné qu'un utilisateur ne se connecte jamais pendant 2 semaines

-

Quand ses actions ont >7 jours

-

Alors elles sont purgées automatiquement -Et évite une queue qui grandit indéfiniment

-
-

20. Retry manuel après échec

-

Étant donné que la synchronisation a échoué

-

Quand je clique sur "Réessayer maintenant"

-

Alors une nouvelle tentative de synchronisation est lancée immédiatement -Et si elle réussit, les actions sont synchronisées

-
-

21. Backend retourne contenus supprimés

-

Étant donné que j'ai liké un contenu offline -Mais que le contenu a été supprimé entre temps

-

Quand le backend traite la synchronisation

-

Alors il retourne:

-
-

22. App supprime fichiers locaux contenus supprimés

-

Étant donné que le backend retourne deleted_content_ids: [123, 456]

-

Quand l'app traite la réponse

-

Alors elle supprime les fichiers locaux des contenus 123 et 456 -Et libère l'espace disque -Et les actions associées sont retirées de la queue

-
-

23. Contenu supprimé en cours d'écoute

-

Étant donné que j'écoute le contenu 123 en offline -Et que la sync détecte que le contenu a été supprimé

-

Quand la lecture actuelle se termine

-

Alors l'app attend 2 secondes -Et passe automatiquement au contenu suivant -Et le fichier du contenu 123 est supprimé en arrière-plan

-
-

24. Toast notification contenu retiré

-

Étant donné que 2 contenus téléchargés ont été supprimés

-

Quand la synchronisation se termine

-

Alors je vois un toast:

-
-

25. Contenu modéré après téléchargement

-

Étant donné que j'ai téléchargé un contenu qui est ensuite modéré

-

Quand la synchronisation détecte la modération

-

Alors le contenu est immédiatement supprimé du device -Et je ne peux plus l'écouter -Et cela garantit la conformité même offline

-
-

26. Justification pas de conflit possible

-

Étant donné que les actions offline sont unilatérales (likes, abonnements)

-

Quand elles sont synchronisées

-

Alors il n'y a pas de conflit de version possible -Et pas de merge complexe nécessaire

-
-

27. Justification UX fluide offline

-

Étant donné que toutes les actions fonctionnent offline

-

Quand l'utilisateur interagit sans connexion

-

Alors l'expérience est identique au mode online -Et l'utilisateur n'est pas bloqué -Et peut utiliser l'app normalement

-
-

28. Justification batch = Économie requêtes

-

Étant donné que 15 actions sont en attente

-

Quand elles sont synchronisées en batch

-

Alors 1 seule requête HTTP est envoyée (vs 15 si individuelles) -Et cela économise la bande passante et la batterie -Et réduit la charge serveur

-
-

29. Justification conformité modération offline

-

Étant donné qu'un contenu illégal est modéré pendant qu'un user est offline

-

Quand le user se reconnecte

-

Alors le contenu est immédiatement supprimé de son device -Et cela garantit que les contenus illégaux disparaissent même offline

-
-

30. Historique synchronisations

-

Étant donné que j'accède à "Paramètres > Synchronisation"

-

Quand je consulte l'historique

-

Alors je vois:

-
| date | actions sync | statut |
-|---|---|---|
-| 15/06/2025 14:30:00 | 15 | Réussi ✅ |
-| 14/06/2025 09:15:00 | 7 | Réussi ✅ |
-| 13/06/2025 18:45:00 | 3 | Échec ❌ |
-
-
-

31. Détail d'une synchronisation

-

Étant donné que je clique sur une ligne de l'historique

-

Quand le détail s'affiche

-

Alors je vois:

-
-

32. Compteur actions en attente visible

-

Étant donné que j'ai 12 actions en attente de synchronisation

-

Quand j'accède à l'onglet Profil

-

Alors je vois un badge "12" sur l'icône de synchronisation -Et je sais qu'il y a des actions en attente

-
-

33. Synchronisation manuelle forcée

-

Étant donné que je veux forcer une synchronisation immédiate

-

Quand je vais dans "Paramètres > Synchronisation" -Et que je clique sur "Synchroniser maintenant"

-

Alors la synchronisation démarre immédiatement -Et toutes les actions en attente sont envoyées

-
-

34. Statistiques utilisateur - Syncs effectuées

-

Étant donné que j'accède à mes statistiques

-

Quand je consulte la section Synchronisation

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Synchronisations depuis début | 87 |
-| Actions synchronisées total | 1,234 |
-| Taux de succès | 94% |
-| Dernière sync | Il y a 2h |
-
-
-

35. Statistiques admin - Volume synchronisations

-

Étant donné qu'un admin consulte les métriques de synchronisation

-

Quand il accède au dashboard

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Synchronisations/jour | 45,678 |
-| Actions synchronisées/jour | 234,567 |
-| Taux succès sync | 96.5% |
-| Temps moyen traitement batch | 0.8s |
-| Actions en attente (global) | 12,345 |
-
-
-

36. Alerte admin si taux échec sync >10%

-

Étant donné que le taux d'échec sync dépasse 10%

-

Quand le système détecte cette anomalie

-

Alors une alerte est envoyée:

-
-

37. Synchronisation rapide <2s

-

Étant donné que j'ai 20 actions en attente

-

Quand la synchronisation démarre

-

Alors le traitement prend <2 secondes -Et je ne remarque aucun ralentissement de l'app

-
-

38. Synchronisation de gros batch (100 actions)

-

Étant donné que je n'ai pas synchronisé pendant 1 semaine -Et que j'ai 100 actions en attente

-

Quand la synchronisation démarre

-

Alors le batch de 100 actions est traité en <5 secondes -Et toutes les actions sont synchronisées avec succès

-
-

39. Gestion charge serveur - 10 000 syncs simultanées

-

Étant donné que 10 000 utilisateurs se reconnectent simultanément

-

Quand chacun envoie un batch de 20 actions

-

Alors le serveur traite 200 000 actions -Et grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s -Et aucun timeout n'est constaté

-
-

40. Stockage SQLite optimisé

-

Étant donné que la table pending_actions stocke des centaines d'actions

-

Quand des requêtes sont exécutées

-

Alors la table est indexée sur created_at -Et les requêtes SELECT et DELETE sont instantanées (<10ms) -Et l'expérience utilisateur reste fluide

-
-

41. Nettoyage automatique table pending_actions

-

Étant donné que la table pending_actions grossit avec le temps

-

Quand les actions sont synchronisées et supprimées

-

Alors la table est automatiquement optimisée (VACUUM sur SQLite) -Et l'espace disque est libéré -Et les performances restent optimales

-
-

42. Action dupliquée - Idempotence

-

Étant donné que j'ai liké un contenu offline -Et que la sync échoue et retry

-

Quand le backend reçoit 2 fois le même like

-

Alors il applique l'idempotence (1 seul like enregistré) -Et le compteur de likes n'est pas faussé

-
-

43. Séquence like/unlike offline

-

Étant donné que j'ai liké puis unliké un contenu offline

-

Quand les 2 actions sont synchronisées

-

Alors le backend applique les 2 actions dans l'ordre -Et le résultat final est "pas de like" (état correct)

-
-

44. Abonnement puis désabonnement offline

-

Étant donné que je me suis abonné puis désabonné d'un créateur offline

-

Quand les 2 actions sont synchronisées

-

Alors le backend applique les 2 actions dans l'ordre -Et le résultat final est "pas abonné" -Et les jauges évoluent correctement (+5% puis -5% = 0% net)

-
-

45. Créateur supprimé pendant offline

-

Étant donné que je me suis abonné à un créateur offline -Mais que le créateur a supprimé son compte entre temps

-

Quand la sync traite l'abonnement

-

Alors le backend retourne "creator_deleted" -Et l'action est ignorée silencieusement -Et aucune erreur n'est affichée à l'utilisateur

-
-
- -

Téléchargement de contenus offline

-
-

En tant qu'utilisateur -Je veux télécharger des contenus pour les écouter sans connexion -Afin de profiter de RoadWave même dans les zones sans réseau

-
-

49 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis connecté à l'application RoadWave

-
-

1. Option "Autour de moi" - Rayon 50 km

-

Étant donné que je suis à Paris (position GPS détectée)

-

Quand je sélectionne "Télécharger > Autour de moi"

-

Alors l'app recherche tous les contenus géolocalisés dans un rayon de 50 km -Et je vois une liste de contenus de Paris et banlieue proche -Et l'estimation affiche "~150 contenus disponibles"

-
-

2. Option "Ma ville" - Limite administrative détectée

-

Étant donné que je suis à Lyon (position GPS détectée)

-

Quand je sélectionne "Télécharger > Ma ville"

-

Alors l'app détecte automatiquement "Lyon" comme ville -Et recherche tous les contenus géolocalisés "Lyon" -Et je vois uniquement les contenus de la ville de Lyon (pas banlieue)

-
-

3. Option "Mon département" - Sélection dans liste

-

Étant donné que je veux télécharger des contenus pour un département

-

Quand je sélectionne "Télécharger > Mon département"

-

Alors je vois une liste de tous les départements français:

-
| département |
-|---|
-| 01 - Ain |
-| 02 - Aisne |
-| 75 - Paris |
-| 69 - Rhône |
-| ... |
-
-

Et je peux choisir un département

-
-

4. Sélection département et téléchargement contenus

-

Étant donné que je sélectionne "75 - Paris" dans la liste des départements

-

Quand la sélection est confirmée

-

Alors l'app recherche tous les contenus géolocalisés "Paris" -Et je vois "~234 contenus disponibles pour Paris"

-
-

5. Option "Ma région" - Sélection dans liste

-

Étant donné que je veux télécharger des contenus pour une région

-

Quand je sélectionne "Télécharger > Ma région"

-

Alors je vois une liste de toutes les régions françaises:

-
| région |
-|---|
-| Auvergne-Rhône-Alpes |
-| Bretagne |
-| Île-de-France |
-| Nouvelle-Aquitaine |
-| Occitanie |
-| ... |
-
-

Et je peux choisir une région

-
-

6. Sélection région et téléchargement contenus

-

Étant donné que je sélectionne "Bretagne" dans la liste des régions

-

Quand la sélection est confirmée

-

Alors l'app recherche tous les contenus géolocalisés des départements bretons:

-
| département |
-|---|
-| Côtes-d'Armor (22) |
-| Finistère (29) |
-| Ille-et-Vilaine (35) |
-| Morbihan (56) |
-
-

Et je vois "~487 contenus disponibles pour Bretagne"

-
-

7. Recherche manuelle ville

-

Étant donné que je veux télécharger des contenus pour une ville spécifique

-

Quand je tape "Marseille" dans la barre de recherche

-

Alors l'app propose des suggestions:

-
| suggestion |
-|---|
-| Marseille (13) |
-| Marseille-en-Beauvaisis |
-
-

Et je peux sélectionner "Marseille (13)"

-
-

8. Recherche manuelle avec autocomplétion

-

Étant donné que je tape "Ly" dans la barre de recherche

-

Quand l'autocomplétion s'active

-

Alors je vois des suggestions:

-
| suggestion |
-|---|
-| Lyon (69) |
-| Lys-lez-Lannoy |
-
-

Et je peux affiner ma recherche

-
-

9. Utilisateur gratuit - Limite 50 contenus max

-

Étant donné que je suis un utilisateur gratuit -Et que j'ai déjà téléchargé 45 contenus

-

Quand j'accède à la page Téléchargements

-

Alors je vois "45 / 50 contenus téléchargés" -Et je peux télécharger 5 contenus supplémentaires maximum

-
-

10. Utilisateur gratuit - Tentative dépasser limite 50

-

Étant donné que je suis gratuit et j'ai déjà 50 contenus téléchargés

-

Quand j'essaie de télécharger un 51ème contenu

-

Alors le téléchargement est refusé -Et je vois le message:

-
-

11. Utilisateur Premium - Téléchargements illimités

-

Étant donné que je suis un utilisateur Premium -Et que j'ai déjà téléchargé 245 contenus

-

Quand j'accède à la page Téléchargements

-

Alors je vois "245 contenus (3.2 GB)" -Et aucune limite n'est affichée -Et je peux télécharger autant de contenus que je veux

-
-

12. Limite Premium = Espace disque disponible

-

Étant donné que je suis Premium -Et que mon device a 500 MB d'espace disque disponible

-

Quand j'essaie de télécharger 100 contenus (2 GB)

-

Alors le téléchargement échoue après ~50 contenus (500 MB) -Et je vois "Espace disque insuffisant. Libérez de l'espace pour continuer."

-
-

13. Calcul temps écoute disponible gratuit

-

Étant donné que je suis gratuit avec 50 contenus téléchargés -Et que la durée moyenne d'un contenu est 5 minutes

-

Quand je calcule le temps d'écoute disponible

-

Alors 50 contenus × 5 min = 250 minutes = 4h10 d'écoute -Et cela suffit pour un trajet quotidien ou road trip court

-
-

14. Calcul temps écoute disponible Premium illimité

-

Étant donné que je suis Premium avec 300 contenus téléchargés -Et que la durée moyenne est 5 minutes

-

Quand je calcule le temps d'écoute disponible

-

Alors 300 contenus × 5 min = 1500 minutes = 25h d'écoute -Et cela suffit pour un road trip de plusieurs jours

-
-

15. Téléchargement par défaut en WiFi uniquement

-

Étant donné que je suis connecté en WiFi

-

Quand je clique sur "Télécharger 20 contenus"

-

Alors le téléchargement démarre immédiatement -Et aucune popup de confirmation n'apparaît

-
-

16. Tentative téléchargement en données mobiles - Popup confirmation

-

Étant donné que je suis connecté en 4G (pas de WiFi)

-

Quand je clique sur "Télécharger 20 contenus"

-

Alors une popup apparaît:

-
-

17. Calcul estimation consommation data mobile

-

Étant donné que je veux télécharger 20 contenus -Et que la durée moyenne est 5 minutes -Et que la qualité Standard est 48 kbps Opus

-

Quand l'estimation est calculée

-

Alors consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB -Et ce montant est affiché dans la popup

-
-

18. Confirmation téléchargement en données mobiles

-

Étant donné que je vois la popup de confirmation données mobiles

-

Quand je clique sur "Continuer quand même"

-

Alors le téléchargement démarre immédiatement via 4G -Et la consommation data est comptabilisée sur mon forfait mobile

-
-

19. Refus téléchargement données mobiles - Attendre WiFi

-

Étant donné que je vois la popup de confirmation données mobiles

-

Quand je clique sur "Attendre WiFi"

-

Alors les téléchargements sont mis en file d'attente -Et ils démarreront automatiquement quand le WiFi sera détecté

-
-

20. Détection automatique WiFi et reprise téléchargements

-

Étant donné que j'ai mis 20 contenus en file d'attente (attente WiFi)

-

Quand l'app détecte une connexion WiFi

-

Alors les téléchargements démarrent automatiquement -Et je reçois une notification "Téléchargements en cours via WiFi"

-
-

21. Qualité Standard (48 kbps) par défaut

-

Étant donné que je configure mes téléchargements

-

Quand j'accède aux paramètres de qualité

-

Alors la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut -Et elle est disponible pour tous (gratuit + Premium)

-
-

22. Qualité Basse (24 kbps) disponible pour tous

-

Étant donné que j'ai peu d'espace disque disponible

-

Quand je sélectionne qualité "Basse (24 kbps - ~10 MB/h)"

-

Alors mes prochains téléchargements seront en 24 kbps -Et l'espace utilisé sera divisé par 2 par rapport à Standard -Et cette option est disponible pour gratuit + Premium

-
-

23. Qualité Haute (64 kbps) réservée Premium

-

Étant donné que je suis un utilisateur gratuit

-

Quand je consulte les options de qualité

-

Alors l'option "Haute (64 kbps - ~30 MB/h)" est grisée -Et je vois "👑 Premium uniquement" -Et je ne peux pas la sélectionner

-
-

24. Utilisateur Premium peut choisir qualité Haute

-

Étant donné que je suis un utilisateur Premium

-

Quand je consulte les options de qualité

-

Alors l'option "Haute (64 kbps - ~30 MB/h)" est disponible -Et je peux la sélectionner pour mes téléchargements -Et la qualité audio sera excellente (meilleure restitution voix et ambiances)

-
-

25. Comparaison taille fichiers selon qualité

-

Étant donné que je veux télécharger 50 contenus de 5 min chacun

-

Quand je compare les qualités

-

Alors les tailles totales sont:

-
| qualité | bitrate | taille totale |
-|---|---|---|
-| Basse | 24 kbps | ~250 MB |
-| Standard | 48 kbps | ~500 MB |
-| Haute | 64 kbps | ~650 MB |
-
-
-

26. Justification Standard = Bon compromis

-

Étant donné que le contenu RoadWave est principalement de la voix

-

Quand la qualité Standard (48 kbps Opus) est utilisée

-

Alors la qualité est très correcte pour la voix -Et équivalente à la radio FM -Et le compromis qualité/taille est optimal

-
-

27. Justification Haute réservée Premium = Incitation upgrade

-

Étant donné qu'un utilisateur gratuit veut la meilleure qualité

-

Quand il voit que Haute est réservée Premium

-

Alors cela l'incite à passer Premium pour 4.99€/mois -Et c'est un avantage tangible supplémentaire de Premium

-
-

28. Changement qualité après téléchargements existants

-

Étant donné que j'ai déjà téléchargé 30 contenus en qualité Standard

-

Quand je change la qualité vers Haute (si Premium)

-

Alors les 30 contenus existants restent en Standard -Et seuls les nouveaux téléchargements seront en Haute -Et je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute

-
-

29. Téléchargement individuel d'un contenu

-

Étant donné que je consulte la page d'un contenu

-

Quand je clique sur l'icône de téléchargement 📥

-

Alors le téléchargement démarre -Et une barre de progression apparaît -Et l'icône devient ✅ quand terminé

-
-

30. Téléchargement batch de contenus sélectionnés

-

Étant donné que je consulte une liste de contenus pour "Paris"

-

Quand je sélectionne 15 contenus manuellement -Et que je clique sur "Télécharger la sélection"

-

Alors les 15 contenus sont téléchargés en parallèle (max 3 simultanés) -Et une notification affiche "15 contenus téléchargés"

-
-

31. Téléchargement automatique recommandations zone

-

Étant donné que je sélectionne "Autour de moi" (Paris)

-

Quand je clique sur "Télécharger les 50 meilleurs contenus"

-

Alors l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents -Et les télécharge tous -Et je n'ai pas besoin de choisir manuellement

-
-

32. Barre de progression téléchargement global

-

Étant donné que je télécharge 20 contenus

-

Quand les téléchargements sont en cours

-

Alors je vois une barre de progression globale:

-
-

33. Téléchargements en tâche de fond

-

Étant donné que je lance le téléchargement de 30 contenus

-

Quand je ferme l'app ou passe à une autre activité

-

Alors les téléchargements continuent en arrière-plan -Et je reçois une notification quand tous sont terminés

-
-

34. Pause et reprise téléchargements

-

Étant donné que je télécharge 20 contenus

-

Quand je clique sur "Pause"

-

Alors les téléchargements en cours se terminent -Et les téléchargements en attente sont mis en pause -Et je peux cliquer sur "Reprendre" plus tard

-
-

35. Annulation téléchargements

-

Étant donné que je télécharge 20 contenus

-

Quand je clique sur "Annuler"

-

Alors tous les téléchargements sont arrêtés -Et les fichiers partiels sont supprimés -Et l'espace disque est libéré

-
-

36. Gestion erreurs téléchargement

-

Étant donné que je télécharge un contenu -Mais que la connexion Internet coupe au milieu

-

Quand la connexion revient

-

Alors le téléchargement reprend automatiquement où il s'était arrêté -Et aucune perte de progression n'a lieu

-
-

37. Retry automatique après échec

-

Étant donné qu'un téléchargement échoue 3 fois consécutives

-

Quand l'échec est détecté

-

Alors le contenu est marqué "Échec" -Et je vois une notification "3 contenus n'ont pas pu être téléchargés" -Et je peux retry manuellement en cliquant sur "Réessayer"

-
-

38. Liste contenus téléchargés

-

Étant donné que j'ai téléchargé 45 contenus

-

Quand j'accède à "Téléchargements"

-

Alors je vois la liste complète de mes 45 contenus -Et pour chaque contenu: titre, créateur, durée, taille, date téléchargement

-
-

39. Tri contenus téléchargés

-

Étant donné que je consulte ma liste de téléchargements

-

Quand je clique sur "Trier par"

-

Alors je peux trier par:

-
| critère | ordre |
-|---|---|
-| Date téléchargement | Plus récent / Plus ancien |
-| Titre | A-Z / Z-A |
-| Créateur | A-Z / Z-A |
-| Durée | Plus long / Plus court |
-| Taille | Plus gros / Plus petit |
-
-
-

40. Recherche dans contenus téléchargés

-

Étant donné que j'ai 200 contenus téléchargés

-

Quand je tape "Tesla" dans la barre de recherche

-

Alors seuls les contenus contenant "Tesla" s'affichent -Et je peux rapidement trouver un contenu spécifique

-
-

41. Suppression individuelle contenu téléchargé

-

Étant donné que je veux supprimer un contenu téléchargé

-

Quand je swipe left (iOS) ou long press (Android) sur le contenu -Et que je clique sur "Supprimer"

-

Alors le fichier est supprimé du device -Et l'espace disque est libéré -Et le compteur est décrémenté (ex: 45/50 → 44/50)

-
-

42. Suppression batch contenus téléchargés

-

Étant donné que je veux supprimer plusieurs contenus

-

Quand je sélectionne 10 contenus -Et que je clique sur "Supprimer la sélection"

-

Alors les 10 fichiers sont supprimés -Et ~100 MB d'espace disque sont libérés -Et une notification confirme "10 contenus supprimés"

-
-

43. Suppression tous les contenus téléchargés

-

Étant donné que j'ai 45 contenus téléchargés

-

Quand je clique sur "Supprimer tout" -Et que je confirme l'action

-

Alors tous les 45 contenus sont supprimés -Et l'espace disque total est libéré (~450 MB) -Et le compteur repasse à 0/50

-
-

44. Espace disque utilisé visible

-

Étant donné que j'ai téléchargé 45 contenus

-

Quand j'accède à la page Téléchargements

-

Alors je vois l'espace disque utilisé:

-
-

45. Statistiques téléchargements

-

Étant donné que j'accède à mes statistiques

-

Quand je consulte la section Téléchargements

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Contenus actuellement téléchargés | 45 |
-| Espace disque utilisé | 478 MB |
-| Contenus téléchargés depuis début | 287 |
-| Total data téléchargée | 3.2 GB |
-| Téléchargements via WiFi | 92% |
-| Téléchargements via mobile | 8% |
-
-
-

46. Lecture contenu téléchargé sans connexion

-

Étant donné que je n'ai aucune connexion Internet (mode avion) -Et que j'ai des contenus téléchargés

-

Quand je lance un contenu téléchargé

-

Alors la lecture démarre normalement depuis le fichier local -Et aucune erreur de connexion n'apparaît

-
-

47. Badge "Téléchargé" sur contenus offline

-

Étant donné que j'ai téléchargé certains contenus

-

Quand je consulte une liste de contenus

-

Alors les contenus téléchargés ont un badge ✅ "Offline" -Et je sais immédiatement lesquels sont disponibles sans connexion

-
-

48. Filtre "Téléchargés uniquement"

-

Étant donné que je veux voir uniquement mes contenus offline

-

Quand j'active le filtre "Téléchargés uniquement"

-

Alors seuls les contenus téléchargés s'affichent -Et je peux facilement naviguer dans mon catalogue offline

-
-

49. Playlist offline automatique

-

Étant donné que j'ai téléchargé 45 contenus

-

Quand j'accède à "Téléchargements"

-

Alors je peux lancer une playlist aléatoire de mes 45 contenus -Et profiter d'une écoute continue offline

-
-
- -

Validité et renouvellement contenus offline

-
-

En tant qu'utilisateur -Je veux que mes contenus téléchargés restent valides un certain temps -Afin de garantir la légalité et la fraîcheur du contenu

-
-

38 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis connecté à l'application RoadWave -Et que j'ai des contenus téléchargés

-
-

1. Validité de 30 jours après téléchargement

-

Étant donné que je télécharge un contenu le 1er juin 2025

-

Quand le téléchargement est terminé

-

Alors le contenu est valide jusqu'au 1er juillet 2025 (30 jours) -Et la date d'expiration est stockée en local

-
-

2. Affichage date expiration sur contenu téléchargé

-

Étant donné que j'ai téléchargé un contenu il y a 20 jours

-

Quand je consulte les détails du contenu

-

Alors je vois "Expire dans 10 jours" -Et je sais combien de temps il reste avant expiration

-
-

3. Standard industrie aligné (Spotify, YouTube, Deezer)

-

Étant donné que Spotify, YouTube Music et Deezer utilisent 30 jours

-

Quand RoadWave fixe également 30 jours

-

Alors c'est le standard accepté par les utilisateurs -Et il n'y a pas de confusion avec les autres plateformes

-
-

4. Justification 30 jours - Force reconnexion régulière

-

Étant donné qu'un utilisateur ne se connecte jamais

-

Quand ses contenus expirent après 30 jours

-

Alors il est obligé de se reconnecter pour les renouveler -Et le système peut vérifier:

-
| vérification |
-|---|
-| Abonnement Premium toujours actif |
-| Contenus non modérés/supprimés |
-| Métadonnées à jour |
-
-
-

5. Justification 30 jours - Évite stockage obsolète

-

Étant donné qu'un contenu a été modéré après téléchargement

-

Quand le contenu expire après 30 jours maximum

-

Alors le contenu illégal est automatiquement supprimé -Et ne reste pas indéfiniment sur le device

-
-

6. Détection WiFi et contenus >25 jours

-

Étant donné que j'ai des contenus téléchargés il y a 26 jours

-

Quand l'app détecte une connexion WiFi

-

Alors une requête GET /offline/contents/refresh est envoyée -Et le backend vérifie chaque contenu

-
-

7. Vérification abonnement Premium toujours actif

-

Étant donné qu'un contenu téléchargé en Premium est à renouveler

-

Quand le backend vérifie le statut -Et que l'abonnement Premium est toujours actif

-

Alors la validité est renouvelée à 30 jours supplémentaires

-
-

8. Abonnement Premium expiré - Contenu non renouvelé

-

Étant donné qu'un contenu Premium téléchargé est à renouveler

-

Quand le backend vérifie le statut -Et que l'abonnement Premium a expiré

-

Alors le contenu n'est pas renouvelé -Et il sera supprimé à l'expiration (J-0) -Et l'utilisateur voit "Contenu Premium expiré (abonnement inactif)"

-
-

9. Vérification contenu pas modéré/supprimé

-

Étant donné qu'un contenu téléchargé est à renouveler

-

Quand le backend vérifie le statut -Et que le contenu a été modéré ou supprimé entre temps

-

Alors le contenu n'est pas renouvelé -Et sera supprimé immédiatement du device -Et l'utilisateur voit "1 contenu retiré (violation règles)"

-
-

10. Mise à jour métadonnées lors du renouvellement

-

Étant donné qu'un contenu téléchargé est renouvelé

-

Quand le backend traite le renouvellement

-

Alors les métadonnées sont mises à jour:

-
| métadonnée | mise à jour si changée |
-|---|---|
-| Titre | ✅ |
-| Nom créateur | ✅ |
-| Description | ✅ |
-| Tags | ✅ |
-| Statut Premium | ✅ |
-
-

Et l'utilisateur voit les infos à jour

-
-

11. Pas de re-téléchargement audio si fichier OK

-

Étant donné qu'un contenu est renouvelé

-

Quand le fichier audio local est intact

-

Alors seules les métadonnées sont mises à jour -Et le fichier audio n'est pas re-téléchargé -Et cela économise la bande passante

-
-

12. Re-téléchargement audio si fichier corrompu

-

Étant donné qu'un contenu est renouvelé

-

Quand le fichier audio local est corrompu (checksum invalide)

-

Alors le fichier audio est re-téléchargé entièrement -Et le nouveau fichier remplace le corrompu

-
-

13. Renouvellement silencieux si WiFi régulier

-

Étant donné que je me connecte en WiFi tous les jours

-

Quand mes contenus atteignent 25-30 jours

-

Alors ils sont automatiquement renouvelés en arrière-plan -Et je ne vois aucune notification (processus transparent) -Et mes contenus restent valides indéfiniment

-
-

14. Renouvellement batch de plusieurs contenus

-

Étant donné que j'ai 30 contenus à renouveler

-

Quand le renouvellement automatique se déclenche

-

Alors une requête batch est envoyée: -Et le backend traite les 30 contenus en une seule requête -Et cela économise les requêtes HTTP

-
-

15. Temps de traitement renouvellement

-

Étant donné que 30 contenus sont à renouveler

-

Quand la requête batch est traitée

-

Alors le backend répond en <2 secondes -Et les métadonnées sont mises à jour localement -Et l'utilisateur ne remarque aucun ralentissement

-
-

16. Notification J-3 avant expiration

-

Étant donné que j'ai 15 contenus qui expirent dans 3 jours

-

Quand le système vérifie les expirations

-

Alors je reçois une notification: -Et je peux agir avant l'expiration

-
-

17. Pas de notification si connexion WiFi régulière

-

Étant donné que je me connecte en WiFi tous les jours -Et que mes contenus sont automatiquement renouvelés

-

Quand le système vérifie les expirations

-

Alors aucune notification J-3 n'est envoyée

-
-

18. Notification uniquement si contenus non renouvelés

-

Étant donné que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés

-

Quand le J-3 arrive pour les 5 non renouvelés

-

Alors je reçois "5 contenus expirent dans 3 jours" -Et seuls les contenus à risque sont mentionnés

-
-

19. Action utilisateur après notification J-3

-

Étant donné que je reçois la notification J-3

-

Quand je clique sur la notification

-

Alors l'app s'ouvre sur la page Téléchargements -Et je vois les contenus qui vont expirer en rouge -Et je peux me connecter en WiFi pour les renouveler

-
-

20. Suppression automatique J-0 (expiration)

-

Étant donné qu'un contenu n'a pas été renouvelé

-

Quand le jour d'expiration arrive (J-0)

-

Alors le fichier est automatiquement supprimé du device -Et l'espace disque est libéré -Et le compteur est décrémenté (ex: 45/50 → 44/50)

-
-

21. Toast après suppression automatique J-0

-

Étant donné que 15 contenus viennent d'expirer

-

Quand l'utilisateur ouvre l'app

-

Alors il voit un toast:

-
-

22. Liste contenus supprimés après expiration

-

Étant donné que 15 contenus ont expiré

-

Quand je consulte l'historique des suppressions

-

Alors je vois la liste des 15 contenus supprimés:

-
| titre | créateur | date expiration |
-|---|---|---|
-| Mon épisode préféré | JeanDupont | 15 juin 2025 |
-| Road trip Bretagne | MarieLambert | 15 juin 2025 |
-| ... | ... | ... |
-
-

Et je peux les re-télécharger si je veux

-
-

23. Re-téléchargement après expiration

-

Étant donné qu'un contenu a expiré et été supprimé

-

Quand je retrouve ce contenu dans l'app

-

Alors le badge ✅ "Offline" n'est plus affiché -Et je peux le re-télécharger normalement -Et la validité repart à 30 jours

-
-

24. Utilisateur ne se connecte jamais pendant 30 jours

-

Étant donné que je télécharge 50 contenus le 1er juin -Mais que je ne me connecte jamais en WiFi pendant 30 jours

-

Quand le 1er juillet arrive

-

Alors tous les 50 contenus expirent -Et sont automatiquement supprimés -Et je n'ai plus aucun contenu offline

-
-

25. Utilisateur en zone blanche 30+ jours

-

Étant donné que je télécharge 50 contenus avant de partir en zone sans réseau -Et que je reste 45 jours sans connexion

-

Quand les contenus expirent après 30 jours

-

Alors ils sont supprimés même si je ne peux pas me connecter -Et je perds l'accès à mes contenus offline

-
-

26. Recommandation téléchargement avant zone blanche longue

-

Étant donné que je prépare un road trip de 60 jours

-

Quand je consulte la FAQ

-

Alors je vois la recommandation:

-
-

27. Changement statut Premium en gratuit pendant validité

-

Étant donné que je suis Premium et j'ai téléchargé 200 contenus

-

Quand mon abonnement Premium expire -Et que je repasse en gratuit

-

Alors au prochain renouvellement, seulement 50 contenus sont conservés -Et les 150 autres sont supprimés (limite gratuit) -Et je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés."

-
-

28. Sélection automatique 50 meilleurs contenus si passage gratuit

-

Étant donné que je repasse en gratuit avec 200 contenus téléchargés

-

Quand le système applique la limite de 50

-

Alors les 50 contenus les plus récemment écoutés sont conservés -Et les 150 autres sont supprimés -Et cela maximise les chances de garder les contenus que j'aime

-
-

29. Contenus Premium exclusifs supprimés si abonnement expire

-

Étant donné que j'ai téléchargé 20 contenus Premium exclusifs

-

Quand mon abonnement Premium expire

-

Alors les 20 contenus Premium sont immédiatement supprimés -Et je vois "20 contenus Premium supprimés (abonnement expiré)"

-
-

30. Affichage temps restant avant expiration

-

Étant donné que j'ai 45 contenus téléchargés

-

Quand je consulte la page Téléchargements

-

Alors je vois pour chaque contenu:

-
| contenu | temps restant |
-|---|---|
-| Mon épisode (récent) | Expire dans 28 jours |
-| Road trip (ancien) | Expire dans 3 jours |
-
-

Et je sais lesquels sont prioritaires pour renouvellement

-
-

31. Tri par date expiration

-

Étant donné que j'ai 45 contenus avec différentes dates d'expiration

-

Quand je trie par "Expiration"

-

Alors les contenus qui expirent le plus tôt apparaissent en premier -Et je peux voir rapidement lesquels nécessitent une reconnexion urgente

-
-

32. Badge rouge si expiration <3 jours

-

Étant donné qu'un contenu expire dans 2 jours

-

Quand je consulte la liste des téléchargements

-

Alors le contenu a un badge rouge "⚠️ Expire bientôt" -Et il est visuellement mis en avant

-
-

33. Statistiques utilisateur - Taux de renouvellement

-

Étant donné que j'accède à mes statistiques

-

Quand je consulte la section Téléchargements

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Contenus actuels | 45 |
-| Contenus expirés depuis début | 87 |
-| Contenus renouvelés (auto) | 234 |
-| Taux renouvellement automatique | 73% |
-
-
-

34. Statistiques admin - Taux expiration global

-

Étant donné qu'un admin consulte les métriques offline

-

Quand il accède au dashboard

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Contenus téléchargés actifs | 1,234,567 |
-| Expirations ce mois | 45,678 |
-| Taux expiration | 3.7% |
-| Renouvellements automatiques/mois | 234,567 |
-
-
-

35. Alerte admin si taux expiration >10%

-

Étant donné que le taux d'expiration mensuel dépasse 10%

-

Quand le système détecte cette anomalie

-

Alors une alerte est envoyée:

-
-

36. Email rappel si pas de connexion WiFi depuis 20 jours

-

Étant donné que je n'ai pas connecté l'app en WiFi depuis 20 jours -Et que j'ai 45 contenus téléchargés

-

Quand le système détecte cette inactivité WiFi

-

Alors je reçois un email:

-
-

37. Performance renouvellement avec 10 000 utilisateurs simultanés

-

Étant donné que 10 000 utilisateurs se connectent en WiFi simultanément

-

Quand chacun demande le renouvellement de 50 contenus

-

Alors le serveur traite 500 000 vérifications -Et grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s -Et les serveurs gèrent la charge sans problème

-
-

38. Logs audit renouvellements

-

Étant donné qu'un contenu est renouvelé

-

Quand l'opération se termine

-

Alors un log est enregistré:

-
| timestamp | user_id | content_id | action | résultat |
-|---|---|---|---|---|
-| 2025-06-15 14:30:00 | abc123 | xyz789 | renew | success (+30d) |
-| 2025-06-15 14:30:01 | abc123 | def456 | renew | failed (deleted) |
-
-

Et ces logs aident à débugger les problèmes

-
-
- -

Modération préventive

-

22 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que le système de modération préventive est actif

-
-

1. Créateur nouvellement inscrit

-

Étant donné que je viens de créer un compte créateur -Et que je n'ai jamais publié de contenu

-

Quand j'examine mon statut de créateur

-

Alors mon compte est marqué comme "Nouveau créateur" -Et mes 3 premiers contenus devront être validés manuellement -Et je suis informé de ce processus lors de l'onboarding

-
-

2. Publication du premier contenu par un nouveau créateur

-

Étant donné que je suis un nouveau créateur -Et que je n'ai publié aucun contenu auparavant

-

Quand je publie mon premier contenu

-

Alors le contenu entre en file d'attente de validation manuelle -Et le statut du contenu est "En attente de validation" -Et le contenu n'est pas diffusé sur la plateforme -Et je reçois une notification:

-
-

3. Validation manuelle par un modérateur

-

Étant donné que j'ai publié mon premier contenu -Et que le contenu est en attente de validation

-

Quand un modérateur examine mon contenu

-

Alors le modérateur utilise la transcription automatique Whisper -Et le modérateur vérifie:

-
| critère | conforme |
-|---|---|
-| Respect des règles communauté | oui |
-| Pas de contenu inapproprié | oui |
-| Qualité audio acceptable | oui |
-| Métadonnées cohérentes | oui |
-| Tags appropriés | oui |
-
-

Et si tout est conforme, le contenu est validé

-
-

4. Délai de validation de 24-48h jours ouvrés

-

Étant donné que j'ai publié mon premier contenu lundi à 10:00

-

Quand le contenu entre en file de validation

-

Alors le contenu est validé avant mercredi 10:00 (48h jours ouvrés) -Et dans la plupart des cas, la validation est effectuée sous 24h -Et je reçois une notification dès que le contenu est validé

-
-

5. Notification de validation réussie

-

Étant donné que mon premier contenu a été validé par un modérateur

-

Quand la validation est approuvée

-

Alors je reçois une notification: -Et le statut du contenu passe à "Publié" -Et le contenu devient visible pour tous les utilisateurs -Et il entre dans l'algorithme de recommandation

-
-

6. Refus de validation si contenu non conforme

-

Étant donné que mon premier contenu viole les règles de la communauté

-

Quand le modérateur examine le contenu

-

Alors le contenu est refusé -Et je reçois une notification détaillée: -Et le contenu reste en statut "Refusé" -Et je peux modifier et republier

-
-

7. Les 3 premiers contenus sont validés manuellement

-

Étant donné que je suis un nouveau créateur

-

Quand je publie mes contenus

-

Alors les contenus suivants nécessitent une validation manuelle:

-
| contenu | validation manuelle |
-|---|---|
-| 1er | oui |
-| 2ème | oui |
-| 3ème | oui |
-| 4ème | non (auto) |
-
-

Et après 3 contenus validés, mes futurs contenus sont publiés automatiquement

-
-

8. Passage en mode automatique après 3 validations

-

Étant donné que mes 3 premiers contenus ont été validés avec succès

-

Quand je publie mon 4ème contenu

-

Alors le contenu est publié automatiquement -Et aucune validation manuelle n'est requise -Et le statut passe directement à "Publié" -Et je reçois une notification:

-
-

9. Évolution du score de confiance

-

Étant donné que je suis un créateur établi

-

Quand le système évalue mon historique

-

Alors un score de confiance est calculé basé sur:

-
| critère | poids |
-|---|---|
-| Nombre de contenus publiés | 20% |
-| Strikes reçus | 40% |
-| Signalements infondés | 20% |
-| Ancienneté du compte | 10% |
-| Taux d'engagement positif | 10% |
-
-

Et le score évolue dynamiquement

-
-

10. Créateur fiable - Publication automatique

-

Étant donné que je suis un créateur -Et que j'ai 0 strike depuis 6 mois -Et que tous mes contenus précédents ont été conformes

-

Quand mon score de confiance est calculé

-

Alors je suis classé comme "Créateur fiable" -Et tous mes nouveaux contenus sont publiés automatiquement -Et aucune validation manuelle n'est nécessaire -Et je bénéficie d'une publication instantanée

-
-

11. Créateur suspect - Validation manuelle systématique

-

Étant donné que je suis un créateur -Et que j'ai reçu 2 strikes récents (< 3 mois)

-

Quand mon score de confiance est recalculé

-

Alors je suis classé comme "Créateur suspect" -Et tous mes nouveaux contenus nécessitent une validation manuelle -Et chaque contenu est examiné avant publication -Et je suis notifié de ce changement de statut:

-
-

12. Réhabilitation après période sans incident

-

Étant donné que j'étais un "Créateur suspect" -Et que je publie 10 contenus conformes sur 6 mois -Et que je ne reçois aucun nouveau strike

-

Quand le système réévalue mon score de confiance

-

Alors je passe en "Créateur fiable" -Et la publication automatique est rétablie -Et je reçois une notification de réhabilitation:

-
-

13. Toute publicité nécessite validation manuelle

-

Étant donné qu'un annonceur soumet une publicité audio

-

Quand la publicité est créée

-

Alors elle entre automatiquement en file de validation manuelle -Et aucune publicité n'est diffusée sans validation préalable -Et cela est obligatoire pour des raisons de responsabilité juridique

-
-

14. Validation d'une publicité - Processus complet

-

Étant donné qu'une publicité est en attente de validation

-

Quand un modérateur senior examine la publicité

-

Alors le modérateur vérifie:

-
| critère | conforme |
-|---|---|
-| Transcription automatique Whisper | effectuée |
-| Contenu conforme aux règles | oui |
-| Pas de fausse publicité / arnaque | oui |
-| Respect du ciblage géographique | oui |
-| Durée conforme (10-60s) | oui |
-| Volume audio acceptable (pas trop fort) | oui |
-| Métadonnées correctes | oui |
-
-

Et si tout est conforme, la publicité est validée

-
-

15. Délai de validation d'une publicité - 24-48h

-

Étant donné qu'un annonceur soumet une publicité lundi à 10:00

-

Quand la publicité entre en file de validation

-

Alors la publicité est validée avant mercredi 10:00 (48h jours ouvrés) -Et l'annonceur est notifié dès la validation -Et la campagne publicitaire peut alors démarrer

-
-

16. Refus de validation d'une publicité

-

Étant donné qu'une publicité contient des éléments non conformes

-

Quand le modérateur examine la publicité

-

Alors la publicité est refusée -Et l'annonceur reçoit une notification détaillée: -Et l'annonceur peut modifier et resoumettre la publicité -Et aucun remboursement n'est effectué pour une publicité refusée

-
-

17. Économie de modération grâce à la prévention

-

Étant donné que la modération préventive est active

-

Quand on analyse l'efficacité du système

-

Alors 80% des contenus inappropriés sont détectés avant publication -Et cela réduit le nombre de signalements de 70% -Et les ressources de modération sont optimisées -Et la qualité de la plateforme est préservée dès le début

-
-

18. Qualité de la plateforme maintenue

-

Étant donné que tous les nouveaux créateurs sont vérifiés

-

Quand on analyse la qualité globale des contenus

-

Alors le taux de contenus inappropriés est <1% -Et les utilisateurs font confiance à la plateforme -Et la réputation de RoadWave est préservée -Et l'expérience utilisateur est optimale

-
-

19. Information claire sur le processus de validation

-

Étant donné que je suis un nouveau créateur

-

Quand je consulte la page d'aide "Validation des contenus"

-

Alors j'apprends que: -Et le processus est clair et transparent

-
-

20. Badge "Créateur vérifié" après validation

-

Étant donné que mes 3 premiers contenus ont été validés avec succès

-

Quand je consulte mon profil créateur

-

Alors un badge discret "✓ Créateur vérifié" s'affiche -Et ce badge rassure les auditeurs sur la qualité de mes contenus -Et il améliore ma crédibilité sur la plateforme

-
-

21. Justification de la modération préventive

-

Étant donné que la modération préventive est en place

-

Quand on évalue les bénéfices

-

Alors les avantages suivants sont constatés:

-
| bénéfice |
-|---|
-| Prévention meilleure que réaction |
-| Économie de ressources de modération (×3-5) |
-| Qualité de la plateforme préservée dès le début |
-| Confiance des utilisateurs renforcée |
-| Moins de contenus inappropriés signalés |
-| Réputation de la plateforme protégée |
-
-

Et l'investissement dans la prévention est rentable

-
-

22. Coût de la modération préventive

-

Étant donné que 100 nouveaux créateurs publient 3 contenus chacun -Et que 50 publicités sont soumises par mois

-

Quand on calcule le coût de modération préventive

-

Alors le coût en temps modérateur est:

-
| type | nombre | temps/contenu | total |
-|---|---|---|---|
-| Nouveaux créateurs | 300 | 5 min | 25h |
-| Publicités | 50 | 10 min | 8.3h |
-
-

Et le coût total est d'environ 33h de modération/mois -Et c'est largement compensé par la réduction des signalements réactifs

-
-
- -

Sanctions et notifications de modération

-

27 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur de contenu -Et que j'ai publié un contenu

-
-

1. Notification multi-canal après sanction

-

Étant donné que mon contenu a été modéré

-

Quand la sanction est appliquée

-

Alors je reçois une notification sur 3 canaux:

-
| canal | timing | contenu |
-|---|---|---|
-| Push | Immédiat | "Votre contenu a été modéré" |
-| In-app | Au prochain lancement | Popup détaillée avec bouton "Voir détails" |
-| Email | Dans l'heure | Notification complète avec lien d'appel |
-
-

Et chaque canal contient un lien vers les détails complets

-
-

2. Notification push immédiate

-

Étant donné que mon contenu vient d'être modéré

-

Quand la sanction est appliquée

-

Alors je reçois une notification push immédiate -Et le message est court: "⚠️ Votre contenu a été modéré" -Et je peux cliquer pour voir les détails -Et la notification utilise Firebase Cloud Messaging (Android) ou APNs (iOS) -Et le coût est de 0€

-
-

3. Popup in-app au prochain lancement

-

Étant donné que mon contenu a été modéré

-

Quand j'ouvre l'application

-

Alors une popup détaillée s'affiche automatiquement -Et la popup contient:

-
| élément | description |
-|---|---|
-| Titre du contenu | "Mon podcast #42" |
-| Icône d'avertissement | ⚠️ |
-| Catégorie violée | 🚫 Haine & violence |
-| Sanction | Strike 2/4 - Suspension 7 jours |
-| Bouton "Voir détails" | Redirige vers page détaillée |
-| Bouton "Compris" | Ferme la popup |
-
-

Et je ne peux pas fermer la popup sans l'avoir vue

-
-

4. Email de notification complet dans l'heure

-

Étant donné que mon contenu a été modéré à 14:00

-

Quand la sanction est appliquée

-

Alors je reçois un email avant 15:00 (dans l'heure) -Et l'objet de l'email est "Modération de votre contenu \"[Titre du contenu]\"" -Et l'email contient toutes les informations détaillées -Et le coût est d'environ 0.001€ par email (Brevo, Resend)

-
-

5. Email de notification complet et structuré

-

Étant donné que mon contenu "Mon podcast #42" a été modéré

-

Quand je reçois l'email de notification

-

Alors l'email contient la structure suivante:

-
-

6. Page détaillée de la sanction in-app

-

Étant donné que je clique sur "Voir détails" dans la notification

-

Quand la page détaillée s'affiche

-

Alors je vois les 6 éléments obligatoires:

-
| élément | contenu |
-|---|---|
-| 1. Catégorie violée | 🚫 Haine & violence (Article 3.2 CGU) |
-| 2. Raison détaillée | Explication claire et non juridique |
-| 3. Extrait audio | Timestamp exact: 3:42-4:15 |
-| 4. Transcription | Texte problématique surligné en rouge |
-| 5. Gravité | Strike actuel + conséquences (Strike 2/4, 7j susp) |
-| 6. Recours | Lien formulaire d'appel + délai 7j |
-
-
-

7. Affichage du passage problématique avec timestamp

-

Étant donné que la page détaillée de la sanction est affichée

-

Quand je consulte l'extrait audio concerné

-

Alors le timestamp exact est affiché: "3:42-4:15" -Et je peux écouter uniquement cette portion de l'audio -Et un player audio intégré permet l'écoute du passage -Et la transcription correspondante est affichée en dessous -Et les mots/phrases problématiques sont surlignés en rouge

-
-

8. Référence précise aux CGU

-

Étant donné que la sanction fait référence à l'Article 3.2 des CGU

-

Quand je clique sur "Article 3.2"

-

Alors je suis redirigé vers la section correspondante des CGU -Et la section "Haine & violence" est mise en évidence -Et je peux lire exactement ce qui est interdit -Et cela m'aide à comprendre mon erreur

-
-

9. Gravité de la sanction avec système de strikes

-

Étant donné que c'est mon 2ème strike

-

Quand je consulte les détails de la sanction

-

Alors je vois clairement "Strike 2/4" -Et les conséquences sont explicitées: -Et je comprends l'escalade des sanctions

-
-

10. Accès au formulaire d'appel depuis la notification

-

Étant donné que j'ai reçu une notification de modération

-

Quand je clique sur "Contester cette décision"

-

Alors je suis redirigé vers le formulaire d'appel -Et le formulaire est pré-rempli avec les informations de la sanction -Et je peux commencer à rédiger mon appel

-
-

11. Accès au formulaire d'appel depuis "Mes sanctions"

-

Étant donné que j'ai reçu une sanction il y a 2 jours

-

Quand j'ouvre "Profil créateur > Mes sanctions"

-

Alors je vois la liste de mes sanctions -Et chaque sanction a un bouton "Faire appel" (si délai <7j) -Et je peux accéder au formulaire d'appel

-
-

12. Structure du formulaire d'appel

-

Étant donné que j'ouvre le formulaire d'appel

-

Quand le formulaire s'affiche

-

Alors je vois les champs suivants:

-
| champ | type | obligatoire | description |
-|---|---|---|---|
-| Sanction contestée | Pré-rempli (readonly) | oui | "Strike 2 - Podcast #42" |
-| Raison de l'appel | Texte (50-1000 car) | oui | Explication courte de la contestation |
-| Arguments détaillés | Zone texte enrichie | oui | Arguments complets |
-| Preuves | Upload fichiers | non | Max 5 fichiers, 10 MB total |
-
-

Et tous les champs obligatoires sont marqués d'un astérisque

-
-

13. Validation du formulaire d'appel

-

Étant donné que je remplis le formulaire d'appel

-

Quand je clique sur "Soumettre l'appel"

-

Alors le système valide les champs obligatoires -Et si un champ obligatoire est vide, une erreur s'affiche -Et si la raison fait moins de 50 caractères, une erreur s'affiche -Et si tout est valide, l'appel est soumis

-
-

14. Confirmation après soumission de l'appel

-

Étant donné que j'ai soumis un appel valide

-

Quand l'appel est enregistré

-

Alors un numéro de ticket unique est généré: "#MOD-2026-00142" -Et un email de confirmation est envoyé: -Et le statut de l'appel est "En cours d'examen" -Et je peux suivre le statut dans "Mes sanctions"

-
-

15. Délai de soumission de 7 jours maximum

-

Étant donné que j'ai reçu une sanction le 2026-01-15

-

Quand j'essaie de faire appel le 2026-01-25 (10 jours plus tard)

-

Alors le formulaire d'appel est désactivé -Et un message s'affiche: -Et je ne peux plus contester la sanction

-
-

16. Bouton "Faire appel" visible si délai respecté

-

Étant donné que j'ai reçu une sanction il y a 3 jours

-

Quand je consulte "Mes sanctions"

-

Alors le bouton "Faire appel" est actif -Et un compteur indique "4 jours restants pour faire appel" -Et je peux cliquer pour soumettre un appel

-
-

17. SLA de 72h garanti pour appel standard

-

Étant donné que j'ai soumis un appel standard le lundi à 10:00

-

Quand l'appel est en cours de traitement

-

Alors un modérateur senior est assigné -Et l'appel doit être traité avant jeudi 10:00 (72h - 3 jours ouvrés) -Et je reçois une réponse dans ce délai

-
-

18. Appel complexe avec notification intermédiaire

-

Étant donné que j'ai soumis un appel complexe -Et que le traitement nécessite plus de 72h

-

Quand 3 jours se sont écoulés

-

Alors je reçois un email de notification intermédiaire: -Et l'appel est traité sous 5 jours ouvrés au total -Et un modérateur senior + admin modération examinent le cas

-
-

19. Appel CRITIQUE traité en 24h

-

Étant donné que j'ai reçu une suspension longue ou un ban -Et que je soumets un appel

-

Quand l'appel est classé en priorité CRITIQUE

-

Alors l'admin modération traite l'appel sous 24h -Et je reçois une réponse rapide -Et le cas est examiné en priorité absolue

-
-

20. Réponse finale détaillée - Appel accepté

-

Étant donné que mon appel est accepté

-

Quand je reçois la réponse finale

-

Alors l'email contient:

-
| élément | contenu |
-|---|---|
-| Décision | Annulation de la sanction |
-| Justification | Explication de pourquoi l'appel est accepté |
-| Actions | Strike retiré, suspension annulée, contenu rétabli |
-| Définitif | "Cette décision est définitive" |
-
-

Et le strike est retiré de mon compte -Et le contenu est rétabli sur la plateforme -Et je peux continuer normalement

-
-

21. Réponse finale détaillée - Appel rejeté

-

Étant donné que mon appel est rejeté

-

Quand je reçois la réponse finale

-

Alors l'email contient:

-
| élément | contenu |
-|---|---|
-| Décision | Maintien de la sanction |
-| Justification | Explication de pourquoi l'appel est rejeté |
-| Actions | Sanction maintenue, strike conservé |
-| Définitif | "Cette décision est définitive" |
-
-

Et la sanction reste active -Et je ne peux pas faire de second appel -Et je dois respecter la suspension

-
-

22. Réponse finale - Réduction de sanction

-

Étant donné que mon appel est partiellement accepté

-

Quand je reçois la réponse finale

-

Alors la décision est "Réduction de sanction" -Et l'email explique: -Et le strike est réduit -Et la suspension est raccourcie -Et je suis notifié de la nouvelle date de fin

-
-

23. Suivi du statut de l'appel in-app

-

Étant donné que j'ai soumis un appel

-

Quand je consulte "Mes sanctions"

-

Alors je vois le statut actuel de l'appel:

-
| statut | badge | couleur |
-|---|---|---|
-| En cours d'examen | En cours 🔍 | orange |
-| Appel accepté | Accepté ✓ | vert |
-| Appel rejeté | Rejeté ✗ | rouge |
-| Sanction réduite | Partiellement accepté | bleu |
-
-

Et une notification badge m'alerte quand le statut change

-
-

24. Historique complet des sanctions visible

-

Étant donné que je suis un créateur

-

Quand j'ouvre "Profil créateur > Mes sanctions"

-

Alors je vois la liste complète de mes sanctions passées:

-
| colonne | description |
-|---|---|
-| Date | 15/01/2026 |
-| Contenu | "Mon podcast #42" |
-| Catégorie | 🚫 Haine & violence |
-| Sanction | Strike 2 - Suspension 7j |
-| Statut | Active / Terminée / Annulée |
-| Appel | Aucun / Accepté / Rejeté |
-
-

Et les sanctions sont triées par date décroissante

-
-

25. Conformité DSA - Transparence obligatoire

-

Étant donné que le système de sanction est en place

-

Quand un audit DSA est effectué

-

Alors chaque sanction contient:

-
| élément DSA | présent |
-|---|---|
-| Référence précise à la règle violée | oui |
-| Explication claire et compréhensible | oui |
-| Preuve (extrait + transcription) | oui |
-| Possibilité de recours (appel) | oui |
-| Délai de recours clairement indiqué | oui |
-| Réponse motivée au recours | oui |
-
-

Et le système est conforme au Digital Services Act

-
-

26. Décision définitive après premier appel

-

Étant donné que mon premier appel a été rejeté

-

Quand j'essaie de faire un second appel

-

Alors le bouton "Faire appel" est désactivé -Et un message s'affiche: "Cette décision est définitive. Aucun second appel n'est possible." -Et je ne peux plus contester la sanction -Et je dois respecter la décision finale

-
-

27. Coût des notifications multi-canal

-

Étant donné que 100 sanctions sont appliquées en un mois

-

Quand on calcule le coût des notifications

-

Alors le coût total est d'environ 0.10€:

-
| canal | coût unitaire | coût pour 100 |
-|---|---|---|
-| Email | 0.001€ | 0.10€ |
-| Push | 0€ | 0€ |
-| In-app | 0€ | 0€ |
-
-

Et le coût est négligeable même à grande échelle

-
-
- -

Signalement de contenu inapproprié

-

23 scénarios (22 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que je suis en train d'écouter un contenu

-
-

1. Affichage du formulaire de signalement

-

Étant donné que j'écoute un contenu inapproprié

-

Quand j'ouvre le menu du contenu -Et que je clique sur "Signaler"

-

Alors un formulaire de signalement s'affiche -Et le formulaire contient une liste déroulante "Catégorie du problème" -Et le formulaire contient un champ texte "Commentaire (optionnel)" -Et le formulaire contient un bouton "Envoyer le signalement"

-
-

2. Liste des 7 catégories prédéfinies

-

Étant donné que le formulaire de signalement est affiché

-

Quand je clique sur la liste déroulante "Catégorie du problème"

-

Alors je vois les 7 catégories suivantes:

-
| icône | catégorie | description |
-|---|---|---|
-| 🚫 | Haine & violence | Incitation à la haine, discrimination, menaces |
-| 🔞 | Contenu sexuel | Pornographie, contenu explicite |
-| ⚖️ | Illégalité | Terrorisme, apologie de crimes |
-| 🎵 | Droits d'auteur | Musique/contenu protégé non autorisé |
-| 📧 | Spam | Publicité non sollicitée, répétition |
-| ❌ | Fausse information | Désinformation sur santé, sécurité routière |
-| 🔧 | Autre | Champ texte obligatoire si sélectionné |
-
-

Et chaque catégorie a une description claire

-
-

3. Sélection de la catégorie "Haine & violence"

-

Étant donné que le formulaire de signalement est affiché

-

Quand je sélectionne la catégorie "🚫 Haine & violence"

-

Alors la catégorie est sélectionnée -Et la description "Incitation à la haine, discrimination, menaces" s'affiche -Et je peux passer au champ commentaire

-
-

4. Catégorie "Autre" nécessite un commentaire obligatoire

-

Étant donné que le formulaire de signalement est affiché

-

Quand je sélectionne la catégorie "🔧 Autre"

-

Alors le champ "Commentaire" devient obligatoire -Et un message s'affiche: "Veuillez décrire le problème (obligatoire)" -Et le placeholder change en "Décrivez le problème rencontré" -Et je ne peux pas envoyer le signalement sans commentaire

-
-

5. Champ commentaire optionnel avec incitation

-

Étant donné que le formulaire de signalement est affiché -Et que j'ai sélectionné une catégorie autre que "Autre"

-

Quand je consulte le champ "Commentaire"

-

Alors le champ est optionnel (pas d'astérisque rouge) -Et le placeholder indique "Décrivez le problème (optionnel mais recommandé)" -Et la limite de caractères est de 500 -Et un compteur affiche "0/500"

-
-

6. Envoi de signalement sans commentaire

-

Étant donné que j'ai sélectionné la catégorie "📧 Spam" -Et que je n'ai pas rempli le champ commentaire

-

Quand je clique sur "Envoyer le signalement"

-

Alors le signalement est envoyé avec succès -Et aucune erreur de validation ne s'affiche -Et le commentaire est enregistré comme vide

-
-

7. Envoi de signalement avec commentaire

-

Étant donné que j'ai sélectionné la catégorie "🚫 Haine & violence" -Et que j'ai saisi le commentaire "Le créateur tient des propos discriminatoires à 2:30"

-

Quand je clique sur "Envoyer le signalement"

-

Alors le signalement est envoyé avec succès -Et le commentaire est enregistré avec le signalement -Et il sera visible par les modérateurs

-
-

8. Limite de 500 caractères pour le commentaire

-

Étant donné que le formulaire de signalement est affiché

-

Quand je saisis un commentaire de 501 caractères

-

Alors le champ limite automatiquement à 500 caractères -Et le compteur affiche "500/500" -Et les caractères supplémentaires ne sont pas acceptés

-
-

9. Toast de confirmation après signalement

-

Étant donné que j'ai envoyé un signalement

-

Quand le signalement est enregistré

-

Alors un toast notification s'affiche -Et le toast contient le message "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." -Et le toast s'affiche pendant 5 secondes -Et le toast contient un bouton "Voir mes signalements" -Et je peux fermer le toast manuellement avec un bouton X

-
-

10. Accès à l'historique des signalements via le toast

-

Étant donné que le toast de confirmation est affiché

-

Quand je clique sur "Voir mes signalements"

-

Alors je suis redirigé vers la page "Mes signalements" -Et je vois la liste de tous mes signalements -Et le signalement que je viens d'envoyer apparaît en premier

-
-

11. Historique personnel des signalements

-

Étant donné que j'ai envoyé 3 signalements précédemment

-

Quand j'ouvre "Profil > Mes signalements"

-

Alors je vois la liste de mes 3 signalements -Et chaque signalement affiche:

-
| information | description |
-|---|---|
-| Titre du contenu | "Podcast #42" |
-| Créateur | @pseudo_createur |
-| Catégorie | 🚫 Haine & violence |
-| Date | 15/01/2026 |
-| Statut | En cours / Traité / Rejeté |
-| Mon commentaire | Texte que j'ai saisi |
-
-

Et les signalements sont triés par date décroissante

-
-

12. 📋 Plan: Statuts possibles d'un signalement

-

Étant donné que j'ai envoyé un signalement

-

Quand le statut du signalement est ""

-

Alors le badge affiché est "" -Et la couleur du badge est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
statutbadgecouleur
En coursEn coursorange
TraitéTraité ✓vert
RejetéRejeté ✗rouge
-
-

13. Notification in-app si action prise

-

Étant donné que j'ai signalé un contenu il y a 24h

-

Quand le modérateur traite mon signalement -Et que le contenu est effectivement retiré

-

Alors je reçois une notification in-app -Et la notification indique "Votre signalement a été traité. Le contenu a été retiré." -Et le statut de mon signalement passe à "Traité ✓" -Et je peux voir les détails de l'action prise

-
-

14. Notification si signalement rejeté

-

Étant donné que j'ai signalé un contenu

-

Quand le modérateur rejette mon signalement

-

Alors je reçois une notification in-app -Et la notification indique "Votre signalement a été examiné. Le contenu ne viole pas les règles de la communauté." -Et le statut de mon signalement passe à "Rejeté ✗" -Et je peux voir la raison du rejet

-
-

15. Un contenu peut être signalé plusieurs fois

-

Étant donné qu'un contenu a déjà été signalé par 5 autres utilisateurs

-

Quand je signale le même contenu

-

Alors mon signalement est enregistré indépendamment -Et le compteur de signalements du contenu passe à 6 -Et mon signalement rejoint la file d'attente de modération -Et les signalements cumulés augmentent la priorité de traitement

-
-

16. Limite de signalements par utilisateur

-

Étant donné que j'ai déjà signalé le même contenu il y a 2 jours

-

Quand j'essaie de signaler à nouveau le même contenu

-

Alors un message m'informe "Vous avez déjà signalé ce contenu" -Et le formulaire de signalement n'est pas affiché -Et je peux consulter le statut de mon signalement précédent

-
-

17. Détection de signalements abusifs répétés

-

Étant donné que j'ai envoyé 10 signalements ce mois-ci -Et que 8 d'entre eux ont été rejetés comme infondés

-

Quand j'essaie d'envoyer un nouveau signalement

-

Alors mon compte est marqué comme "signaleur suspect" -Et un avertissement s'affiche: -Et je peux toujours envoyer le signalement -Mais mes futurs signalements auront une priorité réduite

-
-

18. Sanction pour signalements abusifs graves

-

Étant donné que j'ai envoyé 20 signalements abusifs en 1 mois -Et que tous ont été rejetés comme volontairement faux

-

Quand le modérateur détecte le pattern abusif

-

Alors mon compte reçoit un avertissement formel -Et je perds la possibilité de signaler pendant 30 jours -Et je reçois un email m'expliquant la sanction

-
-

19. Signalement depuis le player audio

-

Étant donné que j'écoute un contenu

-

Quand j'ouvre le menu "⋮" du player

-

Alors je vois l'option "Signaler" -Et je peux ouvrir le formulaire de signalement

-
-

20. Signalement depuis la page de détails du contenu

-

Étant donné que je consulte la page de détails d'un contenu

-

Quand je clique sur le bouton "⋮" en haut à droite

-

Alors je vois l'option "Signaler" -Et je peux ouvrir le formulaire de signalement

-
-

21. Signalement depuis l'historique d'écoute

-

Étant donné que je consulte mon historique d'écoute

-

Quand je clique sur "⋮" à côté d'un contenu passé

-

Alors je vois l'option "Signaler" -Et je peux signaler ce contenu même si je ne l'écoute plus actuellement

-
-

22. Identité du signaleur anonyme pour le créateur

-

Étant donné que j'ai signalé un contenu

-

Quand le créateur est notifié de la modération

-

Alors mon identité reste anonyme -Et le créateur ne peut pas savoir qui a signalé -Et seuls les modérateurs ont accès à l'identité du signaleur

-
-

23. Coût du système de signalement

-

Étant donné que le système de signalement est en place

-

Quand on calcule le coût

-

Alors le coût est de 0€ -Et le formulaire est développé en interne -Et aucun service tiers n'est utilisé -Et les notifications in-app sont gratuites

-
-
- -

Traitement des signalements par l'IA et les modérateurs

-

25 scénarios (21 standards, 4 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que le système de modération est actif

-
-

1. Signalement ajouté à la file d'attente asynchrone

-

Étant donné qu'un utilisateur envoie un signalement pour un contenu audio

-

Quand le signalement est reçu

-

Alors le signalement est ajouté à la file d'attente asynchrone -Et un worker de traitement est déclenché -Et le traitement se fait en arrière-plan sans bloquer l'utilisateur

-
-

2. Transcription automatique avec Whisper large-v3

-

Étant donné qu'un contenu audio signalé dure 5 minutes

-

Quand le worker de traitement démarre

-

Alors le système utilise Whisper large-v3 pour transcrire l'audio -Et la transcription est en self-hosted (pas de service cloud) -Et le texte transcrit est enregistré en base de données -Et le délai de transcription est de 1-3 minutes

-
-

3. 📋 Plan: Délai de transcription selon durée audio

-

Étant donné qu'un contenu audio signalé dure minutes

-

Quand le système transcrit l'audio

-

Alors la transcription prend environ

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
dureedelai
21-3 minutes
103-10 minutes
4510-20 minutes
-
-

4. Analyse automatique du contenu transcrit

-

Étant donné que la transcription audio est terminée

-

Quand le système analyse le texte transcrit

-

Alors les analyses suivantes sont effectuées:

-
| analyse | technologie |
-|---|---|
-| Analyse de sentiment | distilbert-base-uncased |
-| Détection de haine | facebook/roberta-hate-speech |
-| Mots-clés interdits | Liste noire FR/EN + regex |
-
-

Et chaque analyse génère un score de confiance (0-100%)

-
-

5. Génération du score de confiance IA

-

Étant donné que toutes les analyses sont terminées

-

Quand le système calcule le score final

-

Alors un score de confiance IA entre 0-100% est généré -Et le score indique la probabilité que le contenu viole les règles -Et la catégorie la plus probable est identifiée -Et les timestamps des passages problématiques sont extraits

-
-

6. Détection automatique de contenu clairement inapproprié

-

Étant donné qu'un contenu contient des insultes graves et répétées

-

Quand l'IA analyse la transcription

-

Alors le score de confiance IA est >95% -Et la catégorie détectée est "Haine & violence" -Et les passages problématiques sont identifiés avec timestamps:

-
| timestamp | texte problématique |
-|---|---|
-| 02:15 | [insulte discriminatoire] |
-| 03:42 | [propos haineux] |
-
-

Et le signalement est classé en priorité CRITIQUE

-
-

7. 📋 Plan: SLA selon priorité du signalement

-

Étant donné qu'un signalement a une priorité ""

-

Quand le signalement entre en file d'attente

-

Alors le délai de traitement cible est "" -Et le responsable du traitement est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
prioritedelairesponsable
CRITIQUE<2h (24/7)Modérateur senior (astreinte)
HAUTE<24h (jours ouvrés)Modérateur junior/senior
MOYENNE<24h (jours ouvrés)Modérateur junior
BASSE<72h (jours ouvrés)Modérateur junior
-
-

8. Traitement automatique pour score IA >95%

-

Étant donné qu'un signalement a un score IA de 97% -Et que la catégorie détectée est "Spam" (évidente)

-

Quand le système évalue le signalement

-

Alors une action automatique immédiate est déclenchée -Et le contenu est retiré automatiquement -Et le créateur est notifié de la modération -Et le créateur peut faire appel de la décision -Et un modérateur senior vérifie l'action a posteriori

-
-

9. Signalement CRITIQUE traité en moins de 2h

-

Étant donné qu'un signalement de priorité CRITIQUE est reçu à 14:00 -Et que le contenu concerne une menace de violence

-

Quand le signalement est assigné à un modérateur senior d'astreinte

-

Alors le modérateur est alerté immédiatement (push + SMS) -Et le signalement est traité avant 16:00 (2h) -Et une décision est prise et appliquée -Et les autorités peuvent être contactées si nécessaire

-
-

10. Astreinte modérateur 24/7 pour signalements CRITIQUES

-

Étant donné qu'un signalement CRITIQUE est reçu un dimanche à 03:00

-

Quand le signalement est classé en priorité CRITIQUE

-

Alors le modérateur senior d'astreinte est alerté -Et le signalement est traité dans les 2h (avant 05:00) -Et le service d'astreinte garantit une disponibilité 24/7

-
-

11. Signalement HAUTE priorité traité en moins de 24h

-

Étant donné qu'un signalement de priorité HAUTE est reçu lundi à 10:00 -Et que le contenu concerne du harcèlement

-

Quand le signalement entre en file d'attente

-

Alors le signalement est assigné à un modérateur (junior ou senior) -Et le signalement est traité avant mardi 10:00 (24h jours ouvrés) -Et une décision est prise et appliquée

-
-

12. Signalement BASSE priorité traité en moins de 72h

-

Étant donné qu'un signalement de priorité BASSE est reçu lundi à 10:00 -Et que le contenu concerne des tags incorrects

-

Quand le signalement entre en file d'attente

-

Alors le signalement est traité avant jeudi 10:00 (72h jours ouvrés) -Et un modérateur junior peut traiter ce type de signalement

-
-

13. Calcul du score de priorité

-

Étant donné qu'un signalement a les caractéristiques suivantes:

-
| caractéristique | valeur |
-|---|---|
-| Score IA | 85% |
-| Signalements cumulés | 3 |
-| Fiabilité du signaleur | 75% |
-
-

Quand le système calcule la priorité

-

Alors la formule appliquée est: -Et le score de priorité est: (85 × 0.7) + (3 × 0.2) + (75 × 0.1) = 67.5 -Et le signalement est classé en priorité MOYENNE

-
-

14. 📋 Plan: Classification selon score de priorité

-

Étant donné qu'un signalement a un score de priorité de

-

Quand le système classe le signalement

-

Alors la priorité assignée est "" -Et le signalement entre dans la file ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
scoreprioritefile
95CRITIQUEImmédiate
82HAUTEPrioritaire
55MOYENNENormale
25BASSEDifférée
-
-

15. Boost de priorité avec signalements cumulés

-

Étant donné qu'un contenu a été signalé par 1 utilisateur avec un score IA de 60% -Et que le signalement est classé en priorité MOYENNE (score 42)

-

Quand 5 autres utilisateurs signalent le même contenu

-

Alors le nombre de signalements cumulés passe à 6 -Et le score de priorité augmente significativement -Et le signalement peut passer en priorité HAUTE -Et le traitement est accéléré

-
-

16. Impact de la fiabilité du signaleur

-

Étant donné qu'un utilisateur de confiance (90% fiabilité) envoie un signalement -Et qu'un utilisateur suspect (20% fiabilité) envoie un signalement similaire

-

Quand le système calcule les priorités

-

Alors le signalement de l'utilisateur de confiance a un score plus élevé -Et son signalement est traité en priorité -Et le signalement de l'utilisateur suspect est traité plus tard

-
-

17. Évolution du score de fiabilité du signaleur

-

Étant donné qu'un utilisateur a envoyé 10 signalements -Et que 8 d'entre eux ont été acceptés par les modérateurs

-

Quand le système calcule son score de fiabilité

-

Alors le score est de 80% (8 acceptés / 10 total) -Et ses futurs signalements auront plus de poids -Et il peut devenir "utilisateur de confiance"

-
-

18. Files d'attente séparées par priorité

-

Étant donné que 50 signalements sont en attente

-

Quand le système organise la file d'attente

-

Alors les signalements sont répartis dans les files suivantes:

-
| file | nombre | priorité |
-|---|---|---|
-| Immédiate (24/7) | 5 | CRITIQUE |
-| Prioritaire | 15 | HAUTE |
-| Normale | 20 | MOYENNE |
-| Différée | 10 | BASSE |
-
-

Et les modérateurs traitent en priorité la file Immédiate

-
-

19. Modérateurs assignés selon compétences

-

Étant donné qu'un signalement complexe de harcèlement est reçu

-

Quand le système assigne un modérateur

-

Alors un modérateur senior est prioritairement assigné -Et les modérateurs juniors peuvent traiter les cas simples (spam, tags) -Et les modérateurs seniors traitent les cas complexes (haine, violence, appels)

-
-

20. Stack technique 100% opensource

-

Étant donné que le système de modération IA est déployé

-

Quand on analyse les technologies utilisées

-

Alors toutes les technologies sont opensource:

-
| composant | technologie | hébergement |
-|---|---|---|
-| Transcription | Whisper large-v3 | Self-hosted |
-| Analyse sentiment | distilbert-base-uncased | Self-hosted |
-| Détection haine | facebook/roberta-hate-speech | Self-hosted |
-| Mots-clés interdits | Liste noire FR/EN + regex | PostgreSQL |
-
-

Et aucune dépendance à Google, AWS, Azure

-
-

21. 📋 Plan: Coût selon phase du projet

-

Étant donné que RoadWave est en phase ""

-

Quand on calcule le coût de l'infrastructure IA

-

Alors le coût mensuel est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - -
phasecout
MVP0-50€ (CPU)
Scale50-200€ (GPU VPS)
-
-

22. Processing asynchrone en MVP avec CPU

-

Étant donné que RoadWave est en phase MVP -Et que le volume est <1000 signalements/mois

-

Quand le système traite les signalements

-

Alors un serveur CPU standard est suffisant -Et le coût est de 0€ (serveur existant) -Et le processing asynchrone absorbe les pics de charge -Et les délais restent acceptables (1-20 minutes)

-
-

23. Scaling avec GPU pour gros volumes

-

Étant donné que RoadWave reçoit >1000 signalements/jour

-

Quand le système nécessite un scaling

-

Alors un VPS avec GPU est requis -Et le coût passe à 50-200€/mois -Et les délais de transcription sont divisés par 5-10 -Et le système peut gérer 10 000+ signalements/mois

-
-

24. Logs d'audit pour chaque traitement

-

Étant donné qu'un signalement est traité

-

Quand une action est prise (rejet, acceptation, sanction)

-

Alors un log d'audit complet est créé:

-
| champ | description |
-|---|---|
-| signalement_id | ID unique du signalement |
-| content_id | ID du contenu signalé |
-| ia_score | Score de confiance IA |
-| ia_category | Catégorie détectée par IA |
-| priority | CRITIQUE / HAUTE / MOYENNE / BASSE |
-| moderator_id | ID du modérateur assigné |
-| action_taken | Retiré / Rejeté / Strike |
-| processing_time | Durée du traitement |
-| timestamp | Date et heure de la décision |
-
-

Et le log est conservé pour conformité DSA -Et les logs sont anonymisés après 3 ans (RGPD)

-
-

25. Traçabilité complète pour conformité DSA

-

Étant donné que le système de modération est actif

-

Quand un audit DSA est effectué

-

Alors toutes les actions de modération sont tracées -Et les délais de traitement sont mesurés et respectés -Et les décisions sont justifiées et documentées -Et la transparence vis-à-vis des utilisateurs est garantie -Et le système est conforme au Digital Services Act

-
-
- -

Conditions d'activation de la monétisation

-
-

En tant que créateur -Je veux pouvoir activer la monétisation quand je remplis les critères -Afin de générer des revenus avec mes contenus

-
-

28 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant que créateur

-
-

1. Critère 1 - Ancienneté de 3 mois validée

-

Étant donné que mon compte a été créé il y a 91 jours

-

Quand je consulte les critères de monétisation

-

Alors le critère "Ancienneté ≥ 3 mois" est validé ✅

-
-

2. Critère 1 - Ancienneté insuffisante

-

Étant donné que mon compte a été créé il y a 60 jours

-

Quand je consulte les critères de monétisation

-

Alors le critère "Ancienneté ≥ 3 mois" n'est pas validé ❌ -Et je vois "Encore 30 jours avant d'être éligible"

-
-

3. Critère 2 - 500 abonnés atteints

-

Étant donné que j'ai exactement 500 abonnés

-

Quand je consulte les critères de monétisation

-

Alors le critère "≥ 500 abonnés" est validé ✅

-
-

4. Critère 2 - Pas assez d'abonnés

-

Étant donné que j'ai 347 abonnés

-

Quand je consulte les critères de monétisation

-

Alors le critère "≥ 500 abonnés" n'est pas validé ❌ -Et je vois "Encore 153 abonnés nécessaires"

-
-

5. Critère 3 - 10 000 écoutes complètes atteintes

-

Étant donné que mes contenus ont cumulé 10 487 écoutes complètes

-

Quand je consulte les critères de monétisation

-

Alors le critère "≥ 10 000 écoutes complètes" est validé ✅

-
-

6. Critère 3 - Écoutes incomplètes non comptabilisées

-

Étant donné que mes contenus ont:

-
| type écoute | nombre |
-|---|---|
-| Écoutes complètes | 8 500 |
-| Écoutes <80% | 3 000 |
-
-

Quand je consulte les critères de monétisation

-

Alors seules les 8 500 écoutes complètes comptent -Et je vois "Encore 1 500 écoutes complètes nécessaires"

-
-

7. Critère 4 - Aucun strike actif

-

Étant donné que je n'ai aucun strike actif -Et que je n'ai eu aucun contenu modéré dans les 6 derniers mois

-

Quand je consulte les critères de monétisation

-

Alors le critère "Fiabilité" est validé ✅

-
-

8. Critère 4 - Strike actif bloque l'éligibilité

-

Étant donné que j'ai 1 strike actif pour contenu inapproprié

-

Quand je consulte les critères de monétisation

-

Alors le critère "Fiabilité" n'est pas validé ❌ -Et je vois "Vous devez résoudre votre strike avant d'être éligible"

-
-

9. Critère 4 - Contenu modéré dans les 6 derniers mois

-

Étant donné que je n'ai pas de strike actif -Mais qu'un de mes contenus a été modéré il y a 4 mois

-

Quand je consulte les critères de monétisation

-

Alors le critère "Fiabilité" n'est pas validé ❌ -Et je vois "Attendre 2 mois après le dernier contenu modéré"

-
-

10. Critère 5 - 5 contenus publiés dans les 90 derniers jours

-

Étant donné que j'ai publié:

-
| date de publication | titre |
-|---|---|
-| Il y a 15 jours | Contenu 1 |
-| Il y a 30 jours | Contenu 2 |
-| Il y a 45 jours | Contenu 3 |
-| Il y a 60 jours | Contenu 4 |
-| Il y a 75 jours | Contenu 5 |
-
-

Quand je consulte les critères de monétisation

-

Alors le critère "≥ 5 contenus publiés dans les 90 derniers jours" est validé ✅

-
-

11. Critère 5 - Contenus trop anciens ne comptent pas

-

Étant donné que j'ai publié:

-
| date de publication | titre |
-|---|---|
-| Il y a 15 jours | Contenu 1 |
-| Il y a 30 jours | Contenu 2 |
-| Il y a 95 jours | Contenu 3 |
-| Il y a 120 jours | Contenu 4 |
-
-

Quand je consulte les critères de monétisation

-

Alors seuls 2 contenus comptent (dans les 90 jours) -Et je vois "Encore 3 contenus à publier dans les 90 prochains jours"

-
-

12. Tous les critères validés - Bouton disponible

-

Étant donné que tous mes critères sont validés:

-
| critère | statut |
-|---|---|
-| Ancienneté ≥ 3 mois | ✅ |
-| ≥ 500 abonnés | ✅ |
-| ≥ 10 000 écoutes | ✅ |
-| Fiabilité | ✅ |
-| Régularité (5 contenus) | ✅ |
-
-

Quand j'accède à mon profil créateur

-

Alors le bouton "Demander la monétisation" est actif -Et je peux cliquer pour démarrer le KYC

-
-

13. Critères incomplets - Bouton grisé avec progression

-

Étant donné que mes critères sont:

-
| critère | statut | progression |
-|---|---|---|
-| Ancienneté ≥ 3 mois | ✅ | 100% |
-| ≥ 500 abonnés | ❌ | 347/500 (69%) |
-| ≥ 10 000 écoutes | ❌ | 8500/10000 (85%) |
-| Fiabilité | ✅ | 100% |
-| Régularité (5 contenus) | ✅ | 100% |
-
-

Quand j'accède à mon profil créateur

-

Alors le bouton "Demander la monétisation" est grisé -Et je vois la progression détaillée de chaque critère

-
-

14. Vérification automatique SQL lors de la demande

-

Étant donné que je clique sur "Demander la monétisation"

-

Quand le système vérifie mes critères

-

Alors une requête SQL est exécutée: -Et si tous les critères sont TRUE, je suis redirigé vers le KYC

-
-

15. Notification par email quand critères atteints

-

Étant donné que je viens d'atteindre 500 abonnés -Et que c'était mon dernier critère manquant

-

Quand le système détecte l'éligibilité

-

Alors je reçois un email:

-
-

16. Badge "Éligible monétisation" dans profil

-

Étant donné que je remplis tous les critères -Mais que je n'ai pas encore activé la monétisation

-

Quand un utilisateur consulte mon profil

-

Alors il voit un badge "Éligible monétisation 💰" -Et cela renforce ma crédibilité de créateur

-
-

17. Justification anti-fraude - Délai 3 mois

-

Étant donné qu'un compte suspect crée du contenu frauduleux

-

Quand le compte est détecté dans les 2 premiers mois

-

Alors le compte est banni avant d'atteindre les 3 mois -Et le créateur n'a jamais été éligible à la monétisation -Et aucun paiement n'a été effectué

-
-

18. Justification qualité - 10 000 écoutes

-

Étant donné qu'un créateur produit du contenu de mauvaise qualité

-

Quand ses contenus ne génèrent que 2 000 écoutes complètes

-

Alors il ne peut pas activer la monétisation -Et seuls les créateurs avec contenu apprécié sont monétisés

-
-

19. Réduction coût administratif plateforme

-

Étant donné que RoadWave a 10 000 créateurs inscrits -Et que seuls 500 remplissent tous les critères

-

Quand le système calcule le coût administratif

-

Alors seulement 500 KYC sont à gérer (vs 10 000) -Et seulement 500 virements mensuels (vs 10 000) -Et la charge comptable est réduite de 95%

-
-

20. Statistiques publiques pour transparence

-

Quand un utilisateur consulte la page "Devenir créateur"

-

Alors il voit les statistiques:

-
| métrique | valeur exemple |
-|---|---|
-| Nombre créateurs monétisés | 1 247 |
-| Revenus moyens par créateur | 127€/mois |
-| Top créateur (anonymisé) | 2 450€/mois |
-| Critères d'éligibilité à remplir | 5 critères |
-
-

Et cela permet de fixer des attentes réalistes

-
-

21. Cache Redis pour calcul rapide critères

-

Étant donné que je consulte mes critères de monétisation

-

Quand le système charge la page

-

Alors les compteurs sont récupérés depuis Redis:

-
| clé Redis | exemple valeur |
-|---|---|
-| creator:[id]:subscribers_count | 347 |
-| creator:[id]:complete_listens_total | 8500 |
-| creator:[id]:recent_contents_count | 7 |
-
-

Et le temps de réponse est <50ms

-
-

22. Mise à jour temps réel des compteurs

-

Étant donné que je viens de publier un nouveau contenu

-

Quand un utilisateur écoute ce contenu en entier

-

Alors le compteur "complete_listens_total" est incrémenté immédiatement -Et si je rafraîchis la page critères, je vois la nouvelle valeur -Et cela encourage les créateurs à continuer de produire

-
-

23. Historique des tentatives d'activation

-

Étant donné que j'ai tenté d'activer la monétisation il y a 2 mois -Mais que les critères n'étaient pas remplis

-

Quand j'accède à mes logs d'activité

-

Alors je vois:

-
| date | action | résultat | raison |
-|---|---|---|---|
-| 2025-11-15 | Demande monétisation | Refusée | Seulement 300 abonnés |
-
-

Et cela m'aide à suivre ma progression

-
-

24. Performance avec 100 000 créateurs

-

Étant donné que RoadWave a 100 000 créateurs -Et que chacun consulte ses critères 1 fois par jour

-

Quand le système traite ces requêtes

-

Alors la table users est indexée sur created_at -Et la table subscriptions est indexée sur creator_id -Et la table contents est indexée sur creator_id et published_at -Et chaque requête reste <50ms grâce aux index

-
-

25. Export des critères pour support client

-

Étant donné que je contacte le support car je pense être éligible

-

Quand l'agent support consulte mon compte

-

Alors il voit un export JSON complet: -Et l'agent peut expliquer précisément pourquoi je ne suis pas éligible

-
-

26. Notification 30 jours avant éligibilité probable

-

Étant donné que mes critères sont:

-
| critère | statut | progression |
-|---|---|---|
-| Ancienneté ≥ 3 mois | ❌ | 60/90 jours |
-| Tous les autres critères | ✅ | 100% |
-
-

Quand il reste exactement 30 jours avant les 90 jours

-

Alors je reçois une notification:

-
-

27. Pas de bypass possible pour amis/influenceurs

-

Étant donné qu'un créateur influent me contacte directement -Et qu'il demande un bypass des critères

-

Quand je consulte la politique RoadWave

-

Alors la réponse est "Aucune exception possible, critères automatiques uniquement" -Et cela garantit l'équité pour tous les créateurs

-
-

28. A/B test futur sur seuils (post-MVP)

-

Étant donné que RoadWave veut tester des seuils différents

-

Quand un A/B test est lancé en 2027

-

Alors groupe A voit: 500 abonnés, 10 000 écoutes -Et groupe B voit: 300 abonnés, 5 000 écoutes -Et les métriques (taux activation, fraude, qualité) sont comparées -Et le meilleur seuil est déployé définitivement

-
-
- -

Contenus Premium exclusifs

-
-

En tant que créateur monétisé -Je veux pouvoir rendre certains contenus exclusifs aux abonnés Premium -Afin d'inciter les utilisateurs à s'abonner

-
-

34 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur avec la monétisation activée

-
-

1. Toggle "Réservé Premium" lors de la création

-

Étant donné que je crée un nouveau contenu

-

Quand j'accède aux options de publication

-

Alors je vois un toggle "Réservé aux abonnés Premium 👑" -Et je peux l'activer ou le désactiver

-
-

2. Contenu marqué Premium lors de la création

-

Étant donné que je crée un nouveau contenu

-

Quand j'active le toggle "Réservé Premium" -Et que je publie le contenu

-

Alors le champ is_premium en base est mis à true -Et le contenu est visible uniquement pour les utilisateurs Premium

-
-

3. Contenu gratuit par défaut

-

Étant donné que je crée un nouveau contenu

-

Quand je ne touche pas au toggle "Réservé Premium" -Et que je publie le contenu

-

Alors le champ is_premium en base est mis à false (défaut) -Et le contenu est accessible à tous les utilisateurs

-
-

4. Modification d'un contenu existant en Premium

-

Étant donné que j'ai publié un contenu gratuit il y a 2 jours

-

Quand je modifie le contenu et active le toggle "Réservé Premium" -Et que j'enregistre les modifications

-

Alors le contenu devient immédiatement Premium -Et les utilisateurs gratuits ne peuvent plus y accéder

-
-

5. Passage d'un contenu Premium en gratuit

-

Étant donné que j'ai publié un contenu Premium il y a 1 mois

-

Quand je modifie le contenu et désactive le toggle "Réservé Premium" -Et que j'enregistre les modifications

-

Alors le contenu devient immédiatement gratuit -Et tous les utilisateurs peuvent maintenant y accéder

-
-

6. Aucune limite sur pourcentage de contenus Premium

-

Étant donné que je publie 10 nouveaux contenus

-

Quand je décide de rendre les 10 contenus Premium (100%)

-

Alors le système accepte sans limitation -Et je peux avoir 100% de mon catalogue en Premium

-
-

7. Stratégie freemium - Mix gratuit/premium

-

Étant donné que je publie 10 nouveaux contenus

-

Quand je décide de rendre 5 contenus Premium et 5 gratuits (50/50)

-

Alors le système accepte cette stratégie -Et je peux tester différents mix pour optimiser mes revenus

-
-

8. Stratégie tout gratuit possible

-

Étant donné que je suis monétisé via publicités

-

Quand je décide de ne mettre aucun contenu en Premium (0%)

-

Alors le système accepte cette stratégie -Et je génère des revenus uniquement via les publicités

-
-

9. Badge 👑 visible sur l'interface utilisateur

-

Étant donné qu'un utilisateur consulte ma liste de contenus

-

Quand il voit un contenu Premium

-

Alors un badge 👑 "Premium" est affiché -Et le contenu est clairement identifiable comme réservé

-
-

10. Utilisateur gratuit voit les contenus Premium dans la liste

-

Étant donné que je suis un utilisateur gratuit

-

Quand je consulte les contenus d'un créateur

-

Alors je vois aussi les contenus Premium dans la liste -Et ils sont affichés avec un badge 👑 -Mais je ne peux pas les lire

-
-

11. Tentative de lecture Premium par utilisateur gratuit - Overlay bloquant

-

Étant donné que je suis un utilisateur gratuit

-

Quand je clique sur un contenu Premium pour le lire

-

Alors un overlay bloquant apparaît -Et je vois le message: -Et un bouton "Passer Premium" est affiché

-
-

12. CTA "Passer Premium" redirige vers abonnement

-

Étant donné que je vois l'overlay de contenu Premium bloqué

-

Quand je clique sur "Passer Premium"

-

Alors je suis redirigé vers la page d'abonnement Premium -Et je peux m'abonner pour 4.99€/mois

-
-

13. Utilisateur Premium peut lire tous les contenus Premium

-

Étant donné que je suis un utilisateur Premium actif

-

Quand je clique sur un contenu Premium

-

Alors le contenu se lance immédiatement -Et je n'ai aucun overlay bloquant -Et je peux profiter pleinement du contenu exclusif

-
-

14. Contenus Premium inclus dans les recommandations

-

Étant donné que l'algorithme génère ma file de 5 contenus

-

Quand je suis un utilisateur gratuit

-

Alors les contenus Premium peuvent apparaître dans les recommandations -Et cela me fait découvrir qu'il existe du contenu exclusif

-
-

15. Contenu Premium skippé automatiquement pour utilisateur gratuit

-

Étant donné que je suis un utilisateur gratuit -Et qu'un contenu Premium apparaît dans ma file de recommandation

-

Quand j'écoute le contenu précédent jusqu'à la fin

-

Alors le contenu Premium est automatiquement skippé -Et le contenu suivant (gratuit) est lancé -Et le slot Premium ne compte pas dans ma file de 5 contenus

-
-

16. Contenu Premium diffusé normalement pour utilisateur Premium

-

Étant donné que je suis un utilisateur Premium -Et qu'un contenu Premium apparaît dans ma file de recommandation

-

Quand j'écoute le contenu précédent jusqu'à la fin

-

Alors le contenu Premium est lancé normalement -Et je profite du contenu exclusif sans interruption

-
-

17. Champ is_premium boolean en base PostgreSQL

-

Étant donné qu'un contenu est créé

-

Quand il est stocké en base de données

-

Alors la table contents contient un champ is_premium BOOLEAN DEFAULT FALSE -Et ce champ est indexé pour requêtes rapides

-
-

18. Index PostgreSQL sur is_premium

-

Étant donné que l'algorithme doit filtrer les contenus selon le statut Premium

-

Quand une requête SQL est exécutée:

-

Alors l'index sur is_premium accélère la requête -Et le temps de réponse reste <20ms

-
-

19. Cache Redis pour statut Premium

-

Étant donné qu'un contenu Premium est consulté fréquemment

-

Quand l'API vérifie le statut Premium

-

Alors la valeur est récupérée depuis Redis: -Et le cache a un TTL de 1 heure -Et cela évite des requêtes SQL inutiles

-
-

20. Invalidation cache lors de modification statut Premium

-

Étant donné qu'un contenu est passé de gratuit à Premium

-

Quand le créateur enregistre la modification

-

Alors le cache Redis content:[id]:premium est invalidé immédiatement -Et la nouvelle valeur est mise à jour -Et les utilisateurs voient le changement en temps réel

-
-

21. Justification liberté créateur - Stratégie personnalisée

-

Étant donné que chaque créateur a une audience différente

-

Quand un créateur décide de sa stratégie Premium

-

Alors il peut tester différentes approches:

-
| stratégie | % Premium | objectif |
-|---|---|---|
-| Tout gratuit | 0% | Maximiser audience + revenus pub |
-| Mix 50/50 | 50% | Équilibrer audience et exclusivité |
-| Premium majoritaire | 80% | Cibler abonnés fidèles |
-| 100% Premium | 100% | Contenu ultra-exclusif |
-
-
-

22. Justification incitation Premium - Argument fort pour s'abonner

-

Étant donné qu'un utilisateur gratuit voit beaucoup de contenus Premium

-

Quand il consulte les profils de ses créateurs préférés

-

Alors il voit que 60% de leur contenu est réservé Premium -Et cela l'incite à s'abonner pour 4.99€/mois -Et RoadWave augmente son taux de conversion vers Premium

-
-

23. Justification équité - Petit créateur peut tout mettre en Premium

-

Étant donné que je suis un petit créateur avec 600 abonnés -Et que 50 sont abonnés Premium

-

Quand je mets 100% de mon contenu en Premium

-

Alors je génère des revenus uniquement via mes 50 abonnés Premium -Et cela me permet de vivre de mon contenu malgré une petite audience

-
-

24. Justification équité - Gros créateur peut tout offrir gratuitement

-

Étant donné que je suis un gros créateur avec 50 000 abonnés -Et que je génère déjà beaucoup de revenus publicitaires

-

Quand je laisse 100% de mon contenu gratuit

-

Alors je maximise mon audience et mes revenus pub -Et je n'ai pas besoin de mettre du contenu en Premium

-
-

25. Statistiques créateur - Ratio Premium/Gratuit

-

Étant donné que j'accède à mon tableau de bord créateur

-

Quand je consulte mes statistiques de contenus

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Contenus totaux | 47 |
-| Contenus gratuits | 32 (68%) |
-| Contenus Premium | 15 (32%) |
-| Écoutes Premium ce mois | 12,345 |
-| Écoutes gratuites ce mois | 28,901 |
-
-
-

26. Statistiques créateur - Revenus par type

-

Étant donné que j'ai des contenus gratuits et Premium

-

Quand je consulte mes revenus détaillés

-

Alors je vois:

-
| source | montant |
-|---|---|
-| Revenus pub (gratuit) | 86.70€ |
-| Revenus Premium (exclusifs) | 34.20€ |
-| Revenus Premium (tout contenu) | 78.90€ |
-
-

Et je peux comparer l'efficacité de chaque stratégie

-
-

27. Notification créateur - Contenu Premium très écouté

-

Étant donné que j'ai publié un contenu Premium il y a 3 jours -Et qu'il a généré 5 000 écoutes Premium (très élevé)

-

Quand le système détecte cette performance

-

Alors je reçois une notification:

-
-

28. A/B test utilisateur - Impact badge Premium sur conversion

-

Étant donné que RoadWave veut optimiser le taux de conversion Premium

-

Quand un A/B test est lancé

-

Alors groupe A voit le badge 👑 "Premium" -Et groupe B voit le badge 💎 "Exclusif" -Et les taux de clic et conversion sont mesurés -Et le badge le plus performant est déployé définitivement

-
-

29. Analytics plateforme - Adoption fonctionnalité Premium

-

Étant donné que RoadWave suit l'adoption de la fonctionnalité

-

Quand un admin consulte les métriques

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Créateurs utilisant Premium | 847 (68%) |
-| % moyen contenus Premium | 23% |
-| Taux conversion vers Premium (users) | 8.5% |
-| Revenus Premium/mois | 47,890€ |
-
-
-

30. Impact sur churn - Contenus Premium réduisent le churn Premium

-

Étant donné qu'un utilisateur Premium envisage de résilier -Mais qu'il a accès à 150 contenus Premium de ses créateurs préférés

-

Quand il voit la valeur exclusive qu'il perdrait

-

Alors il est moins susceptible de résilier (churn réduit de ~30%) -Et les contenus Premium augmentent la rétention

-
-

31. Transparence - Créateur voit combien de contenus Premium il a

-

Étant donné que j'accède à mon profil créateur

-

Quand je consulte mes contenus

-

Alors je peux filtrer par statut:

-
| filtre | résultats |
-|---|---|
-| Tous | 47 |
-| Gratuits | 32 |
-| Premium 👑 | 15 |
-
-

Et je peux facilement gérer mon catalogue

-
-

32. Export liste contenus avec statut Premium (RGPD)

-

Étant donné que je demande l'export de mes données

-

Quand l'export est généré

-

Alors la liste de mes contenus inclut le statut Premium:

-
-

33. Suppression compte créateur et contenus Premium

-

Étant donné que je supprime définitivement mon compte créateur

-

Quand la suppression est confirmée

-

Alors tous mes contenus (gratuits et Premium) sont supprimés -Et les utilisateurs Premium ne peuvent plus y accéder -Et les fichiers audio sont supprimés du CDN sous 7 jours

-
-

34. Performance avec 1 million de contenus Premium

-

Étant donné que RoadWave a 1 million de contenus dont 300 000 Premium

-

Quand l'algorithme génère une recommandation

-

Alors la requête SQL filtre efficacement avec l'index is_premium -Et le temps de réponse reste <50ms -Et la scalabilité est garantie

-
-
- -

Désactivation et suspension monétisation

-
-

En tant que créateur ou plateforme -Je veux pouvoir désactiver ou suspendre la monétisation selon certaines conditions -Afin de gérer les pauses, problèmes techniques ou violations des règles

-
-

35 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur avec la monétisation activée

-
-

1. Désactivation temporaire par le créateur

-

Étant donné que je veux faire une pause dans ma création de contenu

-

Quand j'accède à "Paramètres > Monétisation" -Et que je clique sur "Désactiver temporairement la monétisation"

-

Alors ma monétisation est désactivée immédiatement -Et je ne génère plus de revenus à partir de maintenant

-
-

2. Confirmation avant désactivation

-

Étant donné que je clique sur "Désactiver temporairement"

-

Quand une popup de confirmation apparaît

-

Alors je vois le message: -Et je dois confirmer pour continuer

-
-

3. Solde conservé pendant désactivation

-

Étant donné que mon solde actuel est 87.45€

-

Quand je désactive ma monétisation le 15 du mois

-

Alors mon solde de 87.45€ est conservé -Et il sera reporté au mois suivant -Et si le total dépasse 50€, il sera versé normalement le 15 du mois prochain

-
-

4. Contenus restent accessibles pendant désactivation

-

Étant donné que j'ai désactivé ma monétisation

-

Quand des utilisateurs écoutent mes contenus

-

Alors mes contenus restent accessibles normalement -Mais je ne génère aucun revenu (ni pub ni Premium)

-
-

5. Réactivation sans refaire le KYC si <2 ans

-

Étant donné que j'ai désactivé ma monétisation il y a 8 mois -Et que mes documents KYC sont toujours valides

-

Quand je clique sur "Réactiver la monétisation"

-

Alors la réactivation est immédiate -Et je n'ai pas besoin de refaire le KYC -Et je recommence à générer des revenus dès maintenant

-
-

6. Nouveau KYC requis si inactivité >2 ans

-

Étant donné que j'ai désactivé ma monétisation il y a 25 mois

-

Quand j'essaie de réactiver

-

Alors le système demande un nouveau KYC -Et je vois: -Et je dois soumettre à nouveau mes documents

-
-

7. Historique des désactivations/réactivations

-

Étant donné que j'ai désactivé et réactivé ma monétisation plusieurs fois

-

Quand j'accède à "Paramètres > Monétisation > Historique"

-

Alors je vois la liste complète:

-
| date | action | raison |
-|---|---|---|
-| 15/06/2025 | Réactivation | Reprise création contenu |
-| 01/03/2025 | Désactivation | Pause vacances |
-| 20/01/2025 | Activation | KYC validé |
-
-
-

8. Suspension si 3+ strikes actifs

-

Étant donné que je reçois un 3ème strike pour violation des règles

-

Quand le strike devient actif

-

Alors ma monétisation est suspendue automatiquement -Et je vois:

-
-

9. Réactivation après résolution des strikes

-

Étant donné que ma monétisation est suspendue pour 3 strikes

-

Quand je résous tous mes strikes (après expiration ou contestation) -Et que mon compteur de strikes passe à 0

-

Alors ma monétisation est réactivée automatiquement -Et je reçois un email de confirmation

-
-

10. Suspension si RIB invalide après 3 échecs de virement

-

Étant donné que 3 tentatives de virement ont échoué (15, 18, 22 du mois)

-

Quand le 3ème échec est confirmé

-

Alors ma monétisation est suspendue automatiquement -Et je vois:

-
-

11. Réactivation après mise à jour RIB valide

-

Étant donné que ma monétisation est suspendue pour RIB invalide

-

Quand je mets à jour mon RIB avec un compte bancaire valide -Et que Mangopay valide le nouveau RIB

-

Alors ma monétisation est réactivée automatiquement -Et un virement est tenté immédiatement pour le solde en attente

-
-

12. Suspension si documents KYC expirés

-

Étant donné que ma carte d'identité expire dans 30 jours

-

Quand je reçois un email de rappel de mise à jour -Mais que je ne mets pas à jour mes documents -Et que ma CNI expire

-

Alors ma monétisation est suspendue automatiquement après 30 jours de grâce

-
-

13. Préavis 30 jours avant suspension pour docs expirés

-

Étant donné que ma CNI expire le 15 juin 2025

-

Quand le 15 mai 2025 arrive (30 jours avant)

-

Alors je reçois un email d'alerte:

-
-

14. Réactivation après renouvellement documents KYC

-

Étant donné que ma monétisation est suspendue pour CNI expirée

-

Quand je soumets une nouvelle CNI valide -Et que Mangopay valide le document sous 24-72h

-

Alors ma monétisation est réactivée automatiquement -Et je recommence à générer des revenus

-
-

15. Suspension si fraude détectée

-

Étant donné que le système détecte une activité frauduleuse (bots, écoutes artificielles)

-

Quand l'équipe modération confirme la fraude

-

Alors ma monétisation est suspendue immédiatement -Et mon compte est mis sous enquête -Et je reçois un email m'informant de la suspension

-
-

16. Enquête fraude - Vérification manuelle

-

Étant donné que ma monétisation est suspendue pour suspicion de fraude

-

Quand l'équipe modération enquête

-

Alors elle analyse:

-
| élément à vérifier | outil |
-|---|---|
-| Patterns d'écoute suspects | Analytics + logs |
-| Origine géographique | Logs IP |
-| Vitesse de croissance anormale | Graphiques statistiques |
-| Plaintes utilisateurs | Système de signalement |
-
-
-

17. Levée suspension si fraude non confirmée

-

Étant donné que mon compte était suspendu pour suspicion de fraude

-

Quand l'enquête conclut qu'il n'y a pas eu de fraude

-

Alors ma monétisation est réactivée -Et les revenus suspendus pendant l'enquête sont versés normalement -Et je reçois un email d'excuses avec explication

-
-

18. Suspension définitive si fraude confirmée

-

Étant donné que l'enquête confirme une fraude avérée

-

Quand l'équipe modération prend la décision

-

Alors ma monétisation est définitivement désactivée -Et mon solde en attente est gelé (non versé) -Et je peux recevoir un strike 4 (ban définitif du compte)

-
-

19. Suppression définitive sur demande créateur

-

Étant donné que je veux arrêter définitivement la monétisation

-

Quand j'accède à "Paramètres > Monétisation > Supprimer définitivement"

-

Alors une confirmation stricte est demandée -Et je dois taper "SUPPRIMER" pour confirmer

-
-

20. Solde versé sous 30 jours après suppression

-

Étant donné que je supprime définitivement ma monétisation -Et que mon solde en attente est 127.45€

-

Quand la suppression est confirmée

-

Alors mon solde sera versé sous 30 jours -Et je reçois un dernier virement de clôture -Et mon e-wallet Mangopay est clôturé

-
-

21. Suppression auto si inactivité 24 mois + solde <50€

-

Étant donné que je n'ai plus publié de contenu depuis 24 mois -Et que mon solde en attente est 12.30€ (<50€)

-

Quand le processus de purge RGPD s'exécute

-

Alors ma monétisation est automatiquement supprimée -Et mon solde de 12.30€ est perdu (trop faible pour virement) -Et mes données KYC sont archivées puis supprimées selon la législation

-
-

22. Email de préavis 60 jours avant purge RGPD

-

Étant donné que je suis inactif depuis 22 mois

-

Quand le système détecte l'inactivité

-

Alors je reçois un email:

-
-

23. Ban définitif compte - Strike 4

-

Étant donné que je reçois un 4ème strike (violation grave ou répétée)

-

Quand l'équipe modération applique le strike 4

-

Alors mon compte est banni définitivement -Et ma monétisation est supprimée définitivement -Et mon solde en attente est gelé (non versé) -Et je ne peux plus créer de nouveau compte (blacklist email/SIRET)

-
-

24. Email pour toute suspension

-

Étant donné que ma monétisation est suspendue (quelle qu'en soit la raison)

-

Quand la suspension devient effective

-

Alors je reçois immédiatement un email:

-
-

25. Notification in-app avec raison explicite

-

Étant donné que ma monétisation est suspendue

-

Quand je me connecte à l'application

-

Alors je vois une bannière en haut de mon dashboard:

-
-

26. Email de confirmation lors de réactivation

-

Étant donné que ma monétisation était suspendue

-

Quand elle est réactivée (automatiquement ou manuellement)

-

Alors je reçois un email:

-
-

27. Dashboard admin - Suspensions actives

-

Étant donné qu'un admin RoadWave consulte les suspensions

-

Quand il accède au dashboard admin "Monétisation > Suspensions"

-

Alors il voit:

-
| raison suspension | nombre actif | taux |
-|---|---|---|
-| Strikes (3+) | 23 | 1.8% |
-| RIB invalide | 12 | 0.9% |
-| Documents KYC expirés | 8 | 0.6% |
-| Fraude sous enquête | 3 | 0.2% |
-| TOTAL | 46 | 3.7% |
-
-
-

28. Alertes si taux de suspension >5%

-

Étant donné que le taux de suspension dépasse 5%

-

Quand le système détecte cette anomalie

-

Alors une alerte est envoyée à l'équipe:

-
-

29. Statistiques personnelles - Temps actif monétisation

-

Étant donné que j'accède à mon dashboard créateur

-

Quand je consulte "Statistiques > Monétisation"

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Date activation monétisation | 20 janvier 2025 |
-| Temps actif total | 8 mois |
-| Périodes de désactivation | 2 (3 mois total) |
-| Suspensions subies | 0 |
-| Statut actuel | ✅ Actif |
-
-
-

30. Export données suspension (RGPD)

-

Étant donné que je demande l'export de mes données

-

Quand l'export est généré

-

Alors l'historique des suspensions est inclus:

-
-

31. Suppression compte et données monétisation

-

Étant donné que je supprime définitivement mon compte RoadWave

-

Quand la suppression est confirmée

-

Alors toutes mes données de monétisation sont supprimées:

-
| donnée | action |
-|---|---|
-| Solde en attente | Versé sous 30 jours puis supprimé |
-| Historique revenus | Archivé 10 ans (obligation légale) |
-| Documents KYC | Archivés 10 ans chez Mangopay puis supprimés |
-| E-wallet Mangopay | Clôturé après versement final |
-
-
-

32. Conservation archives 10 ans obligation légale

-

Étant donné que je supprime mon compte

-

Quand mes données sont archivées

-

Alors RoadWave conserve 10 ans:

-
| donnée archivée | raison |
-|---|---|
-| Relevés mensuels PDF | Obligation comptable France |
-| Déclarations DAS2 | Obligation fiscale France |
-| Justificatifs virements | Preuve paiement en cas d'audit |
-
-

Et après 10 ans, tout est supprimé définitivement

-
-

33. Suspension temporaire pour maintenance technique

-

Étant donné que Mangopay effectue une maintenance planifiée

-

Quand la maintenance est programmée

-

Alors tous les créateurs reçoivent un email préventif 7 jours avant:

-
-

34. Réactivation progressive après incident majeur

-

Étant donné qu'un incident technique majeur suspend toutes les monétisations

-

Quand l'incident est résolu

-

Alors les réactivations se font progressivement:

-
| vague | critère | % créateurs |
-|---|---|---|
-| 1 | Top 10% créateurs (revenus) | 10% |
-| 2 | Créateurs vérifiés | 30% |
-| 3 | Tous les autres créateurs | 60% |
-
-

Et cela évite une surcharge système lors de la reprise

-
-

35. Support prioritaire pour créateurs suspendus injustement

-

Étant donné que ma monétisation est suspendue -Et que je pense que c'est une erreur

-

Quand je contacte le support avec tag "Suspension monétisation"

-

Alors mon ticket est traité en priorité (SLA 24h) -Et un agent expert examine mon cas -Et si suspension injustifiée, je suis réactivé immédiatement avec excuses

-
-
- -

KYC et inscription à la monétisation

-
-

En tant que créateur éligible -Je veux compléter le KYC pour activer la monétisation -Afin de recevoir des paiements légalement

-
-

37 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je remplis tous les critères de monétisation -Et que j'ai cliqué sur "Demander la monétisation"

-
-

1. Redirection vers formulaire KYC Mangopay

-

Quand je démarre le processus d'activation

-

Alors je suis redirigé vers un formulaire KYC -Et le formulaire est fourni par Mangopay (iframe sécurisée) -Et toutes les données sont chiffrées et hébergées en EU

-
-

2. Statut auto-entrepreneur accepté

-

Étant donné que je suis auto-entrepreneur

-

Quand je renseigne mon statut juridique

-

Alors l'option "Auto-entrepreneur (micro-BNC)" est disponible -Et je peux continuer le processus

-
-

3. Statut société SARL/SAS/SASU accepté

-

Étant donné que j'ai créé une société

-

Quand je renseigne mon statut juridique

-

Alors les options suivantes sont disponibles:

-
| statut juridique |
-|---|
-| SARL |
-| SAS |
-| SASU |
-
-

Et je peux continuer le processus

-
-

4. Statut particulier refusé

-

Étant donné que je n'ai pas de statut professionnel

-

Quand j'essaie de m'inscrire en tant que "Particulier"

-

Alors le formulaire affiche: -Et je ne peux pas continuer sans statut professionnel

-
-

5. Document SIRET obligatoire et validé

-

Étant donné que je renseigne mon SIRET

-

Quand je saisis "12345678901234" (14 chiffres)

-

Alors le format est validé -Et Mangopay vérifie l'existence du SIRET auprès du répertoire SIRENE -Et si valide, le document est accepté

-
-

6. SIRET invalide ou inexistant

-

Étant donné que je renseigne un SIRET inexistant

-

Quand je saisis "99999999999999"

-

Alors Mangopay rejette le SIRET -Et je vois "SIRET non trouvé dans le répertoire SIRENE. Vérifiez le numéro." -Et je dois corriger avant de continuer

-
-

7. RIB professionnel obligatoire

-

Étant donné que j'upload mon RIB

-

Quand le RIB est scanné par Mangopay

-

Alors le système vérifie que le titulaire correspond à mon SIRET -Et que l'IBAN commence par "FR" (compte français) -Et si valide, le document est accepté

-
-

8. RIB particulier refusé

-

Étant donné que j'upload un RIB de compte particulier

-

Quand Mangopay détecte que le compte n'est pas professionnel

-

Alors le RIB est rejeté -Et je vois:

-
-

9. Pièce d'identité CNI en cours de validité

-

Étant donné que j'upload ma carte nationale d'identité

-

Quand Mangopay analyse le document

-

Alors la date d'expiration est vérifiée -Et si la CNI est valide, le document est accepté -Et mon identité est vérifiée par OCR + vérification manuelle

-
-

10. Pièce d'identité expirée refusée

-

Étant donné que j'upload une CNI expirée depuis 2 ans

-

Quand Mangopay analyse le document

-

Alors le document est rejeté -Et je vois "Pièce d'identité expirée. Veuillez fournir un document en cours de validité."

-
-

11. Passeport accepté comme alternative

-

Étant donné que je n'ai pas de CNI

-

Quand j'upload mon passeport en cours de validité

-

Alors Mangopay accepte le passeport -Et mon identité est vérifiée de la même manière

-
-

12. Numéro TVA intracommunautaire si applicable

-

Étant donné que mon CA dépasse 37 000€/an -Et que je suis sorti de la franchise en base

-

Quand je renseigne mon numéro TVA intracommunautaire

-

Alors le format "FR + 11 chiffres" est validé -Et Mangopay vérifie l'existence auprès de la Commission Européenne (VIES)

-
-

13. TVA non applicable pour micro-BNC sous franchise

-

Étant donné que je suis auto-entrepreneur sous franchise en base -Et que mon CA est <37 000€/an

-

Quand je remplis le formulaire KYC

-

Alors le champ "Numéro TVA" est optionnel -Et je peux continuer sans TVA

-
-

14. Kbis <3 mois pour sociétés

-

Étant donné que je suis gérant d'une SARL

-

Quand j'upload mon extrait Kbis

-

Alors Mangopay vérifie que le Kbis date de moins de 3 mois -Et que le SIRET correspond -Et si valide, le document est accepté

-
-

15. Kbis trop ancien refusé

-

Étant donné que j'upload un Kbis de 5 mois

-

Quand Mangopay analyse le document

-

Alors le Kbis est rejeté -Et je vois "Le Kbis doit dater de moins de 3 mois. Téléchargez un extrait récent sur infogreffe.fr"

-
-

16. Vérification identité ne correspond pas au compte

-

Étant donné que mon compte RoadWave est au nom de "Jean Dupont" -Mais que ma CNI est au nom de "Pierre Martin"

-

Quand Mangopay compare les identités

-

Alors le KYC est rejeté -Et je vois:

-
-

17. Liste noire anti-blanchiment détectée

-

Étant donné que mon identité apparaît sur une liste anti-blanchiment

-

Quand Mangopay effectue la vérification AML (Anti-Money Laundering)

-

Alors le KYC est automatiquement rejeté -Et je vois "Votre demande ne peut être acceptée pour des raisons de conformité légale" -Et mon compte créateur peut être suspendu

-
-

18. Délai de vérification 24-72h si documents conformes

-

Étant donné que j'ai soumis tous les documents valides

-

Quand Mangopay traite ma demande

-

Alors je reçois un email "KYC en cours de vérification (24-72h)" -Et mon statut est "En attente de validation" -Et je peux continuer à publier des contenus en attendant

-
-

19. Validation KYC réussie

-

Étant donné que mes documents sont conformes

-

Quand Mangopay valide mon KYC après 48h

-

Alors je reçois un email "Monétisation activée !" -Et mon statut passe à "Monétisé" -Et je commence à générer des revenus dès maintenant

-
-

20. Rejet KYC pour documents invalides

-

Étant donné que j'ai soumis une CNI floue et illisible

-

Quand Mangopay analyse les documents

-

Alors le KYC est rejeté après 24h -Et je reçois un email détaillant les documents à refournir:

-
-

21. E-wallet Mangopay créé automatiquement

-

Étant donné que mon KYC est validé

-

Quand Mangopay finalise mon inscription

-

Alors un e-wallet Mangopay est créé automatiquement à mon nom -Et tous mes futurs revenus seront transférés vers ce wallet -Et les virements SEPA vers mon RIB seront effectués depuis ce wallet

-
-

22. Conformité RGPD - Données hébergées EU

-

Étant donné que je fournis mes documents KYC

-

Quand Mangopay stocke mes données

-

Alors toutes les données sont hébergées en Union Européenne -Et Mangopay est régulé par l'ACPR (Autorité de Contrôle Prudentiel) -Et mes données sont protégées selon le RGPD

-
-

23. KYC gratuit inclus dans Mangopay

-

Étant donné que je complète le KYC

-

Quand le processus se termine

-

Alors aucun frais ne m'est facturé (0€) -Et aucun frais n'est facturé à RoadWave (inclus dans l'offre Mangopay)

-
-

24. Base légale - Conformité fiscale française

-

Étant donné que RoadWave est une plateforme française

-

Quand je génère des revenus >1200€/an

-

Alors RoadWave doit déclarer ces revenus aux impôts (DAS2) -Et le KYC permet de garantir l'identité réelle du bénéficiaire -Et cela respecte la réglementation fiscale française

-
-

25. Base légale - Directive anti-blanchiment EU 2018/843

-

Étant donné que RoadWave verse de l'argent aux créateurs

-

Quand le KYC est effectué

-

Alors RoadWave respecte la 5ème directive anti-blanchiment EU -Et Mangopay effectue les vérifications requises (identité, liste noire, origine fonds)

-
-

26. Notification de mise à jour documents expirés

-

Étant donné que ma CNI va expirer dans 30 jours

-

Quand le système détecte l'expiration proche

-

Alors je reçois un email:

-
-

27. Suspension monétisation si documents expirés

-

Étant donné que ma CNI est expirée depuis 10 jours -Et que je n'ai pas mis à jour mes documents

-

Quand le système vérifie mon statut KYC

-

Alors ma monétisation est suspendue automatiquement -Et je ne génère plus de revenus jusqu'à mise à jour

-
-

28. Réactivation sans nouveau KYC si données à jour

-

Étant donné que j'ai désactivé temporairement ma monétisation il y a 6 mois -Et que mes documents KYC sont toujours valides

-

Quand je réactive la monétisation

-

Alors je n'ai pas besoin de refaire le KYC -Et la réactivation est immédiate

-
-

29. Nouveau KYC requis après 2 ans d'inactivité

-

Étant donné que j'ai désactivé ma monétisation il y a 25 mois

-

Quand j'essaie de réactiver

-

Alors le système demande un nouveau KYC -Et je dois soumettre des documents à jour (CNI peut avoir changé)

-
-

30. Support créateur pour problèmes KYC

-

Étant donné que mon KYC est rejeté et je ne comprends pas pourquoi

-

Quand je contacte le support RoadWave

-

Alors un agent peut consulter les raisons du rejet Mangopay -Et m'aider à fournir les bons documents

-
-

31. Export données KYC pour RGPD

-

Étant donné que je demande l'export de mes données personnelles

-

Quand l'export est généré

-

Alors les informations KYC sont incluses: -Et les documents scannés (CNI, RIB) sont exclus pour sécurité

-
-

32. Suppression compte et données KYC

-

Étant donné que je supprime définitivement mon compte RoadWave

-

Quand la suppression est confirmée

-

Alors mes données KYC chez Mangopay sont archivées 10 ans (obligation légale) -Mais supprimées de la base RoadWave immédiatement -Et mon e-wallet est clôturé après versement du solde final

-
-

33. Statistiques KYC pour monitoring plateforme

-

Étant donné que RoadWave suit la qualité du processus KYC

-

Quand un admin consulte les métriques

-

Alors il voit:

-
| métrique | valeur exemple |
-|---|---|
-| Demandes KYC ce mois | 247 |
-| Taux de validation | 87% |
-| Délai moyen validation | 36h |
-| Taux de rejet (documents invalides) | 13% |
-
-

Et cela permet d'optimiser le processus

-
-

34. Vérification SIRET via API INSEE

-

Étant donné que je saisis mon SIRET

-

Quand le système le valide

-

Alors une requête est faite à l'API SIRENE de l'INSEE -Et le système vérifie que le SIRET existe et est actif -Et récupère le nom de l'entreprise pour pré-remplir le formulaire

-
-

35. Détection fraude - Même SIRET utilisé par plusieurs comptes

-

Étant donné qu'un SIRET "12345678901234" est déjà utilisé par un autre créateur

-

Quand j'essaie d'utiliser le même SIRET

-

Alors le système détecte la duplication -Et affiche "Ce SIRET est déjà utilisé par un autre compte RoadWave" -Et je dois contacter le support si c'est une erreur

-
-

36. Protection données sensibles - Logs chiffrés

-

Étant donné que des données KYC sensibles transitent dans le système

-

Quand les logs sont enregistrés

-

Alors les numéros SIRET, IBAN et données CNI sont masqués: -Et seule l'équipe sécurité peut accéder aux données complètes

-
-

37. Backup Mangopay des documents KYC

-

Étant donné que mes documents KYC sont stockés chez Mangopay

-

Quand un audit est demandé par les autorités

-

Alors Mangopay peut fournir les documents originaux -Et RoadWave n'a pas besoin de stocker ces documents (réduction risque RGPD)

-
-
- -

Obligations fiscales

-
-

En tant que créateur monétisé -Je veux que RoadWave génère automatiquement les documents fiscaux requis -Afin de faciliter ma comptabilité et respecter la loi

-
-

30 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur avec la monétisation activée -Et que je génère des revenus sur RoadWave

-
-

1. Génération automatique relevé mensuel PDF

-

Étant donné que le mois de janvier se termine

-

Quand le système calcule mes revenus du mois

-

Alors un relevé mensuel PDF est généré automatiquement -Et le PDF est disponible dans mon tableau de bord

-
-

2. Contenu du relevé mensuel PDF

-

Étant donné que mon relevé de janvier est généré

-

Quand je télécharge le PDF

-

Alors le document contient:

-
-

3. Téléchargement relevé depuis tableau de bord

-

Étant donné que je suis sur mon tableau de bord créateur

-

Quand j'accède à l'onglet "Revenus > Historique"

-

Alors je vois la liste de mes relevés mensuels:

-
| mois | montant | actions |
-|---|---|---|
-| Janvier 2025 | 150.00€ | 📄 Télécharger PDF |
-| Décembre 2024 | 123.50€ | 📄 Télécharger PDF |
-| Novembre 2024 | 98.75€ | 📄 Télécharger PDF |
-
-
-

4. Conservation relevés accessibles 10 ans

-

Étant donné que j'ai commencé la monétisation en janvier 2025

-

Quand je consulte mes relevés en janvier 2035 (10 ans plus tard)

-

Alors tous les relevés depuis 2025 sont toujours accessibles -Et je peux télécharger n'importe quel relevé historique -Et cela respecte l'obligation de conservation comptable de 10 ans

-
-

5. Export CSV à la demande

-

Étant donné que je clique sur "Exporter pour comptable"

-

Quand je choisis la période "Année 2025"

-

Alors un fichier CSV est généré et téléchargé

-
-

6. Contenu export CSV détaillé

-

Étant donné que j'exporte mes données comptables 2025

-

Quand je télécharge le fichier CSV

-

Alors le fichier contient:

-
-

7. Transmission à l'expert-comptable

-

Étant donné que j'ai téléchargé mon export CSV 2025

-

Quand je l'envoie à mon expert-comptable

-

Alors il peut importer le fichier dans son logiciel comptable -Et il saisit rapidement mes revenus RoadWave -Et cela facilite ma déclaration fiscale annuelle

-
-

8. DAS2 généré automatiquement si revenus >1200€/an

-

Étant donné que mes revenus 2025 totalisent 2,450€

-

Quand l'année 2025 se termine

-

Alors RoadWave génère automatiquement une DAS2 pour les impôts -Et la DAS2 est transmise à la DGFIP en janvier 2026

-
-

9. Contenu de la DAS2

-

Étant donné que RoadWave génère ma DAS2 pour 2025

-

Quand la DGFIP reçoit la déclaration

-

Alors le document contient:

-
-

10. Créateur reçoit une copie de la DAS2

-

Étant donné que RoadWave transmet ma DAS2 aux impôts

-

Quand la transmission est confirmée

-

Alors je reçois un email avec une copie de la DAS2 en pièce jointe -Et je peux consulter le document dans mon tableau de bord

-
-

11. Pas de DAS2 si revenus <1200€/an

-

Étant donné que mes revenus 2025 totalisent seulement 890€

-

Quand l'année 2025 se termine

-

Alors aucune DAS2 n'est générée car le seuil de 1200€ n'est pas atteint -Mais je dois quand même déclarer mes revenus dans ma déclaration personnelle

-
-

12. Base légale DAS2 - Obligation France

-

Étant donné que RoadWave verse des honoraires à des prestataires

-

Quand les revenus dépassent 1200€/an

-

Alors la déclaration DAS2 est obligatoire selon l'article 87 du Code Général des Impôts -Et le non-respect entraîne une amende de 15€ par bénéficiaire non déclaré

-
-

13. Transmission DAS2 via EDI-TDFC

-

Étant donné que RoadWave génère 1,247 DAS2 pour l'année 2025

-

Quand la transmission aux impôts est effectuée

-

Alors la transmission se fait via le portail EDI-TDFC de la DGFIP -Et la transmission est automatisée (pas de saisie manuelle) -Et un accusé de réception est reçu sous 48h

-
-

14. Créateur responsable de déclarer aux impôts

-

Étant donné que j'ai reçu 2,450€ de revenus RoadWave en 2025

-

Quand je fais ma déclaration fiscale en mai 2026

-

Alors je dois déclarer ces 2,450€ dans ma déclaration annuelle -Et si je suis auto-entrepreneur, je déclare en BNC (Bénéfices Non Commerciaux)

-
-

15. Créateur responsable des cotisations URSSAF

-

Étant donné que je suis auto-entrepreneur -Et que j'ai reçu 2,450€ de revenus RoadWave en 2025

-

Quand je fais ma déclaration URSSAF trimestrielle

-

Alors je dois déclarer ces revenus à l'URSSAF -Et je paie ~22% de cotisations sociales (soit ~539€)

-
-

16. TVA non applicable en franchise en base

-

Étant donné que je suis auto-entrepreneur en micro-BNC -Et que mon chiffre d'affaires est <37,800€/an

-

Quand je génère des revenus sur RoadWave

-

Alors je bénéficie de la franchise en base de TVA -Et je ne facture pas de TVA à RoadWave -Et je ne récupère pas la TVA sur mes achats

-
-

17. TVA applicable si CA >37,800€/an

-

Étant donné que mon chiffre d'affaires total 2025 est 45,000€

-

Quand je dépasse le seuil de franchise en base (37,800€)

-

Alors je dois facturer de la TVA (20%) à RoadWave -Et je dois obtenir un numéro TVA intracommunautaire -Et je dois déclarer ma TVA mensuellement ou trimestriellement

-
-

18. Conservation justificatifs 10 ans - Obligation légale

-

Étant donné que je génère des revenus sur RoadWave

-

Quand je télécharge mes relevés mensuels et exports CSV

-

Alors je dois les conserver 10 ans (obligation comptable France) -Et en cas de contrôle fiscal, je dois pouvoir les fournir

-
-

19. Mangopay transmet automatiquement via DAC7

-

Étant donné que je suis créateur monétisé sur RoadWave

-

Quand l'année se termine

-

Alors Mangopay transmet automatiquement mes revenus aux autorités fiscales EU -Et cela respecte la directive DAC7 (2021/514) sur la transparence fiscale des plateformes

-
-

20. Directive DAC7 - Obligations plateforme

-

Étant donné que RoadWave est une plateforme facilitant des transactions

-

Quand Mangopay gère les paiements

-

Alors Mangopay transmet automatiquement:

-
| information | destinataire |
-|---|---|
-| Identité créateur (SIRET) | Autorités fiscales pays EU |
-| Revenus annuels | Autorités fiscales pays EU |
-| Nombre transactions | Autorités fiscales pays EU |
-
-

Et RoadWave n'a pas besoin de faire cette transmission manuellement

-
-

21. Justificatif virement = Preuve bancaire comptable

-

Étant donné que je reçois un virement de 150.00€ de Mangopay

-

Quand je consulte mon relevé bancaire

-

Alors je vois le virement avec la référence MANGOPAY-ABC123 -Et ce relevé bancaire sert de justificatif comptable -Et je peux le fournir à mon expert-comptable ou aux impôts

-
-

22. Notification annuelle rappel déclaration fiscale

-

Étant donné que je suis créateur monétisé

-

Quand le mois d'avril 2026 arrive (période déclaration impôts France)

-

Alors je reçois un email de rappel:

-
-

23. Page ressources fiscales pour créateurs

-

Étant donné que je suis créateur monétisé

-

Quand j'accède à "Aide > Fiscalité"

-

Alors je vois une page avec:

-
| ressource | description |
-|---|---|
-| Guide auto-entrepreneur RoadWave | PDF expliquant démarches et déclarations |
-| FAQ fiscalité | Questions fréquentes sur TVA, cotisations, etc. |
-| Liens URSSAF et impots.gouv.fr | Portails officiels |
-| Contact expert-comptable partenaire | Recommandations d'experts connaissant RoadWave |
-
-
-

24. Dashboard créateur - Récapitulatif annuel

-

Étant donné que je consulte mon dashboard en décembre 2025

-

Quand j'accède à "Revenus > Récapitulatif annuel"

-

Alors je vois:

-
-

25. Génération automatique minimise erreurs

-

Étant donné que tous les documents fiscaux sont générés automatiquement

-

Quand un créateur télécharge ses documents

-

Alors les montants sont garantis corrects (issus de la base de données) -Et il n'y a pas d'erreur de saisie manuelle -Et cela réduit les risques de contrôle fiscal

-
-

26. Conformité RGPD - Données fiscales chiffrées

-

Étant donné que les documents fiscaux contiennent des données sensibles (SIRET, revenus)

-

Quand les documents sont stockés

-

Alors ils sont chiffrés au repos (encryption AES-256) -Et seul le créateur et les admins autorisés peuvent y accéder -Et les logs d'accès sont conservés pour audit

-
-

27. Backup documents fiscaux 10 ans

-

Étant donné qu'un document fiscal est généré

-

Quand il est stocké dans la base de données

-

Alors une copie est sauvegardée sur S3 (stockage durable) -Et les backups sont répliqués sur 3 zones de disponibilité -Et la conservation est garantie 10 ans minimum

-
-

28. Audit trail génération DAS2

-

Étant donné que 1,247 DAS2 sont générées en janvier 2026

-

Quand un audit est demandé

-

Alors tous les événements sont loggés:

-
| événement | timestamp | détails |
-|---|---|---|
-| Calcul revenus annuels | 2025-12-31 23:59:00 | 1,247 créateurs éligibles |
-| Génération fichier EDI | 2026-01-10 08:00:00 | Format EDI-TDFC |
-| Transmission DGFIP | 2026-01-10 10:30:00 | Via portail EDI-TDFC |
-| Accusé réception DGFIP | 2026-01-11 14:20:00 | Transmission confirmée |
-| Email créateurs | 2026-01-11 16:00:00 | 1,247 emails envoyés |
-
-
-

29. Statistiques admin - Conformité fiscale

-

Étant donné qu'un admin RoadWave consulte les métriques fiscales

-

Quand il accède au dashboard admin

-

Alors il voit:

-
| métrique | valeur 2025 |
-|---|---|
-| Créateurs monétisés | 1,247 |
-| Créateurs éligibles DAS2 (>1200€) | 847 (68%) |
-| Revenus totaux versés | 1,890,345€ |
-| DAS2 transmises à la DGFIP | 847 |
-| Taux conformité | 100% |
-
-
-

30. Support créateur pour questions fiscales

-

Étant donné que j'ai une question sur ma déclaration fiscale

-

Quand je contacte le support RoadWave

-

Alors l'agent peut consulter mes documents fiscaux -Et m'aider à comprendre ce que je dois déclarer -Mais il ne peut pas me conseiller fiscalement (pas expert-comptable) -Et il me recommande de consulter un expert-comptable si nécessaire

-
-
- -

Paiement des créateurs

-
-

En tant que créateur monétisé -Je veux recevoir mes paiements mensuels de manière fiable -Afin d'être rémunéré pour mon travail

-
-

35 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur avec la monétisation activée -Et que mon KYC est validé

-
-

1. Seuil minimum de 50€ atteint - Paiement effectué

-

Étant donné que mes revenus du mois sont 73.45€

-

Quand le dernier jour du mois arrive

-

Alors mon solde de 73.45€ est transféré vers "en attente de paiement" -Et le paiement sera effectué le 15 du mois prochain

-
-

2. Seuil minimum de 50€ non atteint - Report mois suivant

-

Étant donné que mes revenus du mois sont 32.17€

-

Quand le dernier jour du mois arrive

-

Alors mon solde de 32.17€ est reporté au mois suivant -Et je vois "Solde insuffisant pour paiement (<50€). Report mois prochain."

-
-

3. Cumul sur plusieurs mois jusqu'à atteindre 50€

-

Étant donné que mes revenus sont:

-
| mois | revenus | solde cumulé |
-|---|---|---|
-| Janvier | 18.50€ | 18.50€ |
-| Février | 22.30€ | 40.80€ |
-| Mars | 15.70€ | 56.50€ |
-
-

Quand la fin du mois de mars arrive

-

Alors le solde cumulé de 56.50€ dépasse les 50€ -Et un paiement de 56.50€ est effectué le 15 avril

-
-

4. Calcul des revenus le dernier jour du mois

-

Étant donné que nous sommes le 31 janvier à 23h59

-

Quand le système calcule les revenus du mois

-

Alors une requête SQL agrège tous les revenus pub et premium -Et le solde final du mois est figé dans monthly_revenues -Et le compteur du mois en cours repart à 0€ le 1er février

-
-

5. Période de traitement contestations 1-14 du mois

-

Étant donné que mes revenus de janvier sont calculés à 150.00€

-

Quand la période du 1-14 février arrive

-

Alors RoadWave analyse les éventuelles fraudes ou contestations -Et si une fraude est détectée, les revenus concernés sont retirés du solde -Et le solde final est validé le 14 février

-
-

6. Virement SEPA le 15 du mois suivant

-

Étant donné que mes revenus de janvier validés sont 150.00€

-

Quand le 15 février arrive

-

Alors Mangopay initie un virement SEPA depuis mon e-wallet vers mon RIB -Et le statut du paiement passe à "En cours"

-
-

7. Réception virement 16-18 du mois (1-3 jours SEPA)

-

Étant donné qu'un virement SEPA a été initié le 15 février

-

Quand 1-3 jours ouvrés s'écoulent

-

Alors je reçois le virement sur mon compte bancaire entre le 16 et 18 février -Et je peux consulter l'historique des paiements dans mon dashboard

-
-

8. Virement SEPA gratuit pour comptes EU

-

Étant donné que mon RIB est français (IBAN FR)

-

Quand Mangopay effectue le virement

-

Alors aucun frais n'est prélevé (virement SEPA gratuit) -Et je reçois 100% du montant annoncé

-
-

9. Virement international hors EU avec frais variables

-

Étant donné que je suis créateur expatrié avec RIB hors Union Européenne

-

Quand Mangopay effectue le virement international

-

Alors des frais variables s'appliquent selon le pays -Et les frais sont déduits du montant final -Et je vois le détail des frais dans mon historique

-
-

10. E-wallet Mangopay automatique

-

Étant donné que mon KYC est validé

-

Quand mes revenus sont calculés

-

Alors les revenus sont automatiquement transférés vers mon e-wallet Mangopay -Et l'e-wallet est débité lors du virement SEPA vers mon RIB -Et je n'ai aucune action manuelle à faire

-
-

11. Tableau de bord - Revenus pub temps réel

-

Étant donné que j'accède à mon tableau de bord créateur

-

Quand je consulte l'onglet "Revenus"

-

Alors je vois:

-
| métrique | valeur exemple |
-|---|---|
-| Revenus pub ce mois | 123.45€ |
-| Revenus premium ce mois | 67.89€ |
-| Solde disponible ce mois | 191.34€ |
-| Prochain paiement | 15 mars 2025 |
-
-

Et ces valeurs sont mises à jour en temps réel (cache Redis, refresh 10 min)

-
-

12. Tableau de bord - Solde en attente de paiement

-

Étant donné que mes revenus de janvier sont calculés et validés -Et que nous sommes le 10 février

-

Quand je consulte mon tableau de bord

-

Alors je vois:

-
| métrique | valeur exemple |
-|---|---|
-| Solde en attente | 150.00€ |
-| Date de paiement | 15 février 2025 |
-| Statut | En attente |
-
-
-

13. Historique des virements permanents

-

Étant donné que je suis monétisé depuis 6 mois

-

Quand je consulte l'historique des paiements

-

Alors je vois la liste complète:

-
| date paiement | montant | statut | référence virement |
-|---|---|---|---|
-| 15/02/2025 | 150.00€ | Payé | MANGOPAY-ABC123 |
-| 15/01/2025 | 123.50€ | Payé | MANGOPAY-XYZ789 |
-| 15/12/2024 | 98.75€ | Payé | MANGOPAY-DEF456 |
-| ... | ... | ... | ... |
-
-
-

14. Export comptable CSV téléchargeable

-

Étant donné que je clique sur "Télécharger export comptable"

-

Quand le fichier CSV est généré

-

Alors je télécharge un fichier contenant: -Et je peux transmettre ce fichier à mon expert-comptable

-
-

15. Échec virement - Tentative 1 échouée

-

Étant donné qu'un virement est initié le 15 février -Mais que mon RIB est invalide ou le compte est fermé

-

Quand Mangopay détecte l'échec

-

Alors le statut passe à "Échec - Retry programmé le 18 février" -Et je reçois un email m'alertant du problème

-
-

16. Échec virement - Retry automatique J+3

-

Étant donné que le virement du 15 février a échoué

-

Quand le 18 février arrive (J+3)

-

Alors Mangopay tente automatiquement un nouveau virement -Et si le RIB est toujours invalide, le virement échoue à nouveau

-
-

17. Échec virement - Retry automatique J+7

-

Étant donné que les 2 premières tentatives ont échoué

-

Quand le 22 février arrive (J+7)

-

Alors Mangopay tente une 3ème et dernière fois -Et si le virement échoue encore, la monétisation est suspendue

-
-

18. Échec virement - Suspension monétisation après 3 échecs

-

Étant donné que les 3 tentatives de virement ont échoué

-

Quand le système détecte le 3ème échec

-

Alors ma monétisation est suspendue automatiquement -Et je reçois un email:

-
-

19. Mise à jour RIB et réactivation paiement

-

Étant donné que ma monétisation est suspendue pour RIB invalide -Et que mon solde en attente est 150.00€

-

Quand je mets à jour mon RIB avec un compte valide

-

Alors Mangopay tente immédiatement un nouveau virement -Et si le virement réussit, ma monétisation est réactivée automatiquement

-
-

20. Notification email lors de chaque paiement

-

Étant donné qu'un virement de 150.00€ est effectué le 15 février

-

Quand le virement est confirmé par Mangopay

-

Alors je reçois un email:

-
-

21. Justification seuil 50€ - Éviter frais bancaires micro-sommes

-

Étant donné que Mangopay facture des frais fixes par virement -Et que les banques peuvent facturer des frais de réception

-

Quand un créateur génère seulement 5€/mois

-

Alors un virement mensuel coûterait proportionnellement trop cher -Et le seuil de 50€ garantit des frais proportionnels raisonnables

-
-

22. Comparaison avec YouTube (seuil 100$)

-

Étant donné que YouTube fixe le seuil à 100$ (~90€)

-

Quand RoadWave fixe le seuil à 50€

-

Alors RoadWave est plus accessible pour petits créateurs -Et les paiements arrivent plus rapidement

-
-

23. Comparaison avec Twitch (seuil 50$)

-

Étant donné que Twitch fixe le seuil à 50$ (~45€)

-

Quand RoadWave fixe le seuil à 50€

-

Alors le seuil est aligné sur Twitch -Et les créateurs comprennent facilement le système

-
-

24. Comparaison avec Spotify (seuil 10€ mais délais longs)

-

Étant donné que Spotify a un seuil bas de 10€ mais verse tous les 3 mois

-

Quand RoadWave a un seuil de 50€ mais verse chaque mois

-

Alors les créateurs reçoivent leurs paiements plus régulièrement -Et la trésorerie est plus prévisible

-
-

25. Relevé mensuel PDF automatique

-

Étant donné que mes revenus de janvier sont calculés

-

Quand le 1er février arrive

-

Alors un relevé mensuel PDF est généré automatiquement: -Et le PDF est téléchargeable depuis mon tableau de bord

-
-

26. Conservation relevés 10 ans (obligation comptable)

-

Étant donné que je génère des revenus sur RoadWave

-

Quand je télécharge mes relevés mensuels

-

Alors je dois les conserver 10 ans (obligation légale France) -Et RoadWave conserve également une copie pendant 10 ans pour audit

-
-

27. Dashboard admin - Monitoring paiements

-

Étant donné qu'un admin RoadWave consulte les paiements du mois

-

Quand il accède au dashboard admin

-

Alors il voit:

-
| métrique | valeur exemple |
-|---|---|
-| Créateurs payés ce mois | 1,247 |
-| Montant total versé | 127,345€ |
-| Paiements en attente | 34 |
-| Échecs virements | 3 |
-| Délai moyen réception (jours) | 1.8 |
-
-
-

28. Alerte admin si taux échec >5%

-

Étant donné que 8% des virements du mois ont échoué

-

Quand le système détecte le taux d'échec élevé

-

Alors une alerte est envoyée à l'équipe technique:

-
-

29. Statistiques personnelles - Moyenne revenus sur 6 mois

-

Étant donné que je suis monétisé depuis 6 mois

-

Quand je consulte mes statistiques

-

Alors je vois:

-
| métrique | valeur |
-|---|---|
-| Revenus moyens/mois | 134.50€ |
-| Meilleur mois | 189.00€ |
-| Mois le plus bas | 87.30€ |
-| Tendance | +12% ↗ |
-
-

Et cela m'aide à suivre ma progression

-
-

30. Projection revenus annuels

-

Étant donné que mes revenus moyens sont 134.50€/mois

-

Quand je consulte les projections

-

Alors le système estime mes revenus annuels à ~1,614€ -Et je peux anticiper mes déclarations fiscales

-
-

31. Notification seuil symbolique 1000€ cumulés

-

Étant donné que mes revenus cumulés depuis inscription atteignent 1000€

-

Quand le paiement qui franchit ce seuil est effectué

-

Alors je reçois une notification:

-
-

32. Performance calcul avec 100 000 créateurs monétisés

-

Étant donné que RoadWave a 100 000 créateurs monétisés

-

Quand le calcul des paiements du 15 du mois est lancé

-

Alors un job asynchrone traite les paiements par batch de 1000 -Et tous les virements sont initiés en 2-4 heures -Et les serveurs Mangopay gèrent la charge sans problème

-
-

33. Backup des données de paiement

-

Étant donné que les paiements sont critiques pour les créateurs

-

Quand un paiement est effectué

-

Alors les données sont sauvegardées dans PostgreSQL (principal) -Et répliquées vers une base de backup (replica) -Et une copie d'archive est stockée sur S3 (conservation 10 ans)

-
-

34. Audit trail complet des paiements

-

Étant donné qu'un paiement est initié, traité et complété

-

Quand un audit est demandé

-

Alors tous les événements sont loggés:

-
| événement | timestamp | détails |
-|---|---|---|
-| Calcul revenus mois | 2025-01-31 23:59:00 | Montant: 150.00€ |
-| Validation période fraude | 2025-02-14 23:59:00 | Aucune fraude détectée |
-| Initiation virement | 2025-02-15 09:00:00 | Mangopay ref: ABC123 |
-| Confirmation virement | 2025-02-16 14:30:00 | Reçu par banque créateur |
-
-

Et ces logs sont conservés 10 ans pour conformité

-
-

35. Protection fraude - Détection pattern suspect

-

Étant donné qu'un créateur génère subitement 10 000€ de revenus en 1 mois

-

Alors que sa moyenne est de 50€/mois

-

Quand le système détecte cette anomalie

-

Alors le paiement est mis en attente pour vérification manuelle -Et l'équipe modération analyse le compte avant validation

-
-
- -

Sources de revenus créateurs

-
-

En tant que créateur monétisé -Je veux générer des revenus via publicités et abonnés Premium -Afin d'être rémunéré pour mon travail

-
-

34 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un créateur avec la monétisation activée -Et que mon KYC est validé

-
-

1. CPM créateur de 3€ / 1000 écoutes complètes

-

Étant donné que mes contenus ont généré 1000 écoutes complètes par des utilisateurs gratuits

-

Quand le calcul des revenus du mois est effectué

-

Alors je touche 3.00€ pour ces 1000 écoutes -Et ce montant est ajouté à mon solde disponible

-
-

2. 10 000 écoutes gratuits → 30€ de revenus pub

-

Étant donné que mes contenus ont généré 10 000 écoutes complètes (utilisateurs gratuits)

-

Quand le mois se termine

-

Alors je touche 30.00€ de revenus publicitaires -Et ces revenus sont visibles en temps réel dans mon tableau de bord

-
-

3. 50 000 écoutes gratuits → 150€ de revenus pub

-

Étant donné que mes contenus ont généré 50 000 écoutes complètes (utilisateurs gratuits)

-

Quand le mois se termine

-

Alors je touche 150.00€ de revenus publicitaires

-
-

4. 100 000 écoutes gratuits → 300€ de revenus pub

-

Étant donné que mes contenus ont généré 100 000 écoutes complètes (utilisateurs gratuits)

-

Quand le mois se termine

-

Alors je touche 300.00€ de revenus publicitaires

-
-

5. Répartition économique - Plateforme garde 94%

-

Étant donné qu'une publicité facturée 0.05€/écoute génère 50€ CPM

-

Quand la plateforme calcule la répartition

-

Alors le créateur touche 3€ (6% du CA pub) -Et la plateforme garde 47€ (94%) pour:

-
| poste budgétaire | coût estimé |
-|---|---|
-| CDN + infrastructure | 10-15€ |
-| Modération + support | 5-10€ |
-| Développement + R&D | 10-15€ |
-| Marge opérationnelle | 10-15€ |
-
-
-

6. Écoute complète = ≥80% du contenu écouté

-

Étant donné qu'un utilisateur gratuit écoute mon contenu de 10 minutes

-

Quand il écoute 8 minutes (80%)

-

Alors l'écoute compte comme "complète" -Et je génère 0.003€ de revenus pub (3€/1000)

-
-

7. Écoute incomplète <80% ne compte pas

-

Étant donné qu'un utilisateur gratuit écoute mon contenu de 10 minutes -Mais il skip après 5 minutes (50%)

-

Quand le calcul des revenus est effectué

-

Alors cette écoute ne compte pas comme "complète" -Et je ne génère aucun revenu publicitaire pour cette écoute

-
-

8. Écoutes Premium ne comptent pas pour les revenus pub

-

Étant donné qu'un utilisateur Premium écoute 100% de mon contenu

-

Quand le calcul des revenus publicitaires est effectué

-

Alors cette écoute ne compte pas dans les revenus pub -Mais elle compte dans les revenus Premium (système séparé)

-
-

9. Détection bots - Écoutes exclues

-

Étant donné qu'un bot génère 10 000 écoutes artificielles sur mes contenus

-

Quand le système détecte le pattern suspect (rate limiting, IP unique, etc.)

-

Alors ces écoutes sont marquées comme frauduleuses -Et elles sont exclues du calcul des revenus publicitaires

-
-

10. Comparaison avec YouTube (3-5€/1000 vues)

-

Étant donné que YouTube paie 3-5€/1000 vues

-

Quand RoadWave fixe le CPM créateur à 3€/1000 écoutes

-

Alors le tarif est aligné sur le bas de la fourchette YouTube -Et cela est compétitif pour un MVP sans marché publicitaire mature

-
-

11. Comparaison avec Spotify (3-4€/1000 écoutes)

-

Étant donné que Spotify paie ~3-4€/1000 écoutes

-

Quand RoadWave fixe le CPM créateur à 3€/1000 écoutes

-

Alors le tarif est aligné sur l'industrie musicale -Et les créateurs audio peuvent anticiper des revenus similaires

-
-

12. Tableau de bord - Revenus pub temps réel

-

Étant donné que j'accède à mon tableau de bord créateur

-

Quand je consulte mes revenus publicitaires

-

Alors je vois:

-
| métrique | valeur exemple |
-|---|---|
-| Écoutes complètes ce mois (gratuit) | 23 456 |
-| Revenus pub ce mois | 70.37€ |
-| CPM effectif | 3.00€ |
-
-

Et ces valeurs sont mises à jour toutes les 10 minutes

-
-

13. Répartition 70/30 - Créateur touche 70%

-

Étant donné qu'un utilisateur Premium paie 4.99€/mois

-

Quand la répartition est calculée

-

Alors 3.49€ sont reversés aux créateurs écoutés (70%) -Et 1.50€ sont gardés par la plateforme (30%)

-
-

14. Utilisateur écoute 3 créateurs - Répartition proportionnelle

-

Étant donné qu'un utilisateur Premium paie 4.99€/mois -Et qu'il écoute 3 créateurs ce mois:

-
| créateur | temps écoute | ratio |
-|---|---|---|
-| Créateur A | 10h | 50% |
-| Créateur B | 6h | 30% |
-| Créateur C | 4h | 20% |
-
-

Quand le calcul des revenus Premium est effectué

-

Alors la répartition est:

-
| créateur | revenus |
-|---|---|
-| Créateur A | 1.75€ |
-| Créateur B | 1.05€ |
-| Créateur C | 0.70€ |
-
-

Et la somme totale versée aux créateurs est 3.50€ (70% de 4.99€)

-
-

15. Calcul SQL proportionnel au temps d'écoute

-

Étant donné qu'un utilisateur Premium a écouté plusieurs créateurs

-

Quand le système calcule les revenus du mois

-

Alors la requête SQL suivante est exécutée:

-
-

16. Utilisateur écoute un seul créateur - 100% à ce créateur

-

Étant donné qu'un utilisateur Premium paie 4.99€/mois -Et qu'il n'écoute qu'un seul créateur (moi)

-

Quand le mois se termine

-

Alors je touche 3.49€ (70% de 4.99€) -Et je reçois 100% de la part créateurs

-
-

17. Utilisateur Premium inactif - Aucun revenu généré

-

Étant donné qu'un utilisateur Premium paie 4.99€/mois -Mais qu'il n'écoute aucun contenu ce mois

-

Quand le calcul des revenus Premium est effectué

-

Alors aucun créateur ne reçoit de revenus de cet utilisateur -Et les 3.49€ de la part créateurs restent à la plateforme -Et cela couvre les coûts d'infrastructure

-
-

18. Comparaison avec YouTube Premium (70/30)

-

Étant donné que YouTube Premium reverse 70% aux créateurs

-

Quand RoadWave fixe également 70/30

-

Alors le modèle est aligné sur le standard industrie -Et les créateurs ont confiance dans l'équité du système

-
-

19. Comparaison avec Spotify (70/30)

-

Étant donné que Spotify reverse 70% aux artistes

-

Quand RoadWave fixe également 70/30

-

Alors le modèle est identique à Spotify -Et les créateurs audio comprennent facilement le système

-
-

20. Apple Music moins avantageux (52/48)

-

Étant donné qu'Apple Music ne reverse que 52% aux artistes

-

Quand RoadWave offre 70% aux créateurs

-

Alors RoadWave est plus avantageux de 18 points -Et cela devient un argument marketing fort

-
-

21. Justification équité - Créateurs les plus écoutés gagnent plus

-

Étant donné que 2 créateurs ont le même nombre d'abonnés Premium -Mais que le Créateur A est écouté 20h/mois et le Créateur B seulement 2h/mois

-

Quand les revenus Premium sont calculés

-

Alors le Créateur A gagne 10× plus que le Créateur B -Et cela récompense la qualité et l'engagement (pas juste l'abonnement)

-
-

22. Pas de "winner takes all" - Équité totale

-

Étant donné qu'un utilisateur Premium écoute 10 créateurs différents

-

Quand les revenus sont calculés

-

Alors chacun des 10 créateurs reçoit sa part proportionnelle -Et il n'y a pas de système où un seul créateur prend tout

-
-

23. Marge plateforme 30% couvre absence revenus pub Premium

-

Étant donné qu'un utilisateur Premium ne voit aucune publicité

-

Quand la plateforme calcule ses revenus

-

Alors elle ne touche que les 30% de l'abonnement Premium (1.50€) -Et cette marge compense la perte des revenus publicitaires (qui auraient été ~47€/1000 écoutes)

-
-

24. Tableau de bord - Revenus Premium temps réel

-

Étant donné que j'accède à mon tableau de bord créateur

-

Quand je consulte mes revenus Premium

-

Alors je vois:

-
| métrique | valeur exemple |
-|---|---|
-| Abonnés Premium actifs ayant écouté | 47 |
-| Heures d'écoute Premium ce mois | 234h |
-| Revenus Premium ce mois | 89.23€ |
-
-

Et ces valeurs sont mises à jour toutes les 10 minutes

-
-

25. Revenus cumulés pub + premium

-

Étant donné que j'ai généré ce mois:

-
| source | montant |
-|---|---|
-| Revenus pub | 150.00€ |
-| Revenus Premium | 89.23€ |
-
-

Quand je consulte mon solde disponible

-

Alors le total est 239.23€ -Et ce solde sera versé le 15 du mois prochain (si ≥50€)

-
-

26. Dashboard créateur - Vue d'ensemble

-

Étant donné que j'accède à mon tableau de bord créateur

-

Quand je consulte la page revenus

-

Alors je vois:

-
-

27. Export comptable CSV pour expert-comptable

-

Étant donné que je clique sur "Exporter pour comptable"

-

Quand l'export est généré

-

Alors je télécharge un fichier CSV: -Et je peux transmettre ce fichier à mon expert-comptable

-
-

28. Notification hebdomadaire progression revenus

-

Étant donné que je suis créateur monétisé

-

Quand chaque lundi matin arrive

-

Alors je reçois un email récapitulatif:

-
-

29. Graphique évolution revenus sur 12 mois

-

Étant donné que je suis monétisé depuis 12 mois

-

Quand j'accède à mes statistiques

-

Alors je vois un graphique en courbes montrant:

-
| mois | revenus pub | revenus premium | total |
-|---|---|---|---|
-| Jan 25 | 150€ | 89€ | 239€ |
-| Déc 24 | 123€ | 55€ | 178€ |
-| Nov 24 | 100€ | 56€ | 156€ |
-| ... | ... | ... | ... |
-
-

Et cela m'aide à suivre ma progression

-
-

30. Top 3 contenus les plus rentables du mois

-

Étant donné que j'ai publié 20 contenus ce mois

-

Quand je consulte mes statistiques détaillées

-

Alors je vois mon top 3 contenus:

-
| titre | écoutes | revenus pub | revenus premium | total |
-|---|---|---|---|---|
-| Mon meilleur épisode | 12,345 | 37.04€ | 23.45€ | 60.49€ |
-| Discussion tech | 8,901 | 26.70€ | 15.67€ | 42.37€ |
-| Road trip Bretagne | 7,234 | 21.70€ | 12.34€ | 34.04€ |
-
-

Et cela m'aide à comprendre quel type de contenu plaît le plus

-
-

31. Alertes seuils de revenus

-

Étant donné que j'ai activé les notifications de seuils

-

Quand mes revenus du mois dépassent 100€ pour la première fois

-

Alors je reçois une notification:

-
-

32. Performance calcul avec 100 000 créateurs

-

Étant donné que RoadWave a 100 000 créateurs monétisés

-

Quand le calcul des revenus mensuels est lancé le dernier jour du mois

-

Alors un job asynchrone traite tous les créateurs -Et le calcul prend environ 2-4 heures pour tous les créateurs -Et les résultats sont stockés dans la table monthly_revenues

-
-

33. Cache Redis pour métriques temps réel

-

Étant donné que je consulte mon dashboard plusieurs fois par jour

-

Quand la page se charge

-

Alors les compteurs sont récupérés depuis Redis:

-
| clé Redis | valeur exemple |
-|---|---|
-| creator:[id]:complete_listens:202501 | 50234 |
-| creator:[id]:premium_hours:202501 | 234 |
-| creator:[id]:revenue_ads:202501 | 150.70 |
-| creator:[id]:revenue_premium:202501 | 89.23 |
-
-

Et le temps de réponse est <30ms

-
-

34. Prévision revenus fin de mois

-

Étant donné que nous sommes le 20 du mois -Et que mes revenus actuels sont 160€

-

Quand le système calcule la projection

-

Alors il estime les revenus fin de mois à ~240€ (extrapolation linéaire) -Et affiche "Projection fin de mois: ~240€" -Et cela m'aide à anticiper mes revenus

-
-
- -

Actions complémentaires à l'arrêt

-
-

En tant qu'auditeur avec véhicule arrêté -Je veux accéder à des actions avancées depuis l'application mobile -Afin de liker explicitement, m'abonner ou signaler du contenu

-
-

23 scénarios (21 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté -Et que le véhicule est à l'arrêt (vitesse GPS = 0 km/h)

-
-

1. Like explicite avec bouton cœur

-

Étant donné que j'écoute un contenu tagué "Automobile" -Et que ma jauge "Automobile" est à 60%

-

Quand je clique sur le bouton cœur "Like"

-

Alors ma jauge "Automobile" augmente de 2% -Et une animation de cœur rouge s'affiche -Et une vibration courte est déclenchée -Et ma jauge "Automobile" est maintenant à 62%

-
-

2. Like explicite cumulable avec like automatique

-

Étant donné que j'ai écouté un contenu "Voyage" à 85% -Et que j'ai reçu un like automatique renforcé (+2%) -Et que ma jauge "Voyage" est à 52%

-

Quand je clique sur le bouton cœur "Like"

-

Alors ma jauge "Voyage" augmente encore de 2% -Et ma jauge "Voyage" passe à 54% -Et les deux likes sont cumulés

-
-

3. Unlike retire le like manuel uniquement

-

Étant donné que j'ai liké manuellement un contenu "Sport" -Et que ma jauge "Sport" est à 57%

-

Quand je clique à nouveau sur le bouton cœur (toggle)

-

Alors le cœur redevient vide (unlike) -Et ma jauge "Sport" diminue de 2% -Et ma jauge "Sport" revient à 55%

-
-

4. Unlike ne retire pas le like automatique

-

Étant donné que j'ai écouté un contenu "Musique" à 90% -Et que j'ai reçu un like automatique renforcé (+2%) -Et que ma jauge "Musique" est à 52% -Et que je n'ai PAS liké manuellement

-

Quand je consulte l'interface

-

Alors le bouton "Unlike" n'est pas disponible -Et le cœur reste grisé (aucun like manuel) -Et ma jauge reste à 52%

-
-

5. Abonnement à un créateur

-

Étant donné qu'un créateur publie des contenus tagués "Automobile" et "Technologie" -Et que mes jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 50% |
-| Technologie | 45% |
-
-

Quand je clique sur "S'abonner" sur le profil du créateur

-

Alors ma jauge "Automobile" augmente de 5% -Et ma jauge "Technologie" augmente de 5% -Et une animation d'étoile dorée s'affiche -Et un badge "Abonné ✓" apparaît sur le profil -Et mes nouvelles jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 55% |
-| Technologie | 50% |
-
-
-

6. Désabonnement d'un créateur

-

Étant donné que je suis abonné à un créateur -Et que mes jauges "Automobile" et "Technologie" sont à 55% et 50%

-

Quand je clique sur "Se désabonner"

-

Alors ma jauge "Automobile" diminue de 5% -Et ma jauge "Technologie" diminue de 5% -Et le badge "Abonné ✓" disparaît -Et mes nouvelles jauges sont:

-
| catégorie | niveau |
-|---|---|
-| Automobile | 50% |
-| Technologie | 45% |
-
-
-

7. Signalement d'un contenu inapproprié

-

Étant donné que j'écoute un contenu

-

Quand je clique sur le menu contextuel "⋮" -Et que je sélectionne "Signaler"

-

Alors un formulaire de signalement s'ouvre -Et je dois sélectionner une catégorie:

-
| Catégorie |
-|---|
-| Haine et violence |
-| Contenu sexuel |
-| Illégalité |
-| Droits d'auteur |
-| Spam |
-| Désinformation (fake news) |
-| Autre |
-
-

Et je peux ajouter un commentaire optionnel -Et le signalement est envoyé au flux de modération

-
-

8. Feedback visuel pour like explicite

-

Étant donné que je clique sur le bouton cœur

-

Quand le like est enregistré

-

Alors une animation de cœur rouge se lance (0.5s) -Et le cœur reste rouge plein -Et une vibration haptique courte est déclenchée (iOS: .light, Android: 50ms) -Et un badge "♥ Ajouté à vos favoris" s'affiche 2 secondes

-
-

9. Feedback visuel pour abonnement

-

Étant donné que je clique sur "S'abonner"

-

Quand l'abonnement est enregistré

-

Alors une animation d'étoile dorée se lance (0.8s) -Et le bouton devient "Abonné ✓" avec badge doré -Et une notification "Abonné à [Créateur]" s'affiche -Et les contenus du créateur seront boostés +30% dans l'algo

-
-

10. Bouton like désactivé si vitesse >10 km/h

-

Étant donné que je conduis à 50 km/h

-

Quand j'essaie d'accéder au bouton cœur dans l'app mobile

-

Alors le bouton est grisé et non cliquable -Et un message "Arrêtez-vous pour liker" s'affiche si clic tenté -Et seules les commandes au volant physiques fonctionnent

-
-

11. Bouton abonnement désactivé en conduite

-

Étant donné que je conduis à 40 km/h

-

Quand j'essaie d'accéder au profil créateur dans l'app

-

Alors le bouton "S'abonner" est désactivé -Et un message "Arrêtez-vous pour vous abonner" s'affiche -Et la navigation dans l'app est limitée aux fonctions lecture

-
-

12. Signalement possible en conduite via vocal

-

Étant donné que je conduis à 60 km/h -Et que j'utilise CarPlay avec Siri

-

Quand je dis "Hey Siri, signale ce contenu"

-

Alors Siri demande "Quelle catégorie ?" -Et je peux répondre vocalement "Spam" ou autre catégorie -Et le signalement est enregistré sans toucher l'écran

-
-

13. Actions vocales disponibles avec CarPlay/Android Auto

-

Étant donné que je conduis avec CarPlay activé

-

Quand je dis "Hey Siri, like ce podcast"

-

Alors un like explicite (+2%) est enregistré -Et Siri confirme "J'ai ajouté ce contenu à vos favoris"

-

Quand je dis "OK Google, abonne-moi à ce créateur"

-

Alors l'abonnement est enregistré (+5% toutes jauges) -Et Google Assistant confirme "Vous êtes maintenant abonné"

-
-

14. Menu contextuel accessible à l'arrêt uniquement

-

Étant donné que le véhicule est à l'arrêt

-

Quand je clique sur le menu "⋮" (3 points verticaux)

-

Alors les options disponibles sont:

-
| Option |
-|---|
-| Like (cœur) |
-| S'abonner au créateur |
-| Signaler |
-| Partager |
-| Voir le profil du créateur |
-| Télécharger (mode offline) |
-
-

Et toutes les options sont cliquables

-
-

15. Menu contextuel limité en conduite

-

Étant donné que je conduis à 30 km/h

-

Quand j'essaie d'ouvrir le menu "⋮"

-

Alors seules 2 options sont disponibles:

-
| Option |
-|---|
-| Signaler (vocal possible) |
-| Suivant |
-
-

Et les actions complexes sont désactivées

-
-

16. Persistance des likes manuels en base de données

-

Étant donné que je like manuellement 5 contenus

-

Quand je ferme l'application -Et que je me reconnecte plus tard

-

Alors tous mes likes manuels sont toujours présents -Et les cœurs rouges sont affichés sur les contenus likés -Et mes jauges reflètent toujours l'impact (+2% × 5 likes)

-
-

17. Liste "Mes contenus likés" accessible dans profil

-

Étant donné que j'ai liké manuellement 10 contenus

-

Quand j'accède à mon profil utilisateur

-

Alors je vois une section "❤️ Mes favoris" -Et la liste affiche les 10 contenus likés -Et je peux cliquer pour réécouter -Et je peux retirer un like (unlike) depuis cette liste

-
-

18. Liste "Mes abonnements" accessible dans profil

-

Étant donné que je suis abonné à 5 créateurs

-

Quand j'accède à mon profil utilisateur

-

Alors je vois une section "⭐ Mes abonnements" -Et la liste affiche les 5 créateurs avec leurs avatars -Et je peux accéder au profil de chaque créateur -Et je peux me désabonner depuis cette liste

-
-

19. Impact abonnement sur tous les tags du créateur

-

Étant donné qu'un créateur a publié des contenus avec ces tags:

-
| Contenu | Tags |
-|---|---|
-| C1 | Automobile, Voyage |
-| C2 | Automobile, Technologie |
-| C3 | Voyage, Famille |
-
-

Et que mes jauges sont toutes à 50%

-

Quand je m'abonne à ce créateur

-

Alors les jauges impactées sont:

-
| Tag | Impact |
-|---|---|
-| Automobile | +5% |
-| Voyage | +5% |
-| Technologie | +5% |
-| Famille | +5% |
-
-

Et toutes les autres jauges restent à 50%

-
-

20. Limite d'abonnements (200 maximum)

-

Étant donné que je suis abonné à 200 créateurs

-

Quand j'essaie de m'abonner à un 201ème créateur

-

Alors un message "Limite de 200 abonnements atteinte" s'affiche -Et je dois me désabonner d'un créateur existant pour en ajouter un nouveau

-
-

21. Confirmation avant désabonnement

-

Étant donné que je suis abonné à un créateur

-

Quand je clique sur "Se désabonner"

-

Alors une popup de confirmation s'affiche: -Et je dois confirmer pour valider -Et je peux annuler pour conserver l'abonnement

-
-

22. 📋 Plan: Cumul like automatique + like manuel

-

Étant donné qu'un contenu est tagué "Sport" -Et que ma jauge "Sport" est à 50%

-

Quand j'écoute à % (like auto ) -Et que je like manuellement (+2%)

-

Alors l'impact total est -Et ma nouvelle jauge est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pourcentageautototalnouveau_niveau
100+2%52%
30+1%+3%53%
50+1%+3%53%
80+2%+4%54%
95+2%+4%54%
-
-

23. 📋 Plan: Actions disponibles selon vitesse GPS

-

Étant donné que je roule à km/h

-

Quand j'essaie d'accéder à

-

Alors l'action est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vitesseactiondisponibilite
0Like manueldisponible
0Abonnementdisponible
0Signalementdisponible
5Like manueldisponible
5Abonnementdisponible
10Like manueldésactivée
10Abonnementdésactivée
50Like manueldésactivée
50Abonnementdésactivée
50Signalement vocaldisponible
-
-
- -

Commande "Précédent"

-
-

En tant qu'auditeur -Je veux que le bouton "Précédent" ait un comportement intelligent -Afin de rejouer le contenu actuel ou revenir au précédent selon la progression

-
-

19 scénarios (17 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté

-
-

1. Précédent après <10s revient au contenu précédent

-

Étant donné que j'ai écouté le contenu "A" pendant 2 minutes -Et que j'écoute maintenant le contenu "B" depuis 5 secondes

-

Quand j'appuie sur "Précédent"

-

Alors la lecture revient au contenu "A" -Et la position de lecture est à 2 minutes (position exacte sauvegardée) -Et le contenu "B" reste en historique

-
-

2. Précédent après ≥10s rejoue le contenu actuel

-

Étant donné que j'écoute le contenu "C" depuis 15 secondes

-

Quand j'appuie sur "Précédent"

-

Alors le contenu "C" rejoue depuis le début (position 0:00) -Et la lecture ne revient pas au contenu précédent -Et la progress bar revient à 0%

-
-

3. Précédent exactement à 10s rejoue le contenu actuel

-

Étant donné que j'écoute le contenu "D" depuis exactement 10 secondes

-

Quand j'appuie sur "Précédent"

-

Alors le contenu "D" rejoue depuis le début -Et la lecture ne revient pas au contenu précédent

-
-

4. Précédent sur le premier contenu de session

-

Étant donné que je viens de démarrer l'application -Et que j'écoute le contenu "Premier" depuis 3 secondes

-

Quand j'appuie sur "Précédent"

-

Alors le contenu "Premier" rejoue depuis le début -Et aucun contenu précédent n'existe

-
-

5. Historique de navigation limité à 10 contenus

-

Étant donné que j'ai écouté 10 contenus [C1, C2, ..., C10] -Et que l'historique Redis contient 10 entrées

-

Quand je passe au contenu C11

-

Alors le contenu C1 est supprimé de l'historique (FIFO) -Et l'historique contient [C2, C3, ..., C10, C11] -Et la taille reste à 10 contenus maximum

-
-

6. Position exacte sauvegardée dans l'historique

-

Étant donné que j'écoute le contenu "A" (durée 5 minutes)

-

Quand j'atteins 2 minutes 30 secondes -Et que j'appuie sur "Suivant"

-

Alors l'historique enregistre:

-
| content_id | position_seconds | listened_at |
-|---|---|---|
-| A | 150 | 2026-01-21T10:30:00 |
-
-

Quand je reviens au contenu "A" via "Précédent"

-

Alors la lecture reprend exactement à 2 minutes 30 secondes

-
-

7. Navigation arrière sur plusieurs contenus

-

Étant donné que j'ai écouté dans l'ordre: A (2min), B (30s), C (3min) -Et que j'écoute maintenant D depuis 1 seconde

-

Quand j'appuie sur "Précédent" (1ère fois)

-

Alors je reviens au contenu C à la position 3 minutes

-

Quand j'appuie sur "Précédent" (<10s sur C)

-

Alors je reviens au contenu B à la position 30 secondes

-

Quand j'appuie sur "Précédent" (<10s sur B)

-

Alors je reviens au contenu A à la position 2 minutes

-
-

8. Précédent après milieu du contenu rejoue depuis début

-

Étant donné que j'écoute un contenu de 5 minutes

-

Quand j'atteins 2 minutes 30 secondes (milieu) -Et que j'appuie sur "Précédent"

-

Alors le contenu actuel rejoue depuis 0:00 -Et je ne reviens pas au contenu précédent

-
-

9. Enchaînement Suivant puis Précédent rapide

-

Étant donné que j'écoute le contenu "A" depuis 1 minute

-

Quand j'appuie sur "Suivant"

-

Alors le contenu "B" démarre

-

Quand j'appuie immédiatement sur "Précédent" (2s après)

-

Alors je reviens au contenu "A" à la position 1 minute -Et le contenu "B" reste dans l'historique

-
-

10. Transition fluide avec animation 0.3s

-

Étant donné que j'appuie sur "Précédent"

-

Quand le changement de contenu se produit

-

Alors la transition audio utilise un fade out/in de 0.3 secondes -Et la progress bar revient avec une animation fluide -Et l'interface ne montre aucun message de confirmation

-
-

11. Historique survit au changement de réseau

-

Étant donné que j'ai un historique de 5 contenus en cache Redis

-

Quand je perds la connexion réseau temporairement -Et que je reviens en ligne

-

Alors l'historique de navigation est toujours disponible -Et je peux toujours utiliser "Précédent"

-
-

12. Historique stocké en Redis avec structure complète

-

Étant donné que j'ai écouté 3 contenus

-

Quand je consulte le cache Redis

-

Alors la structure est: -Et l'ordre est du plus récent au plus ancien

-
-

13. Précédent sur contenu en cours au début (<10s) du premier

-

Étant donné que je démarre une session avec le contenu "Initial" -Et que j'écoute depuis 3 secondes

-

Quand j'appuie sur "Précédent"

-

Alors le contenu "Initial" rejoue depuis le début -Et aucune erreur n'est générée -Et l'historique reste vide

-
-

14. Compteur de temps respecte les seuils exacts

-

Étant donné que j'écoute un contenu

-

Quand le temps écoulé est de 9.9 secondes -Et que j'appuie sur "Précédent"

-

Alors je reviens au contenu précédent

-

Quand le temps écoulé est de 10.0 secondes -Et que j'appuie sur "Précédent"

-

Alors le contenu actuel rejoue depuis le début

-
-

15. Progress bar visuelle reflète le retour exact

-

Étant donné que j'ai écouté le contenu "A" jusqu'à 75% (3min45 sur 5min) -Et que je suis passé au contenu "B"

-

Quand je reviens au contenu "A" via "Précédent"

-

Alors la progress bar affiche 75% -Et l'indicateur de temps affiche "3:45 / 5:00" -Et la lecture reprend exactement à cet endroit

-
-

16. Métadonnées d'historique incluent timestamp précis

-

Étant donné que j'écoute un contenu "X" pendant 45 secondes à 10:30:15

-

Quand je passe au contenu suivant

-

Alors l'historique enregistre:

-
| content_id | position_seconds | listened_at |
-|---|---|---|
-| X | 45 | 2026-01-21T10:30:15Z |
-
-

Et le timestamp précis permet l'analyse d'usage

-
-

17. Suppression FIFO respecte l'ordre chronologique

-

Étant donné un historique de [C1@10:00, C2@10:02, ..., C10@10:20]

-

Quand j'ajoute C11 à 10:22

-

Alors C1 (le plus ancien) est supprimé -Et l'historique contient [C2@10:02, ..., C11@10:22] -Et la taille reste exactement 10 entrées

-
-

18. 📋 Plan: Comportement selon temps écouté

-

Étant donné que j'écoute un contenu depuis secondes

-

Quand j'appuie sur "Précédent"

-

Alors l'action est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tempscomportement
1revenir au contenu précédent
5revenir au contenu précédent
9revenir au contenu précédent
10rejouer le contenu actuel depuis 0:00
11rejouer le contenu actuel depuis 0:00
30rejouer le contenu actuel depuis 0:00
180rejouer le contenu actuel depuis 0:00
-
-

19. 📋 Plan: Positions de reprise exactes

-

Étant donné que j'écoute un contenu de 10 minutes

-

Quand j'atteins et passe au suivant -Et que je reviens via "Précédent"

-

Alors la lecture reprend exactement à

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - -
position
0:15
1:30
3:45
5:00
7:23
9:50
-
-
- -

Commandes vocales CarPlay et Android Auto

-
-

En tant que conducteur avec CarPlay ou Android Auto -Je veux utiliser des commandes vocales pour interagir avec l'application -Afin de garder les mains sur le volant et les yeux sur la route

-
-

25 scénarios (23 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté -Et que CarPlay ou Android Auto est activé

-
-

1. Disponibilité des commandes vocales uniquement avec CarPlay/Android Auto

-

Étant donné que je conduis avec CarPlay activé

-

Quand je dis "Hey Siri"

-

Alors Siri est disponible pour les commandes RoadWave

-

Étant donné que je conduis avec Android Auto activé

-

Quand je dis "OK Google"

-

Alors Google Assistant est disponible pour les commandes RoadWave

-
-

2. Parc automobile compatible avec vocal (30-40% en 2026)

-

Étant donné que nous sommes en 2026

-

Quand je consulte les statistiques du parc automobile EU

-

Alors environ 30-40% des véhicules ont CarPlay ou Android Auto -Et ces utilisateurs peuvent utiliser les commandes vocales -Et les 60-70% restants utilisent les commandes au volant uniquement

-
-

3. Commande vocale "Like ce podcast" avec Siri

-

Étant donné que j'écoute un contenu tagué "Automobile" -Et que ma jauge "Automobile" est à 60%

-

Quand je dis "Hey Siri, like ce podcast"

-

Alors un like explicite (+2%) est enregistré -Et ma jauge "Automobile" passe à 62% -Et Siri confirme vocalement "J'ai ajouté ce contenu à vos favoris" -Et aucune interaction écran n'est requise

-
-

4. Commande vocale "Like ce contenu" avec Google Assistant

-

Étant donné que j'écoute un contenu tagué "Voyage"

-

Quand je dis "OK Google, like ce contenu"

-

Alors un like explicite est enregistré (+2%) -Et Google Assistant confirme "J'ai liké ce contenu pour vous" -Et la commande fonctionne sans toucher l'écran

-
-

5. Commande vocale "Abonne-moi à ce créateur"

-

Étant donné que j'écoute un contenu d'un créateur tagué "Automobile" et "Technologie" -Et que mes jauges sont à 50% et 45%

-

Quand je dis "Hey Siri, abonne-moi à ce créateur"

-

Alors l'abonnement est enregistré -Et mes jauges augmentent de 5% chacune (55% et 50%) -Et Siri confirme "Vous êtes maintenant abonné à [Nom du créateur]"

-
-

6. Commande vocale "Passe au contenu suivant"

-

Étant donné que j'écoute un contenu "A"

-

Quand je dis "Hey Siri, passe au contenu suivant"

-

Alors le contenu "B" démarre immédiatement -Et la commande a le même effet que le bouton physique "Suivant"

-
-

7. Commande vocale "Signale ce contenu"

-

Étant donné que j'écoute un contenu inapproprié

-

Quand je dis "OK Google, signale ce contenu"

-

Alors Google Assistant demande "Quelle catégorie ?" -Et je réponds vocalement "Spam" -Alors le signalement est enregistré avec la catégorie "Spam" -Et Google Assistant confirme "J'ai signalé ce contenu"

-
-

8. Commande vocale avec catégorie de signalement

-

Étant donné que j'écoute un contenu

-

Quand je dis "Hey Siri, signale ce contenu pour haine"

-

Alors le signalement est enregistré avec la catégorie "Haine et violence" -Et Siri confirme "J'ai signalé ce contenu pour haine et violence" -Et le flux de modération reçoit le signalement

-
-

9. Liste des catégories de signalement vocales supportées

-

Étant donné que je dis "signale ce contenu pour [catégorie]"

-

Quand la catégorie est:

-
| Mot-clé vocal | Catégorie mappée |
-|---|---|
-| "haine" | Haine et violence |
-| "sexuel" | Contenu sexuel |
-| "illégalité" | Illégalité |
-| "droits d'auteur" | Droits d'auteur |
-| "spam" | Spam |
-| "fake news" | Désinformation |
-| "autre" | Autre |
-
-

Alors le signalement est enregistré avec la bonne catégorie

-
-

10. Commande vocale non reconnue - fallback

-

Étant donné que je dis "Hey Siri, super ce podcast"

-

Quand Siri ne reconnaît pas l'intent RoadWave

-

Alors Siri répond "Je ne comprends pas cette commande RoadWave" -Et elle suggère "Dites 'like ce podcast' ou 'passe au suivant'"

-
-

11. Commandes vocales disponibles en conduite uniquement

-

Étant donné que je roule à 50 km/h

-

Quand j'utilise les commandes vocales

-

Alors toutes les commandes sont disponibles:

-
| Commande | Action |
-|---|---|
-| "Like ce podcast" | Like explicite +2% |
-| "Abonne-moi à ce créateur" | Abonnement +5% |
-| "Passe au suivant" | Contenu suivant |
-| "Reviens au précédent" | Contenu précédent (règle 10s) |
-| "Pause" | Pause lecture |
-| "Reprends la lecture" | Play |
-| "Signale ce contenu" | Signalement |
-
-
-

12. Intent iOS personnalisé pour RoadWave

-

Étant donné que l'app iOS implémente les Intents

-

Quand je configure les Shortcuts iOS

-

Alors les intents suivants sont disponibles:

-
| Intent Name | Action |
-|---|---|
-| LikeCurrentContentIntent | Like explicite |
-| SubscribeToCreatorIntent | Abonnement |
-| ReportContentIntent | Signalement |
-| SkipToNextContentIntent | Suivant |
-
-

Et Siri les reconnaît automatiquement

-
-

13. Intent Android personnalisé pour RoadWave

-

Étant donné que l'app Android implémente les Voice Actions

-

Quand je configure les actions Google Assistant

-

Alors les actions suivantes sont disponibles:

-
| Action Name | Action |
-|---|---|
-| com.roadwave.LIKE_CONTENT | Like explicite |
-| com.roadwave.SUBSCRIBE_CREATOR | Abonnement |
-| com.roadwave.REPORT_CONTENT | Signalement |
-| com.roadwave.SKIP_NEXT | Suivant |
-
-

Et Google Assistant les reconnaît

-
-

14. Confirmation vocale après action réussie

-

Étant donné que je dis "Hey Siri, like ce podcast"

-

Quand l'action est enregistrée avec succès

-

Alors Siri répond immédiatement avec confirmation: -Et la réponse est naturelle et concise -Et elle ne distrait pas de la conduite

-
-

15. Gestion d'erreur vocale si action échoue

-

Étant donné que je dis "Hey Siri, abonne-moi à ce créateur" -Et que j'ai atteint la limite de 200 abonnements

-

Quand Siri essaie d'enregistrer l'abonnement

-

Alors l'action échoue -Et Siri répond "Impossible de s'abonner, limite de 200 abonnements atteinte" -Et elle suggère "Désabonnez-vous d'un créateur pour continuer"

-
-

16. Commandes vocales multilingues (français)

-

Étant donné que mon Siri est configuré en français

-

Quand je dis "Hey Siri, j'aime ce podcast"

-

Alors la commande est reconnue (variante de "like ce podcast")

-

Quand je dis "Hey Siri, mets une étoile"

-

Alors la commande est reconnue (variante de "like")

-
-

17. Implémentation post-MVP (Sprint 5)

-

Étant donné que les commandes vocales sont une feature Sprint 5

-

Quand le MVP est lancé

-

Alors seules les commandes au volant physiques sont disponibles

-

Quand le Sprint 5 est déployé

-

Alors les intents iOS/Android sont activés -Et les commandes vocales deviennent disponibles

-
-

18. Priorisation commandes vocales vs boutons physiques

-

Étant donné que je conduis avec CarPlay -Et que j'ai accès aux boutons physiques ET aux commandes vocales

-

Quand je veux liker un contenu

-

Alors je peux soit: -Et les 3 méthodes sont valides

-
-

19. Statistiques d'usage des commandes vocales

-

Étant donné que 100 utilisateurs avec CarPlay utilisent RoadWave

-

Quand je consulte les analytics

-

Alors je peux voir:

-
| Métrique | Exemple valeur |
-|---|---|
-| Taux d'utilisation commandes vocal | 15% |
-| Commande la plus utilisée | "Like" |
-| Taux de reconnaissance réussie | 92% |
-| Taux d'échec / incompréhension | 8% |
-
-
-

20. Feedback haptique désactivé pour commandes vocales

-

Étant donné que je like un contenu via commande vocale

-

Quand l'action est enregistrée

-

Alors aucune vibration haptique n'est déclenchée -Et seule la confirmation vocale est donnée

-
-

21. Badge visuel mis à jour après commande vocale

-

Étant donné que je dis "Hey Siri, like ce podcast"

-

Quand l'action est enregistrée

-

Alors le badge "♥ Ajouté à vos favoris" s'affiche sur l'écran CarPlay -Et le cœur devient rouge plein dans l'interface -Et la mise à jour est visible même sans toucher l'écran

-
-

22. Commandes vocales avec contenu sans créateur

-

Étant donné que j'écoute un contenu anonyme (créateur supprimé)

-

Quand je dis "Hey Siri, abonne-moi à ce créateur"

-

Alors Siri répond "Ce créateur n'est plus disponible" -Et aucun abonnement n'est enregistré

-
-

23. Limitation temporelle des commandes vocales

-

Étant donné que je dis "Hey Siri, like ce podcast" -Et que le contenu change 1 seconde après

-

Quand Siri traite la commande 2 secondes plus tard

-

Alors la commande s'applique au contenu qui était en lecture au moment de la commande -Et non au contenu actuel (système de timestamp)

-
-

24. 📋 Plan: Commandes vocales avec différents assistants

-

Étant donné que j'utilise

-

Quand je dis

-

Alors l'action est exécutée -Et la confirmation est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
assistantcommandeactionconfirmation
Siri"Like ce podcast"Like +2%"Ajouté à vos favoris"
Google Assistant"Like ce contenu"Like +2%"J'ai liké ce contenu"
Siri"Abonne-moi à ce créateur"Abonnement +5%"Vous êtes abonné"
Google Assistant"Abonne-moi à ce créateur"Abonnement +5%"Abonnement enregistré"
Siri"Signale ce contenu"Signalement"J'ai signalé ce contenu"
Google Assistant"Signale ce contenu"Signalement"Contenu signalé"
-
-

25. 📋 Plan: Mapping catégories signalement vocal

-

Étant donné que je dis "signale ce contenu pour "

-

Quand est reconnu

-

Alors la catégorie mappée est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
mot_clecategorie
haineHaine et violence
violenceHaine et violence
sexuelContenu sexuel
pornoContenu sexuel
illégalIllégalité
terrorismeIllégalité
copyrightDroits d'auteur
droits auteurDroits d'auteur
spamSpam
fake newsDésinformation
fausse infoDésinformation
-
-
- -

Commandes au volant et interactions simplifiées

-
-

En tant que conducteur en sécurité -Je veux utiliser uniquement les commandes simplifiées au volant -Afin de naviguer sans distraction et en toute sécurité

-
-

21 scénarios (19 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté -Et que l'application est connectée via CarPlay ou Android Auto

-
-

1. Trois commandes disponibles au volant uniquement

-

Étant donné que je conduis à 50 km/h

-

Quand je consulte les commandes physiques disponibles

-

Alors seules 3 actions sont disponibles:

-
| Commande | Action |
-|---|---|
-| Suivant | Passer au contenu suivant |
-| Précédent | Revenir au précédent (règle 10s) |
-| Play/Pause | Pause/reprise avec fade 0.3s |
-
-

Et aucune commande complexe n'est proposée

-
-

2. Commande "Suivant" au volant

-

Étant donné que j'écoute un contenu "A"

-

Quand j'appuie sur le bouton physique "Suivant" au volant

-

Alors le contenu "B" démarre immédiatement -Et aucune action supplémentaire n'est requise -Et l'interface ne demande aucune confirmation

-
-

3. Commande "Précédent" au volant respecte règle 10s

-

Étant donné que j'écoute un contenu depuis 5 secondes

-

Quand j'appuie sur "Précédent" au volant

-

Alors je reviens au contenu précédent (règle <10s)

-

Étant donné que j'écoute un contenu depuis 15 secondes

-

Quand j'appuie sur "Précédent" au volant

-

Alors le contenu actuel rejoue depuis le début (règle ≥10s)

-
-

4. Commande "Play/Pause" avec fade audio

-

Étant donné qu'un contenu est en lecture

-

Quand j'appuie sur "Pause" au volant

-

Alors la lecture se met en pause avec un fade out de 0.3 secondes -Et la position de lecture est sauvegardée

-

Quand j'appuie sur "Play" au volant

-

Alors la lecture reprend avec un fade in de 0.3 secondes -Et la reprise se fait à la position exacte

-
-

5. Aucune commande complexe supportée

-

Étant donné que je conduis

-

Quand j'essaie un appui long sur "Suivant"

-

Alors l'action n'est pas détectée (non supporté iOS/Android)

-

Quand j'essaie un double-appui sur "Pause"

-

Alors l'action n'est pas détectée -Et seules les actions simples (clic simple) fonctionnent

-
-

6. Compatibilité 100% tous véhicules

-

Étant donné que je conduis une voiture avec commandes basiques -Et que mon véhicule a seulement Suivant/Précédent/Pause

-

Quand j'utilise RoadWave

-

Alors toutes les fonctions essentielles sont accessibles -Et je n'ai pas besoin de boutons supplémentaires

-
-

7. Feedback visuel discret après action

-

Étant donné que j'appuie sur "Suivant"

-

Quand le contenu change

-

Alors l'interface CarPlay/Android Auto affiche le nouveau titre -Et aucune popup ne bloque la vue -Et le changement est fluide et immédiat

-
-

8. Like automatique renforcé après écoute ≥80%

-

Étant donné que j'écoute un contenu de 5 minutes tagué "Automobile"

-

Quand j'écoute pendant 4 minutes 30 secondes (90%)

-

Alors un like automatique renforcé (+2 points) est enregistré -Et un badge discret "♥ Ajouté à vos favoris" s'affiche 2 secondes -Et aucune action manuelle n'est requise

-
-

9. Like automatique standard après écoute 30-79%

-

Étant donné que j'écoute un contenu de 5 minutes tagué "Voyage"

-

Quand j'écoute pendant 2 minutes (40%) -Et que j'appuie sur "Suivant"

-

Alors un like automatique standard (+1 point) est enregistré -Et un badge discret s'affiche brièvement -Et je peux continuer à conduire sans interruption

-
-

10. Signal négatif après skip rapide <10s

-

Étant donné que j'écoute un contenu tagué "Politique"

-

Quand j'appuie sur "Suivant" après seulement 5 secondes

-

Alors un signal négatif (-0.5 point) est enregistré -Et la jauge "Politique" diminue légèrement -Et aucun message n'est affiché (transparence)

-
-

11. Pas de like si écoute <30%

-

Étant donné que j'écoute un contenu de 10 minutes

-

Quand j'écoute pendant 2 minutes (20%) -Et que j'appuie sur "Suivant"

-

Alors aucun like n'est enregistré -Et les jauges ne changent pas -Et le système considère l'écoute comme neutre

-
-

12. Badge de feedback visuel disparaît après 2 secondes

-

Étant donné que je reçois un like automatique

-

Quand le badge "♥ Ajouté à vos favoris" apparaît

-

Alors il reste visible 2 secondes en bas de l'écran -Et il disparaît automatiquement sans action -Et il ne bloque pas la vue de la route

-
-

13. Tracking du temps d'écoute précis côté client

-

Étant donné que je démarre la lecture d'un contenu

-

Quand le player audio iOS/Android enregistre le temps

-

Alors le startTime est enregistré à la milliseconde

-

Quand j'arrête la lecture (Suivant, Pause, ou fin)

-

Alors la durée exacte écoutée est calculée -Et le pourcentage (durée / durée_totale * 100) est envoyé à l'API

-
-

14. API reçoit les événements d'écoute pour calcul

-

Étant donné que j'écoute un contenu de 5 minutes à 80%

-

Quand l'événement est envoyé à l'API

-

Alors le backend reçoit: -Et le backend calcule le like automatique (+2 points) -Et les jauges sont mises à jour immédiatement (Redis + PostgreSQL)

-
-

15. Actions différentes selon arrêt du contenu

-

Étant donné que j'écoute un contenu

-

Quand j'appuie sur "Suivant"

-

Alors l'action envoyée est "skipped"

-

Quand le contenu se termine naturellement

-

Alors l'action envoyée est "completed"

-

Quand j'appuie sur "Pause"

-

Alors l'action envoyée est "paused" -Et le backend traite chaque action différemment

-
-

16. Calcul immédiat côté backend sans délai

-

Étant donné que l'API reçoit un événement d'écoute

-

Quand le backend traite l'événement

-

Alors les jauges sont mises à jour immédiatement (< 100ms) -Et les nouvelles recommandations utilisent les valeurs actualisées -Et il n'y a aucun batch différé

-
-

17. Compatibilité iOS avec AVPlayer

-

Étant donné que l'app iOS utilise AVPlayer

-

Quand les commandes physiques sont interceptées

-

Alors les événements MPRemoteCommandCenter sont capturés:

-
| Commande | Événement iOS |
-|---|---|
-| Suivant | nextTrackCommand |
-| Précédent | previousTrackCommand |
-| Play/Pause | playCommand / pauseCommand |
-
-

Et le tracking du temps utilise CMTime

-
-

18. Compatibilité Android avec MediaSession

-

Étant donné que l'app Android utilise MediaPlayer

-

Quand les commandes physiques sont interceptées

-

Alors les événements MediaSession sont capturés:

-
| Commande | Action Android |
-|---|---|
-| Suivant | ACTION_SKIP_TO_NEXT |
-| Précédent | ACTION_SKIP_TO_PREVIOUS |
-| Play/Pause | ACTION_PLAY / ACTION_PAUSE |
-
-

Et le tracking du temps utilise SystemClock.elapsedRealtime()

-
-

19. Sécurité maximale - pas de distraction

-

Étant donné que je conduis à 80 km/h

-

Quand j'utilise RoadWave avec les commandes au volant

-

Alors je n'ai jamais besoin de regarder mon téléphone -Et je n'ai jamais besoin de toucher l'écran CarPlay/Android Auto -Et toutes les actions sont accessibles via boutons physiques -Et les likes sont enregistrés automatiquement

-
-

20. 📋 Plan: Calcul du like automatique selon pourcentage

-

Étant donné que j'écoute un contenu tagué "Sport"

-

Quand j'écoute pendant %

-

Alors le like automatique est -Et l'impact sur la jauge est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pourcentagetypepoints
10aucun0
25aucun0
29aucun0
30standard+1
50standard+1
79standard+1
80renforcé+2
90renforcé+2
100renforcé+2
-
-

21. 📋 Plan: Signal négatif uniquement si skip très rapide

-

Étant donné que j'écoute un contenu

-

Quand je skip après secondes

-

Alors le signal est -Et l'impact est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
secondestypepoints
3négatif-0.5
5négatif-0.5
9négatif-0.5
10neutre0
15neutre0
30neutre0
-
-
- -

File d'attente et commande "Suivant"

-
-

En tant qu'auditeur en déplacement -Je veux que l'application pré-calcule intelligemment les prochains contenus -Afin d'avoir une navigation fluide sans latence

-
-

20 scénarios (19 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté -Et que la géolocalisation est activée

-
-

1. Pré-calcul initial de 5 contenus en cache

-

Étant donné que je viens de démarrer l'application -Et que je suis situé à Paris (48.8566, 2.3522) -Et que je suis en mode voiture (vitesse ≥ 5 km/h)

-

Quand l'application initialise la lecture

-

Alors une file d'attente de 5 contenus est pré-calculée -Et la file est stockée en cache Redis avec la clé "user:{user_id}:queue" -Et les métadonnées incluent ma position, le timestamp de calcul et le mode -Et le cache a un TTL de 15 minutes

-
-

2. Commande "Suivant" sans latence

-

Étant donné qu'une file d'attente de 5 contenus est en cache -Et que j'écoute actuellement le contenu "A"

-

Quand j'appuie sur le bouton "Suivant"

-

Alors le contenu suivant démarre immédiatement (< 100ms) -Et le contenu est retiré de la file d'attente -Et il reste 4 contenus dans la file

-
-

3. Recalcul automatique après déplacement >10km

-

Étant donné que la file a été calculée à Paris (48.8566, 2.3522) -Et que j'ai 5 contenus en cache

-

Quand je me déplace à Versailles (48.8049, 2.1204) soit 12km

-

Alors la file d'attente est invalidée automatiquement -Et une nouvelle file de 5 contenus est recalculée -Et elle est basée sur ma nouvelle position

-
-

4. Recalcul automatique toutes les 10 minutes

-

Étant donné qu'une file a été calculée il y a 10 minutes -Et que ma position n'a pas changé

-

Quand le timer de rafraîchissement expire

-

Alors une nouvelle file de 5 contenus est recalculée -Et les anciens contenus non écoutés sont remplacés -Et les nouveaux contenus publiés depuis sont inclus

-
-

5. Recalcul quand il reste moins de 3 contenus

-

Étant donné qu'il reste 3 contenus dans ma file d'attente

-

Quand j'appuie sur "Suivant"

-

Alors il reste 2 contenus -Et un recalcul asynchrone est déclenché en arrière-plan -Et 3 nouveaux contenus sont ajoutés à la file -Et la file contient maintenant 5 contenus

-
-

6. Insertion prioritaire d'un contenu géolocalisé en mode voiture

-

Étant donné que j'ai une file de 5 contenus pré-calculée -Et que je suis en mode voiture -Et que je me déplace à 50 km/h vers un point avec contenu géolocalisé

-

Quand je suis à 98m du point (ETA = 7 secondes)

-

Alors une notification est envoyée (icône + compteur 7→1 + son) -Et je dois appuyer sur "Suivant" dans les 7 secondes pour valider

-

Quand j'appuie sur "Suivant"

-

Alors un décompte de 5 secondes démarre -Et après 5 secondes, le contenu géolocalisé s'insère et démarre -Et il remplace le contenu actuel dans la lecture

-
-

7. Contenu géolocalisé ignoré est perdu (cooldown activé)

-

Étant donné qu'une notification géolocalisée est affichée (compteur 7→1)

-

Quand je ne clique pas sur "Suivant" pendant les 7 secondes

-

Alors la notification disparaît -Et le contenu géolocalisé est perdu (pas d'insertion dans la file) -Et un cooldown de 10 minutes est activé -Et aucune nouvelle notification géolocalisée ne sera envoyée pendant 10 minutes

-
-

8. Validation d'une notification géolocalisée

-

Étant donné qu'une notification géolocalisée est affichée (compteur à 5) -Et que j'écoute un podcast

-

Quand j'appuie sur "Suivant"

-

Alors le compteur bascule à "5" (décompte final) -Et le podcast actuel continue de jouer -Et après 5 secondes, le contenu géolocalisé démarre -Et le podcast est mis en pause et sauvegardé dans l'historique

-
-

9. Invalidation immédiate après modification des préférences

-

Étant donné que j'ai une file de 5 contenus en cache -Et que ma vitesse GPS est de 5 km/h (piéton)

-

Quand je modifie mes curseurs de préférences (géo/découverte/politique)

-

Alors la file d'attente est invalidée immédiatement -Et une nouvelle file est recalculée avec les nouvelles préférences -Et les anciens contenus en cache sont supprimés

-
-

10. Blocage modification préférences en conduite (>10 km/h)

-

Étant donné que ma vitesse GPS est de 50 km/h (en voiture)

-

Quand j'essaie d'accéder aux réglages de préférences

-

Alors l'interface affiche "Paramètres verrouillés en conduite" -Et je ne peux pas modifier les curseurs géo/découverte/politique -Et un message "Arrêtez-vous pour modifier vos préférences" s'affiche

-
-

11. Invalidation lors du démarrage d'un live suivi

-

Étant donné que je suis abonné au créateur "RadioVoyage" -Et que j'ai une file de 5 contenus en cache -Et que je suis dans la zone géographique du créateur

-

Quand le créateur "RadioVoyage" démarre une radio live

-

Alors je reçois une notification push -Et le contenu live s'insère en tête de la file d'attente -Et la file d'attente est recalculée

-
-

12. Métadonnées de cache Redis

-

Étant donné qu'une file d'attente est calculée

-

Quand elle est stockée dans Redis

-

Alors la clé est "user:{user_id}:queue" -Et les métadonnées incluent:

-
| champ | valeur |
-|---|---|
-| last_lat | 48.8566 |
-| last_lon | 2.3522 |
-| computed_at | 2026-01-21T10:30:00Z |
-| mode | voiture |
-
-

Et le TTL est de 15 minutes (900 secondes)

-
-

13. Contenu géolocalisé remplace le contenu actuel (pas d'insertion en file)

-

Étant donné que j'écoute le contenu C2 de ma file [C1, C2, C3, C4, C5] -Et qu'une notification géolocalisée "Tour Eiffel" est déclenchée

-

Quand je valide la notification -Et que le décompte de 5s se termine

-

Alors le contenu "Tour Eiffel" remplace C2 et démarre -Et C2 est sauvegardé dans l'historique de navigation -Et la file reste [C3, C4, C5] (pas de contenu retiré) -Et quand "Tour Eiffel" se termine, C3 démarre

-
-

14. Invalidation après déplacement exactement 10km

-

Étant donné que la file a été calculée à une position donnée

-

Quand je me déplace d'exactement 10.0 km

-

Alors la file d'attente n'est PAS invalidée (seuil strict >10km) -Et les contenus en cache restent valides

-

Quand je me déplace de 10.1 km supplémentaires (total 10.1km)

-

Alors la file d'attente est invalidée -Et une nouvelle file est calculée

-
-

15. Rafraîchissement exactement après 10 minutes

-

Étant donné qu'une file a été calculée à 10:00:00

-

Quand l'heure actuelle est 10:10:00

-

Alors le timer de rafraîchissement expire -Et une nouvelle file de 5 contenus est recalculée -Et le timestamp "computed_at" est mis à jour

-
-

16. Recalcul asynchrone non-bloquant

-

Étant donné qu'il reste 2 contenus dans la file -Et que j'appuie sur "Suivant"

-

Quand le recalcul asynchrone démarre

-

Alors la lecture du contenu actuel n'est pas interrompue -Et le recalcul se fait en arrière-plan -Et les nouveaux contenus sont ajoutés dès disponibles (< 500ms) -Et l'utilisateur ne perçoit aucune latence

-
-

17. Notification basée sur ETA (pas distance fixe)

-

Étant donné qu'un contenu géolocalisé existe à un point GPS -Et que je roule à 130 km/h

-

Quand je suis à 252m du point (ETA = 7 secondes)

-

Alors une notification est envoyée

-

Quand je suis à 300m du point (ETA = 8 secondes)

-

Alors aucune notification n'est envoyée (ETA >7s)

-
-

18. 📋 Plan: Différentes distances de déplacement et invalidation

-

Étant donné qu'une file a été calculée à une position donnée

-

Quand je me déplace de km

-

Alors la file est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
distanceaction
5conservée
9.9conservée
10.0conservée
10.1invalidée et recalculée
15invalidée et recalculée
50invalidée et recalculée
-
-

19. Quota de 6 contenus géolocalisés par heure

-

Étant donné que j'ai validé 6 notifications géolocalisées dans la dernière heure

-

Quand un 7ème contenu géolocalisé est détecté (ETA 7s)

-

Alors aucune notification n'est envoyée -Et le quota horaire est respecté

-
-

20. Mode piéton - pas de notification avec compteur 7s

-

Étant donné que je suis en mode piéton (vitesse <5 km/h) -Et qu'un audio-guide géolocalisé existe à 150m

-

Quand je passe dans le rayon de 200m

-

Alors une notification push système est envoyée -Et aucun compteur 7s n'est affiché -Et je peux ouvrir l'app en tapant sur la notification

-
-
- -

Lecture en boucle et enchaînement automatique

-
-

En tant qu'auditeur -Je veux que les contenus s'enchaînent automatiquement avec un délai paramétrable -Afin d'avoir une expérience fluide sans interruption

-
-

27 scénarios (24 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur est connecté

-
-

1. Passage automatique après 2 secondes (mode standard)

-

Étant donné que j'écoute un contenu "A" en mode standard

-

Quand la lecture se termine naturellement

-

Alors un timer de 2 secondes démarre -Et un overlay s'affiche: "Contenu suivant dans 2s..." -Et une barre de décompte visuelle s'affiche

-

Quand le timer atteint 0

-

Alors le contenu "B" démarre automatiquement -Et l'overlay disparaît

-
-

2. Passage automatique après 1 seconde (mode Kids)

-

Étant donné que je suis en mode Kids -Et que j'écoute un contenu pour enfants

-

Quand la lecture se termine

-

Alors un timer de 1 seconde démarre -Et le message "Contenu suivant dans 1s..." s'affiche

-

Quand le timer expire

-

Alors le contenu suivant démarre automatiquement

-
-

3. Passage immédiat après une radio live (0 seconde)

-

Étant donné que j'écoute une radio live

-

Quand le créateur arrête la diffusion

-

Alors le passage au contenu suivant est immédiat (0s de délai) -Et aucun overlay de décompte n'est affiché -Et la transition est fluide

-
-

4. Annulation du passage automatique

-

Étant donné qu'un contenu se termine -Et que le timer de 2 secondes démarre

-

Quand je clique sur "Rester sur ce contenu" pendant le décompte

-

Alors le timer est annulé -Et le contenu actuel reste en pause à la fin -Et le contenu suivant n'est pas lancé

-
-

5. Insertion de publicité pendant le délai de transition

-

Étant donné que j'ai écouté 4 contenus sans publicité -Et que le 5ème contenu se termine

-

Quand le délai de 2 secondes démarre

-

Alors une publicité s'insère dans la file d'attente -Et le message devient "Publicité (15s)" -Et la publicité démarre après les 2 secondes -Et elle ne coupe jamais un contenu en cours

-
-

6. Fréquence de publicité paramétrable admin

-

Étant donné que la fréquence pub est configurée à "1/5 contenus"

-

Quand j'écoute 10 contenus

-

Alors 2 publicités sont insérées (après les contenus 5 et 10)

-

Étant donné que l'admin change la fréquence à "1/3 contenus"

-

Quand j'écoute 9 contenus

-

Alors 3 publicités sont insérées (après les contenus 3, 6 et 9)

-
-

7. Publicité skippable après 5 secondes par défaut

-

Étant donné qu'une publicité de 30 secondes démarre -Et que le délai minimal de visionnage est configuré à 5 secondes

-

Quand j'écoute pendant 3 secondes

-

Alors le bouton "Passer" n'est pas encore visible

-

Quand j'atteins 5 secondes d'écoute

-

Alors le bouton "Passer" apparaît -Et je peux cliquer pour passer au contenu suivant

-
-

8. Délai minimal de publicité paramétrable admin

-

Étant donné qu'une publicité démarre -Et que l'admin a configuré le délai à 10 secondes

-

Quand j'écoute pendant 9 secondes

-

Alors le bouton "Passer" n'est pas visible

-

Quand j'atteins 10 secondes

-

Alors le bouton "Passer" apparaît -Et je peux skipper la publicité

-
-

9. Like et abonnement autorisés sur une publicité

-

Étant donné qu'une publicité est en lecture

-

Quand je clique sur le bouton cœur (véhicule arrêté)

-

Alors la publicité reçoit un like (+2% jauges tags pub)

-

Quand je clique sur "S'abonner" au créateur de la pub

-

Alors je suis abonné (+5% jauges tags créateur) -Et le créateur de pub bénéficie de l'engagement

-
-

10. Métriques d'engagement publicité trackées

-

Étant donné qu'une publicité de 30s est diffusée à 100 auditeurs

-

Quand 40 auditeurs écoutent entièrement (30s) -Et que 50 auditeurs skippent après 10s -Et que 10 auditeurs skippent avant 5s

-

Alors les métriques sont:

-
| Métrique | Valeur |
-|---|---|
-| Taux d'écoute complète | 40% |
-| Taux de skip après seuil | 50% |
-| Taux de skip immédiat | 10% |
-| Durée moyenne d'écoute | 18s |
-
-
-

11. Message "Aucun contenu disponible" si file vide

-

Étant donné que la file d'attente est vide -Et qu'aucun contenu n'est disponible dans ma zone

-

Quand le contenu actuel se termine

-

Alors un message s'affiche: "Aucun contenu disponible dans cette zone" -Et une proposition apparaît: "Élargir la zone de recherche ?" -Et un bouton "Élargir" est disponible -Et la lecture se met en pause automatiquement

-
-

12. Élargissement automatique de la zone de recherche

-

Étant donné que le message "Aucun contenu disponible" s'affiche

-

Quand je clique sur "Élargir la zone"

-

Alors l'algorithme relance une recherche avec rayon +50km -Et une notification "Recherche élargie à 50km" s'affiche -Et la file d'attente est recalculée -Et la lecture reprend automatiquement

-
-

13. Refus d'élargissement laisse en pause

-

Étant donné que le message "Aucun contenu disponible" s'affiche

-

Quand je clique sur "Annuler"

-

Alors la lecture reste en pause -Et l'interface affiche "En attente de contenu" -Et je peux manuellement naviguer ou chercher du contenu

-
-

14. Retry avec backoff exponentiel en cas d'échec réseau

-

Étant donné que le contenu suivant échoue au chargement

-

Quand la première tentative échoue

-

Alors le système retente après 1 seconde (backoff 1s)

-

Quand la 2ème tentative échoue

-

Alors le système retente après 2 secondes (backoff 2s)

-

Quand la 3ème tentative échoue

-

Alors le système retente après 4 secondes (backoff 4s) -Et après 3 échecs totaux, le système bascule en mode offline

-
-

15. Basculement mode offline après 3 échecs réseau

-

Étant donné que j'ai eu 3 échecs de chargement consécutifs

-

Quand le 3ème échec se produit

-

Alors un message "Connexion instable, basculement mode offline" s'affiche -Et la lecture continue avec les contenus téléchargés uniquement -Et les contenus en ligne sont temporairement désactivés

-

Quand la connexion revient

-

Alors le mode en ligne est automatiquement rétabli

-
-

16. Overlay de décompte avec barre visuelle

-

Étant donné qu'un contenu se termine

-

Quand le timer de 2 secondes démarre

-

Alors un overlay semi-transparent s'affiche en bas de l'écran -Et le texte "Contenu suivant dans 2s..." est visible -Et une barre de progression décroît de 100% à 0% en 2 secondes -Et la couleur de la barre passe de vert à orange -Et l'overlay disparaît automatiquement après le décompte

-
-

17. Bouton "Rester sur ce contenu" pendant décompte

-

Étant donné que le décompte de 2 secondes est actif

-

Quand l'overlay s'affiche

-

Alors un bouton "Rester sur ce contenu" est visible -Et il est cliquable pendant les 2 secondes

-

Quand je clique dessus

-

Alors le timer est annulé immédiatement -Et l'overlay disparaît -Et le contenu actuel reste affiché en pause

-
-

18. Pas d'interruption d'un contenu en cours

-

Étant donné que j'écoute un contenu de 10 minutes -Et que je suis à 5 minutes de lecture

-

Quand une publicité devrait s'insérer (fréquence 1/5)

-

Alors la publicité n'interrompt jamais le contenu en cours -Et elle attend la fin du contenu actuel -Et elle s'insère pendant le délai de transition (2s)

-
-

19. Publicités uniquement pour utilisateurs gratuits

-

Étant donné que je suis un utilisateur gratuit

-

Quand j'écoute 5 contenus

-

Alors une publicité est insérée après le 5ème contenu

-

Étant donné que je passe en compte Premium

-

Quand j'écoute 100 contenus

-

Alors aucune publicité n'est insérée -Et l'enchaînement est direct (2s de transition seulement)

-
-

20. Message clair pour l'utilisateur lors de la publicité

-

Étant donné qu'une publicité va démarrer

-

Quand le délai de transition démarre

-

Alors le message affiché est: "Publicité (15s)" -Et la durée totale de la pub est indiquée -Et l'utilisateur sait qu'il s'agit d'une pub -Et la transparence est maximale

-
-

21. Transition fluide entre contenus sans coupure

-

Étant donné qu'un contenu se termine -Et que le suivant est pré-chargé en cache

-

Quand le timer de 2s expire

-

Alors la transition audio utilise un crossfade de 0.3s -Et il n'y a aucun blanc ou coupure -Et l'expérience est fluide

-
-

22. Gestion des erreurs de chargement avec retry

-

Étant donné que le contenu suivant échoue au chargement

-

Quand la 1ère tentative échoue

-

Alors une notification "Chargement..." s'affiche -Et le système retente automatiquement

-

Quand la 2ème tentative réussit

-

Alors la lecture démarre normalement -Et aucune action utilisateur n'est requise

-
-

23. Mode offline après échecs multiples

-

Étant donné que j'ai 50 contenus téléchargés en mode offline -Et que j'ai eu 3 échecs réseau consécutifs

-

Quand le mode offline s'active

-

Alors seuls les contenus téléchargés sont disponibles -Et un badge "Mode offline" s'affiche en haut de l'écran -Et la lecture continue sans interruption

-
-

24. Compteur de contenus avant prochaine publicité

-

Étant donné que la fréquence pub est 1/5 contenus -Et que j'ai écouté 3 contenus depuis la dernière pub

-

Quand je consulte l'interface

-

Alors un indicateur discret affiche "2 contenus avant pub" -Et l'utilisateur sait quand attendre la prochaine publicité

-
-

25. 📋 Plan: Délai de transition selon mode

-

Étant donné que je suis en mode

-

Quand un contenu se termine

-

Alors le délai de transition est secondes -Et le message affiché est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
modedelaimessage
Standard2"Contenu suivant dans 2s..."
Kids1"Contenu suivant dans 1s..."
Live0(aucun message)
-
-

26. 📋 Plan: Fréquence d'insertion des publicités

-

Étant donné que la fréquence pub est configurée à

-

Quand j'écoute contenus

-

Alors publicités sont insérées

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
frequencecontenuspubs
1/362
1/393
1/5102
1/5153
1/7142
1/7213
-
-

27. 📋 Plan: Backoff exponentiel retry

-

Étant donné que le chargement échoue

-

Quand je suis à la tentative

-

Alors le délai de retry est secondes

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
tentativedelai
11
22
34
-
-
- -

Partage de contenu

-
-

En tant qu'utilisateur de RoadWave -Je veux pouvoir partager du contenu audio -Afin de faire découvrir l'application à d'autres personnes

-
-

22 scénarios (20 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté

-
-

1. Bouton partager disponible dans le player en lecture

-

Étant donné que le contenu "Balade à Paris" est en cours de lecture

-

Quand l'utilisateur consulte les contrôles du player

-

Alors le bouton "Partager" ⬆️ est visible

-
-

2. Bouton partager disponible sur la page profil créateur

-

Étant donné que l'utilisateur consulte le profil de "@paris_stories"

-

Quand l'utilisateur consulte un contenu dans la liste

-

Alors le bouton "Partager" est disponible pour chaque contenu

-
-

3. Bouton partager dans la liste de recherche

-

Étant donné que l'utilisateur effectue une recherche "voyage paris"

-

Quand l'utilisateur ouvre le menu contextuel d'un résultat

-

Alors l'option "Partager" est disponible

-
-

4. Bouton partager dans l'historique personnel

-

Étant donné que l'utilisateur consulte son historique d'écoute

-

Quand l'utilisateur sélectionne un contenu de l'historique

-

Alors le bouton "Partager" est accessible

-
-

5. 📋 Plan: Menu de partage avec options multiples

-

Étant donné que le contenu "" est disponible

-

Quand l'utilisateur clique sur le bouton "Partager"

-

Alors le menu natif OS s'ouvre -Et les options suivantes sont disponibles:

-
| option |
-|---|
-| Copier le lien |
-| WhatsApp |
-| Email |
-| SMS |
-| Plus... |
-
-

📊 Exemples de données:

- - - - - - - - - - - - - - -
contenu
Balade à Paris
Secrets de Montmartre
-
-

6. Génération du lien de partage

-

Étant donné un contenu avec l'ID "content_12345"

-

Quand l'utilisateur copie le lien de partage

-

Alors le lien généré est "https://roadwave.fr/share/c/content_12345"

-
- -

Étant donné que l'application RoadWave est installée sur l'appareil -Et qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

-

Quand l'utilisateur clique sur le lien

-

Alors l'application RoadWave s'ouvre automatiquement -Et le contenu "content_12345" commence à jouer

-
-

8. Ouverture du lien partagé sans l'application installée (Web player)

-

Étant donné que l'application RoadWave n'est pas installée -Et qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

-

Quand l'utilisateur clique sur le lien

-

Alors une page web responsive s'affiche -Et le web player HTML5 est visible -Et les boutons de téléchargement App Store et Google Play sont affichés

-
-

9. Contenu de la page web de partage

-

Étant donné un contenu public avec les métadonnées suivantes:

-
| champ | valeur |
-|---|---|
-| titre | Balade à Paris |
-| créateur | @paris_stories |
-| durée | 12 min |
-| écoutes | 2300 |
-| localisation | Paris 5e |
-| type_geo | Ancré |
-| tags | Voyage, Histoire |
-
-

Quand la page de partage est affichée

-

Alors la page contient:

-
| élément |
-|---|
-| Cover image 16:9 |
-| Titre "Balade à Paris" |
-| "@paris_stories" |
-| "12 min · 🎧 2.3K" |
-| "📍 Paris 5e · Ancré" |
-| "🏷️ #Voyage #Histoire" |
-| Description |
-| Player HTML5 |
-| Bouton App Store |
-| Bouton Google Play |
-
-
-

10. Métadonnées Open Graph pour partage social

-

Étant donné un contenu "Balade à Paris" par "@paris_stories"

-

Quand la page de partage est générée

-

Alors les métadonnées Open Graph incluent:

-
| propriété | valeur |
-|---|---|
-| og:title | Balade à Paris - RoadWave |
-| og:description | Écoutez ce contenu par @paris_stories |
-| og:type | music.song |
-| og:site_name | RoadWave |
-| twitter:card | player |
-
-

Et l'aperçu s'affiche correctement sur WhatsApp -Et l'aperçu s'affiche correctement sur Facebook -Et l'aperçu s'affiche correctement sur Twitter

-
-

11. 📋 Plan: Deep linking par plateforme

-

Étant donné que l'application RoadWave est installée sur -Et qu'un lien de partage est ouvert

-

Quand le système détecte l'application

-

Alors l'application s'ouvre via

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - -
plateformemécanisme
iOSUniversal Links
AndroidApp Links
-
-

12. Fallback URL scheme pour deep linking

-

Étant donné que les App Links ne fonctionnent pas

-

Quand le système tente d'ouvrir le contenu

-

Alors l'URL scheme "roadwave://content/content_12345" est utilisé

-
-

13. Badge Premium visible sur le lien partagé

-

Étant donné un contenu Premium "Visite VIP Louvre"

-

Quand l'utilisateur non-premium clique sur le lien partagé

-

Alors la page web affiche le badge "👑 Contenu Premium"

-
-

14. Preview 30 secondes d'un contenu Premium partagé

-

Étant donné un contenu Premium "Visite VIP Louvre" de 15 minutes -Et qu'un utilisateur non-premium ouvre le lien partagé

-

Quand le player démarre automatiquement

-

Alors l'audio joue pendant 30 secondes exactement -Et un fade out de 2 secondes est appliqué -Et un overlay "Contenu réservé Premium" s'affiche après 32 secondes

-
-

15. Contenu de l'overlay paywall Premium

-

Étant donné qu'un contenu Premium a atteint la limite de 30 secondes

-

Quand l'overlay paywall s'affiche

-

Alors le texte suivant est visible:

-
-

16. Actions disponibles sur l'overlay Premium

-

Étant donné que l'overlay paywall Premium est affiché

-

Quand l'utilisateur consulte les options

-

Alors les actions suivantes sont disponibles:

-
| action | comportement |
-|---|---|
-| Passer Premium | Redirection vers paiement Mangopay web |
-| Télécharger l'app | Redirection vers App Store/Google Play |
-| Rejouer les 30 premières sec | Relecture illimitée du preview |
-
-
-

17. Relecture illimitée du preview Premium

-

Étant donné un contenu Premium partagé -Et que l'utilisateur a écouté les 30 premières secondes

-

Quand l'utilisateur clique sur "Rejouer"

-

Alors les 30 premières secondes sont rejouées -Et cette action est possible de manière illimitée

-
-

18. Tracking des partages Premium

-

Étant donné un créateur "@guide_louvre" avec un contenu Premium

-

Quand son contenu est partagé

-

Alors les métriques suivantes sont enregistrées:

-
| métrique | valeur |
-|---|---|
-| Partages Premium | +1 |
-| Ouvertures lien | compteur |
-| Conversions Premium | si souscription |
-
-
-

19. Rémunération créateur sur conversion Premium via partage

-

Étant donné un contenu Premium partagé par "@guide_louvre"

-

Quand un utilisateur s'abonne via le lien partagé

-

Alors le créateur reçoit 70% des revenus de cet abonnement -Et la conversion est trackée dans son dashboard

-
-

20. Partage d'un contenu supprimé

-

Étant donné qu'un lien de partage "https://roadwave.fr/share/c/deleted_content" est ouvert -Et que le contenu n'existe plus

-

Quand la page web se charge

-

Alors un message "Ce contenu n'est plus disponible" s'affiche -Et les boutons de téléchargement de l'app sont affichés

-
-

21. Partage d'un contenu en attente de modération

-

Étant donné un contenu en cours de validation modération

-

Quand un lien de partage est ouvert

-

Alors le message "Ce contenu est en cours de validation" s'affiche

-
-

22. Génération du lien hors connexion

-

Étant donné que l'utilisateur n'a pas de connexion réseau

-

Quand l'utilisateur tente de partager un contenu

-

Alors le lien est copié dans le presse-papiers -Et un message "Lien copié (nécessite connexion pour ouvrir)" s'affiche

-
-
- -

Avantages Premium

-
-

En tant qu'abonné Premium -Je veux bénéficier d'avantages exclusifs -Afin de profiter d'une expérience audio améliorée sans publicité

-
-

37 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis connecté à l'application RoadWave

-
-

1. Utilisateur gratuit voit 1 publicité tous les 5 contenus

-

Étant donné que je suis un utilisateur gratuit

-

Quand j'écoute ma file de contenus

-

Alors je vois une publicité tous les 5 contenus -Et la publicité dure 30 secondes en moyenne -Et je ne peux pas la skip

-
-

2. Utilisateur Premium ne voit aucune publicité

-

Étant donné que je suis un utilisateur Premium

-

Quand j'écoute mes contenus

-

Alors aucune publicité n'est diffusée -Et je passe directement d'un contenu à l'autre -Et l'expérience d'écoute est fluide et ininterrompue

-
-

3. Badge "0 publicité" sur page Premium

-

Étant donné que je consulte la page des avantages Premium

-

Quand je lis la liste des avantages

-

Alors je vois en premier: -Et c'est l'argument principal mis en avant

-
-

4. Utilisateur gratuit voit contenus Premium bloqués

-

Étant donné que je suis un utilisateur gratuit

-

Quand je consulte les contenus d'un créateur

-

Alors je vois les contenus marqués Premium avec badge 👑 -Mais je ne peux pas les lire (overlay bloquant)

-
-

5. Utilisateur Premium accède à tous les contenus exclusifs

-

Étant donné que je suis un utilisateur Premium

-

Quand je consulte les contenus d'un créateur

-

Alors tous les contenus Premium sont accessibles -Et je peux les lire sans restriction -Et j'ai accès à 100% du catalogue (gratuit + Premium)

-
-

6. Nombre de contenus Premium disponibles

-

Étant donné que je suis Premium

-

Quand je consulte les statistiques

-

Alors je vois combien de contenus Premium sont disponibles sur la plateforme -Et par exemple: "8,547 contenus Premium exclusifs disponibles" -Et cela justifie la valeur de l'abonnement

-
-

7. Utilisateur gratuit écoute en 48 kbps Opus

-

Étant donné que je suis un utilisateur gratuit

-

Quand je lance un contenu

-

Alors l'audio est streamé en 48 kbps Opus -Et cela consomme environ 20 MB/heure -Et la qualité est très correcte pour de la voix

-
-

8. Utilisateur Premium écoute en 64 kbps Opus

-

Étant donné que je suis un utilisateur Premium

-

Quand je lance un contenu

-

Alors l'audio est streamé en 64 kbps Opus -Et cela consomme environ 30 MB/heure -Et la qualité est excellente (détails audio supérieurs)

-
-

9. Comparaison qualité 48 kbps vs 64 kbps

-

Étant donné que je consulte la page Premium

-

Quand je lis la section qualité audio

-

Alors je vois l'explication:

-
-

10. Justification 48 kbps suffisant pour gratuit

-

Étant donné que le contenu RoadWave est principalement de la voix

-

Quand la qualité est fixée à 48 kbps pour gratuit

-

Alors c'est largement suffisant pour comprendre clairement -Et équivalent à la qualité radio FM -Et les utilisateurs gratuits ne sont pas frustrés

-
-

11. Justification 64 kbps avantage tangible Premium

-

Étant donné que les audiophiles et créateurs audio sont exigeants

-

Quand la qualité Premium est à 64 kbps

-

Alors la différence est perceptible à l'oreille -Et les ambiances, musiques de fond, nuances de voix sont mieux rendues -Et cela justifie l'abonnement Premium

-
-

12. Switch automatique qualité selon abonnement

-

Étant donné que je suis gratuit et j'écoute en 48 kbps

-

Quand je souscris à Premium

-

Alors dès le contenu suivant, je passe automatiquement en 64 kbps -Et je peux entendre la différence de qualité immédiatement

-
-

13. Consommation data Premium vs Gratuit

-

Étant donné que je roule 1 heure par jour

-

Quand je calcule la consommation mensuelle

-

Alors en gratuit: 20 MB/h × 1h × 22 jours = 440 MB/mois -Et en Premium: 30 MB/h × 1h × 22 jours = 660 MB/mois -Et la différence est de 220 MB/mois (acceptable pour 4G/5G illimitée)

-
-

14. Utilisateur gratuit limité à 50 contenus téléchargés

-

Étant donné que je suis un utilisateur gratuit

-

Quand j'accède au mode offline

-

Alors je peux télécharger jusqu'à 50 contenus maximum -Et si j'essaie de télécharger un 51ème, je vois:

-
-

15. Utilisateur Premium téléchargements illimités

-

Étant donné que je suis un utilisateur Premium

-

Quand j'accède au mode offline

-

Alors je peux télécharger autant de contenus que je veux -Et la seule limite est l'espace de stockage de mon device -Et par exemple 500 contenus × 10 MB = 5 GB

-
-

16. Justification limite 50 contenus gratuit

-

Étant donné que 50 contenus de 10 minutes = ~8 heures d'écoute

-

Quand un utilisateur gratuit prépare un road trip

-

Alors 8 heures couvrent largement une journée de trajet -Et cela permet un usage offline raisonnable sans abuser

-
-

17. Justification illimité Premium pour longs road trips

-

Étant donné qu'un road trip de plusieurs jours nécessite 20-50h de contenu

-

Quand un utilisateur Premium télécharge 200 contenus

-

Alors il peut partir serein sans connexion internet pendant 1 semaine -Et cela justifie pleinement l'abonnement Premium

-
-

18. Affichage compteur téléchargements gratuit

-

Étant donné que je suis gratuit et j'ai téléchargé 37 contenus

-

Quand j'accède à la page Téléchargements

-

Alors je vois:

-
-

19. Pas de compteur pour Premium

-

Étant donné que je suis Premium et j'ai téléchargé 187 contenus

-

Quand j'accède à la page Téléchargements

-

Alors je vois simplement: -Et aucune limite n'est affichée

-
-

20. Utilisateur gratuit historique limité à 100 derniers

-

Étant donné que je suis un utilisateur gratuit

-

Quand j'accède à mon historique d'écoute

-

Alors je vois les 100 derniers contenus écoutés -Et les contenus plus anciens ne sont pas affichés -Et je vois un message "Historique limité à 100 contenus. Passez Premium pour un historique illimité."

-
-

21. Utilisateur Premium historique illimité

-

Étant donné que je suis un utilisateur Premium

-

Quand j'accède à mon historique d'écoute

-

Alors je vois tous les contenus que j'ai écoutés depuis mon inscription -Et je peux scroller jusqu'au premier contenu jamais écouté -Et l'historique est complet et permanent

-
-

22. Recherche dans historique Premium

-

Étant donné que je suis Premium et j'ai 2 000 contenus dans mon historique

-

Quand je recherche "Tesla" dans mon historique

-

Alors tous les contenus écoutés contenant "Tesla" sont affichés -Et je peux retrouver facilement un contenu écouté il y a 6 mois

-
-

23. Justification limite 100 gratuit suffisante

-

Étant donné que 100 contenus de 10 min = ~16 heures d'écoute

-

Quand un utilisateur gratuit écoute 1h/jour

-

Alors l'historique couvre les 16 derniers jours -Et cela suffit pour retrouver un contenu récent

-
-

24. Justification illimité Premium pour power users

-

Étant donné qu'un power user écoute 3h/jour depuis 2 ans

-

Quand il veut retrouver un contenu spécifique écouté il y a 1 an

-

Alors l'historique illimité Premium lui permet de retrouver ce contenu -Et cela apporte une vraie valeur ajoutée

-
-

25. Export historique complet (Premium uniquement)

-

Étant donné que je suis Premium

-

Quand je demande l'export de mes données

-

Alors l'historique complet est inclus dans l'export:

-
-

26. Affichage tableau comparatif Gratuit vs Premium

-

Étant donné que je consulte la page Premium

-

Quand je vois le tableau comparatif

-

Alors il affiche:

-
-

27. Justification 0 pub = argument principal

-

Étant donné qu'une publicité de 30s tous les 5 contenus = 6 min/h de pub

-

Quand un utilisateur écoute 1h/jour

-

Alors il subit 180 min de pub/mois (3 heures !) -Et payer 4.99€ pour éviter 3h de pub/mois est très rentable -Et c'est l'argument de conversion n°1

-
-

28. Justification qualité audio avantage tangible

-

Étant donné que la différence 48 kbps → 64 kbps est audible

-

Quand un audiophile compare les deux

-

Alors il entend clairement la différence sur un bon système audio voiture -Et cela justifie l'abonnement pour les exigeants

-
-

29. Justification offline illimité pour road trips

-

Étant donné qu'un road trip de 2 semaines nécessite 50-100h de contenu

-

Quand un utilisateur Premium télécharge 300 contenus avant de partir

-

Alors il peut partir en zone sans réseau sereinement -Et cela apporte une vraie valeur pratique

-
-

30. Justification pas d'over-engineering

-

Étant donné que RoadWave se concentre sur l'essentiel

-

Quand les avantages Premium sont définis

-

Alors il n'y a pas de:

-
| fonctionnalité superflue | raison exclusion |
-|---|---|
-| Badges cosmétiques | Pas de valeur réelle |
-| Avatar Premium exclusif | Inutile pour audio |
-| Fonctionnalités sociales avancées | Pas prioritaire MVP |
-| Early access nouveaux contenus | Complexité > bénéfice |
-
-

Et cela réduit la complexité et le coût de développement

-
-

31. CTA Premium après 5ème publicité

-

Étant donné que je suis gratuit et je viens d'entendre ma 5ème pub

-

Quand la publicité se termine

-

Alors je vois un message:

-
-

32. CTA Premium quand limite 50 téléchargements atteinte

-

Étant donné que je suis gratuit et j'ai atteint 50 téléchargements

-

Quand j'essaie de télécharger un 51ème contenu

-

Alors je vois une popup:

-
-

33. CTA Premium quand contenu exclusif bloqué

-

Étant donné que je suis gratuit et je clique sur un contenu Premium

-

Quand l'overlay bloquant apparaît

-

Alors je vois:

-
-

34. Statistiques conversion - Quel avantage convertit le mieux ?

-

Étant donné qu'un admin consulte les statistiques de conversion

-

Quand il analyse les sources de conversion

-

Alors il voit:

-
| source de conversion | % conversions |
-|---|---|
-| CTA après 5ème pub | 42% |
-| CTA contenu Premium bloqué | 28% |
-| CTA limite 50 téléchargements | 18% |
-| Page Premium directe | 12% |
-
-

Et cela aide à optimiser le placement des CTA

-
-

35. A/B test message CTA

-

Étant donné que RoadWave veut optimiser les conversions

-

Quand un A/B test est lancé sur le CTA après pub

-

Alors groupe A voit "Marre des pubs ?" (focus négatif) -Et groupe B voit "Profitez de 0 publicité" (focus positif) -Et le taux de conversion est mesuré -Et le message le plus performant est déployé

-
-

36. Notification Premium après 30 jours d'utilisation gratuite

-

Étant donné que je suis utilisateur gratuit depuis 30 jours -Et que j'écoute régulièrement (15h cumulées)

-

Quand le 30ème jour arrive

-

Alors je reçois une notification:

-
-

37. Trial gratuit refusé mais onboarding amélioré

-

Étant donné qu'il n'y a pas de trial gratuit

-

Quand un nouvel utilisateur s'inscrit

-

Alors un onboarding explique clairement les avantages Premium -Et il peut comparer gratuit vs Premium dès le premier lancement -Et cela l'aide à décider rapidement s'il veut payer

-
-
- -

Gestion abonnement Premium

-
-

En tant qu'utilisateur -Je veux gérer facilement mon abonnement Premium -Afin de souscrire, renouveler ou annuler en toute transparence

-
-

41 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis connecté à l'application RoadWave

-
-

1. Souscription via Web (desktop/mobile) avec Mangopay

-

Étant donné que je consulte la page Premium sur le site web

-

Quand je clique sur "S'abonner - Mensuel 4.99€"

-

Alors je suis redirigé vers le formulaire de paiement Mangopay -Et je saisis mes informations de carte bancaire -Et le paiement de 4.99€ est prélevé immédiatement -Et la commission Mangopay est de 1.8% + 0.18€ = 0.27€ -Et RoadWave reçoit 4.72€ net

-
-

2. Calcul commission Mangopay

-

Étant donné qu'un utilisateur paie 4.99€ via Mangopay

-

Quand la commission est calculée

-

Alors la commission est : 4.99€ × 1.8% + 0.18€ = 0.09€ + 0.18€ = 0.27€ -Et RoadWave reçoit : 4.99€ - 0.27€ = 4.72€ -Et la commission représente 5.4% du prix

-
-

3. Souscription via iOS App avec Apple IAP

-

Étant donné que j'utilise l'app iOS

-

Quand je clique sur "S'abonner - Mensuel 5.99€"

-

Alors je suis redirigé vers l'interface Apple In-App Purchase -Et le prix affiché est 5.99€ (majoré de 20%) -Et le paiement est effectué via mon compte Apple -Et Apple prend 30% de commission = 1.80€ -Et RoadWave reçoit 4.19€ net

-
-

4. Souscription via Android App avec Google Play Billing

-

Étant donné que j'utilise l'app Android

-

Quand je clique sur "S'abonner - Mensuel 5.99€"

-

Alors je suis redirigé vers l'interface Google Play Billing -Et le prix affiché est 5.99€ (majoré de 20%) -Et le paiement est effectué via mon compte Google -Et Google prend 30% de commission = 1.80€ -Et RoadWave reçoit 4.19€ net

-
-

5. Majoration 20% sur mobile pour compenser commission 30%

-

Étant donné que Apple/Google prennent 30% de commission

-

Quand RoadWave fixe le prix mobile

-

Alors le prix web est 4.99€ (commission Mangopay 5.4%) -Et le prix mobile est 5.99€ (commission Apple/Google 30%) -Et la majoration est de 1€ (+20%) -Et cela compense partiellement la commission excessive

-
-

6. Email incitation souscription web moins chère

-

Étant donné que je consulte Premium depuis l'app mobile

-

Quand je vois le prix 5.99€

-

Alors je vois aussi un message: -Et un lien vers le site web est fourni

-
-

7. Calcul économie souscription web vs mobile

-

Étant donné que le prix web est 4.99€/mois -Et que le prix mobile est 5.99€/mois

-

Quand je calcule l'économie annuelle

-

Alors web : 4.99€ × 12 = 59.88€/an -Et mobile : 5.99€ × 12 = 71.88€/an -Et économie : 12€/an (soit 20% d'économie)

-
-

8. Activation immédiate après paiement réussi

-

Étant donné que je viens de payer mon abonnement Premium

-

Quand le paiement est confirmé

-

Alors mon statut passe immédiatement à "Premium" -Et je peux accéder aux avantages Premium dès maintenant -Et je reçois un email de confirmation

-
-

9. Email confirmation souscription

-

Étant donné que j'ai souscrit à Premium

-

Quand la souscription est confirmée

-

Alors je reçois un email:

-
-

10. Email rappel 7 jours avant renouvellement

-

Étant donné que mon abonnement mensuel se renouvelle le 15 juillet

-

Quand le 8 juillet arrive (7 jours avant)

-

Alors je reçois un email de rappel:

-
-

11. Renouvellement automatique réussi

-

Étant donné que mon abonnement mensuel arrive à échéance le 15 juillet

-

Quand le 15 juillet arrive

-

Alors Mangopay/Apple/Google prélève automatiquement 4.99€ (ou 5.99€) -Et mon abonnement est renouvelé pour 1 mois supplémentaire -Et je reçois un email de confirmation

-
-

12. Email confirmation renouvellement

-

Étant donné que mon abonnement vient d'être renouvelé

-

Quand le paiement est confirmé

-

Alors je reçois un email:

-
-

13. Échec paiement renouvellement - Tentative 1

-

Étant donné que mon abonnement doit se renouveler le 15 juillet -Mais que ma carte bancaire est expirée ou sans fonds

-

Quand le prélèvement échoue

-

Alors je reçois un email:

-
-

14. Retry automatique paiement après 3 jours

-

Étant donné que le paiement a échoué le 15 juillet

-

Quand le 18 juillet arrive (J+3)

-

Alors Mangopay/Apple/Google tente automatiquement un nouveau prélèvement -Et si le paiement réussit, l'abonnement est renouvelé normalement -Et si le paiement échoue encore, un 2ème retry est programmé

-
-

15. Retry automatique paiement après 7 jours

-

Étant donné que 2 tentatives ont échoué (15 juillet et 18 juillet)

-

Quand le 22 juillet arrive (J+7)

-

Alors une 3ème et dernière tentative est effectuée -Et si le paiement réussit, l'abonnement est sauvé -Et si le paiement échoue, l'abonnement est annulé automatiquement

-
-

16. Annulation automatique après 3 échecs paiement

-

Étant donné que les 3 tentatives de renouvellement ont échoué (J+0, J+3, J+7)

-

Quand la 3ème tentative échoue

-

Alors mon abonnement Premium est annulé automatiquement -Et mon statut repasse à "Gratuit" -Et je perds accès aux avantages Premium -Et je reçois un email d'annulation

-
-

17. Email annulation automatique pour impayé

-

Étant donné que mon abonnement a été annulé pour échec paiement

-

Quand l'annulation devient effective

-

Alors je reçois un email:

-
-

18. Annulation self-service dans Settings

-

Étant donné que je veux annuler mon abonnement

-

Quand j'accède à "Paramètres > Abonnement"

-

Alors je vois un bouton "Annuler l'abonnement" -Et je peux annuler en 2 clics sans contacter le support

-
-

19. Confirmation avant annulation

-

Étant donné que je clique sur "Annuler l'abonnement"

-

Quand une popup de confirmation apparaît

-

Alors je vois:

-
-

20. Accès Premium maintenu jusqu'à fin période payée

-

Étant donné que j'ai annulé mon abonnement le 1er juillet -Et que mon abonnement mensuel était valable jusqu'au 15 juillet

-

Quand l'annulation est confirmée

-

Alors je garde l'accès Premium jusqu'au 15 juillet -Et à partir du 16 juillet, je repasse en gratuit -Et je ne suis pas remboursé pour les 14 jours restants

-
-

21. Justification pas de remboursement prorata

-

Étant donné que l'industrie (Spotify, Netflix, YouTube) ne rembourse pas prorata

-

Quand RoadWave applique la même règle

-

Alors c'est le standard accepté par les utilisateurs -Et cela simplifie la gestion comptable -Et évite les abus (souscription puis annulation immédiate pour remboursement)

-
-

22. Email confirmation annulation

-

Étant donné que j'ai annulé mon abonnement

-

Quand l'annulation est enregistrée

-

Alors je reçois un email:

-
-

23. Pas de renouvellement après annulation

-

Étant donné que j'ai annulé mon abonnement le 1er juillet

-

Quand le 15 juillet arrive (date de renouvellement prévue)

-

Alors aucun prélèvement n'est effectué -Et mon statut passe automatiquement à "Gratuit" -Et je ne reçois pas d'email de renouvellement

-
-

24. Réabonnement possible immédiatement

-

Étant donné que j'ai annulé mon abonnement il y a 5 jours

-

Quand j'accède à la page Premium

-

Alors je peux me réabonner immédiatement -Et le processus de paiement est le même que la première fois

-
-

25. Pas de nouvelle période d'essai au réabonnement

-

Étant donné que j'ai annulé mon abonnement il y a 3 mois

-

Quand je me réabonne

-

Alors je paie immédiatement 4.99€ (pas d'essai gratuit)

-
-

26. Offre win-back pour utilisateurs ayant annulé

-

Étant donné que j'ai annulé mon abonnement il y a 1 mois

-

Quand je reçois un email de win-back

-

Alors je vois une offre spéciale:

-
-

27. Table subscriptions en base PostgreSQL

-

Étant donné qu'un utilisateur souscrit à Premium

-

Quand les données sont enregistrées

-

Alors la table subscriptions contient:

-
-

28. Statuts possibles dans subscription.status

-

Étant donné qu'un abonnement peut avoir différents statuts

-

Quand le statut est stocké en base

-

Alors les valeurs possibles sont:

-
| statut | description |
-|---|---|
-| active | Abonnement actif et payé |
-| cancelled | Annulé par utilisateur (accès jusqu'à fin période) |
-| expired | Période terminée, pas renouvelé |
-| past_due | Échec paiement, en retry automatique |
-
-
-

29. Cache Redis pour vérification Premium temps réel

-

Étant donné qu'un utilisateur lance un contenu

-

Quand l'app vérifie s'il est Premium

-

Alors une clé Redis est consultée: -Et si la clé n'existe pas, elle est recalculée depuis PostgreSQL -Et cela garantit des performances <10ms

-
-

30. Refresh cache Redis via webhooks

-

Étant donné qu'un paiement est confirmé par Mangopay/Apple/Google

-

Quand un webhook est reçu par RoadWave

-

Alors le cache Redis premium:{user_id} est mis à jour immédiatement -Et l'utilisateur voit son statut Premium activé sans délai

-
-

31. Webhooks Mangopay - PAYIN_NORMAL_SUCCEEDED

-

Étant donné qu'un paiement Mangopay réussit

-

Quand Mangopay envoie le webhook PAYIN_NORMAL_SUCCEEDED

-

Alors RoadWave met à jour subscriptions.status = 'active' -Et met à jour current_period_end = NOW() + 1 mois -Et refresh le cache Redis premium:{user_id} = true

-
-

32. Webhooks Mangopay - PAYIN_NORMAL_FAILED

-

Étant donné qu'un paiement Mangopay échoue

-

Quand Mangopay envoie le webhook PAYIN_NORMAL_FAILED

-

Alors RoadWave met à jour subscriptions.status = 'past_due' -Et programme un retry automatique dans 3 jours -Et envoie un email à l'utilisateur

-
-

33. Webhooks Apple - App Store Server Notifications

-

Étant donné qu'un paiement Apple IAP change de statut

-

Quand Apple envoie une notification serveur

-

Alors RoadWave parse la notification (JSON) -Et met à jour la subscription en conséquence -Et refresh le cache Redis

-
-

34. Webhooks Google - Real-time Developer Notifications

-

Étant donné qu'un paiement Google Play change de statut

-

Quand Google envoie une notification temps réel

-

Alors RoadWave parse la notification (JSON) -Et met à jour la subscription en conséquence -Et refresh le cache Redis

-
-

35. Dashboard admin - Métriques abonnements

-

Étant donné qu'un admin consulte les métriques Premium

-

Quand il accède au dashboard

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Abonnés actifs | 12,547 |
-| Nouveaux abonnements ce mois | 1,234 |
-| Annulations ce mois | 287 (2.3%) |
-| Churn rate mensuel | 2.3% |
-| MRR (Revenus mensuels récurrents) | 58,890€ |
-| Taux conversion gratuit → Premium | 8.5% |
-
-
-

36. Calcul churn rate mensuel

-

Étant donné que 287 utilisateurs ont annulé ce mois -Et qu'il y avait 12,547 abonnés au début du mois

-

Quand le churn rate est calculé

-

Alors churn = 287 / 12,547 = 2.3% -Et un churn <5% est considéré comme excellent -Et RoadWave surveille cette métrique de près

-
-

37. Alerte si churn rate >5%

-

Étant donné que le churn rate mensuel dépasse 5%

-

Quand le système détecte cette anomalie

-

Alors une alerte est envoyée à l'équipe:

-
-

38. Enquête satisfaction à l'annulation

-

Étant donné que je viens d'annuler mon abonnement

-

Quand l'annulation est confirmée

-

Alors je vois un questionnaire rapide: -Et les réponses aident à améliorer l'offre Premium

-
-

39. Répartition canaux souscription

-

Étant donné qu'un admin analyse les canaux de souscription

-

Quand il consulte les statistiques

-

Alors il voit:

-
| canal | abonnés | % total | revenus/mois |
-|---|---|---|---|
-| Web (Mangopay) | 8,234 | 65.6% | 41,088€ |
-| iOS (Apple) | 2,845 | 22.7% | 17,042€ |
-| Android (Google) | 1,468 | 11.7% | 8,793€ |
-
-

Et cela aide à orienter les efforts marketing (inciter web = moins de commission)

-
-

40. Performance vérification Premium <10ms

-

Étant donné que 100 000 utilisateurs consultent des contenus simultanément

-

Quand chaque requête vérifie le statut Premium via Redis

-

Alors le temps de réponse moyen est <10ms -Et Redis gère facilement 100 000 requêtes/seconde -Et l'expérience utilisateur est fluide

-
-

41. Backup données abonnements

-

Étant donné que les données d'abonnements sont critiques

-

Quand un backup est effectué

-

Alors PostgreSQL est répliqué en temps réel sur un replica -Et un snapshot quotidien est stocké sur S3 -Et en cas de crash, les données peuvent être restaurées <5 minutes

-
-
- -

Multi-devices et détection simultanée

-
-

En tant qu'abonné Premium -Je veux utiliser mon compte sur plusieurs appareils -Mais limité à 1 seul stream actif à la fois pour éviter le partage abusif

-
-

30 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur Premium actif -Et que mon compte est valide

-
-

1. 1 seul stream actif autorisé par compte

-

Étant donné que je n'écoute rien actuellement

-

Quand je lance un contenu sur mon iPhone

-

Alors le stream démarre normalement -Et Redis enregistre: active_streams:{user_id} = {device_id: "iPhone", started_at: timestamp}

-
-

2. Détection connexion simultanée - Arrêt premier device

-

Étant donné que j'écoute un contenu sur mon iPhone

-

Quand je lance un contenu sur mon iPad

-

Alors le système détecte une session active sur iPhone -Et la lecture sur iPhone est arrêtée immédiatement (WebSocket close) -Et je vois sur iPhone: "Lecture interrompue : votre compte est utilisé sur un autre appareil" -Et la lecture démarre sur iPad normalement

-
-

3. Message explicite sur device interrompu

-

Étant donné que ma lecture sur iPhone vient d'être interrompue

-

Quand je regarde l'écran de mon iPhone

-

Alors je vois une overlay avec le message: -Et un bouton "Reprendre ici" est disponible

-
-

4. Reprendre lecture sur device interrompu

-

Étant donné que ma lecture sur iPhone a été interrompue -Et que je veux reprendre sur iPhone

-

Quand je clique sur "Reprendre ici"

-

Alors la lecture démarre sur iPhone -Et l'iPad est à son tour interrompu avec le même message -Et le "ping-pong" entre devices est possible (mais pénible)

-
-

5. Enregistrement session active dans Redis

-

Étant donné que je lance un contenu sur mon iPhone

-

Quand la lecture démarre

-

Alors une entrée Redis est créée:

-
-

6. Heartbeat toutes les 30 secondes pour maintenir session

-

Étant donné que j'écoute un contenu sur mon iPhone

-

Quand 30 secondes s'écoulent

-

Alors l'app envoie un heartbeat au serveur -Et le serveur refresh le TTL Redis à 300 secondes -Et la session reste active

-
-

7. Session considérée morte après 5 minutes sans heartbeat

-

Étant donné que j'écoute un contenu sur mon iPhone -Mais que l'app crash ou que le réseau coupe

-

Quand 5 minutes s'écoulent sans heartbeat

-

Alors l'entrée Redis expire automatiquement (TTL atteint) -Et je peux relancer sur n'importe quel device sans conflit

-
-

8. Vérification session avant démarrage lecture

-

Étant donné que je veux lancer un contenu sur mon iPad

-

Quand j'appuie sur Play

-

Alors le serveur vérifie Redis: active_streams:{user_id} -Et si une session existe sur un autre device, elle est tuée -Et la nouvelle session iPad est enregistrée dans Redis

-
-

9. Gestion multi-utilisateurs simultanés

-

Étant donné que 100 000 utilisateurs Premium écoutent simultanément

-

Quand Redis stocke 100 000 entrées active_streams

-

Alors chaque entrée a un TTL de 5 minutes -Et Redis gère facilement cette charge (~10 MB de RAM) -Et les vérifications sont quasi-instantanées (O(1))

-
-

10. Contenus téléchargés (offline) ne comptent pas comme stream

-

Étant donné que j'ai téléchargé 20 contenus en mode offline

-

Quand j'écoute un contenu téléchargé sur mon iPhone sans réseau

-

Alors aucune session active n'est enregistrée dans Redis -Et je peux écouter offline pendant qu'un autre device stream online

-
-

11. Transition rapide device <10s tolérée

-

Étant donné que j'écoute dans ma voiture sur mon iPhone -Et que j'arrive chez moi

-

Quand je lance la lecture sur mon iPad dans les 10 secondes

-

Alors la transition est considérée comme un changement de device légitime -Et aucun message d'erreur n'est affiché sur iPhone -Et la lecture reprend exactement où j'étais sur iPad

-
-

12. Détection transition rapide via timestamps

-

Étant donné que la session iPhone a started_at = 14:30:00

-

Quand je lance sur iPad à 14:30:05 (5 secondes après)

-

Alors le serveur détecte: diff = 5s < 10s -Et applique une "graceful transition" (pas de message d'erreur iPhone) -Et Redis met à jour: active_streams:{user_id} = {device_id: "iPad", ...}

-
-

13. Plusieurs devices disponibles mais 1 seul actif

-

Étant donné que je possède:

-
| device | status |
-|---|---|
-| iPhone | Installé |
-| iPad | Installé |
-| MacBook (web) | Connecté |
-| Android (conjoint) | Installé |
-
-

Quand je lance un stream sur n'importe quel device

-

Alors seulement 1 peut être actif à la fois -Et les autres devices sont en "standby"

-
-

14. Justification anti-partage compte

-

Étant donné qu'un utilisateur Premium partage son compte avec un ami

-

Quand les 2 personnes essaient d'écouter simultanément

-

Alors la lecture est constamment interrompue sur l'un ou l'autre -Et l'expérience devient inutilisable -Et cela décourage fortement le partage de compte

-
-

15. Justification protection revenus créateurs

-

Étant donné que 1 abonnement Premium = 4.99€/mois

-

Quand 70% sont reversés aux créateurs (3.49€)

-

Alors les créateurs sont rémunérés pour 1 personne -Et si 2 personnes utilisent le même compte simultanément, c'est injuste -Et la limite 1 stream protège l'équité du système

-
-

16. Justification UX claire

-

Étant donné qu'un stream est interrompu sur un device

-

Quand l'utilisateur voit le message explicite

-

Alors il comprend immédiatement pourquoi (autre device actif) -Et il peut choisir de reprendre sur le device actuel ou l'autre -Et il n'y a pas de confusion ou frustration

-
-

17. Comparaison avec Spotify (limite 1 stream)

-

Étant donné que Spotify Premium limite aussi à 1 stream actif

-

Quand RoadWave applique la même règle

-

Alors les utilisateurs connaissent déjà ce comportement -Et cela paraît normal et accepté par l'industrie

-
-

18. Comparaison avec Netflix (plusieurs streams selon formule)

-

Étant donné que Netflix permet 1-4 streams selon la formule

-

Quand RoadWave limite à 1 stream pour tous

-

Alors c'est plus strict que Netflix -Mais Netflix cible le foyer familial (TV partagée) -Alors que RoadWave cible l'individu conducteur (usage personnel)

-
-

19. Détection pattern suspect - Changements devices fréquents

-

Étant donné qu'un utilisateur change de device 50 fois en 1 heure

-

Quand le système détecte ce pattern anormal

-

Alors une alerte est générée pour l'équipe modération -Et le compte peut être marqué pour surveillance -Et si abus confirmé, suspension possible

-
-

20. Logs des changements de device

-

Étant donné que je change de device plusieurs fois par jour

-

Quand les changements sont loggés

-

Alors chaque événement est enregistré:

-
| timestamp | from_device | to_device | content_id |
-|---|---|---|---|
-| 2025-06-15 08:30:00 | null | iPhone | abc123 |
-| 2025-06-15 09:15:00 | iPhone | iPad | def456 |
-| 2025-06-15 18:30:00 | iPad | iPhone | ghi789 |
-
-

Et ces logs aident à détecter les partages de compte

-
-

21. Métriques admin - Changements devices par utilisateur

-

Étant donné qu'un admin consulte les métriques de streaming

-

Quand il accède au dashboard

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Utilisateurs Premium actifs | 12,547 |
-| Changements de device/jour (médiane) | 2 |
-| Utilisateurs >10 changements/jour | 47 (0.4%) |
-| Comptes suspects (>20 changements/j) | 3 |
-
-
-

22. Email d'avertissement si changements excessifs

-

Étant donné que je change de device 30 fois par jour pendant 3 jours

-

Quand le système détecte ce pattern

-

Alors je reçois un email d'avertissement:

-
-

23. Suspension compte après avertissement ignoré

-

Étant donné que j'ai reçu un email d'avertissement il y a 7 jours -Mais que je continue à changer de device 30 fois par jour

-

Quand l'équipe modération examine le compte

-

Alors mon compte Premium peut être suspendu pour partage abusif -Et je reçois un email de suspension avec justification

-
-

24. FAQ - Pourquoi ma lecture s'arrête quand j'utilise un autre device ?

-

Étant donné que je consulte la FAQ Premium

-

Quand je cherche "lecture interrompue"

-

Alors je trouve la réponse:

-
-

25. Support - Utilisateur pense être piraté

-

Étant donné qu'un utilisateur voit constamment "Lecture interrompue" -Et qu'il pense que son compte est piraté

-

Quand il contacte le support

-

Alors le support vérifie les logs de changements de device -Et peut identifier les devices (iPhone, iPad perso vs iPhone inconnu) -Et conseille de changer le mot de passe si device inconnu détecté

-
-

26. Changement mot de passe déconnecte tous les devices

-

Étant donné que je pense que mon compte est compromis

-

Quand je change mon mot de passe

-

Alors tous mes devices sont déconnectés immédiatement -Et les sessions actives dans Redis sont supprimées -Et je dois me reconnecter sur chaque device -Et cela sécurise mon compte

-
-

27. Test charge - 100 000 vérifications/seconde

-

Étant donné que 100 000 utilisateurs Premium lancent des contenus

-

Quand chaque lancement vérifie Redis (GET active_streams:{user_id})

-

Alors Redis peut gérer facilement 100 000 requêtes/seconde -Et le temps de réponse moyen est <1ms -Et aucun ralentissement n'est constaté

-
-

28. Test failover Redis

-

Étant donné que le serveur Redis principal tombe en panne

-

Quand le failover automatique vers le replica Redis s'active

-

Alors les sessions actives peuvent être perdues temporairement (max 5 min) -Mais les utilisateurs peuvent relancer immédiatement -Et l'impact est minimal (pas de perte de données critiques)

-
-

29. Test concurrence - Lancement simultané 2 devices

-

Étant donné que je lance exactement au même instant sur iPhone et iPad

-

Quand les 2 requêtes arrivent en parallèle au serveur

-

Alors Redis utilise un lock (SETNX) pour atomicité -Et 1 seul device gagne (par exemple iPhone) -Et l'autre device (iPad) reçoit immédiatement une erreur -Et l'utilisateur peut retry sur iPad si souhaité

-
-

30. Nettoyage automatique sessions expirées

-

Étant donné que 1000 sessions Redis ont expiré (TTL atteint)

-

Quand Redis supprime automatiquement ces entrées

-

Alors la mémoire est libérée -Et les nouveaux streams peuvent démarrer sans conflit -Et aucune intervention manuelle n'est nécessaire

-
-
- -

Offre et tarification Premium

-
-

En tant qu'utilisateur -Je veux pouvoir souscrire à un abonnement Premium -Afin de profiter d'une expérience sans publicité avec des avantages exclusifs

-
-

31 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'utilisateur

-
-

1. Formule mensuelle à 4.99€/mois

-

Étant donné que je consulte les offres Premium

-

Quand je vois la formule mensuelle

-

Alors le prix affiché est 4.99€/mois -Et il n'y a aucune réduction -Et le prix effectif par mois est 4.99€

-
-

2. Formule annuelle à 49.99€/an (2 mois offerts)

-

Étant donné que je consulte les offres Premium

-

Quand je vois la formule annuelle

-

Alors le prix affiché est 49.99€/an -Et l'économie affichée est "2 mois offerts" -Et le prix effectif par mois est 4.16€ -Et je vois le badge "Meilleure offre"

-
-

3. Calcul économie formule annuelle

-

Étant donné que la formule mensuelle coûte 4.99€/mois

-

Quand je calcule le coût annuel en mensuel

-

Alors 12 mois × 4.99€ = 59.88€/an -Et la formule annuelle coûte 49.99€ -Et l'économie est de 9.89€ (≈ 2 mois gratuits) -Et la réduction est de 16.5%

-
-

4. Pas d'essai gratuit disponible

-

Étant donné que je consulte les offres Premium

-

Quand je recherche une option "Essai gratuit"

-

Alors aucune option d'essai gratuit n'est proposée -Et je dois payer dès le premier jour pour accéder au Premium

-
-

5. Justification absence essai gratuit - Anti-abus vacances

-

Étant donné que RoadWave ne propose pas d'essai gratuit

-

Quand un utilisateur envisage un road trip de 14 jours

-

Alors il ne peut pas s'abonner pour l'essai gratuit puis annuler -Et cela évite les inscriptions opportunistes -Et protège les revenus des créateurs

-
-

6. Justification absence essai gratuit - Protection revenus créateurs

-

Étant donné qu'un utilisateur Premium écoute des contenus

-

Quand il génère des écoutes dès le jour 1

-

Alors les créateurs sont rémunérés immédiatement (70% de 4.99€) -Et il n'y a pas de "période gratuite" sans rémunération créateurs

-
-

7. Justification absence essai gratuit - Simplicité

-

Étant donné que RoadWave gère les abonnements

-

Quand il n'y a pas d'essai gratuit

-

Alors pas de gestion complexe de période trial -Et pas de workflow de conversion trial → payant -Et cela réduit la complexité technique

-
-

8. Justification absence essai gratuit - Engagement

-

Étant donné qu'un utilisateur paie dès le début

-

Quand il souscrit à Premium

-

Alors il est plus engagé qu'un utilisateur en essai gratuit -Et le taux de churn est généralement plus faible -Et la lifetime value (LTV) est plus élevée

-
-

9. Pas de partage familial au MVP

-

Étant donné que je consulte les offres Premium

-

Quand je recherche une option "Famille" ou "Partage"

-

Alors aucune option de partage familial n'est disponible -Et seuls les abonnements individuels sont proposés

-
-

10. Justification absence partage familial - Complexité technique

-

Étant donné que le partage familial nécessite:

-
| fonctionnalité | complexité |
-|---|---|
-| Gestion invitations | Moyenne |
-| Validation liens famille | Moyenne |
-| Limite devices par membre | Élevée |
-| Dashboard admin famille | Élevée |
-
-

Quand RoadWave évalue le ROI

-

Alors le coût dev/support est trop élevé pour le MVP -Et la fonctionnalité est reportée post-MVP

-
-

11. Justification absence partage familial - Risque abus

-

Étant donné qu'une offre famille permet 5-6 membres

-

Quand il n'y a pas de vérification stricte de lien familial

-

Alors des "familles" de 6 inconnus pourraient se former -Et cela réduirait fortement les revenus (6 personnes pour 1 abonnement)

-
-

12. Justification absence partage familial - Cible individuelle

-

Étant donné que RoadWave cible principalement les conducteurs

-

Quand chaque conducteur utilise l'app individuellement en voiture

-

Alors le besoin de partage familial est limité -Et la plupart des utilisateurs sont des individus (pas des familles)

-
-

13. Post-MVP - Offre Famille à 9.99€/mois pour 5 comptes

-

Étant donné que RoadWave envisage une offre Famille post-MVP

-

Quand la fonctionnalité est spécifiée

-

Alors le prix serait 9.99€/mois pour 5 comptes -Et cela représente 2€/mois/personne -Mais cette offre n'est pas disponible au MVP

-
-

14. Comparaison tarif - Spotify à 10.99€/mois

-

Étant donné que Spotify Premium coûte 10.99€/mois

-

Quand RoadWave fixe son prix à 4.99€/mois

-

Alors RoadWave est 54.5% moins cher que Spotify -Et cela positionne RoadWave comme très accessible

-
-

15. Comparaison tarif - YouTube Premium à 11.99€/mois

-

Étant donné que YouTube Premium coûte 11.99€/mois

-

Quand RoadWave fixe son prix à 4.99€/mois

-

Alors RoadWave est 58.4% moins cher que YouTube Premium -Et cela est un argument commercial fort

-
-

16. Comparaison tarif - Apple Music à 10.99€/mois

-

Étant donné qu'Apple Music coûte 10.99€/mois

-

Quand RoadWave fixe son prix à 4.99€/mois

-

Alors RoadWave est 54.5% moins cher qu'Apple Music -Et cela attire les utilisateurs sensibles au prix

-
-

17. Justification tarif bas - Cible conducteurs quotidiens

-

Étant donné que RoadWave cible les trajets quotidiens domicile-travail

-

Quand le prix est fixé à 4.99€/mois

-

Alors c'est un budget raisonnable pour un conducteur -Et équivalent à ~1-2 cafés/mois -Et psychologiquement acceptable pour un usage quotidien

-
-

18. Justification formule annuelle - Engagement long terme

-

Étant donné que la formule annuelle offre 2 mois gratuits

-

Quand un utilisateur souscrit pour 1 an

-

Alors il s'engage sur le long terme -Et RoadWave sécurise 49.99€ de revenus immédiatement -Et le cash flow est amélioré

-
-

19. Justification formule annuelle - Réduction churn

-

Étant donné qu'un utilisateur paie 49.99€ pour l'année

-

Quand il envisage d'arrêter après 3 mois

-

Alors il a déjà payé pour 12 mois -Et il continuera probablement à utiliser l'app -Et le taux de churn est réduit significativement

-
-

20. Affichage comparatif des deux formules

-

Étant donné que je consulte la page Premium

-

Quand je vois les deux formules côte à côte

-

Alors je vois:

-
-

21. Mise en avant formule annuelle

-

Étant donné que je consulte la page Premium

-

Quand je vois les deux formules

-

Alors la formule annuelle a un badge "Meilleure offre" ⭐ -Et elle est visuellement mise en avant (bordure colorée, taille plus grande) -Et l'économie de 2 mois est affichée en gros -Et cela incite à choisir la formule annuelle

-
-

22. Lien "Pourquoi pas d'essai gratuit ?" en FAQ

-

Étant donné que je consulte la page Premium

-

Quand je clique sur "FAQ"

-

Alors je vois une question "Pourquoi pas d'essai gratuit ?" -Et la réponse explique:

-
-

23. A/B test formule annuelle (post-MVP)

-

Étant donné que RoadWave veut optimiser la conversion annuelle

-

Quand un A/B test est lancé

-

Alors groupe A voit "2 mois offerts" (économie en durée) -Et groupe B voit "Économisez 9.89€" (économie en argent) -Et les taux de souscription sont mesurés -Et le message le plus performant est déployé

-
-

24. Promo temporaire exceptionnelle (Black Friday, etc.)

-

Étant donné que c'est le Black Friday

-

Quand une promo temporaire est activée

-

Alors la formule annuelle peut passer à 39.99€/an (au lieu de 49.99€) -Et l'économie affichée est "4 mois offerts !" -Et la promo dure 3 jours uniquement -Et cela génère un pic de souscriptions

-
-

25. Code promo partenariat influenceur

-

Étant donné qu'un influenceur promeut RoadWave

-

Quand il partage un code promo "INFLUENCEUR20"

-

Alors les utilisateurs obtiennent -20% sur le premier mois (3.99€ au lieu de 4.99€) -Et le code est valable 1 mois -Et les conversions sont trackées par code promo

-
-

26. Statistiques admin - Répartition formules

-

Étant donné qu'un admin consulte les métriques d'abonnements

-

Quand il accède au dashboard

-

Alors il voit:

-
| métrique | valeur |
-|---|---|
-| Abonnés Premium total | 12,547 |
-| Abonnés mensuels | 7,234 (58%) |
-| Abonnés annuels | 5,313 (42%) |
-| Revenus mensuels récurrents | 58,890€ |
-
-

Et ces données aident à piloter la stratégie tarifaire

-
-

27. Calcul revenus mensuels récurrents (MRR)

-

Étant donné que RoadWave a:

-
| formule | nombre abonnés | prix |
-|---|---|---|
-| Mensuel | 7,234 | 4.99€/mois |
-| Annuel | 5,313 | 49.99€/an |
-
-

Quand le MRR est calculé

-

Alors MRR mensuel = 7,234 × 4.99€ = 36,098€ -Et MRR annuel ramené au mois = 5,313 × 49.99€ / 12 = 22,139€ -Et MRR total = 58,237€/mois

-
-

28. Projection revenus annuels (ARR)

-

Étant donné que le MRR est de 58,237€

-

Quand l'ARR est calculé

-

Alors ARR = 58,237€ × 12 = 698,844€/an -Et cela aide à évaluer la valorisation de l'entreprise

-
-

29. Affichage prix TTC (TVA incluse)

-

Étant donné que RoadWave est une plateforme française

-

Quand les prix sont affichés

-

Alors tous les prix sont TTC (TVA 20% incluse) -Et le prix 4.99€ inclut déjà la TVA -Et cela respecte la réglementation française

-
-

30. Performance page Premium avec cache

-

Étant donné que la page Premium est consultée fréquemment

-

Quand un utilisateur charge la page

-

Alors les prix et avantages sont servis depuis un cache CDN -Et le temps de chargement est <200ms -Et cela garantit une expérience fluide

-
-

31. Localisation prix selon pays (post-MVP)

-

Étant donné que RoadWave se lance à l'international post-MVP

-

Quand un utilisateur se connecte depuis l'Allemagne

-

Alors les prix peuvent être ajustés (ex: 4.99€ en France, 4.49€ en Pologne) -Et cela respecte le pouvoir d'achat local -Mais cette fonctionnalité n'est pas au MVP (France uniquement)

-
-
- -

Profil créateur

-
-

En tant qu'utilisateur de RoadWave -Je veux consulter les profils des créateurs -Afin de découvrir leur contenu et décider de m'abonner

-
-

31 scénarios (28 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée

-
-

1. URL du profil créateur

-

Étant donné un créateur avec le pseudo "paris_stories"

-

Quand l'utilisateur accède au profil

-

Alors l'URL est "https://roadwave.fr/@paris_stories"

-
-

2. Informations principales du profil

-

Étant donné un créateur "@paris_stories" avec les informations suivantes:

-
| champ | valeur |
-|---|---|
-| photo | avatar_120x120.jpg |
-| pseudo | paris_stories |
-| badge_vérifié | true |
-| bio | Histoires et anecdotes de Paris |
-| abonnés | 1200 |
-| contenus | 42 |
-| durée_totale | 18h |
-| écoutes_totales | 54000 |
-
-

Quand le profil est affiché

-

Alors les éléments suivants sont visibles:

-
| élément | valeur affichée |
-|---|---|
-| Photo profil | 120×120 px |
-| @pseudo | @paris_stories |
-| Badge vérifié | ✓ |
-| Bio | Histoires et... |
-| Nombre abonnés | 1.2K abonnés |
-| Nombre contenus | 42 contenus |
-| Durée totale | 18h de contenu créé |
-| Écoutes totales | 54K écoutes totales |
-
-
-

3. 📋 Plan: Arrondi des statistiques publiques

-

Étant donné un créateur avec

-

Quand le profil est affiché

-

Alors la valeur affichée est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
métriquevaleur_exactevaleur_affichée
abonnés342342
abonnés12001.2K
abonnés5400054K
abonnés12000001.2M
écoutes842842
écoutes54005.4K
écoutes142000142K
écoutes21000002.1M
durée (heures)1818h
durée (heures)142142h
-
-

4. Bio avec markdown basique

-

Étant donné un créateur avec la bio suivante en markdown:

-

Quand le profil est affiché

-

Alors le texte en gras "Histoires de Paris" est formaté -Et le texte en italique "Nouveau contenu chaque semaine" est formaté -Et le lien "https://paris-stories.fr" est cliquable

-
-

5. Limitation de la bio à 300 caractères

-

Étant donné un créateur qui entre une bio de 350 caractères

-

Quand la bio est sauvegardée

-

Alors seuls les 300 premiers caractères sont conservés -Et un message "Maximum 300 caractères" s'affiche

-
-

6. Boutons d'action principaux

-

Étant donné que l'utilisateur consulte un profil créateur

-

Quand la page est chargée

-

Alors les boutons suivants sont visibles:

-
| bouton | action |
-|---|---|
-| S'abonner | Abonnement au créateur |
-| Partager profil | Menu de partage |
-| ••• | Menu contextuel |
-
-
-

7. Menu contextuel du profil [•••]

-

Étant donné que l'utilisateur clique sur le bouton [•••]

-

Quand le menu s'ouvre

-

Alors les options suivantes sont disponibles:

-
| option | description |
-|---|---|
-| Partager profil | Partager le lien du profil |
-| Signaler profil | Signaler spam ou usurpation d'identité |
-| Bloquer créateur | Masquer tous les contenus du créateur |
-
-
-

8. Liste des contenus du créateur

-

Étant donné un créateur avec 3 contenus publiés

-

Quand le profil est affiché

-

Alors chaque contenu affiche:

-
| élément | exemple |
-|---|---|
-| Cover image | Image 16:9 |
-| Titre | Balade à Paris |
-| Durée et écoutes | 12 min · 🎧 2.3K |
-| Localisation | 📍 Paris |
-| Bouton lecture | ▶️ |
-
-
-

9. 📋 Plan: Options de tri des contenus

-

Étant donné un créateur avec 10 contenus publiés

-

Quand l'utilisateur sélectionne le tri ""

-

Alors les contenus sont triés par

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
option_tricritère
Plus récentsDate publication DESC (défaut)
Plus populairesÉcoutes × facteur temporel (90 jours)
Plus anciensDate publication ASC
-
-

10. Filtrage des contenus par tag

-

Étant donné un créateur avec des contenus taggés "Voyage", "Histoire", "Gastronomie"

-

Quand l'utilisateur filtre par tags "Voyage, Histoire"

-

Alors seuls les contenus avec ces tags sont affichés -Et le nombre de résultats est indiqué "12 contenus"

-
-

11. Recherche locale dans le profil

-

Étant donné que l'utilisateur consulte le profil de "@paris_stories" -Et que le créateur a publié 50 contenus

-

Quand l'utilisateur entre "Montmartre" dans la barre de recherche

-

Alors la recherche s'effectue sur les titres et descriptions -Et seuls les contenus correspondants sont affichés -Et le placeholder indique "Rechercher dans les contenus de @paris_stories"

-
-

12. Chargement paginé des contenus

-

Étant donné un créateur avec 100 contenus publiés

-

Quand le profil est affiché

-

Alors 20 contenus sont chargés initialement -Et un bouton "Charger plus" est visible en bas de page

-

Quand l'utilisateur clique sur "Charger plus"

-

Alors 20 contenus supplémentaires sont chargés

-
-

13. Informations publiques visibles par tous

-

Étant donné que l'utilisateur consulte un profil créateur

-

Alors les informations suivantes sont publiques:

-
| information | visible |
-|---|---|
-| Photo et pseudo | ✅ |
-| Badge vérifié | ✅ |
-| Bio | ✅ |
-| Nombre abonnés | ✅ |
-| Nombre contenus | ✅ |
-| Durée totale créée | ✅ |
-| Écoutes totales | ✅ |
-
-
-

14. Informations privées non visibles

-

Étant donné que l'utilisateur consulte un profil créateur

-

Alors les informations suivantes sont privées:

-
| information | visible |
-|---|---|
-| Liste des abonnés | ❌ |
-| Revenus | ❌ |
-| Localisation précise | ❌ |
-| Email | ❌ |
-
-
-

15. Dashboard créateur avec métriques privées

-

Étant donné que le créateur "@paris_stories" consulte son propre dashboard

-

Quand la page statistiques est affichée

-

Alors les métriques suivantes sont accessibles:

-
| métrique | type |
-|---|---|
-| Taux complétion moyen | 78% |
-| Évolution abonnés | Graphique |
-| Écoutes par contenu | Tableau |
-| Revenus | Dashboard |
-| Taux conversion Premium | Pourcentage |
-| Démographie (âge/zone) | Agrégée |
-
-
-

16. Graphique d'évolution des abonnés

-

Étant donné que le créateur consulte son dashboard

-

Quand il sélectionne la période "30 jours"

-

Alors un graphique d'évolution des abonnés est affiché -Et les périodes disponibles sont:

-
| période |
-|---|
-| 30j |
-| 90j |
-| 1 an |
-
-
-

17. Tableau détaillé des écoutes par contenu

-

Étant donné un créateur avec 10 contenus publiés

-

Quand il consulte le tableau des performances

-

Alors chaque contenu affiche:

-
| métrique | exemple |
-|---|---|
-| Titre | Balade |
-| Écoutes totales | 2300 |
-| Écoutes complètes >80% | 1840 |
-| Taux complétion | 80% |
-| Likes | 420 |
-| Partages | 56 |
-
-
-

18. Affichage du badge vérifié

-

Étant donné un créateur vérifié "@paris_stories"

-

Quand son profil est affiché

-

Alors le badge bleu "✓" est accolé au pseudo -Et un tooltip "Compte vérifié" s'affiche au survol

-
-

19. Badge vérifié visible partout

-

Étant donné un créateur vérifié "@paris_stories"

-

Alors le badge "✓" est affiché dans:

-
| emplacement |
-|---|
-| Page profil |
-| Player en lecture |
-| Résultats de recherche |
-| Notifications |
-
-
-

20. 📋 Plan: Attribution automatique du badge selon critères

-

Étant donné un créateur avec

-

Quand les conditions sont validées

-

Alors le badge vérifié est attribué

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
critèreautomatique
KYC Mangopay validéOui
≥10K abonnés + compte >6 moisOui
Célébrité / Média officielManuel
-
-

21. Attribution automatique via KYC

-

Étant donné un créateur qui complète son KYC Mangopay

-

Quand les documents sont validés

-

Alors le badge vérifié est attribué automatiquement -Et une notification "Votre compte est maintenant vérifié ✓" est envoyée

-
-

22. Attribution automatique à 10K abonnés

-

Étant donné un créateur avec 9999 abonnés et un compte de 7 mois

-

Quand il atteint 10000 abonnés

-

Alors le badge vérifié est attribué automatiquement -Et une notification de félicitations est envoyée

-
-

23. Demande manuelle de vérification (célébrité)

-

Étant donné un créateur reconnu publiquement

-

Quand il soumet le formulaire de demande de vérification

-

Alors une requête est créée pour l'équipe RoadWave -Et l'équipe vérifie l'identité sous 48-72h -Et le badge est attribué si validation réussie

-
-

24. Retrait du badge en cas de suspension

-

Étant donné un créateur vérifié avec le badge "✓"

-

Quand sa monétisation est suspendue

-

Alors le badge vérifié est retiré temporairement -Et le badge est restauré après levée de la suspension

-
-

25. Retrait définitif du badge pour strikes multiples

-

Étant donné un créateur vérifié avec 3 strikes actifs

-

Quand un 4ème strike est appliqué (ban)

-

Alors le badge vérifié est retiré définitivement -Et le compte est banni

-
-

26. Retrait du badge pour usurpation d'identité

-

Étant donné un créateur vérifié qui usurpe l'identité d'une célébrité

-

Quand la fraude est détectée

-

Alors le badge est retiré immédiatement -Et le compte est banni -Et une enquête est ouverte

-
-

27. Profil créateur supprimé

-

Étant donné qu'un utilisateur tente d'accéder à "@deleted_user"

-

Quand la page est chargée

-

Alors un message "Ce profil n'existe pas ou a été supprimé" s'affiche

-
-

28. Blocage d'un créateur

-

Étant donné que l'utilisateur bloque le créateur "@spam_account"

-

Quand l'utilisateur consulte son flux de recommandations

-

Alors aucun contenu de "@spam_account" n'est affiché -Et le créateur n'apparaît plus dans les recherches

-
-

29. Déblocage d'un créateur

-

Étant donné que l'utilisateur a bloqué "@paris_stories"

-

Quand il accède à ses paramètres "Comptes bloqués" -Et qu'il débloque "@paris_stories"

-

Alors les contenus du créateur réapparaissent dans les recommandations

-
-

30. Signalement d'un profil pour spam

-

Étant donné que l'utilisateur signale le profil "@spam_account"

-

Quand il sélectionne la raison "Spam"

-

Alors le signalement est envoyé à la modération -Et un message de confirmation s'affiche -Et le profil reste visible jusqu'à décision de modération

-
-

31. Signalement pour usurpation d'identité

-

Étant donné que l'utilisateur signale le profil "@fake_celebrity"

-

Quand il sélectionne "Usurpation d'identité" -Et qu'il fournit une preuve

-

Alors le signalement est priorisé (priorité HAUTE) -Et l'équipe modération traite sous 24h

-
-
- -

Création de campagnes publicitaires

-
-

En tant que publicitaire -Je veux créer des campagnes avec ciblage précis et maîtrise du budget -Afin d'optimiser mes investissements publicitaires

-
-

30 scénarios (27 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un compte publicitaire est créé et vérifié

-
-

1. Création d'une campagne publicitaire complète

-

Étant donné que je suis connecté en tant que publicitaire

-

Quand je crée une nouvelle campagne avec les paramètres:

-
| Paramètre | Valeur |
-|---|---|
-| Budget total | 300€ |
-| Date début | 2026-02-01 |
-| Date fin | 2026-02-14 |
-| Zone géographique | Département du Var |
-| Plages horaires | 7h-9h, 17h-19h |
-| Tags ciblés | Automobile, Voyage |
-| Tranche d'âge | 18+ |
-
-

Alors la campagne est créée avec succès -Et le budget quotidien calculé est de 21.43€/jour -Et les diffusions estimées sont de ~430 écoutes complètes -Et un statut "En attente de validation" est assigné

-
-

2. Budget minimum 50€ requis

-

Étant donné que je crée une nouvelle campagne

-

Quand je définis un budget de 40€

-

Alors une erreur s'affiche: "Budget minimum requis: 50€" -Et la campagne n'est pas créée

-
-

3. Budget de 50€ exactement accepté

-

Étant donné que je crée une nouvelle campagne

-

Quand je définis un budget de 50€

-

Alors la campagne est créée avec succès -Et aucune erreur n'est affichée

-
-

4. Calcul automatique du budget quotidien

-

Étant donné une campagne avec:

-
| Budget total | 300€ |
-|---|---|
-| Durée | 14 j |
-
-

Quand le système calcule le budget quotidien

-

Alors le budget/jour est de 21.43€ -Et le nombre estimé de diffusions/jour est de 430 (à 0.05€/écoute)

-
-

5. Ciblage géographique point GPS précis

-

Étant donné que je crée une campagne

-

Quand je sélectionne "Point GPS" avec coordonnées (43.1234, 5.9234) -Et que je définis un rayon de 5km

-

Alors la campagne cible uniquement les utilisateurs dans ce rayon -Et la zone est représentée par un cercle sur la carte

-
-

6. Ciblage géographique ville

-

Étant donné que je crée une campagne

-

Quand je sélectionne "Ville" et choisis "Marseille"

-

Alors la campagne cible tous les utilisateurs dans la commune de Marseille -Et les limites administratives sont affichées sur la carte

-
-

7. Ciblage géographique département

-

Étant donné que je crée une campagne

-

Quand je sélectionne "Département" et choisis "Var (83)"

-

Alors la campagne cible tout le département du Var -Et une estimation de population cible est affichée

-
-

8. Ciblage géographique région

-

Étant donné que je crée une campagne

-

Quand je sélectionne "Région" et choisis "Provence-Alpes-Côte d'Azur"

-

Alors la campagne cible toute la région PACA -Et l'estimation de population cible est mise à jour

-
-

9. Ciblage géographique national

-

Étant donné que je crée une campagne

-

Quand je sélectionne "National"

-

Alors la campagne cible tous les utilisateurs en France -Et aucune limite géographique n'est appliquée

-
-

10. Ciblage horaire plages multiples

-

Étant donné que je crée une campagne

-

Quand je définis les plages horaires:

-
| Plage |
-|---|
-| 7h-9h |
-| 12h-14h |
-| 17h-19h |
-
-

Alors la publicité est diffusée uniquement pendant ces plages -Et elle n'est jamais diffusée en dehors (ex: 10h, 15h, 20h)

-
-

11. Ciblage horaire toute la journée

-

Étant donné que je crée une campagne

-

Quand je ne définis aucune plage horaire spécifique

-

Alors la publicité est diffusée 24h/24 -Et aucune restriction horaire n'est appliquée

-
-

12. Ciblage par centres d'intérêt

-

Étant donné que je crée une campagne pour un garage automobile

-

Quand je sélectionne les tags:

-
| Tag |
-|---|
-| Automobile |
-| Mécanique |
-| Sport |
-
-

Alors la publicité est prioritaire pour les utilisateurs avec jauges élevées sur ces tags -Et elle peut quand même être diffusée à d'autres utilisateurs (ciblage non exclusif)

-
-

13. Classification d'âge obligatoire

-

Étant donné que je crée une campagne

-

Quand j'essaie de valider sans sélectionner une tranche d'âge

-

Alors une erreur s'affiche: "Classification d'âge obligatoire" -Et les options proposées sont:

-
| Option |
-|---|
-| Tout public |
-| 13+ |
-| 16+ |
-| 18+ |
-
-
-

14. Upload audio publicitaire formats acceptés

-

Étant donné que je crée une campagne

-

Quand j'upload un fichier audio format MP3

-

Alors le fichier est accepté

-

Quand j'upload un fichier audio format AAC (.aac ou .m4a)

-

Alors le fichier est accepté

-

Quand j'upload un fichier audio format WAV

-

Alors une erreur s'affiche: "Format non supporté. Utilisez MP3 ou AAC"

-
-

15. Durée audio publicitaire validée

-

Étant donné que je crée une campagne

-

Quand j'upload un audio de 8 secondes

-

Alors une erreur s'affiche: "Durée minimale: 10 secondes"

-

Quand j'upload un audio de 65 secondes

-

Alors une erreur s'affiche: "Durée maximale: 60 secondes"

-

Quand j'upload un audio de 30 secondes

-

Alors le fichier est accepté

-
-

16. Prépaiement obligatoire via Mangopay

-

Étant donné que j'ai configuré une campagne à 300€

-

Quand j'arrive à l'étape de paiement

-

Alors je dois payer les 300€ avant validation -Et le paiement est traité via Mangopay -Et seule la carte bancaire est acceptée

-
-

17. Recharge automatique optionnelle

-

Étant donné que j'ai une campagne active

-

Quand je configure la recharge automatique à 10% du budget

-

Alors si le budget restant passe sous 30€ (10% de 300€) -Et que la campagne recharge automatiquement 100€ -Et ma carte bancaire est débitée de 100€ -Et le budget total passe à 130€

-
-

18. Désactivation recharge automatique

-

Étant donné que j'ai activé la recharge automatique

-

Quand je désactive cette option

-

Alors aucune recharge ne se produit automatiquement -Et la campagne s'arrête quand le budget atteint 0€

-
-

19. Étalement budget sur période longue

-

Étant donné une campagne avec:

-
| Budget total | 1000€ |
-|---|---|
-| Durée | 30 j |
-
-

Quand le système calcule l'étalement

-

Alors le budget/jour est de 33.33€ -Et si le budget se consomme plus vite (ex: 50€/jour) -Alors une alerte "Budget épuisé dans 10 jours" est envoyée

-
-

20. Estimation population cible selon zone

-

Étant donné que je sélectionne la zone "Marseille"

-

Quand le système calcule la population cible

-

Alors l'estimation affichée est "~15 000 utilisateurs potentiels" -Et un message "Estimation basée sur utilisateurs actifs dans la zone" s'affiche

-
-

21. Campagne avec date de début différée

-

Étant donné que je crée une campagne

-

Quand je définis la date de début au 2026-03-01 (dans 1 mois)

-

Alors la campagne a le statut "Programmée" -Et elle démarre automatiquement le 2026-03-01 à 00h00 -Et le budget n'est pas consommé avant cette date

-
-

22. Interface self-service accessible

-

Étant donné que je suis un publicitaire

-

Quand j'accède à l'interface publicitaire

-

Alors je peux créer une campagne sans contact commercial RoadWave -Et toutes les options sont configurables en autonomie -Et un tutoriel guidé est disponible (première utilisation)

-
-

23. Aperçu zone ciblée sur carte interactive

-

Étant donné que je configure une zone géographique

-

Quand je sélectionne "Département du Var"

-

Alors une carte Leaflet affiche les limites du département en surbrillance -Et un compteur "~50 000 utilisateurs actifs" est affiché -Et je peux zoomer/dézoomer pour visualiser la zone

-
-

24. Tags multiples pour ciblage affiné

-

Étant donné que je crée une campagne pour un restaurant

-

Quand je sélectionne les tags:

-
| Tag |
-|---|
-| Gastronomie |
-| Tourisme |
-| Famille |
-
-

Alors la publicité est prioritaire pour utilisateurs intéressés par ces 3 thèmes -Et le score de ciblage combine les 3 jauges d'intérêt

-
-

25. Validation des dates de campagne

-

Étant donné que je crée une campagne

-

Quand je définis une date de début postérieure à la date de fin

-

Alors une erreur s'affiche: "Date de fin doit être après date de début" -Et la campagne n'est pas créée

-
-

26. Durée minimale de campagne

-

Étant donné que je crée une campagne

-

Quand je définis une durée de moins de 24 heures

-

Alors une erreur s'affiche: "Durée minimale: 1 jour" -Et je dois ajuster les dates

-
-

27. Durée maximale de campagne

-

Étant donné que je crée une campagne

-

Quand je définis une durée de plus de 90 jours

-

Alors une erreur s'affiche: "Durée maximale: 90 jours" -Et je dois ajuster les dates ou créer plusieurs campagnes

-
-

28. 📋 Plan: Calcul budget quotidien selon durée

-

Étant donné une campagne avec un budget de

-

Quand la durée est de jours

-

Alors le budget quotidien est de €/jour

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
budgetdureebudget_jour
1001010.00
3001421.43
5003016.67
10006016.67
-
-

29. 📋 Plan: Estimation diffusions selon budget

-

Étant donné un budget quotidien de

-

Quand le coût par écoute complète est 0.05€

-

Alors le nombre estimé de diffusions/jour est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
budget_jourdiffusions
10.00200
21.43429
50.001000
100.002000
-
-

30. 📋 Plan: Formats audio acceptés/rejetés

-

Étant donné que j'upload un fichier

-

Quand le format est

-

Alors le résultat est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fichierformatresultat
pub.mp3MP3accepté
pub.aacAACaccepté
pub.m4aAACaccepté
pub.wavWAVrejeté
pub.oggOGGrejeté
pub.flacFLACrejeté
-
-
- -

Caractéristiques et facturation des publicités

-
-

En tant que système RoadWave -Je veux appliquer des règles précises de durée, skippabilité et facturation -Afin d'équilibrer expérience utilisateur et rentabilité publicitaire

-
-

32 scénarios (29 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur gratuit écoute du contenu

-
-

1. Durée minimale 10 secondes

-

Étant donné qu'un publicitaire uploade une publicité de 8 secondes

-

Quand le système valide la durée

-

Alors une erreur s'affiche: "Durée minimale: 10 secondes" -Et l'upload est rejeté

-
-

2. Durée maximale 60 secondes

-

Étant donné qu'un publicitaire uploade une publicité de 65 secondes

-

Quand le système valide la durée

-

Alors une erreur s'affiche: "Durée maximale: 60 secondes" -Et l'upload est rejeté

-
-

3. Durée recommandée 15-30 secondes

-

Étant donné qu'un publicitaire crée une campagne

-

Quand il voit les recommandations

-

Alors un message s'affiche:

-
-

4. Publicité de 10 secondes acceptée

-

Étant donné qu'un publicitaire uploade une publicité de 10 secondes

-

Quand le système valide la durée

-

Alors le fichier est accepté -Et aucune erreur n'est affichée

-
-

5. Publicité de 60 secondes acceptée

-

Étant donné qu'un publicitaire uploade une publicité de 60 secondes

-

Quand le système valide la durée

-

Alors le fichier est accepté -Et un avertissement s'affiche: "⚠️ Durée longue: taux de skip potentiellement élevé"

-
-

6. Délai minimum skippable 5 secondes par défaut

-

Étant donné qu'une publicité de 30 secondes démarre -Et que le délai minimal est configuré à 5 secondes

-

Quand j'écoute pendant 3 secondes

-

Alors le bouton "Passer" n'est pas visible -Et je dois attendre 2 secondes supplémentaires

-

Quand j'atteins 5 secondes d'écoute

-

Alors le bouton "Passer" apparaît -Et je peux cliquer pour passer au contenu suivant

-
-

7. Délai minimum paramétrable admin (3 secondes)

-

Étant donné que l'admin configure le délai à 3 secondes -Et qu'une publicité démarre

-

Quand j'écoute pendant 3 secondes

-

Alors le bouton "Passer" apparaît immédiatement -Et je peux skipper

-
-

8. Délai minimum paramétrable admin (10 secondes)

-

Étant donné que l'admin configure le délai à 10 secondes -Et qu'une publicité démarre

-

Quand j'écoute pendant 9 secondes

-

Alors le bouton "Passer" n'est toujours pas visible

-

Quand j'atteins 10 secondes

-

Alors le bouton "Passer" apparaît

-
-

9. Facturation écoute complète (>80%) - 0.05€

-

Étant donné qu'une publicité de 30 secondes est diffusée

-

Quand j'écoute pendant 25 secondes (83%)

-

Alors l'écoute est considérée comme "complète" -Et le publicitaire est facturé 0.05€ -Et le compteur "écoutes complètes" s'incrémente

-
-

10. Facturation écoute complète exactement 80%

-

Étant donné qu'une publicité de 30 secondes est diffusée

-

Quand j'écoute pendant exactement 24 secondes (80%)

-

Alors l'écoute est considérée comme "complète" -Et le publicitaire est facturé 0.05€

-
-

11. Facturation skip après délai minimal - 0.02€

-

Étant donné qu'une publicité de 30 secondes est diffusée -Et que le délai minimal est 5 secondes

-

Quand j'écoute pendant 10 secondes (33%) -Et que je clique sur "Passer"

-

Alors l'écoute est considérée comme "partielle" -Et le publicitaire est facturé 0.02€

-
-

12. Facturation skip immédiat (<5s) - 0€

-

Étant donné qu'une publicité de 30 secondes est diffusée -Et que le délai minimal est 5 secondes

-

Quand j'écoute pendant 3 secondes -Et que je clique sur "Suivant" (pas de bouton skip encore)

-

Alors l'écoute est considérée comme "non engagée" -Et le publicitaire n'est PAS facturé (0€)

-
-

13. Comptabilisation écoute complète à 79%

-

Étant donné qu'une publicité de 30 secondes est diffusée

-

Quand j'écoute pendant 23 secondes (77%)

-

Alors l'écoute est considérée comme "partielle" (pas complète) -Et le publicitaire est facturé 0.02€

-
-

14. Comptabilisation écoute complète à 100%

-

Étant donné qu'une publicité de 30 secondes est diffusée

-

Quand j'écoute les 30 secondes complètes (100%)

-

Alors l'écoute est considérée comme "complète" -Et le publicitaire est facturé 0.05€

-
-

15. Budget consommé selon mix écoutes

-

Étant donné qu'une campagne à 300€ a généré:

-
| Type écoute | Nombre | Coût unitaire | Total |
-|---|---|---|---|
-| Complète (>80%) | 4000 | 0.05€ | 200€ |
-| Partielle (5-80%) | 2000 | 0.02€ | 40€ |
-| Skip immédiat | 1000 | 0€ | 0€ |
-
-

Quand je calcule le budget consommé

-

Alors le total est 240€ -Et il reste 60€ de budget disponible

-
-

16. Affichage compteur secondes restantes

-

Étant donné qu'une publicité de 30s démarre -Et que le délai minimal est 5s

-

Quand j'écoute pendant 2 secondes

-

Alors un compteur s'affiche: "Passer dans 3s..."

-

Quand j'atteins 5 secondes

-

Alors le compteur disparaît -Et le bouton "Passer la publicité" s'affiche

-
-

17. Progress bar publicité visible

-

Étant donné qu'une publicité de 30s est en lecture

-

Quand 10 secondes se sont écoulées

-

Alors la progress bar affiche 33% (10/30) -Et l'indicateur temporel affiche "0:10 / 0:30" -Et l'utilisateur visualise la progression

-
-

18. Message "Publicité" clairement affiché

-

Étant donné qu'une publicité démarre

-

Quand l'audio commence

-

Alors un badge "Publicité" est affiché en haut de l'écran -Et la durée totale est indiquée: "Publicité (30s)" -Et la transparence est maximale (utilisateur sait que c'est une pub)

-
-

19. Transition fluide après publicité

-

Étant donné qu'une publicité de 30s se termine

-

Quand la lecture atteint 30 secondes

-

Alors le délai de transition de 2s démarre -Et le contenu normal suivant est annoncé -Et l'enchaînement est naturel (même UX que entre contenus)

-
-

20. Like autorisé sur publicité

-

Étant donné qu'une publicité est en lecture -Et que le véhicule est à l'arrêt

-

Quand je clique sur le bouton cœur

-

Alors un like explicite (+2%) est enregistré -Et mes jauges d'intérêt sont mises à jour selon les tags de la pub -Et le publicitaire voit un compteur "Likes" incrémenté

-
-

21. Abonnement autorisé sur publicité

-

Étant donné qu'une publicité est diffusée par un créateur -Et que le véhicule est à l'arrêt

-

Quand je clique sur "S'abonner"

-

Alors l'abonnement est enregistré (+5% jauges) -Et le publicitaire bénéficie de l'engagement fort -Et cela compte comme une conversion majeure

-
-

22. Bouton skip visible et accessible

-

Étant donné qu'une publicité a dépassé le délai minimal

-

Quand le bouton "Passer" s'affiche

-

Alors il est positionné en bas à droite de l'écran -Et il a une taille de clic confortable (44×44px minimum iOS) -Et il est clairement visible (contraste élevé)

-
-

23. Analytics tracking précis par type

-

Étant donné qu'une publicité est diffusée

-

Quand un événement se produit

-

Alors il est tracké en temps réel:

-
| Événement | Données enregistrées |
-|---|---|
-| Impression | timestamp, user_id, pub_id, zone_geo |
-| Écoute complète | durée_ecoutee, pourcentage, coût (0.05€) |
-| Skip après délai | durée_ecoutee, pourcentage, coût (0.02€) |
-| Skip immédiat | durée_ecoutee, pourcentage, coût (0€) |
-| Like | timestamp, tags impactés |
-| Abonnement | timestamp, creator_id |
-
-
-

24. Recommandation sweet spot 15-30s

-

Étant donné les statistiques RoadWave globales:

-
| Durée pub | Taux complétion moyen |
-|---|---|
-| 10s | 65% |
-| 15s | 55% |
-| 30s | 45% |
-| 45s | 30% |
-| 60s | 20% |
-
-

Quand un publicitaire consulte les recommandations

-

Alors le sweet spot affiché est "15-30 secondes" -Et l'explication est "Meilleur compromis engagement/message"

-
-

25. Optimisation durée selon taux de skip campagne

-

Étant donné qu'une campagne de 60s a un taux de skip de 85%

-

Quand le publicitaire consulte les recommandations

-

Alors le système suggère:

-
-

26. Coût effectif moyen (CEM) calculé

-

Étant donné une campagne avec:

-
| Type écoute | Nombre | Coût unitaire | Total |
-|---|---|---|---|
-| Complète | 2000 | 0.05€ | 100€ |
-| Partielle | 3000 | 0.02€ | 60€ |
-| Skip immédiat | 1000 | 0€ | 0€ |
-
-

Quand je calcule le coût effectif moyen

-

Alors CEM = 160€ / 6000 impressions = 0.027€/impression -Et cette métrique aide à comparer avec CPM industrie

-
-

27. Publicité non skippable interdite

-

Étant donné qu'un publicitaire demande "Publicité non skippable"

-

Quand il configure sa campagne

-

Alors cette option n'existe pas -Et toutes les publicités sont obligatoirement skippables après 5s minimum

-
-

28. Délai minimal jamais <3 secondes

-

Étant donné qu'un admin essaie de configurer le délai à 2 secondes

-

Quand il valide le paramètre

-

Alors une erreur s'affiche: "Délai minimal: 3 secondes minimum"

-
-

29. Délai minimal jamais >10 secondes

-

Étant donné qu'un admin essaie de configurer le délai à 15 secondes

-

Quand il valide le paramètre

-

Alors une erreur s'affiche: "Délai maximal: 10 secondes maximum"

-
-

30. 📋 Plan: Facturation selon durée écoutée

-

Étant donné qu'une publicité de 30s est diffusée

-

Quand j'écoute pendant s (%)

-

Alors le type d'écoute est -Et le coût facturé est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
dureepourcentagetypecout
310skip immédiat0
517partielle0.02
1033partielle0.02
2067partielle0.02
2480complète0.05
2790complète0.05
30100complète0.05
-
-

31. 📋 Plan: Budget consommé selon distribution écoutes

-

Étant donné écoutes complètes à 0.05€ -Et écoutes partielles à 0.02€ -Et skips immédiats à 0€

-

Quand je calcule le budget total consommé

-

Alors le résultat est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
completespartiellesskipsbudget_total
100050010060
20001000500120
500020001000290
01000020
10000050
-
-

32. 📋 Plan: Apparition bouton skip selon délai configuré

-

Étant donné que le délai minimal est configuré à s

-

Quand j'écoute pendant s

-

Alors le bouton "Passer" est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
delaitemps_ecoutevisible
53non visible
55visible
510visible
108non visible
1010visible
32non visible
33visible
-
-
- -

Gestion du budget et alertes publicitaires

-
-

En tant que publicitaire -Je veux suivre en temps réel mon budget et recevoir des alertes -Afin de maîtriser mes dépenses et optimiser mes campagnes

-
-

30 scénarios (27 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un compte publicitaire est connecté -Et qu'une campagne active est en cours

-
-

1. Dashboard budget temps réel

-

Étant donné que ma campagne a un budget de 300€ -Et que j'ai consommé 220€

-

Quand je consulte le dashboard budget

-

Alors je vois:

-
| Métrique | Valeur |
-|---|---|
-| Budget total | 300€ |
-| Budget consommé | 220€ |
-| Budget restant | 80€ |
-| Pourcentage | 73% consommé |
-
-
-

2. Jauge visuelle budget consommé

-

Étant donné que j'ai consommé 220€ sur 300€

-

Quand je consulte le dashboard

-

Alors une jauge de progression affiche 73% -Et la couleur est orange (seuil 50-80%) -Et un indicateur "80€ restants" est affiché clairement

-
-

3. Couleur jauge selon seuil

-

Étant donné un budget de 300€

-

Quand j'ai consommé 150€ (50%)

-

Alors la jauge est verte

-

Quand j'ai consommé 240€ (80%)

-

Alors la jauge est orange

-

Quand j'ai consommé 285€ (95%)

-

Alors la jauge est rouge -Et un message "Budget presque épuisé" s'affiche

-
-

4. Projection épuisement budget

-

Étant donné que j'ai consommé 220€ en 10 jours -Et qu'il reste 4 jours de campagne

-

Quand le système calcule la projection

-

Alors la consommation quotidienne moyenne est 22€/jour -Et la projection affiche "Budget épuisé dans 3.6 jours" -Et un avertissement "Campagne s'arrêtera avant la fin prévue" s'affiche

-
-

5. Projection avec budget suffisant

-

Étant donné que j'ai consommé 100€ en 10 jours -Et qu'il reste 4 jours de campagne -Et que le budget total est 300€

-

Quand le système calcule la projection

-

Alors la consommation quotidienne moyenne est 10€/jour -Et la projection affiche "Budget suffisant pour toute la campagne" -Et le budget restant estimé à la fin est 160€

-
-

6. Alerte 80% budget consommé

-

Étant donné que mon budget est de 300€

-

Quand je consomme 240€ (80%)

-

Alors je reçois immédiatement un email: -Et une notification push est envoyée -Et une notification in-app s'affiche

-
-

7. Alerte 90% budget consommé

-

Étant donné que mon budget est de 300€

-

Quand je consomme 270€ (90%)

-

Alors je reçois immédiatement un email:

-
-

8. Alerte budget épuisé (100%)

-

Étant donné que mon budget est de 300€

-

Quand je consomme les 300€ (100%)

-

Alors je reçois immédiatement un email: -Et la campagne est automatiquement mise en pause -Et plus aucune diffusion ne se produit

-
-

9. Pause manuelle de campagne

-

Étant donné que ma campagne est active -Et qu'il reste 150€ de budget

-

Quand je clique sur "Mettre en pause"

-

Alors le statut passe à "En pause" -Et les diffusions s'arrêtent immédiatement -Et le budget de 150€ est conservé -Et je peux réactiver la campagne plus tard

-
-

10. Reprise campagne pausée

-

Étant donné que ma campagne est en pause -Et qu'il reste 150€ de budget

-

Quand je clique sur "Reprendre la campagne"

-

Alors le statut passe à "Active" -Et les diffusions reprennent immédiatement -Et le budget restant de 150€ continue de se consommer

-
-

11. Prolongation campagne avec recharge

-

Étant donné que ma campagne se termine dans 2 jours -Et qu'il reste 20€ de budget

-

Quand je clique sur "Prolonger la campagne" -Et que j'ajoute 200€ supplémentaires

-

Alors le budget total passe à 220€ -Et la date de fin peut être prolongée de 10 jours -Et un nouveau paiement Mangopay de 200€ est traité

-
-

12. Recharge automatique activée

-

Étant donné que j'ai configuré la recharge automatique -Et que le seuil est fixé à 10% (30€ sur budget 300€) -Et que le montant de recharge est 100€

-

Quand le budget restant passe sous 30€

-

Alors une recharge automatique de 100€ est déclenchée -Et ma carte bancaire est débitée via Mangopay -Et le budget total passe à budget_restant + 100€ -Et je reçois un email de confirmation

-
-

13. Échec recharge automatique (carte expirée)

-

Étant donné que la recharge automatique est activée -Et que ma carte bancaire a expiré

-

Quand le budget passe sous le seuil de 10%

-

Alors la recharge automatique échoue -Et je reçois un email urgent: -Et la campagne continue jusqu'à épuisement du budget restant

-
-

14. Modification ciblage si budget <50% consommé

-

Étant donné que j'ai consommé 120€ sur 300€ (40%)

-

Quand j'essaie de modifier le ciblage géographique

-

Alors la modification est autorisée -Et le ciblage est mis à jour immédiatement -Et les nouvelles diffusions utilisent le nouveau ciblage

-
-

15. Blocage modification ciblage si budget >50% consommé

-

Étant donné que j'ai consommé 180€ sur 300€ (60%)

-

Quand j'essaie de modifier le ciblage géographique

-

Alors une erreur s'affiche:

-
-

16. Modification audio nécessite nouvelle validation

-

Étant donné que ma campagne est active

-

Quand je veux modifier le fichier audio

-

Alors un message s'affiche:

-
-

17. Modification plages horaires autorisée

-

Étant donné que ma campagne cible 7h-9h et 17h-19h

-

Quand je modifie pour cibler 12h-14h aussi

-

Alors la modification est appliquée immédiatement -Et les diffusions suivantes incluent la nouvelle plage -Et aucune re-validation n'est nécessaire

-
-

18. Historique consommation budget jour par jour

-

Étant donné que ma campagne a duré 10 jours

-

Quand je consulte l'historique

-

Alors je vois un graphique avec:

-
| Jour | Consommation | Cumulé |
-|---|---|---|
-| 1 | 22€ | 22€ |
-| 2 | 25€ | 47€ |
-| 3 | 20€ | 67€ |
-| ... | ... | ... |
-| 10 | 18€ | 220€ |
-
-

Et je peux identifier les pics de consommation

-
-

19. Notification fin de campagne programmée

-

Étant donné que ma campagne se termine le 14/02

-

Quand la date de fin est atteinte

-

Alors je reçois un email:

-
-

20. Remboursement budget non utilisé

-

Étant donné que ma campagne avait 300€ de budget -Et qu'elle s'est terminée avec 280€ consommés

-

Quand la campagne se termine (date ou épuisement)

-

Alors un remboursement de 20€ est initié via Mangopay -Et le délai est de 5-7 jours ouvrés -Et je reçois une notification de confirmation

-
-

21. Aucun remboursement si budget entièrement consommé

-

Étant donné que ma campagne avait 300€ de budget -Et qu'elle s'est terminée avec 300€ consommés

-

Quand la campagne se termine

-

Alors aucun remboursement n'est initié -Et le message final indique "Budget entièrement utilisé"

-
-

22. Statistiques comparatives budget vs objectif

-

Étant donné que j'avais défini un objectif de 5000 impressions -Et que mon budget était 300€

-

Quand je consulte les statistiques finales

-

Alors je vois:

-
| Métrique | Objectif | Réalisé | Écart |
-|---|---|---|---|
-| Impressions | 5000 | 6000 | +20% |
-| Budget | 300€ | 280€ | -7% |
-| Coût/impression | 0.06€ | 0.047€ | -22% |
-
-

Et une analyse "✅ Objectifs dépassés avec budget optimisé"

-
-

23. Export rapport financier détaillé

-

Étant donné que je veux analyser mes dépenses

-

Quand je clique sur "Exporter rapport financier"

-

Alors je télécharge un CSV avec:

-
| Colonne |
-|---|
-| Date/Heure |
-| Type écoute |
-| Coût unitaire |
-| Zone géographique |
-| Utilisateur (anonyme) |
-| Durée écoutée |
-
-

Et je peux l'importer dans Excel pour analyses

-
-

24. Tableau de bord multi-campagnes

-

Étant donné que j'ai 3 campagnes actives

-

Quand je consulte la vue d'ensemble

-

Alors je vois un tableau récapitulatif:

-
| Campagne | Budget | Consommé | % | Jours restants | Projection |
-|---|---|---|---|---|---|
-| A | 300€ | 220€ | 73 | 4j | Suffisant |
-| B | 500€ | 480€ | 96 | 10j | Épuisé 2j |
-| C | 200€ | 50€ | 25 | 20j | Suffisant |
-
-

Et un badge alerte rouge sur la campagne B

-
-

25. Alerte consolidée multi-campagnes

-

Étant donné que j'ai 5 campagnes actives -Et que 2 campagnes ont >80% budget consommé

-

Quand je reçois les notifications

-

Alors un email consolidé unique est envoyé: -Et je ne reçois pas 2 emails séparés (évite spam)

-
-

26. Configuration seuils alertes personnalisés

-

Étant donné que je configure mes préférences d'alerte

-

Quand je définis les seuils:

-
| Seuil | Valeur |
-|---|---|
-| Alerte 1 | 70% |
-| Alerte 2 | 85% |
-| Alerte 3 | 95% |
-
-

Alors je reçois des alertes à 70%, 85% et 95% -Et non aux seuils par défaut 80%, 90%, 100%

-
-

27. Désactivation alertes email

-

Étant donné que je préfère uniquement les notifications in-app

-

Quand je désactive les alertes email dans mes préférences

-

Alors je ne reçois plus d'emails d'alerte budget -Mais les notifications in-app continuent -Et les alertes critiques (échec paiement) sont toujours envoyées par email

-
-

28. 📋 Plan: Couleur jauge selon pourcentage consommé

-

Étant donné un budget de 300€

-

Quand j'ai consommé € (%)

-

Alors la couleur de la jauge est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
montantpourcentagecouleur
10033verte
15050verte
18060orange
24080orange
27090rouge
28595rouge
300100rouge
-
-

29. 📋 Plan: Projection épuisement selon consommation

-

Étant donné un budget de 300€ -Et une consommation actuelle de € -Et une durée écoulée de jours

-

Quand je calcule la consommation quotidienne moyenne

-

Alors elle est de €/jour -Et le budget sera épuisé dans jours

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
consommejours_ecoulesconso_jourjours_restants
10052010
20010205
150101510
2701222.51.3
-
-

30. 📋 Plan: Alertes envoyées selon seuils

-

Étant donné un budget de 500€

-

Quand je consomme € (%)

-

Alors je reçois une alerte

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
montantpourcentageniveau
35070aucune
40080alerte 80%
45090alerte 90%
500100budget épuisé
-
-
- -

Insertion et fréquence des publicités

-
-

En tant que système RoadWave -Je veux insérer les publicités de manière équilibrée et non intrusive -Afin de préserver l'expérience utilisateur tout en monétisant

-
-

31 scénarios (28 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un utilisateur gratuit est connecté

-
-

1. Fréquence par défaut 1 pub / 5 contenus

-

Étant donné que la fréquence par défaut est configurée à 1/5 -Et que je suis un utilisateur gratuit

-

Quand j'écoute 5 contenus

-

Alors 1 publicité est insérée après le 5ème contenu

-

Quand j'écoute 10 contenus

-

Alors 2 publicités sont insérées (après les contenus 5 et 10)

-
-

2. Aucune publicité pour utilisateurs Premium

-

Étant donné que je suis un utilisateur Premium

-

Quand j'écoute 100 contenus

-

Alors aucune publicité n'est insérée -Et je bénéficie d'une expérience sans interruption publicitaire

-
-

3. Fréquence paramétrable par admin (1/3)

-

Étant donné que l'admin configure la fréquence à 1/3 -Et que je suis un utilisateur gratuit

-

Quand j'écoute 6 contenus

-

Alors 2 publicités sont insérées (après contenus 3 et 6)

-
-

4. Fréquence paramétrable par admin (1/10)

-

Étant donné que l'admin configure la fréquence à 1/10 -Et que je suis un utilisateur gratuit

-

Quand j'écoute 20 contenus

-

Alors 2 publicités sont insérées (après contenus 10 et 20)

-
-

5. Jamais d'interruption d'un contenu en cours

-

Étant donné que j'écoute un contenu de 10 minutes -Et que je suis à 5 minutes de lecture -Et qu'une publicité devrait être insérée selon la fréquence

-

Quand le système vérifie l'insertion

-

Alors la publicité attend la fin du contenu actuel -Et elle s'insère pendant le délai de transition (2s) -Et le contenu n'est jamais interrompu

-
-

6. Insertion entre deux contenus uniquement

-

Étant donné que le contenu "A" se termine -Et que le délai de transition de 2s démarre

-

Quand le système détecte qu'une publicité doit être insérée

-

Alors le message "Publicité (30s)" s'affiche -Et la publicité démarre après les 2 secondes -Et l'enchaînement est naturel et fluide

-
-

7. Rotation limite 3 fois/jour par utilisateur

-

Étant donné qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui

-

Quand le système sélectionne une nouvelle publicité à diffuser

-

Alors la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui -Et une autre publicité "B" est sélectionnée -Et cela évite la saturation publicitaire

-
-

8. Compteur de diffusions par pub et par utilisateur

-

Étant donné qu'un utilisateur écoute la pub "RestaurantX"

-

Quand la diffusion se termine

-

Alors un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente -Et le TTL est de 24h (reset à minuit)

-

Quand le compteur atteint 3

-

Alors la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui

-
-

9. Limite max 6 pubs/heure par utilisateur

-

Étant donné qu'un utilisateur a entendu 6 publicités dans la dernière heure

-

Quand le système devrait insérer une 7ème pub

-

Alors l'insertion est reportée à l'heure suivante -Et un compteur horaire Redis "pub:user:123:hourly" est vérifié -Et cela évite le spam publicitaire

-
-

10. Ciblage géographique prioritaire - Point GPS

-

Étant donné qu'une publicité cible un point GPS à 2km de ma position -Et qu'une autre publicité cible ma ville entière

-

Quand le système sélectionne une publicité

-

Alors la publicité point GPS est priorisée (score géo plus élevé) -Et le ciblage précis est favorisé

-
-

11. Ciblage géographique prioritaire - Hiérarchie

-

Étant donné que 4 publicités sont éligibles:

-
| Publicité | Zone | Distance |
-|---|---|---|
-| A | Point GPS | 1km |
-| B | Ville | 0km |
-| C | Département | 0km |
-| D | National | N/A |
-
-

Quand le système sélectionne selon priorité géographique

-

Alors l'ordre de priorité est: A > B > C > D -Et la publicité A (Point GPS, la plus précise) est diffusée

-
-

12. Ciblage centres d'intérêt secondaire

-

Étant donné que 2 publicités ciblent ma zone géographique:

-
| Publicité | Tags | Mes jauges |
-|---|---|---|
-| A | Automobile | 80% |
-| B | Voyage | 40% |
-
-

Quand le système applique le score centres d'intérêt

-

Alors la publicité A est favorisée (meilleur match jauges) -Et le ciblage thématique affine la sélection

-
-

13. Ciblage horaire strict

-

Étant donné qu'une campagne cible uniquement 7h-9h -Et qu'il est 10h30

-

Quand le système sélectionne une publicité

-

Alors cette campagne n'est PAS éligible -Et seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées

-
-

14. Ciblage horaire pendant plage active

-

Étant donné qu'une campagne cible 7h-9h et 17h-19h -Et qu'il est 8h15

-

Quand le système sélectionne une publicité

-

Alors cette campagne est éligible -Et elle peut être diffusée

-
-

15. Normalisation volume audio -14 LUFS

-

Étant donné qu'une publicité est uploadée avec volume trop élevé (-6 LUFS)

-

Quand le système encode l'audio via FFmpeg

-

Alors le volume est normalisé automatiquement à -14 LUFS -Et le publicitaire reçoit une notification "Volume audio ajusté pour conformité" -Et cela évite l'effet "pub trop forte" frustrant

-
-

16. Validation volume audio lors encodage

-

Étant donné qu'une publicité est soumise

-

Quand FFmpeg encode le fichier

-

Alors une commande loudnorm est appliquée: -Et le fichier final respecte le standard broadcast -14 LUFS

-
-

17. Sélection aléatoire si critères équivalents

-

Étant donné que 3 publicités ont le même score géo -Et qu'elles ont toutes des jauges centres d'intérêt équivalentes -Et qu'aucune n'a été diffusée 3 fois aujourd'hui

-

Quand le système sélectionne une publicité

-

Alors une sélection aléatoire équitable est faite -Et chaque campagne a 33% de chances d'être diffusée

-
-

18. Exclusion publicités avec budget épuisé

-

Étant donné qu'une campagne "A" a épuisé son budget -Et qu'une campagne "B" a encore du budget disponible

-

Quand le système sélectionne une publicité

-

Alors seule la campagne "B" est éligible -Et la campagne "A" est automatiquement exclue

-
-

19. Exclusion publicités hors dates de campagne

-

Étant donné qu'une campagne "A" est programmée du 01/02 au 14/02 -Et que nous sommes le 20/01

-

Quand le système sélectionne une publicité

-

Alors la campagne "A" n'est pas éligible -Et seules les campagnes actives aujourd'hui sont considérées

-
-

20. Publicité visible uniquement dans zone géographique

-

Étant donné qu'une publicité cible "Marseille uniquement" -Et que je suis à Lyon

-

Quand le système sélectionne une publicité

-

Alors cette publicité n'est jamais éligible pour moi -Et je ne la verrai jamais tant que je reste à Lyon

-
-

21. Tracking compteur horaire avec TTL

-

Étant donné qu'un utilisateur entend une pub à 10h05

-

Quand le compteur horaire est incrémenté

-

Alors la clé Redis "pub:user:123:hourly:2026012110" est créée -Et le TTL est de 1 heure (expire à 11h05) -Et le système compte les pubs dans la fenêtre glissante d'1h

-
-

22. Reset compteur quotidien à minuit

-

Étant donné qu'un utilisateur a entendu la pub "A" 3 fois le 20/01

-

Quand minuit passe et on est le 21/01

-

Alors le compteur "pub:A:user:123:count" est expiré (TTL 24h) -Et l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois

-
-

23. Aucune pub si aucune campagne éligible

-

Étant donné qu'aucune campagne n'a de budget disponible

-

Quand le système devrait insérer une publicité

-

Alors aucune pub n'est insérée -Et l'enchaînement de contenus continue normalement -Et le prochain contenu démarre directement

-
-

24. Priorisation campagnes avec budget important restant

-

Étant donné que 2 campagnes sont éligibles:

-
| Campagne | Budget restant | Jours restants |
-|---|---|---|
-| A | 500€ | 2j |
-| B | 50€ | 10j |
-
-

Quand le système applique la priorisation budgétaire

-

Alors la campagne A est légèrement favorisée (urgence dépense) -Et cela aide à épuiser les budgets avant fin de campagne

-
-

25. Log des sélections pour analytics

-

Étant donné qu'une publicité "RestaurantX" est sélectionnée

-

Quand elle est diffusée à l'utilisateur "123"

-

Alors un événement est loggé en base:

-
| Champ | Valeur |
-|---|---|
-| pub_id | RestaurantX |
-| user_id | 123 |
-| timestamp | 2026-01-21 10:30 |
-| zone_geo | Marseille |
-| score_geo | 0.85 |
-| score_interet | 0.70 |
-
-

Et cela permet l'analytics publicitaire

-
-

26. Détection changement statut utilisateur (gratuit → premium)

-

Étant donné que je suis un utilisateur gratuit -Et que j'entends des publicités

-

Quand je souscris à Premium

-

Alors le système détecte le changement de statut immédiatement -Et plus aucune publicité n'est insérée dès le prochain contenu -Et mon expérience devient sans pub instantanément

-
-

27. Interface admin pour ajuster fréquence globale

-

Étant donné que je suis admin RoadWave

-

Quand j'accède aux paramètres publicitaires

-

Alors je peux ajuster le curseur de fréquence:

-
| Option | Fréquence |
-|---|---|
-| 1/3 | Haute (agressif) |
-| 1/5 | Standard (défaut) |
-| 1/7 | Modérée |
-| 1/10 | Faible |
-
-

Et le changement s'applique en temps réel à tous les utilisateurs

-
-

28. A/B testing fréquence sur cohortes utilisateurs

-

Étant donné que l'admin active un test A/B

-

Quand 50% des utilisateurs ont fréquence 1/5 -Et 50% des utilisateurs ont fréquence 1/7

-

Alors les métriques sont trackées séparément:

-
| Cohorte | Fréquence | Taux désabonnement | Revenus/user |
-|---|---|---|---|
-| A | 1/5 | 2.5% | 0.50€ |
-| B | 1/7 | 1.8% | 0.40€ |
-
-

Et l'admin peut identifier la fréquence optimale

-
-

29. 📋 Plan: Insertion publicité selon fréquence

-

Étant donné que la fréquence est

-

Quand j'écoute contenus

-

Alors publicités sont insérées

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
frequencecontenuspubs
1/393
1/5102
1/5255
1/7142
1/10303
-
-

30. 📋 Plan: Priorité géographique selon type zone

-

Étant donné qu'une publicité cible

-

Quand le système calcule le score géographique

-

Alors la priorité est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
type_zonescore
Point GPS1.0
Ville0.8
Département0.6
Région0.4
National0.2
-
-

31. 📋 Plan: Exclusion publicité selon compteur quotidien

-

Étant donné qu'une publicité a été entendue fois aujourd'hui

-

Quand le système vérifie l'éligibilité

-

Alors la publicité est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
foiseligible
0éligible
1éligible
2éligible
3non éligible
4non éligible
-
-
- -

Métriques d'engagement et dashboard publicitaire

-
-

En tant que publicitaire -Je veux consulter des métriques détaillées en temps réel -Afin d'optimiser mes campagnes et mesurer leur ROI

-
-

27 scénarios (24 standards, 3 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un compte publicitaire est connecté -Et qu'une campagne active est en cours

-
-

1. Dashboard temps réel avec métriques essentielles

-

Étant donné que ma campagne a généré 1000 diffusions

-

Quand je consulte le dashboard

-

Alors je vois les métriques suivantes mises à jour en temps réel:

-
| Métrique | Valeur |
-|---|---|
-| Impressions | 1000 |
-| Écoutes complètes (>80%) | 400 |
-| Taux d'écoute complète | 40% |
-| Taux de skip | 60% |
-| Durée moyenne d'écoute | 18s |
-| Likes | 25 |
-| Abonnements | 5 |
-| Coût par écoute | 0.05€ |
-
-
-

2. Calcul impressions totales

-

Étant donné que ma publicité a été diffusée 2500 fois

-

Quand je consulte le dashboard

-

Alors le compteur "Impressions" affiche 2500 -Et il s'incrémente en temps réel à chaque nouvelle diffusion

-
-

3. Calcul écoutes complètes (>80%)

-

Étant donné que ma publicité de 30s a été:

-
| Durée écoutée | Nombre |
-|---|---|
-| 25s (83%) | 300 |
-| 20s (67%) | 200 |
-| 10s (33%) | 150 |
-| 5s (17%) | 50 |
-
-

Quand je consulte les écoutes complètes

-

Alors le compteur affiche 300 (uniquement ≥80%) -Et le taux d'écoute complète est de 43% (300/700)

-
-

4. Calcul taux de skip

-

Étant donné 1000 diffusions totales -Et 400 écoutes complètes

-

Quand je consulte le taux de skip

-

Alors il affiche 60% ((1000-400)/1000) -Et il est calculé comme (total - complètes) / total

-
-

5. Durée moyenne d'écoute calculée

-

Étant donné que ma publicité de 30s a été écoutée:

-
| Durée | Nombre d'utilisateurs |
-|---|---|
-| 30s | 400 |
-| 20s | 300 |
-| 10s | 200 |
-| 5s | 100 |
-
-

Quand je consulte la durée moyenne

-

Alors le calcul est: (30×400 + 20×300 + 10×200 + 5×100) / 1000 -Et le résultat affiché est 21s

-
-

6. Métriques de likes sur publicité

-

Étant donné que 50 utilisateurs ont liké ma publicité

-

Quand je consulte le dashboard

-

Alors le compteur "Likes" affiche 50 -Et un taux de like de 5% est calculé (50/1000 impressions) -Et cela indique une forte appréciation du contenu

-
-

7. Métriques d'abonnements générés

-

Étant donné que 10 utilisateurs se sont abonnés après avoir entendu ma pub

-

Quand je consulte le dashboard

-

Alors le compteur "Abonnements" affiche 10 -Et un taux de conversion de 1% est calculé (10/1000) -Et cela représente un engagement très fort

-
-

8. Calcul coût par écoute (CPE)

-

Étant donné que j'ai dépensé 200€ -Et obtenu 4000 écoutes complètes

-

Quand je consulte le coût par écoute

-

Alors le CPE affiché est 0.05€ (200/4000) -Et il correspond au tarif standard RoadWave

-
-

9. Répartition géographique avec heatmap

-

Étant donné que ma campagne cible le département du Var -Et que j'ai 1000 diffusions réparties:

-
| Zone | Diffusions | Pourcentage |
-|---|---|---|
-| Toulon | 400 | 40% |
-| Hyères | 250 | 25% |
-| Fréjus | 200 | 20% |
-| Autres | 150 | 15% |
-
-

Quand je consulte la heatmap géographique

-

Alors une carte Leaflet affiche les zones avec intensité proportionnelle -Et Toulon apparaît en rouge foncé (forte concentration) -Et les autres villes en dégradé orange/jaune

-
-

10. Répartition horaire avec graphique

-

Étant donné que ma campagne cible les plages 7h-9h et 17h-19h -Et que j'ai 1000 diffusions:

-
| Plage horaire | Diffusions |
-|---|---|
-| 7h-8h | 300 |
-| 8h-9h | 250 |
-| 17h-18h | 280 |
-| 18h-19h | 170 |
-
-

Quand je consulte le graphique horaire

-

Alors un histogramme Chart.js affiche les 4 barres -Et je peux identifier que 7h-8h est le pic d'écoute -Et optimiser mes futures campagnes sur cette plage

-
-

11. Taux de complétion par tranche d'âge

-

Étant donné que ma campagne est Tout Public -Et que j'ai des écoutes sur différentes tranches:

-
| Tranche d'âge | Écoutes complètes | Total diffusions | Taux |
-|---|---|---|---|
-| 18-24 ans | 120 | 400 | 30% |
-| 25-34 ans | 200 | 400 | 50% |
-| 35-44 ans | 80 | 200 | 40% |
-
-

Quand je consulte l'analyse par âge

-

Alors je vois que les 25-34 ans ont le meilleur taux (50%) -Et je peux cibler cette tranche pour mes prochaines campagnes

-
-

12. Comparatif de campagnes A/B testing

-

Étant donné que j'ai 2 campagnes actives:

-
| Campagne | Budget | Écoutes complètes | Taux | CPE |
-|---|---|---|---|---|
-| A | 300€ | 4000 | 40% | 0.075€ |
-| B | 300€ | 6000 | 60% | 0.05€ |
-
-

Quand je consulte le comparatif

-

Alors je vois que la campagne B performe mieux -Et le tableau recommande "Campagne B: +50% écoutes, -33% CPE" -Et je peux allouer plus de budget à la campagne B

-
-

13. Export données CSV pour analyse externe

-

Étant donné que je veux analyser mes données dans Excel

-

Quand je clique sur "Exporter CSV"

-

Alors je télécharge un fichier avec les colonnes:

-
| Colonne |
-|---|
-| Date |
-| Heure |
-| Zone géographique |
-| Tranche d'âge |
-| Durée écoute |
-| Skip (Oui/Non) |
-| Like (Oui/Non) |
-| Abonnement (Oui/Non) |
-
-

Et je peux faire des analyses personnalisées

-
-

14. Export graphiques interactifs

-

Étant donné que je consulte le dashboard

-

Quand je clique sur un graphique Chart.js

-

Alors je peux zoomer/filtrer interactivement -Et je peux exporter le graphique en PNG -Et l'image est en haute résolution pour présentations

-
-

15. Rapport PDF automatique fin de campagne

-

Étant donné que ma campagne de 14 jours se termine

-

Quand la date de fin est atteinte

-

Alors un rapport PDF est généré automatiquement -Et il contient:

-
| Section |
-|---|
-| Résumé exécutif |
-| Métriques clés |
-| Graphiques de performance |
-| Heatmap géographique |
-| Répartition horaire |
-| Analyse tranches d'âge |
-| Recommandations optimisation |
-
-

Et je reçois un email avec le PDF en pièce jointe

-
-

16. Métriques temps réel rafraîchies automatiquement

-

Étant donné que je consulte le dashboard à 10h00

-

Quand une nouvelle diffusion se produit à 10h01

-

Alors les métriques sont rafraîchies automatiquement (polling 30s) -Et je vois les nouveaux chiffres sans recharger la page -Et un badge "Mis à jour il y a 15s" s'affiche

-
-

17. Alertes performance personnalisées

-

Étant donné que je configure une alerte "Taux de skip >70%" -Et que ma campagne atteint 72% de skip

-

Quand le seuil est dépassé

-

Alors je reçois un email d'alerte:

-
-

18. Benchmark vs moyennes RoadWave

-

Étant donné que ma campagne a 45% d'écoutes complètes

-

Quand je consulte le benchmark

-

Alors je vois "Votre taux: 45% | Moyenne RoadWave: 40%" -Et un badge "📊 Performance: +12% vs moyenne" s'affiche -Et je sais que ma campagne performe au-dessus de la moyenne

-
-

19. Coût total consommé vs budget

-

Étant donné que j'ai un budget de 300€ -Et que j'ai consommé 220€

-

Quand je consulte le dashboard

-

Alors je vois une jauge "Budget consommé: 73%" (220/300) -Et le montant restant "80€ restants" -Et une projection "Épuisé dans 3 jours à ce rythme"

-
-

20. Répartition coûts par type d'écoute

-

Étant donné que j'ai dépensé 200€ avec:

-
| Type d'écoute | Nombre | Coût unitaire | Total |
-|---|---|---|---|
-| Écoute complète | 3000 | 0.05€ | 150€ |
-| Skip après 5s | 2000 | 0.02€ | 40€ |
-| Skip immédiat | 500 | 0€ | 0€ |
-
-

Quand je consulte la répartition

-

Alors un graphique camembert affiche:

-
| Segment | Pourcentage |
-|---|---|
-| Écoutes complètes | 75% (150€) |
-| Skips partiels | 20% (40€) |
-| Skips immédiats | 5% (0€) |
-
-
-

21. Évolution performance dans le temps

-

Étant donné une campagne de 30 jours

-

Quand je consulte le graphique d'évolution

-

Alors je vois une courbe Chart.js avec:

-
| Axe | Donnée |
-|---|---|
-| X | Jours (1-30) |
-| Y | Taux d'écoute complète (%) |
-
-

Et je peux identifier les tendances (amélioration/dégradation) -Et les jours avec pics d'engagement

-
-

22. Métriques avancées - Taux de réécoute

-

Étant donné qu'un utilisateur a entendu ma pub 3 fois -Et qu'il l'a écoutée complètement les 3 fois

-

Quand je consulte les métriques avancées

-

Alors le "Taux de réécoute" affiche 100% -Et cela indique que le contenu n'est pas perçu comme spam -Et les utilisateurs tolèrent bien la répétition

-
-

23. Recommandations automatiques d'optimisation

-

Étant donné que ma campagne a un taux de skip de 75% -Et que la durée moyenne d'écoute est de 8s sur 30s

-

Quand je consulte les recommandations

-

Alors le système suggère:

-
-

24. Suivi multi-campagnes avec vue consolidée

-

Étant donné que j'ai 3 campagnes actives simultanément

-

Quand je consulte la vue consolidée

-

Alors je vois un tableau récapitulatif:

-
| Campagne | Budget | Dépensé | Diffusions | Taux complète | CPE |
-|---|---|---|---|---|---|
-| A | 300€ | 220€ | 4000 | 40% | 0.05€ |
-| B | 500€ | 150€ | 3000 | 60% | 0.05€ |
-| C | 200€ | 180€ | 3600 | 35% | 0.05€ |
-
-

Et je peux comparer les performances d'un coup d'œil

-
-

25. 📋 Plan: Calcul taux d'écoute complète

-

Étant donné diffusions totales -Et écoutes complètes (≥80%)

-

Quand je calcule le taux

-

Alors le résultat est %

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
totalcompletestaux
100040040
2000120060
50010020
100085085
-
-

26. 📋 Plan: Calcul coût par écoute (CPE)

-

Étant donné un budget dépensé de € -Et écoutes complètes

-

Quand je calcule le CPE

-

Alors le résultat est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
depenseecoutescpe
10020000.05
30060000.05
5010000.05
500100000.05
-
-

27. 📋 Plan: Classification performance vs benchmark

-

Étant donné un taux d'écoute complète de % -Et une moyenne RoadWave de 40%

-

Quand je compare à la moyenne

-

Alors la performance est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tauxclassification
60Excellente (+50%)
50Bonne (+25%)
40Moyenne
30Faible (-25%)
20Très faible (-50%)
-
-
- -

Validation et modération des publicités

-
-

En tant que modérateur RoadWave -Je veux valider manuellement toutes les publicités avant diffusion -Afin de garantir la qualité et la légalité des contenus publicitaires

-
-

29 scénarios (27 standards, 2 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et qu'un modérateur RoadWave est connecté

-
-

1. Validation manuelle obligatoire avant diffusion

-

Étant donné qu'un publicitaire a créé une campagne -Et que le paiement de 300€ a été effectué

-

Quand la campagne est soumise

-

Alors elle passe en statut "En attente de validation" -Et elle est ajoutée à la file d'attente des modérateurs -Et la diffusion ne démarre PAS avant validation manuelle -Et le publicitaire reçoit un email "Votre campagne est en cours de validation (24-48h)"

-
-

2. Délai de validation 24-48h ouvrées

-

Étant donné qu'une campagne est soumise le lundi 10h

-

Quand le modérateur la valide le mardi 15h

-

Alors le délai est de 29h (dans les 48h ouvrées) -Et le publicitaire reçoit une notification "Votre campagne est approuvée"

-
-

3. Validation dépassant 48h avec notification

-

Étant donné qu'une campagne est soumise le lundi 10h

-

Quand 48h ouvrées se sont écoulées -Et que la campagne n'est toujours pas validée

-

Alors le publicitaire reçoit un email automatique: -Et un modérateur senior est assigné automatiquement

-
-

4. Acceptation de campagne publicitaire

-

Étant donné qu'une campagne est en attente de validation -Et que l'audio respecte toutes les règles

-

Quand le modérateur clique sur "Approuver"

-

Alors le statut passe à "Approuvée" -Et la campagne démarre à la date programmée -Et le publicitaire reçoit un email de confirmation -Et le budget commence à être consommé dès le début

-
-

5. Refus de campagne avec motif détaillé

-

Étant donné qu'une campagne contient du contenu alcool

-

Quand le modérateur clique sur "Refuser" -Et qu'il sélectionne le motif "Contenu interdit: Alcool" -Et qu'il ajoute le commentaire "La publicité pour l'alcool est interdite en France"

-

Alors le statut passe à "Refusée" -Et le publicitaire reçoit un email détaillé avec:

-
| Champ | Valeur |
-|---|---|
-| Motif | Contenu interdit: Alcool |
-| Commentaire | La publicité pour l'alcool est interdite en France |
-| Action requise | Modifier votre contenu et soumettre à nouveau |
-
-

Et un remboursement automatique de 300€ est déclenché

-
-

6. Remboursement automatique après refus

-

Étant donné qu'une campagne à 500€ est refusée

-

Quand le statut passe à "Refusée"

-

Alors un remboursement Mangopay de 500€ est initié automatiquement -Et le délai de remboursement est de 5-7 jours ouvrés -Et le publicitaire reçoit un email "Remboursement en cours"

-
-

7. Contenus interdits - Alcool

-

Étant donné qu'une publicité mentionne "Whisky premium 40°"

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Alcool"

-
-

8. Contenus interdits - Tabac

-

Étant donné qu'une publicité mentionne "Cigarettes électroniques"

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Tabac/Vape"

-
-

9. Contenus interdits - Jeux d'argent

-

Étant donné qu'une publicité mentionne "Gagnez 10 000€ - Paris sportifs"

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Jeux d'argent"

-
-

10. Contenus interdits - Politique pendant campagne électorale

-

Étant donné qu'une publicité politique est soumise -Et que nous sommes en période de campagne électorale officielle

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Publicité politique (période électorale)"

-
-

11. Contenus interdits - Contenu sexuel

-

Étant donné qu'une publicité contient des propos sexuellement explicites

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Contenu sexuel"

-
-

12. Contenus interdits - Violence

-

Étant donné qu'une publicité contient des descriptions violentes

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser la campagne -Et sélectionner le motif "Contenu interdit: Violence"

-
- -

Étant donné qu'une publicité pour un restaurant local dit "Découvrez notre menu du jour"

-

Quand le modérateur écoute l'audio

-

Alors il doit approuver la campagne

-
- -

Étant donné qu'une publicité pour un garage dit "Révision complète à partir de 99€"

-

Quand le modérateur écoute l'audio

-

Alors il doit approuver la campagne

-
-

15. Critères de validation - Qualité audio

-

Étant donné qu'une publicité a une qualité audio très basse (bruits, saturation)

-

Quand le modérateur écoute l'audio

-

Alors il peut refuser avec le motif "Qualité audio insuffisante" -Et recommander "Veuillez soumettre un fichier audio de meilleure qualité"

-
-

16. Critères de validation - Classification d'âge correcte

-

Étant donné qu'une publicité contient du langage familier -Et qu'elle est classée "Tout public"

-

Quand le modérateur écoute l'audio

-

Alors il peut refuser avec le motif "Classification d'âge incorrecte" -Et recommander "Reclasser en 13+ minimum"

-
-

17. Critères de validation - Respect réglementation française

-

Étant donné qu'une publicité fait des promesses mensongères "Perdez 10kg en 1 semaine"

-

Quand le modérateur écoute l'audio

-

Alors il doit refuser avec le motif "Non-conformité réglementaire: Publicité mensongère"

-
-

18. File d'attente modération priorisée

-

Étant donné que 10 campagnes sont en attente de validation -Et que la campagne A a été soumise il y a 40h -Et que la campagne B a été soumise il y a 2h

-

Quand le modérateur consulte sa file

-

Alors la campagne A apparaît en premier (priorité temporelle) -Et un badge "Urgente - >40h" est affiché

-
-

19. Dashboard modération - Vue d'ensemble

-

Étant donné que je suis modérateur

-

Quand j'accède au dashboard modération publicités

-

Alors je vois:

-
| Métrique | Exemple valeur |
-|---|---|
-| Campagnes en attente | 5 |
-| Délai moyen de validation | 28h |
-| Campagnes validées aujourd'hui | 12 |
-| Campagnes refusées aujourd'hui | 3 |
-| Taux d'acceptation | 80% |
-
-
-

20. Transcription automatique pour aide modération

-

Étant donné qu'une publicité audio est soumise

-

Quand le système traite l'audio

-

Alors une transcription automatique est générée via Whisper -Et elle est affichée au modérateur pour faciliter la revue -Et elle permet une recherche par mots-clés (alcool, tabac, etc.)

-
-

21. Détection automatique mots-clés interdits

-

Étant donné qu'une publicité audio est soumise

-

Quand la transcription contient "whisky" ou "vodka"

-

Alors un flag automatique "⚠️ Alcool détecté" est ajouté -Et la campagne est priorisée pour validation manuelle rapide -Et le modérateur est alerté du contenu potentiellement interdit

-
-

22. Historique modération publicitaire

-

Étant donné qu'un publicitaire a eu 2 campagnes refusées

-

Quand il soumet une 3ème campagne

-

Alors le modérateur voit l'historique:

-
| Date | Statut | Motif |
-|---|---|---|
-| 2026-01-15 | Refusée | Contenu interdit: Alcool |
-| 2026-01-20 | Refusée | Qualité audio faible |
-
-

Et il peut en tenir compte dans sa décision

-
-

23. Appel possible après refus

-

Étant donné que ma campagne a été refusée pour "Classification incorrecte"

-

Quand je conteste la décision via le formulaire d'appel

-

Alors un modérateur senior revoit la campagne -Et il peut approuver si la classification est en fait correcte -Et le délai de réponse est de 48-72h

-
-

24. Notification temps réel pour modérateurs

-

Étant donné que je suis modérateur connecté

-

Quand une nouvelle campagne est soumise

-

Alors je reçois une notification in-app -Et le compteur "Campagnes en attente" s'incrémente en temps réel -Et je peux cliquer pour consulter immédiatement

-
-

25. Statistiques conformité par catégorie

-

Étant donné que je suis admin modération

-

Quand je consulte les statistiques mensuelles

-

Alors je vois les motifs de refus:

-
| Motif | Nombre | Pourcentage |
-|---|---|---|
-| Alcool | 15 | 30% |
-| Qualité audio | 12 | 24% |
-| Classification erronée | 10 | 20% |
-| Publicité mensongère | 8 | 16% |
-| Autres | 5 | 10% |
-
-
-

26. Export rapport modération

-

Étant donné que je suis modérateur senior

-

Quand j'exporte le rapport mensuel

-

Alors je reçois un fichier CSV avec:

-
| Colonne |
-|---|
-| Campagne ID |
-| Publicitaire |
-| Date soumission |
-| Date décision |
-| Statut |
-| Motif (si refus) |
-| Modérateur |
-
-

Et je peux l'analyser dans Excel

-
-

27. Validation partielle avec demande modification

-

Étant donné qu'une campagne a un contenu acceptable -Mais que la classification d'âge est incorrecte

-

Quand le modérateur clique sur "Demander modification"

-

Alors le publicitaire reçoit un email: -Et le statut devient "Modification requise" -Et le publicitaire peut modifier sans repayer

-
-

28. 📋 Plan: Contenus interdits automatiquement détectés

-

Étant donné qu'une publicité contient le mot

-

Quand la transcription automatique est analysée

-

Alors un flag est ajouté -Et le motif de refus suggéré est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
mot_cleflagmotif
whisky⚠️ AlcoolContenu interdit: Alcool
vodka⚠️ AlcoolContenu interdit: Alcool
cigarette⚠️ TabacContenu interdit: Tabac
casino⚠️ Jeux argentContenu interdit: Jeux
paris sportifs⚠️ Jeux argentContenu interdit: Jeux
-
-

29. 📋 Plan: Délais de validation selon soumission

-

Étant donné qu'une campagne est soumise à

-

Quand elle est validée heures plus tard

-

Alors le statut est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
jourheuredelaiconformite
Lundi10h24Dans les délais (24h)
Lundi10h48Dans les délais (48h)
Lundi10h50Hors délais (>48h)
Vendredi16h72Dans les délais (we)
-
-
- -

Architecture technique radio live

-
-

En tant que système -Je veux gérer efficacement les flux audio en temps réel -Afin d'assurer une diffusion stable et scalable des lives

-
-

24 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'infrastructure RoadWave est opérationnelle -Et que les serveurs Go avec Pion WebRTC sont actifs

-
-

1. Ingestion WebRTC du flux créateur

-

Étant donné qu'un créateur démarre un live depuis son application mobile

-

Quand le flux audio WebRTC (Opus 48 kbps) arrive sur le serveur

-

Alors le serveur Go avec Pion WebRTC accepte la connexion -Et le flux est traité en temps réel

-
-

2. Conversion temps réel Opus vers segments HLS

-

Étant donné qu'un flux WebRTC Opus est reçu par le serveur

-

Quand le serveur traite le flux

-

Alors FFmpeg convertit en segments HLS (.ts) -Et un fichier manifest .m3u8 est généré et mis à jour régulièrement -Et les segments ont une durée de 2 secondes chacun

-
-

3. Distribution via Bunny CDN

-

Étant donné que les segments HLS sont générés

-

Quand un auditeur demande à rejoindre le live

-

Alors le manifest .m3u8 est servi via Bunny CDN -Et les segments .ts sont cachés sur le CDN -Et la distribution est globale avec latence minimale

-
-

4. Lecture HLS native sur mobile iOS

-

Étant donné qu'un auditeur iOS rejoint un live

-

Quand l'application charge le flux HLS

-

Alors le player natif AVPlayer gère la lecture -Et le buffer de 15 secondes est appliqué automatiquement -Et la qualité s'adapte selon la connexion

-
-

5. Lecture HLS native sur mobile Android

-

Étant donné qu'un auditeur Android rejoint un live

-

Quand l'application charge le flux HLS

-

Alors le player natif ExoPlayer gère la lecture -Et le buffer de 15 secondes est configuré -Et la qualité s'adapte selon la connexion

-
-

6. Enregistrement parallèle du flux pour replay

-

Étant donné qu'un live est en cours

-

Alors un processus parallèle enregistre le flux Opus raw -Et l'enregistrement est stocké temporairement sur le serveur -Et l'enregistrement est indépendant de la diffusion HLS

-
-

7. Traitement post-live asynchrone

-

Étant donné qu'un live vient de se terminer

-

Quand le processus post-live démarre

-

Alors un job asynchrone est créé dans la queue Redis -Et un worker Go récupère le job -Et le worker exécute FFmpeg pour les conversions

-
-

8. Conversion Opus raw vers MP3 256 kbps

-

Étant donné qu'un worker traite un job post-live

-

Quand la conversion démarre

-

Alors FFmpeg convertit Opus raw en MP3 256 kbps -Et la normalisation audio à -14 LUFS est appliquée -Et les silences prolongés (>3 secondes) sont détectés et nettoyés

-
-

9. Génération segments HLS pour le replay

-

Étant donné que le MP3 256 kbps est généré

-

Quand le worker crée les segments HLS

-

Alors des segments .ts de 10 secondes sont créés -Et un manifest .m3u8 est généré -Et les segments sont uploadés vers le stockage Bunny

-
-

10. Publication automatique du replay

-

Étant donné que tous les segments HLS sont uploadés

-

Quand le worker finalise le job

-

Alors une entrée de contenu "replay" est créée en base PostgreSQL -Et le titre est "[REPLAY] [Titre live original]" -Et le type géographique est "Géo-neutre" -Et le replay est immédiatement disponible pour les auditeurs

-
-

11. Suppression automatique fichier Opus raw après 7 jours

-

Étant donné qu'un replay est publié depuis 7 jours

-

Quand le job de nettoyage quotidien s'exécute

-

Alors le fichier Opus raw est supprimé du stockage -Et seul le MP3 256 kbps et les segments HLS sont conservés -Et l'espace de stockage est libéré

-
-

12. Scalabilité horizontale des workers de conversion

-

Étant donné que 50 lives se terminent simultanément

-

Quand les jobs post-live sont créés

-

Alors les workers Go disponibles traitent les jobs en parallèle -Et si tous les workers sont occupés, les jobs attendent en queue Redis -Et de nouveaux workers peuvent être lancés automatiquement (Kubernetes)

-
-

13. Limitation du nombre de lives simultanés (MVP)

-

Étant donné que l'infrastructure MVP est configurée pour 100 lives simultanés -Et que 100 lives sont actuellement en cours

-

Quand un nouveau créateur essaie de démarrer un live

-

Alors la demande est refusée avec le code erreur 503 -Et le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" est retourné -Et la demande peut être mise en queue prioritaire si créateur Premium

-
-

14. Monitoring des ressources serveur en temps réel

-

Étant donné que plusieurs lives sont en cours

-

Alors le système monitore en temps réel:

-
| métrique | seuil alerte |
-|---|---|
-| CPU utilisation | >80% |
-| Mémoire utilisation | >85% |
-| Bande passante upload | >80% capacité |
-| Nombre connexions WebRTC | >90 |
-| Latence moyenne CDN | >200ms |
-
-

Et si un seuil est dépassé, une alerte est envoyée à l'équipe technique

-
-

15. Calcul du coût de bande passante CDN

-

Étant donné qu'un live a 100 auditeurs simultanés -Et que la qualité est 48 kbps Opus

-

Quand le live dure 1 heure

-

Alors la bande passante totale est d'environ 2.16 GB -Et le coût Bunny CDN est d'environ 0.02€ (tarif ~0.01€/GB) -Et ces métriques sont enregistrées pour facturation créateur si nécessaire

-
-

16. Cache CDN des segments HLS

-

Étant donné qu'un live est diffusé via Bunny CDN

-

Quand un segment .ts est généré

-

Alors le segment est uploadé vers Bunny origin -Et Bunny CDN cache le segment sur ses edge servers -Et les auditeurs suivants récupèrent le segment depuis le cache -Et la charge sur le serveur origin est réduite de ~90%

-
-

17. Gestion de la latence WebRTC créateur

-

Étant donné qu'un créateur diffuse avec une connexion 4G

-

Quand la latence réseau augmente ponctuellement

-

Alors le buffer côté serveur absorbe les fluctuations -Et la qualité peut être réduite temporairement (48 kbps → 32 kbps) -Et un warning est affiché au créateur si la connexion est trop instable

-
-

18. Détection automatique de la musique protégée (post-MVP)

-

Étant donné qu'un live contient de la musique en arrière-plan

-

Quand le système d'audio fingerprint analyse le flux

-

Alors une empreinte audio est calculée toutes les 30 secondes -Et l'empreinte est comparée à une base de données de contenus protégés -Et si une correspondance est trouvée, un warning est envoyé au créateur -Et si le créateur ne corrige pas sous 30 secondes, le live peut être arrêté

-
-

19. Stockage des métadonnées de live en PostgreSQL

-

Étant donné qu'un créateur démarre un live

-

Alors les métadonnées suivantes sont enregistrées:

-
| champ | exemple valeur |
-|---|---|
-| live_id | uuid v4 |
-| creator_id | uuid créateur |
-| title | "Mon super live" |
-| started_at | timestamp UTC |
-| zone_geo | "Île-de-France" |
-| tags | ["Actualité", "Tech"] |
-| classification_age | "Tout public" |
-
-

Et ces données sont indexées pour recherche et analytics

-
-

20. Cache Redis pour compteurs temps réel

-

Étant donné qu'un live est en cours

-

Alors Redis stocke les compteurs temps réel:

-
| clé Redis | valeur exemple |
-|---|---|
-| live:[live_id]:listeners | 247 |
-| live:[live_id]:likes | 89 |
-| live:[live_id]:reports | 0 |
-
-

Et ces compteurs sont mis à jour toutes les 2 secondes -Et les compteurs sont persistés en PostgreSQL toutes les 60 secondes

-
-

21. Heartbeat auditeurs pour compteur précis

-

Étant donné qu'un auditeur écoute un live

-

Alors l'application envoie un heartbeat toutes les 10 secondes -Et le heartbeat met à jour le timestamp dans Redis -Et si aucun heartbeat n'est reçu pendant 30 secondes, l'auditeur est retiré du compteur

-
-

22. Gestion des pannes serveur pendant un live

-

Étant donné qu'un live est en cours sur serveur A

-

Quand le serveur A tombe en panne

-

Alors Kubernetes redémarre automatiquement un pod -Mais le live en cours est perdu (pas de failover temps réel en MVP) -Et le créateur voit le message "Connexion perdue. Veuillez redémarrer le live" -Et les auditeurs voient "Le live est terminé suite à un problème technique"

-
-

23. Backup automatique des enregistrements live

-

Étant donné qu'un live est enregistré en Opus raw

-

Quand l'enregistrement dépasse 10 minutes

-

Alors un backup incrémental est créé toutes les 10 minutes -Et le backup est stocké sur un stockage secondaire (S3-compatible) -Et en cas de crash serveur, le live peut être récupéré jusqu'au dernier backup

-
-

24. Logs et audit trail des lives

-

Étant donné qu'un live démarre, se déroule et se termine

-

Alors tous les événements sont loggés:

-
| événement | détails enregistrés |
-|---|---|
-| Démarrage live | timestamp, creator_id, zone_geo |
-| Auditeur rejoint | timestamp, user_id, position GPS |
-| Auditeur quitte | timestamp, user_id, durée écoute |
-| Signalement | timestamp, user_id, catégorie |
-| Fin live | timestamp, durée totale, stats finales |
-
-

Et ces logs sont conservés 90 jours pour analytics et conformité RGPD

-
-
- -

Arrêt du live

-
-

En tant que créateur -Je veux arrêter ma diffusion en direct de manière contrôlée -Afin de terminer proprement mon live et générer un replay automatiquement

-
-

19 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant que créateur -Et que je diffuse actuellement un live

-
-

1. Arrêt manuel avec compte à rebours 5 secondes

-

Quand j'appuie sur le bouton "Arrêter live"

-

Alors un compte à rebours de 5 secondes démarre -Et je vois le message "Ce live se termine dans 5... 4... 3... 2... 1" -Et un bouton "Annuler" est affiché pendant le décompte -Et l'audio du compte à rebours est diffusé aux auditeurs

-
-

2. Annulation du compte à rebours

-

Étant donné que j'ai appuyé sur "Arrêter live" -Et que le compte à rebours affiche "3 secondes"

-

Quand j'appuie sur "Annuler"

-

Alors le compte à rebours s'arrête -Et le live continue normalement -Et aucune notification n'est envoyée aux auditeurs

-
-

3. Arrêt effectif après compte à rebours

-

Étant donné que le compte à rebours est à 0

-

Alors le live s'arrête -Et la diffusion aux auditeurs se termine -Et le message "Live terminé" s'affiche -Et le processus de traitement post-live démarre automatiquement

-
-

4. Déconnexion créateur courte (moins de 60 secondes)

-

Étant donné que je diffuse un live

-

Quand ma connexion est perdue pendant 30 secondes

-

Alors les auditeurs voient le message "Connexion créateur perdue, reconnexion en cours..." -Et le live continue de bufferer -Et quand ma connexion revient, le live reprend normalement

-
-

5. Déconnexion créateur longue (60 secondes ou plus)

-

Étant donné que je diffuse un live

-

Quand ma connexion est perdue pendant 60 secondes

-

Alors le live s'arrête automatiquement -Et les auditeurs voient le message "Le live est terminé suite à une coupure de connexion" -Et le processus de traitement post-live démarre

-
-

6. Enregistrement automatique pendant le live

-

Étant donné que je diffuse un live

-

Alors mon flux audio est enregistré en continu -Et le format d'enregistrement est Opus raw -Et l'enregistrement est stocké temporairement sur le serveur

-
-

7. Génération automatique du replay après arrêt

-

Étant donné que mon live vient de se terminer -Et que l'option "Publier replay automatiquement" est activée (par défaut)

-

Quand le traitement post-live démarre

-

Alors un job asynchrone est créé -Et le job effectue les opérations suivantes:

-
| opération | détail |
-|---|---|
-| Conversion format | Opus raw → MP3 256 kbps |
-| Génération segments HLS | Segments .ts pour streaming |
-| Normalisation volume | -14 LUFS |
-| Détection silences prolongés | Nettoyage automatique |
-
-
-

8. Publication du replay

-

Étant donné que le traitement post-live est terminé

-

Alors le replay est publié automatiquement sous 5 à 10 minutes -Et le titre est "[REPLAY] [Titre live original]" -Et la zone de diffusion est la même que le live -Et les tags sont identiques au live -Et la classification d'âge est identique -Et le type géographique est "Géo-neutre" (contenu pérenne)

-
-

9. Notification de disponibilité du replay aux auditeurs

-

Étant donné que le replay de mon live est publié

-

Quand un auditeur qui a écouté le live se reconnecte

-

Alors il voit une notification in-app "Le replay de [Titre] est disponible"

-
-

10. Option désactivation publication automatique replay

-

Étant donné que je configure un nouveau live

-

Quand je désactive l'option "Publier replay automatiquement" -Et que je démarre puis arrête le live

-

Alors le live est enregistré -Mais le replay n'est pas publié automatiquement -Et je peux décider manuellement de le publier plus tard

-
-

11. Suppression manuelle du replay après publication

-

Étant donné que mon live a généré un replay publié

-

Quand j'accède à mes contenus

-

Alors je vois le replay dans ma liste -Et je peux le supprimer comme n'importe quel contenu

-

Quand je supprime le replay

-

Alors le fichier source Opus raw est supprimé immédiatement

-
-

12. Conservation fichier source Opus raw

-

Étant donné que mon live est terminé -Et que le replay est publié

-

Alors le fichier Opus raw est conservé pendant 7 jours -Et après 7 jours, le fichier raw est supprimé automatiquement -Et seul le MP3 256 kbps est conservé

-
-

13. Modification du replay interdite

-

Étant donné que mon live a généré un replay publié

-

Quand j'essaie de modifier l'audio du replay

-

Alors l'action est refusée -Et je vois le message "Les replays ne peuvent pas être modifiés pour garantir l'intégrité de l'enregistrement" -Et je peux uniquement modifier les métadonnées (titre, description)

-
-

14. Statistiques du live disponibles après arrêt

-

Étant donné que mon live est terminé

-

Quand j'accède aux statistiques

-

Alors je vois:

-
| métrique | exemple valeur |
-|---|---|
-| Durée totale | 1h 23min |
-| Nombre d'auditeurs max | 247 |
-| Nombre d'auditeurs moyen | 183 |
-| Nombre de likes | 89 |
-| Nombre d'abonnements | 12 |
-| Signalements reçus | 0 |
-
-
-

15. Live terminé avec signalements en cours

-

Étant donné que mon live a reçu 3 signalements pendant la diffusion

-

Quand le live se termine

-

Alors le replay n'est pas publié automatiquement -Et le contenu est en attente de modération -Et je vois le message "Votre replay sera publié après vérification suite aux signalements reçus" -Et un modérateur doit valider ou refuser le replay sous 24h

-
-

16. Arrêt forcé par un modérateur

-

Étant donné que je diffuse un live -Et qu'un modérateur détecte du contenu interdit

-

Quand le modérateur clique sur "Arrêter le live immédiatement"

-

Alors le live s'arrête sans compte à rebours -Et je vois le message "Votre live a été interrompu par la modération" -Et je reçois une notification détaillant la raison -Et le replay n'est pas publié -Et le fichier source est conservé 30 jours pour appel

-
-

17. Métriques de bande passante pendant le live

-

Étant donné que je diffuse un live -Et que 100 auditeurs écoutent simultanément

-

Alors la bande passante consommée est d'environ 4.8 Mbps via CDN -Et le coût estimé Bunny CDN est d'environ 0.02€ par heure de diffusion -Et je peux voir ces métriques en temps réel dans l'interface créateur

-
-

18. Live sans auditeurs pendant 5 minutes

-

Étant donné que je diffuse un live -Et qu'aucun auditeur n'écoute depuis 5 minutes

-

Alors je vois un message d'information "Aucun auditeur actuellement connecté" -Mais le live continue normalement -Et je peux choisir de continuer ou d'arrêter

-
-

19. Qualité audio du replay supérieure au live

-

Étant donné que mon live était diffusé en Opus 48 kbps

-

Quand le replay est généré

-

Alors le replay est encodé en MP3 256 kbps -Et la qualité audio du replay est supérieure au live -Et la taille du fichier est optimisée pour le stockage long terme

-
-
- -

Comportement auditeur pendant un live

-
-

En tant qu'auditeur -Je veux écouter des lives de manière stable -Afin de profiter du contenu en temps réel sans coupures

-
-

27 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'auditeur -Et qu'un créateur diffuse actuellement un live

-
-

1. Rejoindre un live avec buffer de synchronisation 15 secondes

-

Quand je clique sur "Rejoindre le live"

-

Alors la connexion au flux HLS s'établit -Et je commence à écouter avec un décalage de 15 secondes par rapport au créateur -Et le buffer de 15 secondes garantit une lecture stable

-
-

2. Justification du buffer 15 secondes

-

Étant donné les alternatives de buffer possibles:

-
| buffer | stabilité 3G | stabilité 4G | décalage perceptible | décision |
-|---|---|---|---|---|
-| 5s | Faible | Moyenne | Non | ❌ |
-| 10s | Moyenne | Bonne | Non | ❌ |
-| 15s | Bonne | Excellente | Léger acceptable | ✅ |
-| 20s+ | Excellente | Excellente | Oui | ❌ |
-
-

Alors le buffer optimal est 15 secondes

-
-

3. Lecture stable sur réseau 3G

-

Étant donné que je suis sur réseau 3G -Et que j'écoute un live

-

Quand des micro-coupures réseau surviennent

-

Alors le buffer de 15 secondes absorbe les coupures -Et la lecture continue sans interruption perceptible

-
-

4. Lecture stable sur réseau 4G

-

Étant donné que je suis sur réseau 4G -Et que j'écoute un live

-

Alors la lecture est fluide -Et le buffer de 15 secondes prévient les coupures lors de changement de cellule

-
-

5. Continuation du live en sortant de la zone géographique

-

Étant donné que j'écoute un live régional "Île-de-France" -Et que je suis situé en Île-de-France

-

Quand je me déplace et sors du département

-

Alors le live continue de jouer normalement -Et je peux écouter jusqu'à la fin naturelle du live -Et après la fin du live, l'algorithme propose du contenu correspondant à ma nouvelle position

-
-

6. Abonné dans la zone reçoit notification push

-

Étant donné que je suis abonné au créateur "JeanDupont" -Et que je suis situé en Île-de-France

-

Quand "JeanDupont" démarre un live en Île-de-France

-

Alors je reçois une notification push "🔴 JeanDupont est en direct : [Titre du live]" -Et quand je tape sur la notification, l'app s'ouvre et le live démarre immédiatement

-
-

7. Abonné hors zone ne reçoit pas de notification

-

Étant donné que je suis abonné au créateur "JeanDupont" -Et que je suis situé à Lyon

-

Quand "JeanDupont" démarre un live en Île-de-France

-

Alors je ne reçois pas de notification push -Et cela évite la frustration de ne pas pouvoir écouter un live hors zone

-
-

8. Découverte d'un live via l'algorithme de recommandation

-

Étant donné que je suis dans la zone géographique du live -Et que je navigue dans l'app avec "Suivant"

-

Quand l'algorithme propose un live en cours

-

Alors je vois l'indicateur "🔴 EN DIRECT" -Et je peux choisir de le rejoindre ou de passer au suivant

-
-

9. Reconnexion rapide après coupure réseau (moins de 90 secondes)

-

Étant donné que j'écoute un live

-

Quand je perds ma connexion réseau pendant 45 secondes -Et que je retrouve ma connexion

-

Alors je reprends le live au moment actuel (pas au buffer ancien) -Et le saut temporel est transparent (pas de message d'erreur) -Et je ne rate que quelques secondes de contenu

-
-

10. Reconnexion longue après coupure réseau (90 secondes ou plus)

-

Étant donné que j'écoute un live

-

Quand je perds ma connexion réseau pendant 90 secondes -Et que je retrouve ma connexion

-

Alors je vois le message "Live en cours perdu, passage au contenu suivant" -Et l'algorithme propose automatiquement le contenu suivant -Et je peux manuellement revenir au live s'il est toujours en cours

-
-

11. Interactions disponibles pendant le live - Like

-

Étant donné que j'écoute un live -Et que mon véhicule est à l'arrêt

-

Quand je clique sur le bouton "❤️ Like"

-

Alors le like est enregistré immédiatement -Et le compteur de likes visible par le créateur s'incrémente -Et ma jauge d'intérêt pour les tags du live augmente de +2%

-
-

12. Interactions disponibles pendant le live - Abonnement

-

Étant donné que j'écoute un live -Et que je ne suis pas encore abonné au créateur

-

Quand je clique sur le bouton "S'abonner"

-

Alors je m'abonne au créateur -Et ma jauge d'intérêt pour tous les tags du créateur augmente de +5% -Et je recevrai des notifications pour ses prochains lives

-
-

13. Interactions disponibles pendant le live - Skip

-

Étant donné que j'écoute un live

-

Quand j'appuie sur "Suivant" (ou commande au volant)

-

Alors je quitte le live immédiatement -Et l'algorithme propose le contenu suivant -Et si j'ai écouté moins de 10 secondes, ma jauge d'intérêt diminue de -0.5%

-
-

14. Commande Précédent désactivée pendant un live

-

Étant donné que j'écoute un live

-

Quand j'appuie sur "Précédent" (ou commande au volant)

-

Alors rien ne se passe -Et un message d'information s'affiche brièvement "Précédent non disponible sur les lives"

-
-

15. Chat en direct désactivé (décision définitive)

-

Étant donné que j'écoute un live

-

Alors aucune interface de chat n'est disponible -Et je ne peux pas envoyer de messages au créateur -Et je ne peux pas voir de messages d'autres auditeurs -Et cette fonctionnalité ne sera jamais implémentée

-
-

16. Réactions emoji désactivées (décision définitive)

-

Étant donné que j'écoute un live

-

Alors aucune réaction emoji n'est disponible -Et je ne peux pas envoyer d'emoji en temps réel -Et cette fonctionnalité ne sera jamais implémentée

-
-

17. Message d'information sur l'absence de chat

-

Étant donné que j'écoute mon premier live

-

Quand j'accède à l'interface du live

-

Alors je vois un bandeau informatif "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement." -Et ce bandeau n'apparaît qu'une seule fois (première expérience)

-
-

18. Signalement d'un live en cours

-

Étant donné que j'écoute un live -Et que le contenu me semble inapproprié

-

Quand je clique sur le bouton "Signaler"

-

Alors je vois les catégories de signalement:

-
| catégorie |
-|---|
-| Haine et violence |
-| Contenu sexuel |
-| Illégalité |
-| Droits d'auteur |
-| Désinformation dangereuse |
-| Harcèlement |
-| Autre |
-
-

Et quand je sélectionne une catégorie -Alors le signalement est envoyé en priorité selon la catégorie -Et un modérateur peut écouter le live en temps réel si besoin

-
-

19. Statistiques visibles par les auditeurs pendant le live

-

Étant donné que j'écoute un live

-

Quand je consulte les informations du live

-

Alors je vois:

-
| information | exemple valeur |
-|---|---|
-| Nombre d'auditeurs | 247 personnes |
-| Durée du live | 1h 23min |
-| Nom du créateur | @JeanDupont |
-| Zone de diffusion | Île-de-France |
-| Tags | Actualité, Société |
-
-

Mais je ne vois pas les likes ou autres métriques détaillées

-
-

20. Compteur d'auditeurs arrondi pour préserver la vie privée

-

Étant donné que j'écoute un live avec exactement 247 auditeurs

-

Quand je consulte le nombre d'auditeurs

-

Alors je vois "~250 auditeurs" (arrondi à la dizaine supérieure)

-
-

21. Qualité audio adaptative pendant le live

-

Étant donné que j'écoute un live

-

Quand ma connexion passe de 4G à 3G

-

Alors la qualité audio s'adapte automatiquement -Et je passe de 48 kbps à 24 kbps Opus -Et la transition est transparente sans coupure

-
-

22. Consommation de données pendant un live

-

Étant donné que j'écoute un live en qualité standard 48 kbps -Et que j'écoute pendant 1 heure

-

Alors j'ai consommé environ 21.6 MB de données mobiles -Et cette consommation est affichée dans les paramètres de l'app

-
-

23. Lecture du replay après la fin du live

-

Étant donné que j'écoute un live depuis 30 minutes

-

Quand le créateur arrête le live

-

Alors je vois le message "Le live est terminé. Le replay sera disponible dans quelques minutes" -Et le contenu suivant est automatiquement proposé après 2 secondes

-
-

24. Notification de disponibilité du replay

-

Étant donné que j'ai écouté un live jusqu'à la fin -Et que le replay est publié 8 minutes plus tard

-

Quand je rouvre l'application

-

Alors je vois une notification in-app "Le replay de [Titre] est maintenant disponible" -Et je peux cliquer pour l'écouter immédiatement

-
-

25. Aucune publicité pendant un live pour utilisateurs gratuits

-

Étant donné que je suis un utilisateur gratuit -Et que j'écoute un live

-

Alors aucune publicité n'est insérée pendant le live -Et la publicité apparaît seulement entre le live et le contenu suivant

-
-

26. Détection de contexte voiture pendant un live

-

Étant donné que j'écoute un live -Et que ma vitesse est supérieure à 10 km/h

-

Alors l'interface tactile est désactivée pour la sécurité -Et seules les commandes au volant sont actives (Play/Pause/Suivant)

-
-

27. Détection de contexte piéton pendant un live

-

Étant donné que j'écoute un live -Et que ma vitesse est inférieure à 5 km/h

-

Alors l'interface tactile complète est disponible -Et je peux liker, m'abonner, signaler via l'écran tactile

-
-
- -

Démarrage d'un live

-
-

En tant que créateur -Je veux démarrer une diffusion en direct -Afin de partager du contenu audio en temps réel avec mes auditeurs

-
-

20 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant que créateur vérifié -Et que j'ai les permissions de diffusion live

-
-

1. Vérifications pré-live réussies

-

Étant donné que ma connexion upload est supérieure à 1 Mbps -Et que j'ai autorisé l'accès au microphone -Et que j'ai défini une zone de diffusion "Île-de-France"

-

Quand je lance les vérifications pré-live

-

Alors toutes les vérifications sont validées -Et je peux démarrer le live

-
-

2. Échec pré-live avec connexion insuffisante

-

Étant donné que ma connexion upload est de 0.5 Mbps

-

Quand je lance les vérifications pré-live

-

Alors je vois un warning "Connexion insuffisante pour garantir une diffusion stable (minimum 1 Mbps)" -Et je peux choisir de continuer quand même ou d'annuler

-
-

3. Échec pré-live sans autorisation microphone

-

Étant donné que je n'ai pas autorisé l'accès au microphone

-

Quand j'essaie de démarrer un live

-

Alors je vois le message "Accès au microphone requis pour démarrer un live" -Et je suis redirigé vers les paramètres système

-
-

4. Échec pré-live sans zone de diffusion définie

-

Étant donné que je n'ai pas défini de zone de diffusion

-

Quand j'essaie de démarrer un live

-

Alors je vois le message "Veuillez définir une zone de diffusion avant de démarrer" -Et je suis redirigé vers le formulaire de configuration du live

-
-

5. Démarrage live avec buffer 15 secondes

-

Étant donné que toutes les vérifications pré-live sont validées

-

Quand j'appuie sur "Démarrer live"

-

Alors je vois le message "Live démarre dans 15s... Testez votre micro" -Et un compte à rebours de 15 secondes s'affiche -Et mon flux audio est enregistré pendant ces 15 secondes -Et le live n'est pas encore visible publiquement

-
-

6. Live devient public après buffer initial

-

Étant donné que j'ai démarré un live -Et que le buffer de 15 secondes s'est écoulé

-

Alors le live devient public -Et les auditeurs peuvent le rejoindre -Et les abonnés dans la zone reçoivent une notification push

-
-

7. Notification push aux abonnés dans la zone géographique

-

Étant donné que j'ai 1000 abonnés au total -Et que 300 abonnés sont situés en Île-de-France -Et que 700 abonnés sont situés hors Île-de-France

-

Quand mon live en Île-de-France devient public

-

Alors 300 abonnés reçoivent une notification push "🔴 [Mon pseudo] est en direct : [Titre live]" -Et 700 abonnés ne reçoivent pas de notification

-
-

8. Configuration métadonnées obligatoires pour un live

-

Quand je configure un nouveau live

-

Alors je dois renseigner:

-
| champ | format | validation |
-|---|---|---|
-| Titre | 5-100 caractères | Obligatoire |
-| Tags | 1-3 centres intérêt | Sélection liste prédéfinie |
-| Classification âge | Enum | Tout public / 13+ / 16+ / 18+ |
-| Zone diffusion | Geo | Ville / Département / Région / National |
-
-
-

9. Validation échouée avec titre trop court

-

Quand j'essaie de créer un live avec le titre "Live"

-

Alors la validation échoue -Et je vois le message "Le titre doit contenir entre 5 et 100 caractères"

-
-

10. Validation échouée sans tags

-

Étant donné que j'ai rempli tous les champs sauf les tags

-

Quand j'essaie de démarrer le live

-

Alors la validation échoue -Et je vois le message "Veuillez sélectionner entre 1 et 3 centres d'intérêt"

-
-

11. Limite de durée 8 heures

-

Étant donné que mon live dure depuis 7 heures et 30 minutes

-

Alors je vois un warning "Votre live se terminera dans 30 min" -Et le message est affiché de manière non intrusive

-
-

12. Arrêt automatique à 8 heures

-

Étant donné que mon live dure depuis 8 heures

-

Alors le live s'arrête automatiquement -Et je vois le message "Durée maximale atteinte (8 heures). Vous pouvez redémarrer un nouveau live si nécessaire" -Et le processus de traitement post-live démarre

-
-

13. Diffusion contenu interdit - Concert en direct

-

Étant donné que je diffuse un concert en direct depuis une salle -Et qu'un auditeur signale le contenu pour "Violation droits d'auteur"

-

Quand un modérateur écoute le live -Et qu'il confirme la violation

-

Alors le live est arrêté immédiatement -Et je reçois un Strike 2 (suspension 7 jours) -Et je vois le message "Votre live a été interrompu pour violation des droits d'auteur" -Et le replay n'est pas publié

-
-

14. Diffusion contenu interdit - Événement sportif payant

-

Étant donné que je diffuse un match de football avec droits TV -Et que le contenu est détecté par l'IA audio fingerprint

-

Quand la détection est confirmée

-

Alors le live est arrêté immédiatement -Et je reçois un Strike 2 (suspension 7 jours)

-
-

15. Diffusion contenu violent

-

Étant donné que je diffuse du contenu violent (agression physique) -Et que 5 auditeurs signalent le contenu

-

Quand un modérateur vérifie en temps réel -Et confirme la violence

-

Alors le live est coupé immédiatement -Et mon compte est banni définitivement -Et les autorités sont notifiées

-
-

16. Détection musique protégée en arrière-plan

-

Étant donné que mon live contient de la musique protégée en fond

-

Quand l'IA audio fingerprint détecte la violation après 2 minutes

-

Alors je reçois un avertissement en direct "Musique protégée détectée. Veuillez couper le son ou risquez un arrêt du live" -Et j'ai 30 secondes pour corriger -Et si je ne corrige pas, le live est arrêté avec Strike 1

-
-

17. Signalement pendant un live

-

Étant donné que je diffuse un live -Et qu'un auditeur clique sur "Signaler"

-

Quand l'auditeur sélectionne la catégorie "Harcèlement"

-

Alors le signalement est envoyé en priorité HAUTE -Et un modérateur peut écouter le live en temps réel -Et le live continue pendant l'écoute de vérification

-
-

18. Dépassement nombre de lives simultanés autorisés (limite plateforme)

-

Étant donné que la plateforme héberge actuellement 2000 lives simultanés -Et que c'est la limite de l'infrastructure actuelle

-

Quand j'essaie de démarrer un nouveau live

-

Alors je vois le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" -Et ma demande est mise en file d'attente prioritaire si je suis créateur Premium

-
-

19. Premier live d'un nouveau créateur

-

Étant donné que je n'ai jamais diffusé de live auparavant -Et que j'ai moins de 3 contenus validés

-

Quand j'essaie de démarrer mon premier live

-

Alors je vois le message "Les lives sont disponibles après validation de vos 3 premiers contenus" -Et le bouton "Démarrer live" est désactivé

-
-

20. Créateur avec score de confiance faible

-

Étant donné que j'ai 2 strikes actifs

-

Quand j'essaie de démarrer un live

-

Alors je vois le message "Fonctionnalité live temporairement indisponible suite à vos sanctions" -Et je dois attendre la fin de ma suspension

-
-
- -

Recherche de contenu

-
-

En tant qu'utilisateur de RoadWave -Je veux rechercher des contenus audio par mots-clés, localisation et filtres -Afin de trouver facilement le contenu qui m'intéresse

-
-

55 scénarios (49 standards, 6 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'application RoadWave est démarrée -Et que l'utilisateur "jean@example.com" est connecté

-
-

1. Recherche full-text basique

-

Étant donné que la base contient les contenus suivants:

-
| titre | description | créateur |
-|---|---|---|
-| Balade à Paris | Visite du quartier Latin | @paris_stories |
-| Secrets de Montmartre | Histoire de la butte | @explore_paris |
-| Voyage en Normandie | Découverte des plages | @voyages_fr |
-
-

Quand l'utilisateur recherche "paris"

-

Alors 2 résultats sont retournés -Et les résultats incluent "Balade à Paris" -Et les résultats incluent "Secrets de Montmartre"

-
-

2. Recherche avec stemming français

-

Étant donné un contenu avec le titre "Voyage en Bretagne"

-

Quand l'utilisateur recherche "voyages"

-

Alors le contenu "Voyage en Bretagne" est trouvé -Et le stemming a transformé "voyages" en racine "voyag"

-
-

3. 📋 Plan: Stemming français sur différentes formes

-

Étant donné un contenu avec le mot ""

-

Quand l'utilisateur recherche ""

-

Alors le contenu est trouvé grâce au stemming français

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - -
mot_originalrecherche
voyagevoyages
voyagervoyage
baladesbalade
historiquehistoire
-
-

4. Recherche avec accents ignorés

-

Étant donné un contenu avec le titre "Découverte de l'Élysée"

-

Quand l'utilisateur recherche "decouverte elysee"

-

Alors le contenu est trouvé -Et les accents sont normalisés automatiquement

-
-

5. Champs indexés avec pondération

-

Étant donné les contenus suivants:

-
| titre | description | créateur | tags |
-|---|---|---|---|
-| Voyage Paris | Balade sympa | @user1 | Tourisme |
-| Balade Lyon | Voyage en ville | @paris_guide | Voyage |
-
-

Quand l'utilisateur recherche "paris"

-

Alors "Voyage Paris" est en première position -Et "@paris_guide" apparaît en second

-
-

6. Ranking par pertinence et popularité

-

Étant donné les contenus suivants:

-
| titre | écoutes | rang_texte |
-|---|---|---|
-| Balade Paris | 50000 | 0.8 |
-| Paris la nuit | 1000 | 0.9 |
-
-

Quand l'utilisateur recherche "paris"

-

Alors le score final combine rang_texte × (1 + log(écoutes + 1)) -Et "Balade Paris" est mieux classé grâce à sa popularité

-
-

7. Autocomplete pendant la frappe

-

Étant donné que l'utilisateur commence à taper "par"

-

Quand 3 caractères sont saisis

-

Alors des suggestions apparaissent:

-
| suggestion |
-|---|
-| paris |
-| parc naturel |
-| parvis notre-dame |
-
-

Et le top 5 des suggestions est affiché

-
-

8. Historique des 10 dernières recherches

-

Étant donné que l'utilisateur a effectué les recherches suivantes:

-
| recherche | date |
-|---|---|
-| voyage paris | 2026-01-20 |
-| audio-guide louvre | 2026-01-19 |
-| podcast automobile | 2026-01-18 |
-
-

Quand l'utilisateur ouvre la barre de recherche

-

Alors les 10 dernières recherches sont affichées -Et elles sont triées par date décroissante

-
-

9. Correction automatique si aucun résultat

-

Étant donné que l'utilisateur recherche "ballade paris" (faute d'orthographe) -Et qu'aucun résultat n'est trouvé

-

Quand la page de résultats s'affiche

-

Alors une suggestion "Essayez plutôt : balade paris" est affichée

-
-

10. Recherches populaires suggérées

-

Étant donné qu'aucun résultat n'est trouvé pour une recherche

-

Quand la page s'affiche

-

Alors des suggestions populaires sont affichées:

-
| suggestion |
-|---|
-| balade paris |
-| audio-guide louvre |
-| visite montmartre |
-
-
-

11. Saisie d'un lieu avec autocomplete

-

Étant donné que l'utilisateur ouvre le filtre "Lieu"

-

Quand il tape "Louv"

-

Alors Nominatim retourne des suggestions:

-
| suggestion | type |
-|---|---|
-| Musée du Louvre, Paris | monument |
-| Louvres, Val-d'Oise | commune |
-
-
-

12. Sélection d'un lieu et définition du rayon

-

Étant donné que l'utilisateur sélectionne "Paris, France" -Et que les coordonnées sont (48.8566, 2.3522)

-

Quand il définit un rayon de 50 km

-

Alors la recherche PostGIS utilise ST_DWithin avec 50000 mètres

-
-

13. 📋 Plan: Recherche géographique avec différents rayons

-

Étant donné un contenu à 30 km de Paris

-

Quand l'utilisateur recherche autour de Paris avec un rayon de

-

Alors le contenu est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - -
rayonrésultat
20 kmnon trouvé
50 kmtrouvé
100 kmtrouvé
-
-

14. Utilisation de "Autour de moi" (GPS actuel)

-

Étant donné que l'utilisateur active le GPS -Et que sa position est (48.8566, 2.3522)

-

Quand il sélectionne "Autour de moi"

-

Alors la recherche utilise ses coordonnées GPS actuelles -Et un rayon par défaut de 10 km est appliqué

-
-

15. Curseur de rayon avec limites

-

Étant donné que l'utilisateur ouvre le curseur de rayon

-

Quand il ajuste le curseur

-

Alors les valeurs disponibles vont de 5 km à 500 km -Et la valeur s'affiche en temps réel "50 km"

-
-

16. Affichage de la distance dans les résultats

-

Étant donné une recherche géographique autour de Paris -Et un contenu à 2.3 km de distance

-

Quand les résultats sont affichés

-

Alors la distance "À 2.3 km" est indiquée pour chaque résultat

-
-

17. 📋 Plan: Tri par proximité géographique

-

Étant donné des contenus à différentes distances de Paris:

-
| contenu | distance |
-|---|---|
-| Louvre Guide | 0.5 km |
-| Tour Eiffel | 2.0 km |
-| Versailles | 20 km |
-
-

Quand l'utilisateur trie par "Proximité"

-

Alors les résultats sont affichés dans l'ordre:

-
| position | contenu |
-|---|---|
-| 1 | Louvre Guide |
-| 2 | Tour Eiffel |
-| 3 | Versailles |
-
-
-

18. Géocodage avec Nominatim (MVP)

-

Étant donné que l'application est en phase MVP

-

Quand une requête de géocodage est effectuée

-

Alors l'API publique Nominatim est utilisée -Et le rate limit de 1 req/s est respecté

-
-

19. Géocodage avec fallback Mapbox

-

Étant donné que Nominatim ne retourne aucun résultat

-

Quand l'application tente un fallback

-

Alors l'API Mapbox Geocoding est utilisée -Et le coût de 0.50€ / 1000 requêtes est appliqué

-
-

20. Ouverture du panneau de filtres

-

Étant donné que l'utilisateur est sur la page de recherche

-

Quand il clique sur "Filtres"

-

Alors un panneau latéral s'ouvre -Et 7 catégories de filtres sont affichées:

-
| catégorie |
-|---|
-| Type de contenu |
-| Durée |
-| Classification âge |
-| Géo-pertinence |
-| Tags |
-| Date de publication |
-| Abonnement |
-
-
-

21. Filtre par type de contenu (multi-sélection)

-

Étant donné que l'utilisateur ouvre les filtres

-

Quand il sélectionne:

-
| type |
-|---|
-| Contenu court |
-| Audio-guide |
-
-

Alors seuls ces types de contenus sont recherchés -Et les podcasts et radios live sont exclus

-
-

22. 📋 Plan: Filtre par durée

-

Étant donné un contenu de minutes

-

Quand l'utilisateur filtre par ""

-

Alors le contenu est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
duréetrancherésultat
3<5 mintrouvé
35-15 minnon trouvé
105-15 mintrouvé
2015-30 mintrouvé
45>30 mintrouvé
-
-

23. Filtre par classification âge

-

Étant donné des contenus avec différentes classifications:

-
| contenu | classification |
-|---|---|
-| Conte enfants | Tout public |
-| Podcast news | 13+ |
-| Débat politique | 16+ |
-
-

Quand l'utilisateur filtre "Tout public"

-

Alors seul "Conte enfants" est affiché

-
-

24. Filtre par géo-pertinence

-

Étant donné des contenus avec différents types géo:

-
| contenu | type_geo |
-|---|---|
-| Guide Louvre | Ancré |
-| Podcast Paris | Contextuel |
-| News nationales | Neutre |
-
-

Quand l'utilisateur filtre "Ancré, Contextuel"

-

Alors "Guide Louvre" et "Podcast Paris" sont affichés -Et "News nationales" est exclu

-
-

25. Filtre par tags (multi-sélection)

-

Étant donné des contenus taggés:

-
| contenu | tags |
-|---|---|
-| Voyage en Italie | Voyage, Gastronomie |
-| Histoire de Rome | Voyage, Histoire |
-| Économie italienne | Économie |
-
-

Quand l'utilisateur sélectionne les tags "Voyage, Histoire"

-

Alors "Histoire de Rome" est en priorité (2 tags correspondants) -Et "Voyage en Italie" est affiché (1 tag correspondant) -Et "Économie italienne" est exclu

-
-

26. 📋 Plan: Filtre par date de publication

-

Étant donné un contenu publié il y a

-

Quand l'utilisateur filtre par ""

-

Alors le contenu est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
délaipérioderésultat
12 heuresDernières 24htrouvé
3 joursCette semainetrouvé
15 joursCe moistrouvé
8 moisCette annéetrouvé
2 ansToutes datestrouvé
2 ansCette annéenon trouvé
-
-

27. Filtre par type d'abonnement

-

Étant donné des contenus gratuits et Premium:

-
| contenu | type |
-|---|---|
-| Balade Paris | Gratuit |
-| Visite VIP Louvre | Premium |
-
-

Quand l'utilisateur filtre "Premium uniquement 👑"

-

Alors seul "Visite VIP Louvre" est affiché

-
-

28. Combinaison de filtres multiples (AND logic)

-

Étant donné que l'utilisateur applique les filtres:

-
| filtre | valeur |
-|---|---|
-| Type | Audio-guide |
-| Durée | 5-15 min |
-| Tags | Voyage |
-| Classification | Tout public |
-
-

Quand la recherche est lancée

-

Alors seuls les contenus respectant TOUS les critères sont affichés

-
-

29. Réinitialisation des filtres

-

Étant donné que l'utilisateur a appliqué 5 filtres différents

-

Quand il clique sur "Réinitialiser"

-

Alors tous les filtres sont désactivés -Et la recherche affiche tous les résultats

-
-

30. Sauvegarde d'une recherche

-

Étant donné que l'utilisateur a appliqué plusieurs filtres

-

Quand il clique sur "💾 Sauvegarder cette recherche" -Et qu'il entre le nom "Podcasts voyage Paris"

-

Alors la recherche est sauvegardée -Et elle apparaît dans l'onglet "Recherches sauvegardées"

-
-

31. Limite de 5 recherches sauvegardées

-

Étant donné que l'utilisateur a déjà 5 recherches sauvegardées

-

Quand il tente de sauvegarder une 6ème recherche

-

Alors un message d'erreur s'affiche -Et il doit supprimer une recherche existante avant d'en ajouter une nouvelle

-
-

32. Notifications pour recherches sauvegardées

-

Étant donné une recherche sauvegardée "Podcasts voyage Paris" -Et que l'utilisateur a activé les notifications

-

Quand 3 nouveaux contenus correspondants sont publiés

-

Alors une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée

-
-

33. 📋 Plan: Options de tri des résultats

-

Étant donné une recherche avec plusieurs résultats

-

Quand l'utilisateur sélectionne le tri "

-

Alors les résultats sont triés selon

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
optionalgorithme
PertinenceScore recherche × (1 + log(écoutes + 1))
PopularitéÉcoutes complètes derniers 30j DESC
RécentDate publication DESC
ProximitéDistance GPS ASC (si recherche géo)
DuréeDurée audio ASC ou DESC
-
-

34. Structure d'un résultat de recherche

-

Étant donné un résultat de recherche

-

Quand la page est affichée

-

Alors chaque résultat contient:

-
| élément | exemple |
-|---|---|
-| Cover image | 120×68 px (16:9) |
-| Titre | Balade à Paris (2 lignes max) |
-| Créateur | @paris_stories ✓ |
-| Durée | 12 min |
-| Écoutes | 🎧 2.3K |
-| Localisation | 📍 Paris 5e · Ancré |
-| Tags | 🏷️ #Voyage #Histoire |
-| Badge Premium | 👑 (si applicable) |
-| Distance | À 2.3 km (si recherche géo) |
-| Bouton lecture | ▶️ Écouter |
-| Menu contextuel | ⋮ |
-
-
-

35. Lazy loading des images

-

Étant donné une page avec 20 résultats de recherche

-

Quand la page se charge

-

Alors seules les 5 premières images sont chargées -Et les images suivantes se chargent au scroll

-
-

36. Troncature du titre sur 2 lignes maximum

-

Étant donné un contenu avec un titre de 120 caractères

-

Quand le résultat est affiché

-

Alors le titre est tronqué après 2 lignes -Et "..." est ajouté à la fin

-
-

37. Lien cliquable vers le profil créateur

-

Étant donné un résultat de recherche pour "@paris_stories"

-

Quand l'utilisateur clique sur "@paris_stories"

-

Alors il est redirigé vers "https://roadwave.fr/@paris_stories"

-
-

38. Menu contextuel d'un résultat [⋮]

-

Étant donné que l'utilisateur clique sur [⋮] pour un résultat

-

Quand le menu s'ouvre

-

Alors les actions suivantes sont disponibles:

-
| action |
-|---|
-| Partager |
-| Ajouter à une playlist |
-| Télécharger (offline) |
-| Signaler |
-
-
-

39. Pagination avec 20 résultats par page

-

Étant donné une recherche retournant 100 résultats

-

Quand la page est affichée

-

Alors 20 résultats sont chargés initialement -Et un indicateur "1-20 sur 100 résultats" est visible

-
-

40. Infinite scroll automatique

-

Étant donné que l'utilisateur scroll dans les résultats

-

Quand il atteint 80% de la page

-

Alors les 20 résultats suivants sont chargés automatiquement -Et un loader est affiché pendant le chargement

-
-

41. Bouton fallback "Charger 20 suivants"

-

Étant donné que l'infinite scroll est désactivé (paramètres)

-

Quand l'utilisateur atteint la fin de la page

-

Alors un bouton "Charger 20 suivants" est affiché -Et les résultats se chargent au clic

-
-

42. Basculement entre vue liste et vue carte

-

Étant donné que l'utilisateur est sur la page de résultats

-

Quand il clique sur le toggle "Liste / Carte"

-

Alors la vue carte Leaflet s'affiche -Et les résultats sont affichés comme markers sur la carte

-
-

43. Affichage de la carte Leaflet

-

Étant donné que la vue carte est activée

-

Quand la carte se charge

-

Alors la carte utilise les tuiles OpenStreetMap -Et le centre est la position de recherche (ou GPS utilisateur) -Et le zoom initial montre tous les résultats

-
-

44. Markers cliquables sur la carte

-

Étant donné que 10 résultats sont affichés sur la carte

-

Quand l'utilisateur clique sur un marker

-

Alors une popup s'affiche avec:

-
| élément |
-|---|
-| Titre |
-| Créateur |
-| Durée |
-| Distance |
-| Bouton ▶️ Écouter |
-
-
-

45. Clustering des markers proches

-

Étant donné que 50 résultats sont très proches géographiquement

-

Quand la carte est affichée

-

Alors les markers proches sont groupés en clusters -Et le nombre de contenus est affiché sur le cluster -Et le cluster se décompose au zoom

-
-

46. Synchronisation liste / carte

-

Étant donné que l'utilisateur est en vue carte

-

Quand il clique sur un marker et écoute le contenu -Et qu'il rebascule en vue liste

-

Alors le contenu écouté est marqué dans la liste -Et la position de scroll est maintenue

-
-

47. Index PostgreSQL full-text pour performances

-

Étant donné que la base contient 100K contenus

-

Quand une recherche full-text est effectuée

-

Alors l'index GIN sur to_tsvector est utilisé -Et la requête retourne en moins de 100ms

-
-

48. Index PostGIS GIST pour recherche géo

-

Étant donné une recherche géographique avec rayon 50 km

-

Quand la requête PostGIS ST_DWithin est exécutée

-

Alors l'index GIST sur la colonne location est utilisé -Et la requête retourne en moins de 50ms

-
-

49. Index composites pour filtres

-

Étant donné une recherche avec filtres multiples

-

Quand les filtres type, durée, âge, géo, date sont appliqués

-

Alors l'index composite idx_content_filters est utilisé -Et les performances restent optimales

-
-

50. Index GIN pour recherche par tags

-

Étant donné une recherche filtrée par tags "Voyage, Histoire"

-

Quand la requête est exécutée

-

Alors l'index GIN sur la colonne tags est utilisé -Et la recherche est performante même avec 500K contenus

-
-

51. Aucun résultat trouvé

-

Étant donné que l'utilisateur recherche "xyzabc123"

-

Quand aucun résultat n'est trouvé

-

Alors un message "Aucun résultat pour 'xyzabc123'" s'affiche -Et des suggestions de recherches populaires sont proposées

-
-

52. Recherche vide

-

Étant donné que l'utilisateur clique sur "Rechercher" sans saisir de texte

-

Quand la recherche est lancée

-

Alors un message "Veuillez entrer au moins 2 caractères" s'affiche

-
-

53. Erreur de géocodage Nominatim

-

Étant donné que l'API Nominatim est indisponible

-

Quand l'utilisateur tente une recherche géographique

-

Alors un message "Service de localisation temporairement indisponible" s'affiche -Et la recherche continue sans filtre géographique

-
-

54. GPS désactivé pour "Autour de moi"

-

Étant donné que l'utilisateur a désactivé le GPS

-

Quand il sélectionne "Autour de moi"

-

Alors un message "Veuillez activer la localisation" s'affiche -Et un bouton "Activer" ouvre les paramètres système

-
-

55. Timeout de recherche après 10 secondes

-

Étant donné qu'une recherche complexe est lancée

-

Quand la requête dépasse 10 secondes

-

Alors la recherche est annulée -Et un message "La recherche a pris trop de temps, veuillez réessayer" s'affiche

-
-
- -

Classification de géo-pertinence des contenus

-
-

En tant que plateforme de contenu géolocalisé -Je veux classifier les contenus selon leur pertinence géographique -Afin d'adapter l'algorithme de recommandation

-
-

10 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Créateur choisit le type géo-ancré pour un audio-guide

-

Étant donné que je suis un créateur connecté

-

Quand je publie un audio-guide de la Tour Eiffel -Et que je choisis la classification "Géo-ancré"

-

Alors le contenu est enregistré avec:

-
| champ | valeur |
-|---|---|
-| type_geo | geo_ancre |
-| ponderation_geo | 0.7 |
-| ponderation_interets | 0.1 |
-
-
-

2. Créateur choisit le type géo-contextuel pour actualité régionale

-

Étant donné que je suis un créateur connecté

-

Quand je publie une actualité régionale en Bretagne -Et que je choisis la classification "Géo-contextuel"

-

Alors le contenu est enregistré avec:

-
| champ | valeur |
-|---|---|
-| type_geo | geo_contextuel |
-| ponderation_geo | 0.5 |
-| ponderation_interets | 0.3 |
-
-
-

3. Créateur choisit le type géo-neutre pour un podcast philosophie

-

Étant donné que je suis un créateur connecté

-

Quand je publie un podcast de philosophie -Et que je choisis la classification "Géo-neutre"

-

Alors le contenu est enregistré avec:

-
| champ | valeur |
-|---|---|
-| type_geo | geo_neutre |
-| ponderation_geo | 0.2 |
-| ponderation_interets | 0.6 |
-
-
-

4. Publication impossible sans classification géographique

-

Étant donné que je crée un contenu audio

-

Quand j'essaie de publier sans sélectionner de type géographique

-

Alors la publication échoue -Et je vois le message "Vous devez sélectionner un type de géo-pertinence"

-
-

5. Modérateur reclassifie un contenu mal catégorisé

-

Étant donné qu'un contenu podcast générique est classifié "Géo-ancré" -Et que le modérateur examine le contenu

-

Quand le modérateur le reclassifie en "Géo-neutre"

-

Alors la nouvelle classification est appliquée immédiatement -Et l'algorithme utilise la pondération géo = 0.2 -Et le créateur reçoit une notification de reclassification

-
-

6. Créateur modifie la classification après publication

-

Étant donné que j'ai publié un contenu classifié "Géo-contextuel" -Et que je réalise qu'il devrait être "Géo-neutre"

-

Quand je modifie la classification en "Géo-neutre"

-

Alors la modification est enregistrée -Et l'algorithme utilise la nouvelle pondération -Et je vois le message "Classification modifiée avec succès"

-
-

7. Statistiques de classification dans le profil créateur

-

Étant donné que je suis un créateur -Et que j'ai publié 30 contenus:

-
| type | nombre |
-|---|---|
-| Géo-ancré | 10 |
-| Géo-contextuel | 15 |
-| Géo-neutre | 5 |
-
-

Quand je consulte mes statistiques

-

Alors je vois la répartition de mes classifications -Et des suggestions pour optimiser la portée

-
-

8. Contenu géo-ancré fortement pondéré par la proximité

-

Étant donné qu'un audio-guide "Géo-ancré" existe à la Tour Eiffel -Et qu'un utilisateur est à 100m de la Tour Eiffel

-

Quand l'algorithme calcule le score

-

Alors la pondération géo est de 0.7 -Et le score géo est proche de 1 (très proche) -Et le contenu a un score final élevé

-
-

9. Contenu géo-neutre moins sensible à la distance

-

Étant donné qu'un podcast philosophie "Géo-neutre" existe à Paris -Et qu'un utilisateur est à Marseille (750 km)

-

Quand l'algorithme calcule le score

-

Alors la pondération géo est de 0.2 -Et le score géo est bas (distance élevée) -Mais le score intérêts (0.6) peut compenser -Et le contenu peut quand même être recommandé si intérêts match

-
-

10. Comparaison scores entre types géo pour même distance

-

Étant donné 3 contenus au même endroit (Paris):

-
| type | ponderation_geo |
-|---|---|
-| Géo-ancré | 0.7 |
-| Géo-contextuel | 0.5 |
-| Géo-neutre | 0.2 |
-
-

Et qu'un utilisateur est à 50 km de Paris

-

Quand l'algorithme calcule les scores

-

Alors le contenu "Géo-ancré" a le score géo le plus élevé -Et le contenu "Géo-neutre" a le score géo le plus faible -Mais peut avoir un score final plus élevé si forte correspondance intérêts

-
-
- -

Gestion du contenu politique (MVP simplifié)

-
-

En tant qu'utilisateur -Je veux pouvoir filtrer le contenu politique -Afin de contrôler mon exposition à ce type de contenu

-
-

13 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Créateur tagge son contenu comme "Politique"

-

Étant donné que je suis un créateur connecté

-

Quand je publie un contenu sur un débat politique -Et que je sélectionne le tag "Politique"

-

Alors le contenu est enregistré avec le tag "Politique" -Et aucune classification gauche/droite n'est demandée (MVP)

-
-

2. Tag "Politique" au même niveau que les autres tags

-

Étant donné que je crée un contenu

-

Quand je consulte la liste des tags disponibles

-

Alors je vois les tags suivants au même niveau:

-
| tag |
-|---|
-| Économie |
-| Sport |
-| Culture |
-| Politique |
-| Automobile |
-| Voyage |
-| Musique |
-
-
-

3. Par défaut, tous les contenus politiques sont visibles

-

Étant donné que je suis un nouvel utilisateur -Et que je n'ai pas modifié les paramètres de contenu politique

-

Quand je demande des recommandations

-

Alors les contenus tagués "Politique" sont inclus normalement -Et aucun filtrage n'est appliqué

-
-

4. Activer le filtrage "Masquer contenu politique"

-

Étant donné que je suis connecté

-

Quand j'active l'option "Masquer contenu politique" dans les paramètres

-

Alors tous les contenus tagués "Politique" sont exclus de mes recommandations -Et je vois le message "Contenu politique masqué"

-
-

5. Filtrage politique actif - aucun contenu politique recommandé

-

Étant donné que j'ai activé "Masquer contenu politique" -Et qu'il existe 100 contenus dont 20 tagués "Politique"

-

Quand je demande 50 recommandations

-

Alors je reçois 50 contenus parmi les 80 non-politiques -Et 0% de contenus politiques sont proposés

-
-

6. Désactiver le filtrage "Masquer contenu politique"

-

Étant donné que j'ai activé "Masquer contenu politique"

-

Quand je désactive cette option dans les paramètres

-

Alors les contenus politiques sont à nouveau inclus dans mes recommandations -Et le filtrage est levé immédiatement

-
-

7. Mode Kids filtre automatiquement le contenu politique

-

Étant donné que je suis un utilisateur de 14 ans -Et que le mode Kids est activé

-

Quand je demande des recommandations

-

Alors tous les contenus tagués "Politique" sont automatiquement exclus -Et ce indépendamment du paramètre "Masquer contenu politique"

-
-

8. Statistiques créateur sur contenu politique

-

Étant donné que je suis un créateur -Et que j'ai publié 20 contenus dont 5 tagués "Politique"

-

Quand je consulte mes statistiques

-

Alors je vois le nombre d'utilisateurs ayant masqué le contenu politique -Et le taux d'engagement comparé aux autres tags

-
-

9. Recherche avec tag "Politique"

-

Étant donné que je recherche du contenu

-

Quand je filtre par tag "Politique"

-

Alors seuls les contenus tagués "Politique" sont affichés -Et ce même si j'ai activé "Masquer contenu politique" (recherche explicite)

-
-

10. Partage de contenu politique avec filtre actif

-

Étant donné que j'ai activé "Masquer contenu politique" -Et qu'un ami me partage un lien vers un contenu tagué "Politique"

-

Quand j'ouvre le lien

-

Alors je peux accéder au contenu (partage explicite) -Et je vois un avertissement "Ce contenu est tagué Politique"

-
-

11. Pas de classification gauche/droite en MVP

-

Étant donné que je suis un créateur

-

Quand je publie un contenu tagué "Politique"

-

Alors aucune option de classification idéologique n'est proposée -Et je ne peux pas indiquer "Gauche", "Droite", "Centre", etc.

-
-

12. Pas d'équilibrage imposé en MVP

-

Étant donné qu'un utilisateur écoute majoritairement du contenu politique de gauche

-

Quand l'algorithme génère des recommandations

-

Alors aucun équilibrage droite/gauche n'est appliqué -Et les recommandations suivent l'algorithme standard (intérêts, géo, engagement)

-
-

13. Notification post-MVP pour classification avancée

-

Étant donné que RoadWave passe en phase post-MVP -Et que la classification politique avancée est activée

-

Quand je me connecte

-

Alors je reçois une notification m'informant des nouvelles options -Et je peux configurer mes préférences d'équilibrage politique

-
-
- -

Contenus géolocalisés en mode voiture

-
-

En tant qu'utilisateur en voiture -Je veux recevoir des notifications de contenus géolocalisés au bon moment -Afin de découvrir du contenu contextuel sans distraction au volant

-
-

36 scénarios (32 standards, 4 plans)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que l'application est ouverte (premier plan) -Et que le GPS est activé -Et que l'utilisateur est en mode voiture (vitesse ≥ 5 km/h)

-
-

1. Calcul ETA et notification 7 secondes avant le point GPS

-

Étant donné qu'un contenu géolocalisé existe à la Tour Eiffel (48.8584, 2.2945) -Et que je me déplace à 50 km/h vers ce point -Et que je suis à 98 mètres du point (ETA = 7 secondes)

-

Quand le système calcule l'ETA

-

Alors une notification est déclenchée immédiatement -Et le compteur "7" s'affiche avec l'icône 🏛️ -Et une notification sonore (bip court) est jouée

-
-

2. 📋 Plan: Calcul ETA à différentes vitesses

-

Étant donné qu'un contenu géolocalisé existe à un point GPS -Et que je me déplace à km/h

-

Quand je suis à mètres du point

-

Alors l'ETA calculé est secondes -Et la notification est déclenchée :

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vitessedistanceetanotification
10197Oui
50987Oui
1302527Oui
5020014Non
105018Non
-
-

3. Notification immédiate si vitesse <5 km/h ET distance <50m

-

Étant donné qu'un contenu géolocalisé existe à 30m de ma position -Et que ma vitesse est 3 km/h (arrêté à un feu rouge)

-

Quand le système détecte cette situation

-

Alors une notification est déclenchée immédiatement -Et je n'ai pas besoin d'attendre le calcul ETA

-
-

4. Notification minimaliste sans texte (sécurité routière)

-

Étant donné qu'une notification géolocalisée est déclenchée

-

Quand la notification s'affiche

-

Alors les éléments suivants sont visibles:

-
| élément | présent |
-|---|---|
-| Icône du tag | ✅ |
-| Compteur 7→1 | ✅ |
-| Son bref (bip) | ✅ |
-| Titre texte | ❌ |
-| Description | ❌ |
-| Cover image | ❌ |
-| Bouton Annuler | ❌ |
-
-
-

5. 📋 Plan: Icônes selon le tag du contenu

-

Étant donné qu'un contenu géolocalisé avec le tag est disponible

-

Quand la notification s'affiche

-

Alors l'icône est affichée

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tagicone
Culture générale🏛️
Histoire📜
Voyage✈️
Famille👨‍👩‍👧
Musique🎵
Sport
Technologie💻
Automobile🚗
-
-

6. Compteur décrémentant de 7 à 1

-

Étant donné qu'une notification géolocalisée s'affiche

-

Quand le compteur démarre

-

Alors le compteur affiche "7" -Et après 1 seconde, il affiche "6" -Et après 2 secondes, il affiche "5" -Et après 3 secondes, il affiche "4" -Et après 4 secondes, il affiche "3" -Et après 5 secondes, il affiche "2" -Et après 6 secondes, il affiche "1" -Et après 7 secondes, la notification disparaît

-
-

7. Notification sonore uniquement en mode CarPlay

-

Étant donné que l'application est connectée à CarPlay -Et qu'un contenu géolocalisé est détecté (ETA 7s)

-

Quand la notification est déclenchée

-

Alors seule la notification sonore (bip) est jouée -Et aucun overlay visuel n'est affiché (icône, compteur) -Et l'utilisateur peut valider via le bouton "Suivant" au volant

-
-

8. Notification sonore uniquement en mode Android Auto

-

Étant donné que l'application est connectée à Android Auto -Et qu'un contenu géolocalisé est détecté (ETA 7s)

-

Quand la notification est déclenchée

-

Alors seule la notification sonore (bip) est jouée -Et aucun overlay visuel n'est affiché -Et l'utilisateur peut valider via le bouton "Suivant" au volant

-
-

9. Notification complète (sonore + visuelle) en mode normal

-

Étant donné que l'application n'est PAS connectée à CarPlay/Android Auto -Et qu'un contenu géolocalisé est détecté

-

Quand la notification est déclenchée

-

Alors la notification sonore (bip) est jouée -Et l'overlay visuel s'affiche (icône + compteur 7→1)

-
-

10. Validation via bouton "Suivant" et décompte 5 secondes

-

Étant donné qu'une notification géolocalisée est affichée (compteur à 5) -Et que j'écoute un podcast

-

Quand j'appuie sur le bouton "Suivant"

-

Alors le compteur bascule à "5" (décompte final) -Et le contenu actuel continue de jouer normalement -Et le compteur décrémente: 5→4→3→2→1 -Et après 5 secondes, le contenu géolocalisé démarre (fade in 0.3s)

-
-

11. Transition fluide avec fade out/in

-

Étant donné que le décompte atteint "0"

-

Quand le contenu géolocalisé doit démarrer

-

Alors le contenu actuel fait un fade out de 0.3s -Et le contenu géolocalisé fait un fade in de 0.3s -Et il n'y a pas de silence entre les deux

-
-

12. Contenu actuel se termine pendant le décompte

-

Étant donné que j'ai validé la notification (décompte 5s démarre) -Et que mon contenu actuel se termine après 2 secondes

-

Quand le contenu actuel se termine

-

Alors le contenu suivant du buffer démarre immédiatement -Et le décompte continue (3→2→1) -Et à la fin du décompte, le contenu géolocalisé remplace le buffer

-
-

13. Ignorance de la notification (pas de clic pendant 7s)

-

Étant donné qu'une notification géolocalisée s'affiche (compteur 7)

-

Quand 7 secondes s'écoulent sans que j'appuie sur "Suivant"

-

Alors la notification disparaît automatiquement -Et le contenu géolocalisé est perdu (pas d'insertion dans la file) -Et un cooldown de 10 minutes est activé

-
-

14. Quota de 6 contenus géolocalisés par heure

-

Étant donné que j'ai validé 6 notifications géolocalisées dans la dernière heure

-

Quand un 7ème contenu géolocalisé est détecté

-

Alors aucune notification n'est envoyée -Et le contenu n'est pas inséré dans la file

-
-

15. Fenêtre glissante de 60 minutes

-

Étant donné que j'ai validé 6 contenus géolocalisés -Et que le premier contenu a été validé il y a 61 minutes

-

Quand un nouveau contenu géolocalisé est détecté

-

Alors la notification est envoyée (quota libéré : 5/6) -Et le compteur horaire est mis à jour

-
-

16. 📋 Plan: Gestion du quota horaire

-

Étant donné que notifications ont été validées dans la dernière heure

-

Quand un nouveau contenu géolocalisé est détecté

-

Alors la notification est

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nb_validesaction
0envoyée
3envoyée
5envoyée
6non envoyée
7non envoyée
-
-

17. Exception audio-guides multi-séquences (comptent comme 1)

-

Étant donné que j'ai démarré un audio-guide avec 8 séquences -Et que cet audio-guide compte comme 1 contenu dans le quota

-

Quand toutes les séquences de l'audio-guide sont lues

-

Alors mon quota reste à 1/6 -Et je peux encore valider 5 contenus géolocalisés simples

-
-

18. Cooldown de 10 minutes après notification ignorée

-

Étant donné qu'une notification géolocalisée a été ignorée (pas de clic) -Et qu'un cooldown de 10 minutes est activé

-

Quand 5 minutes s'écoulent -Et qu'un nouveau contenu géolocalisé est détecté

-

Alors aucune notification n'est envoyée (cooldown actif)

-
-

19. Cooldown expire après 10 minutes

-

Étant donné qu'un cooldown a été activé il y a 10 minutes

-

Quand un nouveau contenu géolocalisé est détecté

-

Alors la notification est envoyée (cooldown expiré)

-
-

20. Pas de cooldown si notification validée

-

Étant donné qu'une notification géolocalisée est affichée

-

Quand j'appuie sur "Suivant" dans les 7 secondes

-

Alors aucun cooldown n'est activé -Et la prochaine notification pourra être envoyée normalement

-
-

21. Contenu géolocalisé dans l'historique de navigation

-

Étant donné que j'écoute un contenu du buffer -Et que j'ai validé un contenu géolocalisé "Tour Eiffel" -Et que j'ai écouté 42 secondes du contenu géolocalisé

-

Quand j'appuie sur "Suivant" (skip) -Et que j'appuie ensuite sur "Précédent"

-

Alors le contenu géolocalisé reprend à 42 secondes

-
-

22. Contenu ignoré n'entre pas dans l'historique

-

Étant donné qu'une notification géolocalisée a été ignorée

-

Quand j'appuie sur "Précédent"

-

Alors le contenu géolocalisé ignoré n'apparaît PAS dans l'historique -Et je reviens au contenu d'avant

-
-

23. Skip pendant le décompte annule l'insertion

-

Étant donné que j'ai validé une notification (décompte 5s en cours) -Et que le compteur affiche "3"

-

Quand j'appuie à nouveau sur "Suivant"

-

Alors le décompte est annulé -Et le contenu suivant du buffer démarre -Et le contenu géolocalisé n'entre PAS dans l'historique

-
-

24. Détection mode piéton (vitesse <5 km/h stable 10s)

-

Étant donné que je suis en mode voiture -Et que ma vitesse passe à 3 km/h

-

Quand cette vitesse reste stable pendant 10 secondes

-

Alors le mode piéton est activé automatiquement -Et les notifications passent en mode push arrière-plan (si permission accordée)

-
-

25. Détection mode voiture (vitesse ≥5 km/h stable 10s)

-

Étant donné que je suis en mode piéton -Et que ma vitesse passe à 15 km/h

-

Quand cette vitesse reste stable pendant 10 secondes

-

Alors le mode voiture est activé automatiquement -Et les notifications passent en mode sonore + icône (app premier plan requise)

-
-

26. Hysteresis pour éviter basculements intempestifs

-

Étant donné que ma vitesse passe de 20 km/h à 3 km/h (arrêt feu rouge) -Et que ma vitesse remonte à 20 km/h après 8 secondes

-

Quand le système vérifie le mode

-

Alors aucun basculement n'a lieu (hysteresis de 10s non atteinte) -Et je reste en mode voiture

-
-

27. 📋 Plan: Effets du basculement voiture → piéton

-

Étant donné que je bascule de voiture à piéton

-

Quand le basculement est effectué

-

Alors les paramètres suivants changent:

-
| paramètre | voiture | piéton |
-|---|---|---|
-| App requise | Premier plan | Arrière-plan OK |
-| Notification | Sonore + icône + compteur | Push système |
-| Rayon détection | ETA 7s (variable) | 200m fixes |
-| Type contenu | Tous géolocalisés | Audio-guides uniquement |
-
-
-

28. Haute vitesse (130 km/h sur autoroute)

-

Étant donné que je roule à 130 km/h (36.1 m/s) -Et qu'un contenu géolocalisé est à 252 mètres

-

Quand l'ETA de 7s est atteint -Et que je valide la notification

-

Alors le décompte 5s démarre -Et le contenu géolocalisé démarre encore avant le point GPS (72m avant)

-
-

29. Multiples points géolocalisés proches (route touristique)

-

Étant donné que 3 châteaux sont espacés de 800m chacun -Et que je valide la notification du Château A

-

Quand j'arrive près du Château B (57s plus tard à 50 km/h)

-

Alors la notification du Château B est envoyée (quota 2/6, pas de cooldown)

-
-

30. Mode stationnement (vitesse <1 km/h pendant 2 min)

-

Étant donné que je me gare à 30m d'un château -Et que ma vitesse est <1 km/h pendant 2 minutes

-

Quand le mode stationnement est détecté

-

Alors aucune notification de contenu géolocalisé n'est envoyée -Et le système bascule automatiquement en mode piéton

-
-

31. Reprise conduite après stationnement

-

Étant donné que je suis en mode stationnement -Et que ma vitesse passe à 20 km/h pendant 10 secondes

-

Quand le système détecte la reprise de conduite

-

Alors le mode voiture est réactivé -Et les notifications géolocalisées reprennent (si quota non atteint)

-
-

32. Contenu géolocalisé simple (1 séquence unique)

-

Étant donné qu'un contenu géolocalisé simple existe à un point GPS

-

Quand la notification est déclenchée (ETA 7s) -Et que je valide

-

Alors le contenu démarre après décompte 5s -Et à la fin du contenu, le buffer normal reprend -Et ce contenu compte 1/6 dans le quota

-
-

33. Audio-guide multi-séquences (2+ séquences enchaînées)

-

Étant donné qu'un audio-guide avec 8 séquences existe

-

Quand je démarre l'audio-guide -Et que les séquences s'enchaînent automatiquement (GPS ou manuel)

-

Alors l'audio-guide entier compte 1/6 dans le quota -Et les séquences ne déclenchent PAS de notification avec compteur 7s -Et elles se déclenchent au point GPS exact (rayon 30m)

-
-

34. GPS désactivé en mode voiture

-

Étant donné que je suis en mode voiture

-

Quand le GPS est désactivé

-

Alors aucune notification géolocalisée ne peut être envoyée -Et un message d'erreur s'affiche: "GPS requis pour les contenus géolocalisés"

-
-

35. App en arrière-plan en mode voiture

-

Étant donné que je suis en mode voiture -Et que l'app passe en arrière-plan

-

Quand un contenu géolocalisé est détecté

-

Alors aucune notification n'est envoyée (app premier plan requise) -Et le contenu n'est pas perdu (sera proposé si app rouverte dans le rayon)

-
-

36. Permission "Always Location" refusée (mode piéton indisponible)

-

Étant donné que je refuse la permission "Always Location"

-

Quand ma vitesse passe <5 km/h

-

Alors le mode piéton n'est PAS activé -Et le mode voiture reste actif (avec permission "When In Use") -Et aucune notification arrière-plan n'est envoyée

-
-
- -

Gestion de l'historique et reproposition

-
-

En tant que système de recommandation -Je veux gérer l'historique d'écoute intelligemment -Afin d'éviter les répétitions et offrir une découverte maximale

-
-

19 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Contenu écouté complètement (>80%) - jamais reproposé

-

Étant donné qu'un utilisateur a écouté un contenu à 85%

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu n'est jamais reproposé -Et il est marqué comme "écouté" dans l'historique

-
-

2. Contenu écouté à 80% exactement - jamais reproposé

-

Étant donné qu'un utilisateur a écouté un contenu exactement à 80%

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu n'est pas reproposé (seuil >= 80%)

-
-

3. Contenu skippé rapidement (<10s) - ne pas reproposer

-

Étant donné qu'un utilisateur a skippé un contenu après 8 secondes

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu n'est pas reproposé (signal négatif fort) -Et la jauge d'intérêt correspondante est réduite de 0.5%

-
-

4. Contenu skippé exactement à 10s - ne pas reproposer

-

Étant donné qu'un utilisateur a skippé un contenu après exactement 10 secondes

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu n'est pas reproposé (seuil < 10s strict)

-
-

5. Contenu partiellement écouté (10-80%) - reproposer avec reprise

-

Étant donné qu'un utilisateur a écouté un contenu à 45% -Et qu'il est arrivé à la position 2:30 (150 secondes)

-

Quand l'algorithme propose à nouveau ce contenu

-

Alors le contenu peut être reproposé -Et la position de reprise est 150 secondes -Et l'utilisateur voit "Reprendre à 2:30"

-
-

6. Contenu écouté à 11% - reproposition possible

-

Étant donné qu'un utilisateur a écouté un contenu à 11%

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu peut être reproposé (>10%) -Et la position de reprise est sauvegardée

-
-

7. Contenu écouté à 79% - reproposition possible

-

Étant donné qu'un utilisateur a écouté un contenu à 79%

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu peut être reproposé (<80%) -Et l'utilisateur peut terminer l'écoute

-
-

8. Audio-guide avec flag replayable=true

-

Étant donné qu'un audio-guide a le flag "replayable = true" -Et qu'un utilisateur l'a écouté à 95%

-

Quand l'algorithme génère les recommandations

-

Alors l'audio-guide peut être reproposé -Et il est marqué comme "Écouté - Rejouable"

-
-

9. Podcast standard sans flag replayable

-

Étant donné qu'un podcast n'a pas de flag replayable -Et qu'un utilisateur l'a écouté à 90%

-

Quand l'algorithme génère les recommandations

-

Alors le podcast n'est jamais reproposé

-
-

10. Stockage dans user_content_history

-

Étant donné qu'un utilisateur écoute un contenu

-

Quand l'écoute se termine ou est skippée

-

Alors les données suivantes sont enregistrées:

-
| champ | exemple |
-|---|---|
-| user_id | user-123 |
-| content_id | content-456 |
-| completion_rate | 0.45 (45%) |
-| last_position | 150 (secondes) |
-| listened_at | 2026-01-21 14:30:00 |
-
-
-

11. Historique illimité stocké

-

Étant donné qu'un utilisateur a écouté 5000 contenus

-

Quand il consulte son historique

-

Alors tous les 5000 contenus sont disponibles -Et aucun contenu n'est supprimé automatiquement

-
-

12. Algorithme considère les 100 derniers pour performance

-

Étant donné qu'un utilisateur a écouté 500 contenus

-

Quand l'algorithme génère les recommandations

-

Alors il vérifie uniquement les 100 derniers contenus pour exclusion -Et cette limite est une optimisation de requête SQL

-
-

13. Export historique complet (RGPD)

-

Étant donné qu'un utilisateur demande l'export RGPD

-

Quand l'export est généré

-

Alors l'historique complet est inclus avec:

-
| information | inclus |
-|---|---|
-| Tous les contenus | ✅ |
-| Dates d'écoute | ✅ |
-| Taux complétion | ✅ |
-| Positions reprise | ✅ |
-
-
-

14. Reprise automatique d'un contenu partiellement écouté

-

Étant donné que j'ai écouté un podcast à 60% (position 10:00)

-

Quand ce podcast est reproposé par l'algorithme -Et que je lance la lecture

-

Alors l'écoute reprend automatiquement à 10:00 -Et je vois une notification "Reprise à 10:00"

-
-

15. Option "Reprendre du début" pour contenu partiellement écouté

-

Étant donné que j'ai écouté un podcast à 60%

-

Quand ce podcast est reproposé

-

Alors je vois deux options:

-
| option | action |
-|---|---|
-| Reprendre à 10:00 | Lecture à partir de 10:00 |
-| Depuis le début | Lecture à partir de 0:00 |
-
-
-

16. Contenu écouté il y a 6 mois - toujours en historique

-

Étant donné qu'un utilisateur a écouté un contenu il y a 6 mois à 90%

-

Quand l'algorithme génère les recommandations

-

Alors ce contenu n'est toujours pas reproposé -Et l'historique n'a pas de limite temporelle

-
-

17. Nouveau contenu du même créateur après écoute complète

-

Étant donné qu'un utilisateur a écouté un contenu de "Créateur A" à 90% -Et que "Créateur A" publie un nouveau contenu

-

Quand l'algorithme génère les recommandations

-

Alors le nouveau contenu peut être recommandé -Et seul l'ancien contenu est exclu (pas tout le créateur)

-
-

18. Statistiques personnelles d'historique

-

Étant donné que je consulte mon profil

-

Quand j'accède à la section "Historique"

-

Alors je vois:

-
| métrique | exemple |
-|---|---|
-| Nombre total d'écoutes | 1,234 |
-| Heures écoutées | 456h |
-| Taux complétion moyen | 72% |
-| Top 5 catégories | Voyage, Sport |
-
-
-

19. Filtrer l'historique par date

-

Étant donné que je consulte mon historique

-

Quand je filtre par "Dernière semaine"

-

Alors seuls les contenus écoutés dans les 7 derniers jours sont affichés -Et je peux exporter cette sélection

-
-
- -

Médias traditionnels sur RoadWave

-
-

En tant que média établi -Je veux publier du contenu géolocalisé sur RoadWave -Afin d'atteindre une audience locale et mobile

-
-

21 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Création d'un compte média vérifié

-

Étant donné que je représente Le Monde

-

Quand je crée un compte média -Et que je fournis les justificatifs (SIRET, documents officiels)

-

Alors mon compte est créé en attente de vérification -Et l'équipe RoadWave examine ma demande sous 48-72h

-
-

2. Validation compte média par l'équipe RoadWave

-

Étant donné qu'un compte média "Le Parisien" est en attente

-

Quand l'équipe RoadWave valide le compte

-

Alors le compte reçoit le badge vérifié ✓ -Et le média peut publier sans validation des 3 premiers contenus -Et je vois le message "Compte média vérifié avec succès"

-
-

3. Badge vérifié visible sur profil média

-

Étant donné que "France Inter" a un compte vérifié

-

Quand un utilisateur consulte le profil

-

Alors il voit le badge ✓ à côté du nom -Et une mention "Média vérifié"

-
-

4. Pas de validation des 3 premiers contenus pour médias

-

Étant donné que je suis un média vérifié

-

Quand je publie mon premier contenu

-

Alors le contenu est publié immédiatement sans validation -Et il est visible pour tous les utilisateurs -Et je ne passe pas par la modération initiale

-
-

5. Modération a posteriori uniquement

-

Étant donné que "Libération" publie un contenu

-

Quand le contenu est publié

-

Alors il est immédiatement disponible -Mais peut être signalé et modéré a posteriori -Et suit les mêmes règles de modération que les créateurs

-
-

6. Publication flash info géolocalisé

-

Étant donné que je suis "Ouest-France" (média régional)

-

Quand je publie un flash info sur un événement à Rennes -Et que je le géolocalise en Bretagne (géo-contextuel)

-

Alors le contenu est publié immédiatement -Et il est recommandé aux utilisateurs en Bretagne

-
-

7. Publication chronique thématique

-

Étant donné que je suis "France Culture"

-

Quand je publie une chronique philosophie (géo-neutre)

-

Alors le contenu est disponible partout en France -Et suit l'algorithme de recommandation standard

-
-

8. Publication édito politique

-

Étant donné que je suis "Le Figaro"

-

Quand je publie un édito politique -Et que je le tague "Politique"

-

Alors le contenu est publié immédiatement -Et la classification politique MVP s'applique (pas gauche/droite) -Et les utilisateurs ayant activé "Masquer politique" ne le voient pas

-
-

9. Formats de contenu autorisés pour médias

-

Étant donné que je suis un média vérifié

-

Quand je publie du contenu

-

Alors je peux publier:

-
| format | exemple |
-|---|---|
-| Flash info géolocalisé | Actualité régionale 2-5 min |
-| Chronique thématique | Culture, économie, sport 5-15min |
-| Édito et débats | Opinion 10-30 min |
-| Reportage | Investigation 15-45 min |
-
-
-

10. Médias suivent les règles standard de classification âge

-

Étant donné que je suis "RTL"

-

Quand je publie un contenu sensible

-

Alors je dois obligatoirement classifier par âge:

-
| classification | type contenu |
-|---|---|
-| Tout public | Info générale |
-| 13+ | Actualité avec sujets sensibles |
-| 16+ | Débats avec violence verbale |
-| 18+ | Sujets adultes |
-
-
-

11. Monétisation médias - partage revenus pub standard

-

Étant donné que je suis un média vérifié -Et que mes contenus génèrent des écoutes

-

Quand le mois se termine

-

Alors je reçois 3€ / 1000 écoutes complètes (même taux que créateurs) -Et le paiement suit les mêmes règles (seuil 50€, mensuel)

-
-

12. Sponsoring direct non géré par plateforme

-

Étant donné que je suis "Europe 1" -Et que je veux intégrer un sponsor dans mon contenu

-

Quand je mentionne le sponsor dans l'audio

-

Alors c'est autorisé (sponsoring éditorial) -Mais RoadWave ne gère pas la transaction -Et je gère la relation sponsor directement

-
-

13. Médias peuvent avoir plusieurs comptes créateurs

-

Étant donné que je suis "Le Monde"

-

Quand je veux créer des sous-comptes par rubrique

-

Alors je peux créer:

-
| compte | description |
-|---|---|
-| @lemonde_politique | Actualité politique |
-| @lemonde_economie | Économie et entreprises |
-| @lemonde_culture | Culture et spectacles |
-
-

Et tous sont liés au compte média principal

-
-

14. Médias régionaux privilégiés localement

-

Étant donné que "Sud-Ouest" publie du contenu géo-contextuel en Nouvelle-Aquitaine -Et qu'un utilisateur est à Bordeaux

-

Quand l'algorithme calcule les recommandations

-

Alors le contenu de "Sud-Ouest" a un score géo élevé -Et il est privilégié pour l'audience locale

-
-

15. Médias nationaux accessibles partout

-

Étant donné que "France Inter" publie un podcast géo-neutre

-

Quand des utilisateurs à Paris, Lyon, Marseille demandent des recommandations

-

Alors le podcast est accessible partout sans distinction géographique -Et suit l'algorithme de recommandation standard

-
-

16. Statistiques détaillées pour médias

-

Étant donné que je suis un média vérifié

-

Quand je consulte mes statistiques

-

Alors je vois:

-
| métrique | exemple |
-|---|---|
-| Écoutes par région | Île-de-France: 45% |
-| Taux complétion | 72% |
-| Démographie auditeurs | 25-34 ans: 35% |
-| Top contenus | Flash info Paris |
-| Revenus générés | 1,234€ |
-
-
-

17. Médias peuvent exporter analytics

-

Étant donné que je suis "Libération"

-

Quand je clique sur "Exporter analytics"

-

Alors je reçois un CSV avec données détaillées -Et je peux analyser les données avec mes outils internes

-
-

18. Contact prioritaire équipe RoadWave

-

Étant donné que je suis un média vérifié

-

Quand j'ai un problème technique ou question

-

Alors je peux contacter le support média prioritaire -Et j'obtiens une réponse sous 24h (vs 48-72h standard)

-
-

19. Médias peuvent programmer la publication

-

Étant donné que je suis "France Culture"

-

Quand je prépare un contenu à l'avance

-

Alors je peux programmer la publication pour une date/heure future -Et le contenu sera publié automatiquement au moment choisi

-
-

20. API dédiée pour médias (post-MVP)

-

Étant donné que je suis un grand média avec beaucoup de contenus

-

Quand RoadWave développe l'API médias

-

Alors je peux automatiser la publication via API -Et intégrer RoadWave dans mon workflow de production

-
-

21. Signalement d'un contenu média traité en priorité

-

Étant donné qu'un contenu de "Le Monde" est signalé

-

Quand le signalement arrive en modération

-

Alors il est traité avec la même priorité qu'un créateur standard -Et le badge vérifié ne donne pas d'immunité modération

-
-
- -

Mode Kids pour utilisateurs 13-15 ans

-
-

En tant que parent ou adolescent -Je veux activer un mode Kids avec filtrage de contenu -Afin de protéger les mineurs des contenus inappropriés

-
-

15 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Activation manuelle du mode Kids

-

Étant donné que je suis un utilisateur de 14 ans -Et que le mode Kids n'est pas activé par défaut

-

Quand j'active le mode Kids dans les paramètres

-

Alors le mode Kids est activé sur mon compte -Et je vois le message "Mode Kids activé - Contenus filtrés pour 13-15 ans"

-
-

2. Parent active le mode Kids pour son enfant

-

Étant donné que je suis le parent d'un utilisateur de 13 ans -Et que j'ai accès au compte de mon enfant

-

Quand j'active le mode Kids

-

Alors le mode Kids est activé sur le compte enfant -Et seuls les contenus "Tous publics" sont accessibles

-
-

3. Filtrage contenu - uniquement "Tous publics"

-

Étant donné que le mode Kids est activé sur mon compte -Et qu'il existe des contenus avec les classifications:

-
| classification | nombre |
-|---|---|
-| Tous publics | 100 |
-| 13+ | 50 |
-| 16+ | 30 |
-| 18+ | 20 |
-
-

Quand je demande des recommandations

-

Alors seuls les 100 contenus "Tous publics" sont proposés -Et les contenus 13+, 16+, 18+ sont exclus

-
-

4. Exclusion automatique du contenu politique

-

Étant donné que le mode Kids est activé -Et qu'il existe 20 contenus "Tous publics" dont 5 tagués "Politique"

-

Quand je demande des recommandations

-

Alors seuls les 15 contenus non-politiques sont proposés -Et les 5 contenus politiques sont automatiquement exclus

-
-

5. Pas de publicité en mode Kids

-

Étant donné que le mode Kids est activé -Et que je suis un utilisateur gratuit

-

Quand j'écoute du contenu

-

Alors aucune publicité n'est diffusée -Et je n'ai pas d'insertion publicitaire (règle 1/5 désactivée)

-
-

6. Publicité validée manuellement en mode Kids (post-MVP)

-

Étant donné que le mode Kids est activé -Et qu'une publicité a été validée manuellement pour le mode Kids

-

Quand j'écoute du contenu

-

Alors cette publicité peut être diffusée -Mais la fréquence reste inférieure au mode standard

-
-

7. Interface standard même en mode Kids

-

Étant donné que le mode Kids est activé

-

Quand j'ouvre l'application

-

Alors l'interface est identique au mode normal -Et seul le filtrage de contenu est actif (pas d'UI enfant)

-
-

8. Désactivation du mode Kids

-

Étant donné que le mode Kids est activé

-

Quand je désactive le mode Kids dans les paramètres

-

Alors tous les contenus sont à nouveau accessibles selon mon âge -Et je vois le message "Mode Kids désactivé"

-
-

9. Utilisateur 16 ans ne peut pas activer le mode Kids 13-15 ans

-

Étant donné que je suis un utilisateur de 16 ans

-

Quand j'essaie d'activer le mode Kids

-

Alors l'activation réussit -Et le mode Kids filtre les contenus 16+ et 18+ (pas seulement 13+) -Et je vois uniquement les contenus "Tous publics"

-
-

10. Tentative d'accès direct à contenu 16+ en mode Kids

-

Étant donné que le mode Kids est activé -Et qu'un ami me partage un contenu 16+

-

Quand j'essaie d'accéder au contenu via le lien

-

Alors l'accès est refusé -Et je vois le message "Ce contenu n'est pas accessible en mode Kids"

-
-

11. Recherche en mode Kids filtre automatiquement

-

Étant donné que le mode Kids est activé

-

Quand je recherche "débat"

-

Alors seuls les contenus "Tous publics" apparaissent dans les résultats -Et les contenus 13+, 16+, 18+ sont exclus de la recherche

-
-

12. Audio-guide en mode Kids

-

Étant donné que le mode Kids est activé -Et qu'un audio-guide "Tous publics" existe au musée du Louvre

-

Quand je suis à proximité du Louvre

-

Alors l'audio-guide est proposé normalement -Et toutes les séquences sont accessibles

-
-

13. Statistiques créateur - audience mode Kids

-

Étant donné que je suis un créateur -Et que mes contenus "Tous publics" sont écoutés par des utilisateurs mode Kids

-

Quand je consulte mes statistiques

-

Alors je vois le pourcentage d'écoutes en mode Kids -Et je peux adapter mes contenus en conséquence

-
-

14. Notification lors de l'activation du mode Kids

-

Quand j'active le mode Kids

-

Alors je reçois une notification explicative:

-
| information | description |
-|---|---|
-| Contenu | Seuls les contenus "Tous publics" accessibles |
-| Politique | Contenus politiques automatiquement masqués |
-| Publicité | Aucune publicité affichée |
-
-
-

15. Badge mode Kids visible dans le profil

-

Étant donné que le mode Kids est activé

-

Quand je consulte mon profil

-

Alors je vois un badge "Mode Kids actif 🛡️" -Et je peux le désactiver en un clic

-
-
- -

Paramétrabilité admin et A/B testing

-
-

En tant qu'administrateur RoadWave -Je veux configurer les paramètres de l'algorithme à chaud -Afin d'optimiser l'engagement sans redéploiement

-
-

20 scénarios (19 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté en tant qu'admin

-
-

1. Accès au dashboard admin

-

Quand j'accède au dashboard admin

-

Alors je vois tous les paramètres configurables de l'algorithme -Et je vois les valeurs actuelles et par défaut

-
-

2. Modifier le poids géo pour contenu ancré

-

Étant donné que le poids_geo_ancre est à 0.7 (défaut)

-

Quand je modifie le poids_geo_ancre à 0.8 -Et que je sauvegarde

-

Alors la nouvelle valeur est appliquée immédiatement -Et tous les nouveaux calculs utilisent 0.8 -Et je vois le message "Paramètre mis à jour avec succès"

-
-

3. Validation des plages de valeurs

-

Quand j'essaie de configurer poids_geo_ancre à 1.5 (hors plage 0.5-1.0)

-

Alors la modification échoue -Et je vois le message "Valeur hors plage autorisée (0.5 - 1.0)"

-
-

4. 📋 Plan: Modification de tous les paramètres configurables

-

Quand je modifie "" à ""

-

Alors la modification est appliquée immédiatement -Et la nouvelle valeur respecte la plage ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
parametrenouvelle_valeurplage
poids_geo_ancre0.80.5 - 1.0
poids_geo_contextuel0.60.3 - 0.7
poids_geo_neutre0.30.0 - 0.4
poids_engagement0.30.0 - 0.5
part_aleatoire_global0.150.0 - 0.3
distance_max_km15050 - 500
rayon_gps_point_m1000100 - 2000
seuil_min_ecoutes_engagement10010 - 200
-
-

5. Aucun recalcul batch après modification

-

Étant donné que le poids_geo_ancre est modifié de 0.7 à 0.8

-

Quand la modification est appliquée

-

Alors aucun recalcul batch n'est lancé (économie CPU) -Et seuls les nouveaux calculs utilisent la valeur 0.8

-
-

6. Versioning des configurations

-

Étant donné que je modifie plusieurs paramètres

-

Quand je sauvegarde la configuration

-

Alors une nouvelle version est créée (ex: v1.2.3) -Et je peux voir l'historique des versions -Et je peux comparer deux versions

-
-

7. Rollback en 1 clic

-

Étant donné que la configuration actuelle est v1.2.3 -Et que la version précédente était v1.2.2

-

Quand je clique sur "Restaurer v1.2.2"

-

Alors tous les paramètres de v1.2.2 sont réappliqués -Et je vois le message "Configuration restaurée à v1.2.2"

-
-

8. Créer une variante A/B testing

-

Quand je crée une nouvelle variante "Test engagement élevé" -Et que je configure:

-
| parametre | valeur |
-|---|---|
-| poids_engagement | 0.4 |
-| poids_geo_ancre | 0.6 |
-
-

Et que je lance le test A/B

-

Alors 50% des utilisateurs reçoivent la Config A (défaut) -Et 50% des utilisateurs reçoivent la Config B (test)

-
-

9. Split utilisateurs aléatoire pour A/B test

-

Étant donné qu'un test A/B est actif

-

Quand 1000 nouveaux utilisateurs se connectent

-

Alors environ 500 sont assignés à la Config A -Et environ 500 sont assignés à la Config B -Et l'assignation est aléatoire et équilibrée

-
-

10. Utilisateur reste dans la même variante

-

Étant donné qu'un utilisateur est assigné à la Config B

-

Quand il se reconnecte plusieurs fois

-

Alors il reste toujours dans la Config B -Et il ne change pas de variante pendant le test

-
-

11. Métriques comparatives A/B testing

-

Étant donné qu'un test A/B est actif depuis 7 jours

-

Quand je consulte le dashboard A/B testing

-

Alors je vois les métriques suivantes pour chaque config:

-
| metrique | Config A | Config B |
-|---|---|---|
-| Taux complétion moyen | 68% | 72% |
-| Engagement (likes) | 15% | 18% |
-| Durée session moyenne | 23 min | 27 min |
-| Taux skip rapide (<10s) | 12% | 9% |
-
-
-

12. Dashboard graphique temps réel

-

Étant donné qu'un test A/B est actif

-

Quand je consulte le dashboard

-

Alors je vois des graphiques temps réel:

-
| graphique | type |
-|---|---|
-| Évolution engagement | Ligne |
-| Répartition utilisateurs | Camembert |
-| Taux complétion | Barres |
-| Durée session | Ligne |
-
-
-

13. Terminer un test A/B et appliquer la meilleure config

-

Étant donné qu'un test A/B montre que Config B est meilleure

-

Quand je clique sur "Appliquer Config B pour tous"

-

Alors la Config B devient la configuration par défaut -Et tous les utilisateurs utilisent maintenant Config B -Et l'ancien test est archivé

-
-

14. Audit engagement global

-

Quand je consulte la section "Audit engagement"

-

Alors je vois:

-
| metrique | valeur |
-|---|---|
-| Temps écoute moyen/session | 25 min |
-| Temps écoute médian/session | 18 min |
-| Taux complétion moyen | 70% |
-| % sessions avec ≥1 like | 35% |
-
-
-

15. Graphiques évolution engagement selon config

-

Étant donné que plusieurs modifications de config ont été faites

-

Quand je consulte les graphiques d'évolution

-

Alors je vois l'impact de chaque changement de config -Et je peux corréler changements config avec métriques

-
-

16. Export CSV pour analyse externe

-

Quand je clique sur "Exporter données"

-

Alors je peux télécharger un CSV avec:

-
| colonne | exemple |
-|---|---|
-| date | 2026-01-21 |
-| version_config | v1.2.3 |
-| taux_completion | 0.72 |
-| engagement_moyen | 0.45 |
-| duree_session_min | 27 |
-
-
-

17. Alerte automatique si métrique critique baisse

-

Étant donné que le taux de complétion moyen est à 70%

-

Quand une nouvelle config fait baisser le taux à 55%

-

Alors je reçois une alerte email "Baisse critique du taux de complétion" -Et je peux rollback rapidement

-
-

18. Prévisualisation impact avant application

-

Étant donné que je modifie poids_geo_ancre de 0.7 à 0.9

-

Quand je clique sur "Prévisualiser impact"

-

Alors je vois une simulation sur échantillon de 1000 utilisateurs -Et je vois l'estimation d'impact sur les métriques clés

-
-

19. Notes et commentaires sur modifications config

-

Quand je modifie une configuration

-

Alors je peux ajouter une note "Test pour améliorer contenu local" -Et cette note est visible dans l'historique des versions -Et l'équipe peut comprendre le contexte des changements

-
-

20. Permissions admin pour modification config

-

Étant donné que je suis un admin junior

-

Quand j'essaie de modifier un paramètre critique

-

Alors l'accès est refusé -Et je vois "Permission admin senior requise" -Et seuls les admins seniors peuvent modifier les paramètres

-
-
- -

Paramétrabilité utilisateur et profils

-
-

En tant qu'utilisateur -Je veux personnaliser mon expérience de recommandation -Afin d'adapter l'application à mes différents contextes d'usage

-
-

25 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible -Et que je suis connecté

-
-

1. Accès aux paramètres de personnalisation

-

Quand j'ouvre les paramètres de personnalisation

-

Alors je vois trois curseurs disponibles:

-
| curseur | description |
-|---|---|
-| Géolocalisation | Local ← slider → National |
-| Découverte | 0% ← slider → 50% |
-| Politique | Masquer / Équilibré / Mes préférences |
-
-
-

2. Modifier le curseur Géolocalisation vers Local

-

Étant donné que le curseur Géolocalisation est au centre (défaut)

-

Quand je déplace le curseur vers "Local" (gauche)

-

Alors l'algorithme privilégie fortement les contenus proches -Et la pondération géographique augmente -Et je vois le message "Recommandations locales privilégiées"

-
-

3. Modifier le curseur Géolocalisation vers National

-

Étant donné que le curseur Géolocalisation est au centre

-

Quand je déplace le curseur vers "National" (droite)

-

Alors l'algorithme privilégie la découverte nationale -Et la pondération géographique diminue -Et je reçois des contenus de toute la France

-
-

4. Curseur Découverte à 0% - aucun aléatoire

-

Quand je règle le curseur Découverte à 0%

-

Alors 0% de contenus aléatoires dans mes recommandations -Et 100% de contenus calculés selon score combiné -Et je vois le message "Personnalisation maximale"

-
-

5. Curseur Découverte à 10% - défaut équilibré

-

Quand je règle le curseur Découverte à 10%

-

Alors 10% de contenus aléatoires -Et 90% de contenus calculés -Et je vois le message "Équilibre découverte/personnalisation"

-
-

6. Curseur Découverte à 30% - découverte élevée

-

Quand je règle le curseur Découverte à 30%

-

Alors 30% de contenus aléatoires -Et 70% de contenus calculés -Et je vois le message "Découverte élevée activée"

-
-

7. Curseur Découverte à 50% - découverte maximale

-

Quand je règle le curseur Découverte à 50%

-

Alors 50% de contenus aléatoires -Et 50% de contenus calculés -Et je vois le message "Découverte maximale (équivaut à national)"

-
-

8. Créer un profil personnalisé "Trajet quotidien"

-

Quand je crée un nouveau profil nommé "🚗 Trajet quotidien" -Et que je configure:

-
| parametre | valeur |
-|---|---|
-| Géolocalisation | Local |
-| Découverte | 5% |
-| Politique | Masquer |
-
-

Et que je sauvegarde

-

Alors le profil "🚗 Trajet quotidien" est créé -Et je peux l'activer en un clic

-
-

9. Créer un profil "Road trip"

-

Quand je crée un profil "🛣️ Road trip" -Et que je configure:

-
| parametre | valeur |
-|---|---|
-| Géolocalisation | Régional |
-| Découverte | 30% |
-| Politique | Équilibré |
-
-

Alors le profil est sauvegardé -Et je peux switcher entre profils facilement

-
-

10. Créer un profil "Enfants"

-

Quand je crée un profil "👶 Enfants" -Et que j'active le Mode Kids

-

Alors tous les paramètres sont adaptés pour enfants:

-
| parametre | valeur |
-|---|---|
-| Mode Kids | Activé |
-| Politique | Masquer (forcé) |
-| Publicité | Aucune |
-
-
-

11. Activer un profil existant

-

Étant donné que j'ai créé un profil "🚗 Trajet quotidien"

-

Quand je clique sur "Activer" pour ce profil

-

Alors tous les paramètres du profil sont appliqués -Et je vois le message "Profil 'Trajet quotidien' activé" -Et l'algorithme utilise ces paramètres immédiatement

-
-

12. Synchronisation profils entre devices

-

Étant donné que j'ai créé 3 profils sur mon iPhone

-

Quand je me connecte sur mon iPad

-

Alors mes 3 profils sont automatiquement synchronisés -Et je peux les utiliser sur l'iPad

-
-

13. Modification d'un profil synchronisée

-

Étant donné que j'ai un profil "Road trip" sur iPhone

-

Quand je modifie ce profil sur iPhone

-

Alors la modification est synchronisée sur tous mes devices -Et le profil est mis à jour partout en temps réel

-
-

14. Pas de partage de profils entre utilisateurs

-

Étant donné que j'ai créé des profils personnalisés -Et que ma conjointe a un compte RoadWave

-

Quand elle se connecte sur son compte

-

Alors elle ne voit pas mes profils -Et chaque utilisateur a ses propres profils

-
-

15. Auto-switch selon contexte (détection trajet récurrent)

-

Étant donné que j'utilise toujours le profil "Trajet quotidien" -Et que je pars de chez moi vers mon travail tous les matins à 8h

-

Quand le système détecte ce trajet récurrent

-

Alors le profil "Trajet quotidien" est activé automatiquement -Et je reçois une notification "Profil 'Trajet quotidien' activé"

-
-

16. Désactiver l'auto-switch

-

Étant donné que l'auto-switch de profil est actif

-

Quand je désactive cette option dans les paramètres

-

Alors les profils ne changent plus automatiquement -Et je dois les activer manuellement

-
-

17. Blocage modification si vitesse GPS >10 km/h

-

Étant donné que je conduis à 50 km/h

-

Quand j'essaie de modifier un curseur

-

Alors la modification est bloquée -Et je vois le message "Modification impossible pendant la conduite" -Et je dois m'arrêter ou être passager pour modifier

-
-

18. Modification possible si vitesse <10 km/h

-

Étant donné que je suis arrêté à un feu rouge (5 km/h)

-

Quand j'essaie de modifier un curseur

-

Alors la modification est autorisée -Et je peux ajuster les paramètres

-
-

19. Warning au lancement app

-

Quand je lance l'application pour la première fois

-

Alors je vois un warning "Configurez vos préférences avant de prendre la route" -Et un bouton "Configurer maintenant" -Et je peux accéder rapidement aux paramètres

-
-

20. Modification uniquement app arrêtée ou mode passager

-

Étant donné que je suis passager dans une voiture -Et que le mode passager est activé

-

Quand j'essaie de modifier les paramètres

-

Alors la modification est autorisée -Et le blocage vitesse GPS ne s'applique pas

-
-

21. Statistiques d'utilisation des profils

-

Étant donné que j'utilise plusieurs profils

-

Quand je consulte mes statistiques

-

Alors je vois:

-
| metrique | exemple |
-|---|---|
-| Profil le plus utilisé | Trajet quotidien |
-| Heures par profil | 25h / 10h / 5h |
-| Dernier profil actif | Road trip |
-
-
-

22. Supprimer un profil

-

Étant donné que j'ai créé un profil "Test"

-

Quand je supprime ce profil

-

Alors le profil est définitivement supprimé -Et je vois le message "Profil 'Test' supprimé" -Et il disparaît de tous mes devices

-
-

23. Limite de profils par utilisateur

-

Étant donné que j'ai créé 10 profils

-

Quand j'essaie de créer un 11ème profil

-

Alors la création échoue -Et je vois le message "Maximum 10 profils par utilisateur"

-
-

24. Dupliquer un profil existant

-

Étant donné que j'ai un profil "Trajet quotidien"

-

Quand je clique sur "Dupliquer"

-

Alors un nouveau profil "Trajet quotidien (copie)" est créé -Et il a les mêmes paramètres que l'original -Et je peux le modifier indépendamment

-
-

25. Réinitialiser un profil aux valeurs par défaut

-

Étant donné que j'ai modifié un profil

-

Quand je clique sur "Réinitialiser"

-

Alors tous les paramètres reviennent aux valeurs par défaut:

-
| parametre | valeur défaut |
-|---|---|
-| Géolocalisation | Équilibré |
-| Découverte | 10% |
-| Politique | Équilibré |
-
-
-
- -

Formule de scoring et recommandation

-
-

En tant que système de recommandation -Je veux calculer un score combiné pour chaque contenu -Afin de proposer les contenus les plus pertinents à l'utilisateur

-
-

21 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que l'API RoadWave est disponible

-
-

1. Calcul du score géographique linéaire

-

Étant donné qu'un contenu existe à Paris -Et que la distance_max_km est configurée à 200 km

-

Quand un utilisateur est à 50 km du contenu

-

Alors le score_geo = 1 - (50 / 200) = 0.75

-
-

2. Score géo à distance nulle (sur place)

-

Étant donné qu'un contenu existe à un point GPS précis

-

Quand un utilisateur est exactement au même point (0 km)

-

Alors le score_geo = 1.0 (maximum)

-
-

3. Score géo à distance_max (200 km)

-

Étant donné qu'un contenu existe à Paris

-

Quand un utilisateur est à 200 km du contenu

-

Alors le score_geo = 1 - (200 / 200) = 0.0

-
-

4. Score géo au-delà de distance_max

-

Étant donné qu'un contenu existe à Paris

-

Quand un utilisateur est à 250 km du contenu (au-delà de 200 km max)

-

Alors le score_geo = 0.0 (minimum) -Et le contenu a peu de chances d'être recommandé sauf engagement très élevé

-
-

5. Calcul du score d'intérêts avec jauges utilisateur

-

Étant donné qu'un utilisateur a les jauges suivantes:

-
| categorie | niveau |
-|---|---|
-| Automobile | 80% |
-| Voyage | 60% |
-| Musique | 40% |
-
-

Et qu'un contenu est tagué "Automobile" et "Voyage"

-

Quand l'algorithme calcule le score_interets

-

Alors score_interets = (0.8 + 0.6) / 2 = 0.7

-
-

6. Score d'intérêts avec un seul tag

-

Étant donné qu'un utilisateur a la jauge "Économie" à 90% -Et qu'un contenu est tagué uniquement "Économie"

-

Quand l'algorithme calcule le score_interets

-

Alors score_interets = 0.9

-
-

7. Score d'intérêts avec tags non matchés

-

Étant donné qu'un utilisateur a des jauges "Sport" et "Politique" élevées -Et qu'un contenu est tagué "Musique" et "Philosophie" -Et que l'utilisateur n'a pas ces catégories

-

Quand l'algorithme calcule le score_interets

-

Alors score_interets = 0.5 (neutre par défaut pour catégories inconnues)

-
-

8. Calcul du score d'engagement avec métriques

-

Étant donné qu'un contenu a:

-
| metrique | valeur |
-|---|---|
-| ecoutes | 1000 |
-| ecoutes_completes | 700 |
-| likes | 300 |
-| abonnements_apres | 50 |
-
-

Quand l'algorithme calcule le score_engagement

-

Alors taux_completion = 700 / 1000 = 0.7 -Et ratio_likes = 300 / 1000 = 0.3 -Et ratio_abonnements = 50 / 1000 = 0.05 -Et score_engagement = (0.7 × 0.5) + (0.3 × 0.3) + (0.05 × 0.2) = 0.35 + 0.09 + 0.01 = 0.45

-
-

9. Contenu avec moins de 50 écoutes - score neutre

-

Étant donné qu'un contenu a seulement 30 écoutes

-

Quand l'algorithme calcule le score_engagement

-

Alors score_engagement = 0.5 (neutre par défaut) -Et le contenu n'est pas pénalisé pour manque de données

-
-

10. Contenu avec exactement 50 écoutes - calcul réel

-

Étant donné qu'un contenu a exactement 50 écoutes -Et des métriques d'engagement complètes

-

Quand l'algorithme calcule le score_engagement

-

Alors le score est calculé normalement (pas de seuil neutre)

-
-

11. Bonus aléatoire - 10% des recommandations

-

Étant donné qu'un utilisateur demande 10 recommandations -Et que la part_aleatoire_global est à 10%

-

Quand l'algorithme génère les recommandations

-

Alors 1 contenu sur 10 est tiré aléatoirement -Et 9 contenus sont calculés avec le score combiné -Et le contenu aléatoire n'est pas dans l'historique déjà écouté

-
-

12. Curseur utilisateur découverte à 0% - aucun aléatoire

-

Étant donné qu'un utilisateur configure le curseur découverte à 0%

-

Quand l'utilisateur demande 20 recommandations

-

Alors les 20 contenus sont calculés avec le score combiné -Et aucun contenu aléatoire n'est proposé

-
-

13. Curseur utilisateur découverte à 50% - découverte max

-

Étant donné qu'un utilisateur configure le curseur découverte à 50%

-

Quand l'utilisateur demande 20 recommandations

-

Alors 10 contenus sont tirés aléatoirement -Et 10 contenus sont calculés avec le score combiné

-
-

14. Score final combiné pour contenu géo-ancré

-

Étant donné qu'un contenu "Géo-ancré" a:

-
| parametre | valeur |
-|---|---|
-| score_geo | 0.9 |
-| score_interets | 0.6 |
-| score_engagement | 0.45 |
-| poids_geo | 0.7 |
-| poids_interets | 0.1 |
-| poids_engagement | 0.2 |
-
-

Quand l'algorithme calcule le score_final

-

Alors score_final = (0.9 × 0.7) + (0.6 × 0.1) + (0.45 × 0.2) -Et score_final = 0.63 + 0.06 + 0.09 = 0.78

-
-

15. Score final combiné pour contenu géo-neutre

-

Étant donné qu'un contenu "Géo-neutre" a:

-
| parametre | valeur |
-|---|---|
-| score_geo | 0.3 |
-| score_interets | 0.9 |
-| score_engagement | 0.6 |
-| poids_geo | 0.2 |
-| poids_interets | 0.6 |
-| poids_engagement | 0.2 |
-
-

Quand l'algorithme calcule le score_final

-

Alors score_final = (0.3 × 0.2) + (0.9 × 0.6) + (0.6 × 0.2) -Et score_final = 0.06 + 0.54 + 0.12 = 0.72 -Et le contenu peut être recommandé malgré la distance

-
-

16. Contenu viral lointain peut être recommandé

-

Étant donné qu'un contenu viral existe à Paris -Et qu'il a un score_engagement très élevé de 0.95 -Et qu'un utilisateur est à Marseille (score_geo = 0.1)

-

Quand l'algorithme calcule le score_final

-

Alors le score_engagement élevé compense le score_geo faible -Et le contenu peut apparaître dans les recommandations

-
-

17. Ordre de recommandation par score décroissant

-

Étant donné 5 contenus avec les scores suivants:

-
| contenu | score_final |
-|---|---|
-| Contenu A | 0.85 |
-| Contenu B | 0.72 |
-| Contenu C | 0.90 |
-| Contenu D | 0.65 |
-| Contenu E | 0.78 |
-
-

Quand l'utilisateur demande des recommandations

-

Alors l'ordre de proposition est:

-
| position | contenu |
-|---|---|
-| 1 | Contenu C |
-| 2 | Contenu A |
-| 3 | Contenu E |
-| 4 | Contenu B |
-| 5 | Contenu D |
-
-
-

18. Exclusion de l'historique déjà écouté >80%

-

Étant donné qu'un utilisateur a écouté les contenus suivants:

-
| contenu | completion |
-|---|---|
-| Contenu A | 85% |
-| Contenu B | 95% |
-| Contenu C | 30% |
-
-

Quand l'algorithme génère les recommandations

-

Alors "Contenu A" et "Contenu B" ne sont jamais proposés -Mais "Contenu C" peut être reproposé

-
-

19. Pré-calcul de 5 contenus suivants

-

Étant donné qu'un utilisateur écoute un contenu

-

Quand l'algorithme prépare les contenus suivants

-

Alors 5 contenus sont pré-calculés selon le score -Et ces contenus sont mis en cache pour performance

-
-

20. Recalcul si déplacement >10 km

-

Étant donné que 5 contenus suivants sont pré-calculés -Et que l'utilisateur se déplace de 12 km

-

Quand l'utilisateur demande le contenu suivant

-

Alors l'algorithme recalcule les scores avec la nouvelle position -Et propose de nouveaux contenus plus pertinents géographiquement

-
-

21. Recalcul après 10 minutes d'inactivité

-

Étant donné que 5 contenus suivants sont pré-calculés -Et que 11 minutes se sont écoulées sans action

-

Quand l'utilisateur demande le contenu suivant

-

Alors l'algorithme recalcule les scores -Et prend en compte les nouveaux contenus publiés

-
-
- -

Anonymisation des données GPS après 24h

-

18 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur avec le GPS activé -Et que j'utilise l'application depuis plusieurs jours

-
-

1. Conservation des données GPS précises pendant 24h

-

Étant donné que j'écoute un contenu à la position GPS 48.8566, 2.3522 (Paris, Tour Eiffel) -Et qu'il est 10:00 le 2025-01-20

-

Quand l'événement d'écoute est enregistré en base de données

-

Alors les coordonnées précises 48.8566, 2.3522 sont stockées -Et le champ anonymized est à false -Et le champ created_at contient "2025-01-20 10:00:00" -Et ces données précises servent à la recommandation personnalisée

-
-

2. Conversion en geohash après 24h

-

Étant donné que j'ai écouté un contenu le 2025-01-20 à 10:00 à la position 48.8566, 2.3522

-

Quand le job quotidien d'anonymisation s'exécute le 2025-01-21 à 02:00

-

Alors les coordonnées précises sont converties en geohash précision 5 -Et le geohash correspond à une zone d'environ 5km² -Et les coordonnées originales 48.8566, 2.3522 sont supprimées définitivement -Et le champ anonymized passe à true -Et il est impossible de retrouver la position précise d'origine

-
-

3. Requête SQL d'anonymisation (PostGIS)

-

Étant donné que le job quotidien d'anonymisation s'exécute

-

Quand la requête SQL suivante est exécutée:

-

Alors toutes les positions vieilles de plus de 24h sont anonymisées -Et le processus est automatique et irréversible -Et les données sont conformes RGPD

-
-

4. Précision du geohash niveau 5

-

Étant donné qu'une position GPS est convertie en geohash précision 5

-

Quand on analyse la zone couverte

-

Alors la zone fait environ 5km² (4.9km × 4.9km) -Et cette précision est suffisante pour des analytics agrégées -Et cette précision ne permet pas d'identifier un individu (conformité CNIL)

-
-

5. Exemple de conversion Paris

-

Étant donné que ma position précise est 48.8566, 2.3522 (Tour Eiffel)

-

Quand la conversion en geohash précision 5 est appliquée

-

Alors le geohash généré est "u09wh" -Et ce geohash couvre une zone de ~5km² autour de la Tour Eiffel -Et toutes les positions dans cette zone partagent le même geohash -Et il est impossible de distinguer deux utilisateurs dans cette zone

-
-

6. Conservation de l'historique personnel utilisateur

-

Étant donné que j'ai écouté des contenus aux positions suivantes:

-
| date | heure | latitude | longitude | lieu |
-|---|---|---|---|---|
-| 2025-01-15 | 08:30 | 48.8566 | 2.3522 | Paris |
-| 2025-01-16 | 14:00 | 43.6047 | 1.4442 | Toulouse |
-| 2025-01-17 | 19:00 | 45.7640 | 4.8357 | Lyon |
-
-

Quand j'ouvre mon historique personnel dans "Profil > Mes trajets"

-

Alors je vois mes trajets avec les positions précises intégrales -Et ces données ne sont pas anonymisées tant que mon compte est actif -Et seul moi peut accéder à ces données -Et elles ne sont pas utilisées pour des analytics globales

-
-

7. Anonymisation pour analytics globales uniquement

-

Étant donné que RoadWave génère des analytics agrégées

-

Quand l'équipe analyse les zones géographiques populaires

-

Alors seules les données anonymisées (geohash) sont utilisées -Et les positions précises de l'historique personnel ne sont jamais agrégées -Et les heatmaps de trafic utilisent uniquement les geohash ~5km²

-
-

8. Planification du job d'anonymisation

-

Étant donné que le système est en production

-

Quand on consulte les jobs planifiés (cron)

-

Alors un job "anonymize_gps_data" est configuré -Et le job s'exécute tous les jours à 02:00 (heure creuse) -Et le job traite toutes les positions vieilles de plus de 24h -Et un log est généré pour traçabilité

-
-

9. Exécution du job avec métriques

-

Étant donné que le job d'anonymisation s'exécute le 2025-01-21 à 02:00

-

Quand le job se termine

-

Alors un rapport est généré avec:

-
| métrique | valeur |
-|---|---|
-| Nombre de positions traitées | 15420 |
-| Nombre de positions anonymisées | 15420 |
-| Durée d'exécution | 3.5s |
-| Erreurs | 0 |
-
-

Et le rapport est loggé dans Sentry/Grafana -Et une alerte est envoyée si le job échoue

-
-

10. Performances du job d'anonymisation

-

Étant donné que 100 000 positions doivent être anonymisées

-

Quand le job s'exécute

-

Alors le traitement se fait en moins de 30 secondes -Et la requête PostGIS est optimisée avec index -Et aucun impact sur les performances de l'application en production

-
-

11. Impossibilité de réidentification

-

Étant donné qu'une position a été anonymisée en geohash "u09wh"

-

Quand un attaquant tente de retrouver la position précise d'origine

-

Alors il est impossible de déterminer la position exacte -Et des milliers de positions précises correspondent au même geohash -Et il n'y a aucune traçabilité vers la position originale -Et cette anonymisation est irréversible

-
-

12. Conformité CNIL - données véritablement anonymisées

-

Étant donné que les positions sont converties en geohash précision 5

-

Quand un auditeur CNIL vérifie la conformité

-

Alors les données sont considérées comme véritablement anonymisées -Et elles ne sont plus considérées comme des données personnelles -Et aucun consentement n'est requis pour leur traitement analytique -Et elles peuvent être conservées indéfiniment

-
-

13. Heatmap de trafic avec données anonymisées

-

Étant donné que RoadWave génère une heatmap des zones populaires

-

Quand on analyse les données utilisées

-

Alors seules les positions anonymisées (geohash) sont agrégées -Et la heatmap montre des zones de ~5km² -Et aucune position précise n'est révélée -Et cette analyse ne nécessite pas de consentement utilisateur (données anonymes)

-
-

14. Statistiques géographiques par département

-

Étant donné que RoadWave analyse l'utilisation par département

-

Quand les statistiques sont générées

-

Alors les données anonymisées sont agrégées par département -Et les résultats montrent: "Paris (75): 12 500 écoutes, Lyon (69): 8 300 écoutes" -Et aucune donnée personnelle n'est révélée -Et les statistiques sont RGPD-compliant

-
-

15. Coût de la solution d'anonymisation

-

Étant donné que PostGIS est utilisé pour l'anonymisation GPS

-

Quand on calcule le coût de la solution

-

Alors le coût est de 0€ (PostGIS inclus dans PostgreSQL) -Et aucune librairie tierce n'est nécessaire -Et la solution est entièrement maîtrisée (self-hosted)

-
-

16. Anonymisation respecte les positions en cours de session

-

Étant donné que je suis en train d'écouter du contenu actuellement -Et que certaines de mes positions ont plus de 24h

-

Quand le job d'anonymisation s'exécute

-

Alors mes positions de plus de 24h sont anonymisées -Mais ma position actuelle (session en cours) reste précise -Et la recommandation continue de fonctionner normalement

-
-

17. Suppression de compte et anonymisation GPS

-

Étant donné que je demande la suppression de mon compte

-

Quand le compte est supprimé (après grace period de 30j)

-

Alors toutes mes positions GPS (précises et anonymisées) sont supprimées -Et mon historique personnel de trajets est supprimé -Et aucune donnée GPS ne subsiste, même anonymisée

-
-

18. Export de données avant anonymisation

-

Étant donné que je demande un export de mes données -Et que certaines de mes positions ont été anonymisées

-

Quand l'export est généré

-

Alors les positions précises de mon historique personnel sont incluses -Mais les positions déjà anonymisées (>24h, analytics) apparaissent en geohash -Et l'export précise quelles données ont été anonymisées et pourquoi

-
-
- -

Conformité administrative RGPD (Registre, Breach, DPO)

-

22 scénarios (21 standards, 1 plan)

-
-

1. Registre des traitements en Markdown versionné Git

-

Étant donné que RoadWave doit tenir un registre des traitements

-

Quand on consulte la documentation

-

Alors un fichier docs/rgpd/registre-traitements.md existe -Et le fichier est versionné dans Git -Et l'historique des modifications est traçable via Git -Et chaque traitement est documenté dans une section dédiée

-
-

2. Contenu obligatoire pour chaque traitement

-

Étant donné que le registre des traitements contient le traitement "Géolocalisation utilisateurs"

-

Quand on lit la section correspondante

-

Alors les informations suivantes sont présentes:

-
| information obligatoire | exemple |
-|---|---|
-| Nom du traitement | Géolocalisation utilisateurs |
-| Finalité | Recommandation de contenu géolocalisé |
-| Catégories de données | Coordonnées GPS, historique de position |
-| Base légale | Consentement (Article 6.1.a RGPD) |
-| Durée de conservation | 24h (précis), puis geohash anonymisé |
-| Destinataires | Aucun tiers |
-| Transferts hors UE | Aucun |
-| Mesures de sécurité | TLS 1.3, anonymisation après 24h |
-
-
-

3. 📋 Plan: Traitements documentés dans le registre

-

Étant donné que le registre des traitements est complet

-

Quand on liste tous les traitements

-

Alors le traitement "" est documenté avec la base légale ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
traitementbase_legale
Géolocalisation utilisateursConsentement
Historique d'écouteIntérêt légitime
Création de contenuExécution du contrat
Analytics (Matomo)Consentement
Paiements (Mangopay)Exécution du contrat
Modération contenusIntérêt légitime
Notifications pushConsentement
-
-

4. Review trimestrielle du registre

-

Étant donné que le registre des traitements existe

-

Quand on consulte l'historique Git

-

Alors une mise à jour est effectuée au moins tous les 3 mois -Et chaque mise à jour a un commit avec message explicite -Et un tag Git marque chaque review trimestrielle -Et les modifications sont traçables (auteur, date, changements)

-
-

5. Mise à jour immédiate si nouveau traitement

-

Étant donné qu'une nouvelle fonctionnalité nécessite un traitement de données

-

Quand la fonctionnalité est développée

-

Alors le registre est mis à jour AVANT le déploiement en production -Et le nouveau traitement est documenté complètement -Et un commit Git enregistre l'ajout -Et le DPO valide la conformité RGPD du nouveau traitement

-
-

6. Migration future vers interface admin PostgreSQL

-

Étant donné que RoadWave dépasse 100 000 utilisateurs

-

Quand la complexité du registre augmente

-

Alors une interface admin PostgreSQL est développée -Et le registre Markdown est migré vers la base de données -Et l'historique Git est conservé pour audit -Et l'interface permet une gestion plus efficace des traitements

-
-

7. Détection automatique d'événements critiques

-

Étant donné que le système de monitoring est actif

-

Quand un événement critique se produit

-

Alors une alerte est envoyée selon le type d'événement:

-
| événement | outil | alerte |
-|---|---|---|
-| Erreur backend critique | Sentry | Discord/Slack immédiat |
-| Pic requêtes anormal | Grafana | Email équipe |
-| Accès non autorisé DB | PostgreSQL logs | SMS fondateur |
-| Authentification suspecte | Zitadel alerts | Email équipe |
-
-

Et les alertes permettent une réaction rapide

-
-

8. Runbook de procédure breach disponible

-

Étant donné qu'une violation de données potentielle est détectée

-

Quand l'équipe consulte la documentation

-

Alors un runbook docs/rgpd/procedure-breach.md existe -Et le runbook contient une checklist 72h CNIL -Et chaque étape est clairement documentée -Et les contacts d'urgence sont listés

-
-

9. Checklist 72h en cas de breach

-

Étant donné qu'une violation de données est confirmée

-

Quand l'équipe suit la procédure breach

-

Alors les étapes suivantes sont exécutées dans les délais:

-
| délai | étape |
-|---|---|
-| H+0 | Détection et confinement immédiat |
-| H+24 | Évaluation gravité (données concernées, users impactés) |
-| H+48 | Notification CNIL si risque pour utilisateurs |
-| H+72 | Notification utilisateurs si risque élevé |
-
-

Et chaque étape est documentée pour audit

-
-

10. Évaluation de la gravité du breach

-

Étant donné qu'une violation de données est détectée

-

Quand l'équipe évalue la gravité

-

Alors les critères suivants sont analysés:

-
| critère | exemple |
-|---|---|
-| Type de données concernées | Emails, mots de passe, GPS, etc. |
-| Nombre d'utilisateurs impactés | 10, 100, 10000, etc. |
-| Mesures de sécurité existantes | Chiffrement, hachage, anonymisation |
-| Risque pour les droits et libertés | Faible, modéré, élevé |
-
-

Et si le risque est élevé, la CNIL est notifiée sous 72h

-
-

11. Notification CNIL dans les 72h

-

Étant donné qu'un breach avec risque élevé est confirmé à 10:00 le 2025-01-20

-

Quand la gravité est évaluée comme nécessitant une notification

-

Alors la CNIL est notifiée avant 10:00 le 2025-01-23 (72h) -Et la notification contient:

-
| information |
-|---|
-| Nature de la violation |
-| Données concernées |
-| Nombre d'utilisateurs impactés |
-| Conséquences probables |
-| Mesures prises |
-| Mesures de remédiation |
-
-

Et un email pré-rédigé (template) est utilisé pour gagner du temps

-
-

12. Notification des utilisateurs si risque élevé

-

Étant donné qu'un breach impacte 5000 utilisateurs -Et que le risque est élevé (mots de passe non chiffrés exposés)

-

Quand la CNIL est notifiée

-

Alors les utilisateurs impactés sont notifiés dans les 72h -Et l'email contient: -Et un lien de réinitialisation de mot de passe est inclus

-
-

13. Aucune notification si risque faible

-

Étant donné qu'un breach mineur est détecté (logs techniques exposés, aucune donnée personnelle)

-

Quand l'équipe évalue la gravité

-

Alors le risque est jugé faible -Et aucune notification CNIL n'est requise (Article 33.1 RGPD) -Et aucune notification utilisateur n'est envoyée -Et un log interne est créé pour traçabilité

-
-

14. Monitoring proactif pour éviter découverte tardive

-

Étant donné que Sentry et Grafana sont configurés

-

Quand un comportement anormal est détecté

-

Alors une alerte est envoyée en temps réel -Et l'équipe peut réagir avant qu'un breach majeur ne se produise -Et les logs sont analysés quotidiennement pour détecter des anomalies -Et cette approche proactive limite les risques de découverte tardive

-
-

15. Fondateur = DPO temporaire (MVP)

-

Étant donné que RoadWave est en phase MVP -Et que l'entreprise a moins de 250 employés

-

Quand on vérifie l'obligation légale d'avoir un DPO

-

Alors le DPO n'est pas obligatoire selon le RGPD Article 37 -Et le fondateur assume temporairement le rôle de DPO -Et le fondateur suit la formation CNIL gratuite (4h)

-
-

16. Formation CNIL du DPO temporaire

-

Étant donné que le fondateur est DPO temporaire

-

Quand on vérifie sa formation

-

Alors le fondateur a suivi la formation CNIL en ligne (4h) -Et le fondateur a obtenu la certification "Atelier RGPD" (gratuit) -Et le certificat est conservé pour audit -Et la formation couvre:

-
| sujet |
-|---|
-| Principes fondamentaux du RGPD |
-| Droits des personnes |
-| Sécurité des données |
-| Violations de données (breach) |
-| Registre des traitements |
-
-
-

17. Contact DPO publié et accessible

-

Étant donné que je consulte les mentions légales de RoadWave

-

Quand je cherche le contact du DPO

-

Alors l'email "dpo@roadwave.fr" est clairement affiché -Et cet email est également dans les CGU -Et le délai de réponse garanti est de 1 mois maximum (RGPD Article 12.3) -Et une adresse postale est également fournie

-
-

18. Demande d'exercice de droits RGPD au DPO

-

Étant donné que je veux exercer mon droit d'accès à mes données

-

Quand j'envoie un email à dpo@roadwave.fr

-

Alors je reçois un accusé de réception dans les 48h -Et ma demande est traitée dans un délai maximum de 1 mois -Et si le délai dépasse 1 mois, je suis informé de la prolongation (max 2 mois supplémentaires) -Et la réponse est complète et conforme au RGPD

-
-

19. Types de demandes gérées par le DPO

-

Étant donné que je contacte le DPO

-

Quand j'envoie une demande

-

Alors le DPO peut traiter les demandes suivantes:

-
| type de demande |
-|---|
-| Droit d'accès (Article 15) |
-| Droit de rectification (Article 16) |
-| Droit à l'effacement (Article 17) |
-| Droit à la portabilité (Article 20) |
-| Droit d'opposition (Article 21) |
-| Plainte RGPD |
-| Question sur le traitement des données |
-
-

Et chaque demande reçoit une réponse personnalisée

-
-

20. Migration vers DPO externe si croissance

-

Étant donné que RoadWave dépasse 100 000 utilisateurs

-

Quand la charge de travail DPO augmente

-

Alors un DPO externe mutualisé est engagé -Et le coût est d'environ 200€/mois -Et le DPO externe a les certifications CNIL requises -Et un contrat de sous-traitance RGPD est signé

-
-

21. Recrutement DPO interne si >10 employés

-

Étant donné que RoadWave a plus de 10 employés

-

Quand l'entreprise se structure

-

Alors un DPO interne peut être recruté -Et le DPO interne a une certification CNIL (AFCDP ou équivalent) -Et le DPO est indépendant et ne peut être licencié pour ses fonctions -Et le DPO a un accès direct à la direction

-
-

22. Récapitulatif des coûts RGPD

-

Étant donné que toutes les mesures RGPD sont en place

-

Quand on calcule le coût total mensuel

-

Alors le récapitulatif est le suivant:

-
| mesure | implémentation | coût |
-|---|---|---|
-| Consentement | Tarteaucitron.js + PostgreSQL | 0€ |
-| Anonymisation GPS | Geohash PostGIS (24h) | 0€ |
-| Export données | JSON+HTML+ZIP asynchrone | 0€ |
-| Suppression compte | Grace period 30j + anonymisation | 0€ |
-| Mode dégradé | GeoIP MaxMind + GPS optionnel | 0€ |
-| Conservation | Purge auto 5 ans inactivité | 0€ |
-| Analytics | Matomo self-hosted | ~5€/mois |
-| Registre traitements | Markdown Git | 0€ |
-| Breach detection | Sentry + Grafana + runbook | 0€ (< 5K events) |
-| DPO | Fondateur formé CNIL | 0€ |
-
-

Et le coût total est d'environ 5€/mois -Et cette conformité est 100% opensource et maîtrisée

-
-
- -

Gestion du consentement RGPD

-

16 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un nouvel utilisateur -Et que j'accède à l'application pour la première fois

-
-

1. Affichage du banner de consentement au premier lancement web

-

Étant donné que j'accède à l'application web pour la première fois

-

Quand la page se charge

-

Alors un banner RGPD Tarteaucitron.js s'affiche -Et le banner est en français -Et le banner propose les options suivantes:

-
| option | description |
-|---|---|
-| Tout accepter | Active tous les consentements |
-| Tout refuser | Refuse tous les consentements optionnels |
-| Personnaliser | Ouvre le panneau de personnalisation |
-
-

Et le banner est customisé aux couleurs de RoadWave

-
-

2. Granularité des consentements

-

Étant donné que le banner RGPD est affiché

-

Quand je clique sur "Personnaliser"

-

Alors je vois les catégories de consentements suivantes:

-
| catégorie | type | requis |
-|---|---|---|
-| Fonctionnel | Nécessaire | oui |
-| Analytique | Optionnel | non |
-| Marketing | Optionnel | non |
-
-

Et chaque catégorie a une description claire de son usage -Et je peux accepter ou refuser chaque catégorie individuellement

-
-

3. Consentement géolocalisation précise - obligatoire

-

Étant donné que je suis sur l'application mobile -Et que l'onboarding est terminé

-

Quand l'application a besoin d'accéder à ma position précise

-

Alors un écran de demande de consentement s'affiche -Et le message explique clairement l'usage: -Et je peux accepter ou refuser -Et si je refuse, l'application bascule en mode dégradé (GeoIP uniquement)

-
-

4. Double consentement GPS - banner app + permission OS

-

Étant donné que je veux activer la géolocalisation précise

-

Quand j'accepte le consentement dans l'application

-

Alors l'application demande également la permission au système d'exploitation -Et sur iOS, la popup système s'affiche: "Autoriser RoadWave à accéder à votre position ?" -Et sur Android, la popup système s'affiche avec les options "Toujours autoriser / Autoriser seulement pendant l'utilisation / Refuser" -Et les deux consentements (app + OS) doivent être acceptés pour activer le GPS précis

-
-

5. Enregistrement du consentement en base de données

-

Étant donné que j'ai accepté les consentements suivants:

-
| type | accepté |
-|---|---|
-| Fonctionnel | oui |
-| Analytique | oui |
-| Marketing | non |
-| GPS précis | oui |
-
-

Quand je valide mes choix

-

Alors un enregistrement est créé dans la table user_consents -Et l'enregistrement contient les champs suivants:

-
| champ | valeur |
-|---|---|
-| user_id | [mon ID utilisateur] |
-| consent_type | fonctionnel / analytique / gps |
-| version | 1 |
-| accepted | true / false |
-| timestamp | [date et heure exacte] |
-
-

Et chaque type de consentement a un enregistrement séparé

-
-

6. Versioning des consentements

-

Étant donné que j'ai accepté le consentement "Analytique" version 1 le 2025-01-01 -Et que les CGU sont mises à jour le 2025-06-01

-

Quand je me connecte après la mise à jour

-

Alors un nouveau consentement version 2 m'est demandé -Et mon ancien consentement version 1 reste dans l'historique -Et je dois accepter la nouvelle version pour continuer à utiliser les analytics

-
-

7. Historique complet conservé pour preuve légale

-

Étant donné que j'ai modifié mes consentements plusieurs fois:

-
| date | consent_type | accepted | version |
-|---|---|---|---|
-| 2025-01-01 | Analytique | oui | 1 |
-| 2025-03-15 | Analytique | non | 1 |
-| 2025-06-01 | Analytique | oui | 2 |
-
-

Quand un auditeur CNIL consulte mon historique de consentements

-

Alors tous les enregistrements sont conservés -Et l'historique prouve que chaque consentement a été donné librement -Et les timestamps permettent de prouver la conformité à tout moment

-
-

8. Consentement analytique - optionnel

-

Étant donné que je refuse le consentement "Analytique"

-

Quand j'utilise l'application

-

Alors aucun cookie Matomo _pk_id n'est déposé -Et aucune donnée d'usage n'est envoyée à Matomo -Et l'application fonctionne normalement sans analytics

-
-

9. Consentement notifications push - optionnel

-

Étant donné que je refuse le consentement "Notifications push"

-

Quand un créateur que je suis publie un nouveau contenu

-

Alors je ne reçois pas de notification push -Mais je peux voir le nouveau contenu dans l'application -Et l'application fonctionne normalement

-
-

10. Consentement GPS précis - requis pour fonctionnalités géo

-

Étant donné que je refuse le consentement "GPS précis"

-

Quand j'utilise l'application

-

Alors je peux accéder aux contenus nationaux -Mais les contenus géolocalisés précis (Ancré, Contextuel) ne sont pas disponibles -Et les audio-guides nécessitent l'activation du GPS -Et un banner permanent me rappelle que l'activation du GPS améliore l'expérience

-
-

11. Révocation d'un consentement depuis les paramètres

-

Étant donné que j'ai accepté le consentement "Analytique" -Et que j'utilise l'application depuis 3 mois

-

Quand j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"

-

Alors je vois la liste de tous mes consentements actuels -Et je peux révoquer le consentement "Analytique"

-

Quand je révoque le consentement

-

Alors un nouvel enregistrement est créé avec accepted = false -Et le cookie Matomo est supprimé immédiatement -Et les analytics sont désactivées à partir de ce moment

-
-

12. Acceptation d'un consentement précédemment refusé

-

Étant donné que j'avais refusé le consentement "GPS précis"

-

Quand j'ouvre "Paramètres > Confidentialité > Gérer mes consentements" -Et que je clique sur "Activer la géolocalisation précise"

-

Alors un nouvel enregistrement est créé avec accepted = true -Et la permission OS est demandée si ce n'est pas déjà fait -Et l'application bascule en mode géolocalisation précise -Et les contenus géolocalisés deviennent disponibles immédiatement

-
-

13. Export de l'historique des consentements pour audit

-

Étant donné qu'un contrôle CNIL est en cours

-

Quand l'équipe RoadWave exporte l'historique des consentements

-

Alors l'export contient pour chaque utilisateur:

-
| champ | description |
-|---|---|
-| user_id | ID anonymisé |
-| consent_type | Type de consentement |
-| version | Version des CGU/consentement |
-| accepted | Accepté ou refusé |
-| timestamp | Date et heure exacte |
-| ip_address | IP (anonymisée) au moment du consentement |
-| user_agent | Navigateur/app utilisé |
-
-

Et l'export est au format CSV pour analyse -Et les données prouvent la conformité RGPD

-
-

14. Conformité recommandations CNIL

-

Étant donné que le système de consentement est implémenté

-

Quand un auditeur CNIL vérifie la conformité

-

Alors le système respecte les critères suivants:

-
| critère CNIL | respecté |
-|---|---|
-| Consentement libre | oui |
-| Consentement spécifique (granulaire) | oui |
-| Consentement éclairé (information claire) | oui |
-| Consentement univoque (action positive) | oui |
-| Révocable à tout moment | oui |
-| Preuve du consentement conservée | oui |
-
-
-

15. Tarteaucitron.js self-hosted

-

Étant donné que l'application web utilise Tarteaucitron.js

-

Quand je consulte les sources JavaScript chargées

-

Alors le script Tarteaucitron.js est hébergé sur les serveurs RoadWave -Et aucun script tiers (CDN externe) n'est chargé -Et le code source de Tarteaucitron.js est vérifiable -Et aucune donnée n'est envoyée à un tiers lors de l'affichage du banner

-
-

16. Coût de la solution - 0€

-

Étant donné que Tarteaucitron.js est opensource -Et que PostgreSQL est utilisé pour le backend

-

Quand on calcule le coût de la solution de consentement

-

Alors le coût est de 0€ -Et la solution est entièrement maîtrisée (self-hosted) -Et aucune dépendance à un service SaaS tiers

-
-
- -

Durée de conservation des données et purge automatique

-

19 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que le système de purge automatique est actif

-
-

1. Auditeur inactif depuis 5 ans - suppression automatique

-

Étant donné que je suis un auditeur (sans contenu créé) -Et que je ne me suis pas connecté depuis le 2020-01-01 -Et que la date actuelle est 2025-01-02 (>5 ans)

-

Quand le job de purge automatique s'exécute

-

Alors mon compte est automatiquement supprimé -Et toutes mes données personnelles sont effacées -Et aucune trace ne subsiste dans la base de données

-
-

2. Créateur avec contenus actifs - conservation indéfinie

-

Étant donné que je suis un créateur -Et que j'ai créé 10 contenus qui reçoivent encore des écoutes -Et que je ne me suis pas connecté depuis 6 ans

-

Quand le job de purge automatique s'exécute

-

Alors mon compte n'est pas supprimé -Et mes données personnelles sont conservées tant que mes contenus sont écoutés -Et mes contenus continuent d'être diffusés normalement

-
-

3. Créateur inactif sans écoutes - suppression automatique

-

Étant donné que je suis un créateur -Et que j'ai créé 5 contenus -Et que je ne me suis pas connecté depuis 5 ans (depuis 2020-01-01) -Et que mes contenus n'ont reçu aucune écoute depuis 2 ans (depuis 2023-01-01) -Et que la date actuelle est 2025-01-02

-

Quand le job de purge automatique s'exécute

-

Alors mon compte est automatiquement supprimé -Et mes contenus sont anonymisés (créateur = "Utilisateur supprimé") -Et les fichiers audio restent disponibles mais anonymisés

-
-

4. Notifications par email avant purge

-

Étant donné que je suis inactif depuis 4 ans et 9 mois

-

Quand le système détecte que je suis éligible à la purge dans 90 jours

-

Alors je reçois un email avec le sujet "Votre compte RoadWave sera supprimé dans 90 jours" -Et l'email contient: -Et un lien de connexion est inclus dans l'email

-
-

5. Rappels à 90j, 30j et 7j avant suppression

-

Étant donné que je suis éligible à la purge automatique

-

Quand les délais s'écoulent

-

Alors je reçois les emails suivants:

-
| délai | sujet email |
-|---|---|
-| 90 jours | Votre compte sera supprimé dans 90 jours |
-| 30 jours | Rappel: Votre compte sera supprimé dans 30 jours |
-| 7 jours | Dernière alerte: suppression dans 7 jours |
-
-

Et chaque email contient un lien de connexion pour réactiver le compte -Et les notifications push sont également envoyées si activées

-
-

6. Connexion annule la suppression programmée

-

Étant donné que je suis éligible à la purge dans 15 jours -Et que j'ai reçu plusieurs emails d'avertissement

-

Quand je me connecte à mon compte

-

Alors la suppression programmée est annulée immédiatement -Et le compteur d'inactivité est remis à zéro -Et je reçois un email de confirmation: "Votre compte a été réactivé" -Et je peux continuer à utiliser l'application normalement

-
-

7. Exécution quotidienne du job de purge

-

Étant donné que le système est en production

-

Quand on consulte les jobs planifiés

-

Alors un job "purge_inactive_accounts" est configuré -Et le job s'exécute tous les jours à 03:00 (heure creuse) -Et le job identifie les comptes éligibles à la purge -Et le job traite les suppressions automatiques

-
-

8. Critères d'éligibilité à la purge

-

Étant donné que le job de purge s'exécute

-

Quand le système identifie les comptes éligibles

-

Alors les critères suivants sont appliqués:

-
| type_compte | critères |
-|---|---|
-| Auditeur uniquement | 5 ans sans connexion |
-| Créateur avec contenus actifs | Jamais (tant qu'écoutes) |
-| Créateur inactif | 5 ans sans connexion + 2 ans sans écoute |
-
-

Et seuls les comptes remplissant tous les critères sont supprimés

-
-

9. Métriques du job de purge

-

Étant donné que le job de purge s'exécute le 2025-01-15

-

Quand le job se termine

-

Alors un rapport est généré avec:

-
| métrique | exemple |
-|---|---|
-| Comptes analysés | 150 000 |
-| Comptes éligibles à la purge | 350 |
-| Auditeurs supprimés | 300 |
-| Créateurs inactifs supprimés | 50 |
-| Créateurs conservés (actifs) | 0 |
-| Erreurs | 0 |
-| Durée d'exécution | 45s |
-
-

Et le rapport est loggé pour audit

-
-

10. Contenus de comptes purgés conservés anonymement

-

Étant donné que mon compte créateur est purgé automatiquement

-

Quand la suppression est effective

-

Alors mes contenus créés sont conservés indéfiniment -Et les contenus sont anonymisés (créateur = "Utilisateur supprimé") -Et les fichiers audio restent sur le CDN -Et les statistiques d'écoute sont préservées -Et les utilisateurs peuvent toujours écouter mes contenus

-
-

11. Créateur inactif mais contenus populaires - pas de purge

-

Étant donné que je suis un créateur inactif depuis 6 ans -Mais que mes contenus reçoivent 500+ écoutes par mois

-

Quand le job de purge s'exécute

-

Alors mon compte n'est pas supprimé -Et je continue de recevoir les emails d'avertissement tous les 6 mois -Et mes contenus continuent d'être diffusés -Et je peux me reconnecter à tout moment

-
-

12. Qu'est-ce qu'une "écoute" pour le calcul d'inactivité

-

Étant donné que je suis un créateur

-

Quand le système calcule si mes contenus sont "actifs"

-

Alors une "écoute" est comptabilisée si:

-
| condition | comptabilisée |
-|---|---|
-| Écoute complète (>80%) | oui |
-| Écoute partielle (>30%) | oui |
-| Skip rapide (<30%) | non |
-| Écoute par un bot (détecté) | non |
-
-

Et au moins 1 écoute valide dans les 2 dernières années maintient le compte actif

-
-

13. Conformité principe de minimisation

-

Étant donné que le système de purge automatique est en place

-

Quand un auditeur RGPD vérifie la conformité

-

Alors le système respecte le principe de minimisation:

-
| principe | respecté |
-|---|---|
-| Conservation limitée dans le temps | oui |
-| Suppression automatique après inactivité | oui |
-| Délai raisonnable (5 ans) | oui |
-| Notifications préalables | oui |
-| Exception justifiée (contenus actifs) | oui |
-
-

Et le délai de 5 ans est conforme aux standards de l'industrie

-
-

14. Actions qui réinitialisent le compteur d'inactivité

-

Étant donné que je suis inactif depuis 4 ans

-

Quand j'effectue l'une des actions suivantes:

-
| action |
-|---|
-| Connexion à l'application |
-| Publication d'un nouveau contenu |
-| Like d'un contenu |
-| Abonnement à un créateur |
-| Modification de mon profil |
-
-

Alors le compteur d'inactivité est remis à zéro -Et la suppression programmée est annulée -Et je ne suis plus éligible à la purge pour 5 ans

-
-

15. Traçabilité des suppressions automatiques

-

Étant donné qu'un compte est supprimé automatiquement

-

Quand la suppression est effective

-

Alors un log d'audit est créé avec:

-
| champ | valeur |
-|---|---|
-| user_id | [ID anonymisé] |
-| account_type | auditeur / créateur |
-| last_login | 2020-01-15T10:00:00Z |
-| last_content_listen | 2023-06-01T14:30:00Z |
-| purge_date | 2025-01-15T03:00:00Z |
-| notifications_sent | 3 (90j, 30j, 7j) |
-| reason | 5_years_inactivity |
-
-

Et le log est conservé 5 ans pour audit RGPD -Et l'user_id est pseudonymisé pour anonymat

-
-

16. Compte Premium inactif - pas de privilège spécial

-

Étant donné que je suis un utilisateur Premium -Et que je suis inactif depuis 5 ans

-

Quand le job de purge s'exécute

-

Alors mon compte est supprimé comme un compte gratuit -Et l'abonnement Premium ne prolonge pas la durée de conservation -Et aucun remboursement n'est effectué (compte inactif depuis 5 ans)

-
-

17. Compte avec signalements de modération - purge différée

-

Étant donné que je suis éligible à la purge -Mais que j'ai des signalements de modération en cours

-

Quand le job de purge s'exécute

-

Alors ma purge est différée de 90 jours -Et les signalements sont traités en priorité -Et si les signalements aboutissent à un ban, le compte est supprimé immédiatement -Et si les signalements sont infondés, la purge automatique reprend son cours

-
-

18. Pourquoi 5 ans d'inactivité

-

Étant donné que le délai de purge est fixé à 5 ans

-

Quand on justifie ce choix

-

Alors les raisons suivantes sont avancées:

-
| justification |
-|---|
-| Standard de l'industrie (Google, Facebook: 2-3 ans) |
-| Équilibre raisonnable entre minimisation et utilité |
-| Conforme aux recommandations CNIL |
-| Laisse une marge de réactivation pour utilisateurs |
-| Exception pour créateurs = intérêt légitime communauté |
-
-
-

19. Politique de conservation visible dans les CGU

-

Étant donné que je consulte les CGU de RoadWave

-

Quand je lis la section "Conservation des données"

-

Alors la politique de purge automatique est clairement expliquée: -Et les utilisateurs sont informés dès l'inscription

-
-
- -

Cookies et analytics avec Matomo self-hosted

-

20 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur de l'application web RoadWave

-
-

1. Cookies strictement nécessaires - pas de consentement requis

-

Étant donné que j'accède à l'application web

-

Quand je me connecte

-

Alors les cookies techniques suivants sont déposés:

-
| cookie | type | durée | finalité | consentement |
-|---|---|---|---|---|
-| session | Technique | 30j | Authentification | Non requis |
-| refresh_token | Technique | 30j | Session persistante | Non requis |
-
-

Et ces cookies sont essentiels au fonctionnement de l'application -Et ils sont exemptés de consentement selon l'article 82 de la loi Informatique et Libertés

-
- -

Étant donné que j'ai accepté le consentement "Analytique"

-

Quand je navigue sur l'application web

-

Alors le cookie _pk_id est déposé -Et la durée de conservation est de 13 mois -Et ce cookie sert à Matomo pour analytics -Et mon IP est automatiquement anonymisée (2 derniers octets)

-
- -

Étant donné que j'ai refusé le consentement "Analytique"

-

Quand je navigue sur l'application web

-

Alors aucun cookie _pk_id n'est déposé -Et aucune donnée d'usage n'est collectée -Et l'application fonctionne normalement sans analytics

-
-

4. Matomo hébergé sur les serveurs RoadWave

-

Étant donné que RoadWave utilise Matomo pour les analytics

-

Quand on analyse l'infrastructure

-

Alors Matomo est installé sur les serveurs RoadWave (Docker) -Et aucune donnée n'est envoyée à un service tiers -Et toutes les données restent dans l'UE -Et l'accès à Matomo est restreint à l'équipe RoadWave

-
-

5. IP anonymisées automatiquement

-

Étant donné que Matomo collecte des données d'usage

-

Quand une requête est enregistrée

-

Alors l'adresse IP est automatiquement anonymisée -Et les 2 derniers octets sont remplacés par des zéros -Et une IP 192.168.1.100 devient 192.168.0.0 -Et cette anonymisation est irréversible -Et elle est conforme aux recommandations CNIL

-
-

6. Configuration Matomo conforme RGPD

-

Étant donné que Matomo est configuré pour RoadWave

-

Quand on vérifie les paramètres

-

Alors les configurations suivantes sont activées:

-
| paramètre | valeur |
-|---|---|
-| Anonymisation IP (2 octets) | activé |
-| Respect Do Not Track | activé |
-| Suppression auto anciens logs (25 mois) | activé |
-| Géolocalisation IP désactivée | activé |
-| User ID anonymisé | activé |
-
-

Et la configuration est RGPD-compliant

-
-

7. Aucun tracker tiers utilisé

-

Étant donné que j'accède à l'application web

-

Quand j'inspecte les requêtes réseau avec les DevTools

-

Alors aucune requête n'est envoyée vers les domaines suivants:

-
| domaine tiers interdit |
-|---|
-| google-analytics.com |
-| facebook.com (Pixel) |
-| hotjar.com |
-| mixpanel.com |
-| segment.io |
-| amplitude.com |
-
-

Et toutes les requêtes analytics vont uniquement vers matomo.roadwave.fr

-
- -

Étant donné que j'analyse les cookies déposés sur roadwave.fr

-

Quand je consulte la liste des cookies

-

Alors tous les cookies sont first-party (domaine roadwave.fr) -Et aucun cookie tiers (third-party) n'est présent -Et cette politique respecte les recommandations CNIL 2020

-
-

9. Alternative Plausible SaaS (EU-hosted)

-

Étant donné que RoadWave pourrait utiliser Plausible au lieu de Matomo

-

Quand on compare les deux solutions

-

Alors Plausible a les caractéristiques suivantes:

-
| caractéristique | valeur |
-|---|---|
-| Hébergement | UE (Allemagne) |
-| Conformité RGPD | Natif (pas de cookie) |
-| Coût | 9€/mois (50K pageviews) |
-| IP anonymisées | Automatique |
-| Consentement requis | Non (selon CNIL 2020) |
-
-

Mais Matomo self-hosted reste le choix prioritaire (0€, contrôle total)

-
-

10. Aucun transfert de données hors UE

-

Étant donné que Matomo est self-hosted

-

Quand on analyse les flux de données

-

Alors aucune donnée d'analytics n'est transférée hors de l'UE -Et les serveurs sont localisés en France -Et aucun transfert vers les US (pas de Privacy Shield / DPF requis) -Et la souveraineté des données est garantie

-
-

11. Matomo self-hosted - coût estimé

-

Étant donné que Matomo est hébergé sur l'infrastructure RoadWave

-

Quand on calcule le coût mensuel

-

Alors le coût est d'environ 5€/mois:

-
| composant | coût |
-|---|---|
-| Serveur supplémentaire | 0€ (mutualisé) |
-| Base de données MySQL | 0€ (mutualisé) |
-| Stockage logs (25 mois) | ~5€/mois |
-| License Matomo | 0€ (opensource) |
-
-

Et ce coût est marginal comparé à un SaaS tiers (9-50€/mois)

-
-

12. Respect du signal Do Not Track (DNT)

-

Étant donné que mon navigateur envoie le header "DNT: 1"

-

Quand j'accède à l'application web

-

Alors Matomo détecte le signal DNT -Et aucune donnée d'usage n'est collectée -Et aucun cookie _pk_id n'est déposé -Et l'application fonctionne normalement -Et un message discret s'affiche: "Vos préférences de confidentialité sont respectées (DNT activé)"

-
-

13. Logs Matomo supprimés après 25 mois

-

Étant donné que Matomo collecte des données d'usage

-

Quand les logs atteignent 25 mois d'ancienneté

-

Alors un job automatique supprime les anciens logs -Et seules les données agrégées (rapports) sont conservées -Et les données brutes (logs) sont supprimées définitivement -Et cette politique respecte le principe de minimisation RGPD

-
-

14. Données collectées par Matomo

-

Étant donné que j'ai accepté le consentement "Analytique"

-

Quand je navigue sur l'application web

-

Alors Matomo collecte les données suivantes:

-
| donnée collectée | anonymisée |
-|---|---|
-| Pages visitées | non |
-| Durée de visite | non |
-| Navigateur / OS | non |
-| Résolution écran | non |
-| Provenance (referrer) | non |
-| IP (2 derniers octets) | oui |
-| User ID (hashé) | oui |
-
-

Et aucune donnée personnelle identifiable n'est collectée

-
-

15. User ID hashé pour analytics

-

Étant donné que je suis connecté à l'application -Et que j'ai accepté le consentement "Analytique"

-

Quand Matomo enregistre mes actions

-

Alors mon user_id est hashé (SHA-256) -Et le hash est 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 -Et il est impossible de retrouver mon user_id original depuis ce hash -Et ce processus garantit l'anonymat

-
-

16. Conformité recommandations CNIL sur les cookies

-

Étant donné que RoadWave utilise Matomo self-hosted

-

Quand un auditeur CNIL vérifie la conformité

-

Alors le système respecte les recommandations CNIL 2020:

-
| recommandation CNIL | respecté |
-|---|---|
-| Consentement requis pour cookies analytics | oui |
-| IP anonymisées | oui |
-| Pas de transfert hors UE | oui |
-| Durée conservation limitée (25 mois) | oui |
-| Respect Do Not Track | oui |
-| Transparence (liste cookies dans CGU) | oui |
-
-
-

17. Intégration Tarteaucitron.js pour gérer Matomo

-

Étant donné que Tarteaucitron.js gère les consentements

-

Quand je personnalise mes consentements

-

Alors je vois l'option "Analytique (Matomo)" -Et une description est affichée: -Et je peux activer ou désactiver Matomo indépendamment -Et si je désactive, le cookie _pk_id est supprimé immédiatement

-
-

18. Analytics sur application mobile

-

Étant donné que j'utilise l'application mobile

-

Quand j'accepte le consentement "Analytique"

-

Alors l'app utilise le SDK Matomo Mobile -Et les données sont envoyées à la même instance Matomo self-hosted -Et les mêmes règles d'anonymisation s'appliquent -Et aucun SDK tiers (Google Analytics, Firebase) n'est utilisé

-
-

19. Refus analytics sur mobile

-

Étant donné que j'ai refusé le consentement "Analytique" sur mobile

-

Quand j'utilise l'application

-

Alors aucune donnée d'usage n'est collectée -Et le SDK Matomo est désactivé -Et l'application fonctionne normalement sans différence d'UX

-
-

20. Matomo opensource et auditable

-

Étant donné que Matomo est opensource

-

Quand on consulte le code source

-

Alors le code est disponible publiquement sur GitHub -Et le code peut être audité par des experts indépendants -Et aucune backdoor ou collecte cachée n'est possible -Et cette transparence renforce la confiance utilisateur

-
-
- -

Mode dégradé avec GeoIP (sans GPS précis)

-

20 scénarios (19 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un nouvel utilisateur -Et que je lance l'application pour la première fois

-
-

1. 📋 Plan: Trois niveaux de géolocalisation disponibles

-

Étant donné que j'utilise le niveau de géolocalisation ""

-

Quand le système détermine ma position

-

Alors la technologie utilisée est "" -Et les contenus accessibles sont "" -Et le consentement RGPD est ""

-

📊 Exemples de données:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
niveautechnologiecontenusconsentement
PaysAucune géolocContenus nationaux uniquementNon requis
VilleGeoIP (MaxMind)Contenus régionaux/villeNon requis
PrécisGPSTous contenus (hyperlocaux inclus)Requis
-
-

2. GeoIP activé par défaut au premier lancement

-

Étant donné que je lance l'application pour la première fois -Et que je n'ai pas encore accepté le GPS précis

-

Quand l'application démarre

-

Alors le système utilise automatiquement GeoIP basé sur mon adresse IP -Et ma position est détectée au niveau ville: "Paris, France" -Et aucun consentement n'est requis (GeoIP ne collecte pas de données personnelles) -Et je peux accéder aux contenus régionaux et de ville

-
-

3. Détection de ville avec MaxMind GeoLite2

-

Étant donné que mon adresse IP est 93.184.216.34

-

Quand le système utilise GeoIP MaxMind GeoLite2

-

Alors ma ville est détectée: "Paris" -Et ma région est détectée: "Île-de-France" -Et mon pays est détecté: "France" -Et la précision est d'environ 80% au niveau ville -Et aucune coordonnée GPS précise n'est révélée

-
-

4. Banner d'invitation à activer le GPS

-

Étant donné que j'utilise l'application en mode GeoIP

-

Quand je suis sur l'écran principal

-

Alors un banner discret s'affiche en haut: -Et le banner n'est pas intrusif (pas de popup modale) -Et je peux le fermer temporairement avec un bouton X -Et le banner réapparaît tous les 7 jours si je ne l'active pas

-
-

5. Upgrade volontaire vers GPS depuis le banner

-

Étant donné que le banner d'invitation au GPS est affiché

-

Quand je clique sur "Activer"

-

Alors un écran de consentement GPS s'affiche -Et l'écran explique les avantages: -Et je peux accepter ou refuser -Et si j'accepte, la permission OS est demandée

-
-

6. Contenus disponibles en mode Pays (aucune géoloc)

-

Étant donné que je n'autorise aucune géolocalisation

-

Quand le système recherche du contenu à me proposer

-

Alors seuls les contenus "National" sont disponibles -Et les contenus géolocalisés (Ancré, Contextuel) ne sont pas proposés -Et je vois un message: "Activez la géolocalisation pour plus de contenu local"

-
-

7. Contenus disponibles en mode Ville (GeoIP)

-

Étant donné que j'utilise le mode GeoIP et que je suis détecté à Paris

-

Quand le système recherche du contenu à me proposer

-

Alors les contenus suivants sont disponibles:

-
| type_contenu | disponible |
-|---|---|
-| National | oui |
-| Région Île-de-France | oui |
-| Ville Paris | oui |
-| Hyperlocal (GPS) | non |
-| Audio-guides | non |
-
-

Et je reçois des recommandations pertinentes pour Paris

-
-

8. Tous contenus disponibles en mode Précis (GPS)

-

Étant donné que j'ai activé la géolocalisation précise

-

Quand le système recherche du contenu à me proposer

-

Alors tous les types de contenus sont disponibles:

-
| type_contenu | disponible |
-|---|---|
-| National | oui |
-| Régional | oui |
-| Ville | oui |
-| Hyperlocal (Ancré) | oui |
-| Contextuel | oui |
-| Audio-guides | oui |
-
-
-

9. GeoIP ne nécessite pas de consentement RGPD

-

Étant donné que j'utilise le mode GeoIP

-

Quand un auditeur CNIL vérifie la conformité

-

Alors GeoIP n'est pas considéré comme une donnée personnelle -Et l'adresse IP n'est pas conservée après détection de la ville -Et seule la ville est stockée (non identifiant) -Et aucun consentement n'est requis conformément au RGPD

-
-

10. Base de données MaxMind self-hosted

-

Étant donné que RoadWave utilise MaxMind GeoLite2

-

Quand on analyse l'infrastructure

-

Alors la base de données GeoLite2 est hébergée sur les serveurs RoadWave -Et aucune requête n'est envoyée à un service tiers -Et la base de données est mise à jour automatiquement chaque mois -Et le coût est de 0€ (GeoLite2 est gratuit)

-
-

11. Mise à jour mensuelle de la base GeoIP

-

Étant donné que MaxMind publie des mises à jour mensuelles

-

Quand le 1er du mois arrive

-

Alors un job automatique télécharge la nouvelle base GeoLite2 -Et la base est mise à jour sans interruption de service -Et un log est créé pour traçabilité -Et si la mise à jour échoue, une alerte est envoyée

-
-

12. UX acceptable en mode GeoIP

-

Étant donné que j'utilise le mode GeoIP à Paris

-

Quand je parcours l'application

-

Alors je peux écouter du contenu pertinent pour Paris et l'Île-de-France -Et l'expérience est satisfaisante même sans GPS précis -Et je ne suis pas bloqué dans l'utilisation de l'application -Et je peux choisir d'activer le GPS quand je le souhaite

-
-

13. Incitation progressive à activer le GPS

-

Étant donné que j'utilise le mode GeoIP depuis 2 semaines -Et que je n'ai pas activé le GPS

-

Quand je consulte un audio-guide dans les résultats de recherche

-

Alors un message s'affiche: -Et si je clique "Plus tard", je peux continuer à utiliser l'app normalement -Et l'incitation reste douce et non intrusive

-
-

14. Upgrade GeoIP vers GPS

-

Étant donné que j'utilise le mode GeoIP

-

Quand j'active la géolocalisation précise

-

Alors le système bascule immédiatement en mode GPS -Et les contenus hyperlocaux deviennent disponibles -Et mon feed se rafraîchit avec du contenu plus précis -Et un toast de confirmation s'affiche: "Géolocalisation activée"

-
-

15. Downgrade GPS vers GeoIP

-

Étant donné que j'utilise le mode GPS précis

-

Quand je désactive la géolocalisation dans les paramètres OS

-

Alors le système bascule automatiquement en mode GeoIP -Et les contenus hyperlocaux ne sont plus proposés -Et un banner s'affiche: "Géolocalisation désactivée. Seul le contenu régional est disponible." -Et l'application continue de fonctionner normalement

-
-

16. Détection automatique au démarrage de l'app

-

Étant donné que j'ouvre l'application

-

Quand l'app vérifie les permissions de géolocalisation

-

Alors le système détecte automatiquement le mode disponible:

-
| permission GPS | consentement app | mode activé |
-|---|---|---|
-| Refusée | Non demandé | Pays |
-| Refusée | Accepté | GeoIP |
-| Accordée | Accepté | GPS précis |
-
-

Et le mode est appliqué sans interaction utilisateur

-
-

17. Précision acceptable pour la plupart des cas

-

Étant donné que j'habite à Lyon -Et que mon IP est une IP résidentielle standard

-

Quand le système utilise GeoIP pour me localiser

-

Alors la ville détectée est "Lyon" (correct à 80%) -Et dans 20% des cas, la ville peut être légèrement erronée (banlieue proche) -Et cette précision est suffisante pour proposer du contenu régional pertinent

-
-

18. GeoIP avec VPN ou proxy

-

Étant donné que j'utilise un VPN avec une IP sortante à Paris -Mais que je suis physiquement à Lyon

-

Quand le système utilise GeoIP

-

Alors la ville détectée est "Paris" (IP du VPN) -Et les contenus proposés sont pour Paris -Et je peux activer le GPS précis pour corriger la localisation

-
-

19. Pas de donnée personnelle collectée avec GeoIP

-

Étant donné que j'utilise le mode GeoIP

-

Quand le système détermine ma ville via mon IP

-

Alors l'adresse IP n'est pas conservée après détection -Et seule la ville "Paris" est stockée en base de données -Et la ville seule n'est pas une donnée personnelle (RGPD) -Et aucun consentement n'est donc requis

-
-

20. Solution GeoIP gratuite et self-hosted

-

Étant donné que RoadWave utilise MaxMind GeoLite2

-

Quand on calcule le coût de la solution

-

Alors le coût est de 0€ -Et la solution est opensource -Et la base de données est hébergée sur les serveurs RoadWave -Et aucun coût SaaS tiers

-
-
- -

Portabilité des données (Article 20 RGPD)

-

22 scénarios (21 standards, 1 plan)

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que j'ai utilisé l'application depuis 6 mois

-
-

1. Demande d'export depuis les paramètres

-

Étant donné que je suis dans "Paramètres > Confidentialité"

-

Quand je clique sur "Exporter mes données"

-

Alors une page d'information s'affiche expliquant: -Et un bouton "Confirmer l'export" est disponible

-
-

2. Confirmation et démarrage de l'export

-

Étant donné que je clique sur "Confirmer l'export"

-

Quand la demande est validée

-

Alors un message de confirmation s'affiche: -Et un worker background démarre la génération de l'export -Et le statut de l'export est "En cours de génération" -Et je peux voir le statut dans "Paramètres > Confidentialité > Mes exports"

-
-

3. Contenu de l'archive ZIP

-

Étant donné que mon export est généré

-

Quand je télécharge et ouvre l'archive

-

Alors l'archive a la structure suivante: -Et tous les fichiers sont inclus

-
-

4. Contenu du fichier export.json

-

Étant donné que j'ouvre le fichier export.json

-

Quand j'analyse le contenu

-

Alors le JSON contient les sections suivantes:

-
| section | description |
-|---|---|
-| profile | Email, pseudo, date inscription, bio |
-| listening_history | Historique complet d'écoute |
-| created_contents | Métadonnées des contenus créés |
-| subscriptions | Liste des créateurs suivis |
-| likes | Liste des contenus likés |
-| interest_gauges | Valeurs des jauges d'intérêt |
-| consent_history | Historique des consentements |
-| premium_subscription | Informations abonnement Premium |
-
-

Et le JSON est formaté de manière lisible (indentation) -Et toutes les dates sont au format ISO 8601

-
-

5. Contenu du fichier index.html

-

Étant donné que j'ouvre le fichier index.html dans un navigateur

-

Quand la page se charge

-

Alors je vois un site web stylé avec navigation -Et les sections suivantes sont affichées:

-
| section | contenu |
-|---|---|
-| Mon profil | Email, pseudo, date inscription, statistiques |
-| Historique d'écoute | Liste paginée avec dates, titres, durées |
-| Mes contenus | Liste avec lectures audio intégrées |
-| Mes abonnements | Grille des créateurs suivis |
-| Mes likes | Liste des contenus likés avec liens |
-| Centres d'intérêt | Graphiques des jauges |
-| Consentements | Historique des acceptations/refus |
-
-

Et la navigation est intuitive (menu latéral) -Et le design est responsive (mobile/desktop)

-
-

6. Fichiers audio inclus dans l'export

-

Étant donné que j'ai créé 5 contenus audio

-

Quand mon export est généré

-

Alors le dossier audio/ contient mes 5 fichiers -Et les fichiers sont au format Opus original -Et chaque fichier est nommé: content-[id].opus -Et les fichiers audio correspondent aux métadonnées dans export.json

-
-

7. Fichier README.txt explicatif

-

Étant donné que j'ouvre le fichier README.txt

-

Quand je lis le contenu

-

Alors le fichier explique:

-
-

8. 📋 Plan: Données de profil exportées

-

Étant donné que mon export est généré

-

Quand j'ouvre export.json et lis la section "profile"

-

Alors je trouve les données suivantes:

-
| champ | exemple |
-|---|---|
-| email | user@example.com |
-| pseudo | @roadwave_user |
-| date_inscription | 2025-01-15T10:30:00Z |
-| bio | Passionné d'automobile... |
-| avatar_url | https://cdn.roadwave.fr/... |
-| compte_verifie | false |
-| premium | true |
-
-
-

9. Historique d'écoute exporté

-

Étant donné que j'ai écouté 150 contenus depuis 6 mois

-

Quand mon export est généré

-

Alors la section "listening_history" contient 150 entrées -Et chaque entrée contient:

-
| champ | exemple |
-|---|---|
-| content_id | C123 |
-| content_title | Histoire de la Tour Eiffel |
-| creator_name | @historien_paris |
-| listened_at | 2025-01-20T15:30:00Z |
-| duration_listened | 180 (secondes) |
-| completion_rate | 0.85 (85%) |
-| location | [geohash ou coords précises] |
-
-

Et les contenus sont triés par date décroissante

-
-

10. Centres d'intérêt exportés

-

Étant donné que mes jauges d'intérêt sont:

-
| catégorie | valeur |
-|---|---|
-| Automobile | 78% |
-| Voyage | 65% |
-| Musique | 52% |
-| Politique | 30% |
-
-

Quand mon export est généré

-

Alors la section "interest_gauges" contient ces valeurs -Et chaque jauge indique la date de dernière modification

-
-

11. Historique des consentements exporté

-

Étant donné que j'ai modifié mes consentements plusieurs fois

-

Quand mon export est généré

-

Alors la section "consent_history" contient:

-
| date | consent_type | accepted | version |
-|---|---|---|---|
-| 2025-01-15T10:00 | Fonctionnel | oui | 1 |
-| 2025-01-15T10:00 | Analytique | oui | 1 |
-| 2025-01-15T10:00 | Marketing | non | 1 |
-| 2025-03-20T14:30 | Analytique | non | 1 |
-
-

Et l'historique complet est visible

-
-

12. Génération asynchrone pour éviter timeout

-

Étant donné que j'ai beaucoup de données (500 contenus créés, 10 000 écoutes)

-

Quand je demande un export

-

Alors la génération se fait en arrière-plan via un worker -Et la page web ne timeout pas -Et je peux continuer à utiliser l'application pendant la génération -Et je reçois un email quand l'export est prêt

-
-

13. Délai de génération conforme RGPD

-

Étant donné que je demande un export le 2025-01-20 à 10:00

-

Quand le worker génère l'export

-

Alors l'export est disponible maximum 48h plus tard (avant le 2025-01-22 à 10:00) -Et la plupart des exports sont prêts en moins de 6h -Et le délai respecte l'article 20 du RGPD

-
-

14. Email de notification avec lien de téléchargement

-

Étant donné que mon export est terminé

-

Quand le worker finalise la génération

-

Alors je reçois un email avec le sujet "Votre export de données RoadWave est prêt" -Et l'email contient: -Et le lien de téléchargement est sécurisé (token unique)

-
-

15. Lien de téléchargement expire après 7 jours

-

Étant donné que mon export a été généré le 2025-01-20 -Et que je reçois le lien de téléchargement

-

Quand j'essaie d'accéder au lien le 2025-01-28 (8 jours plus tard)

-

Alors le lien est expiré -Et je reçois un message "Ce lien a expiré. Veuillez demander un nouvel export." -Et je peux demander un nouvel export si nécessaire

-
-

16. Limite de 1 export par mois

-

Étant donné que j'ai demandé un export le 2025-01-15

-

Quand j'essaie de demander un nouvel export le 2025-01-20

-

Alors je reçois un message d'erreur: -Et le bouton "Confirmer l'export" est désactivé -Et la date du prochain export possible est affichée

-
-

17. Nouvel export possible après 1 mois

-

Étant donné que j'ai demandé un export le 2025-01-15

-

Quand la date atteint le 2025-02-15

-

Alors je peux demander un nouvel export -Et le bouton "Confirmer l'export" est actif -Et aucune limite ne s'applique

-
-

18. Lien de téléchargement sécurisé avec token unique

-

Étant donné que mon export est prêt

-

Quand je reçois le lien de téléchargement

-

Alors le lien contient un token unique et non devinable -Et le format du lien est: https://roadwave.fr/exports/download/[token_unique] -Et le token est valide uniquement pour mon compte -Et le token expire après 7 jours ou après 3 téléchargements

-
-

19. Vérification de l'authentification avant téléchargement

-

Étant donné que je reçois le lien d'export

-

Quand je clique sur le lien

-

Alors le système vérifie que je suis connecté -Et si je ne suis pas connecté, je suis redirigé vers la page de connexion -Et après connexion, le téléchargement démarre automatiquement -Et seul le propriétaire du compte peut télécharger l'export

-
-

20. Conformité portabilité des données

-

Étant donné que mon export est généré

-

Quand un auditeur RGPD vérifie la conformité

-

Alors l'export respecte les exigences de l'article 20:

-
| exigence RGPD | respecté |
-|---|---|
-| Format structuré (JSON) | oui |
-| Format couramment utilisé | oui |
-| Format lisible par machine | oui |
-| Format interopérable | oui |
-| Délai raisonnable (48h max) | oui |
-| Exhaustivité des données | oui |
-| Gratuité pour l'utilisateur | oui |
-
-
-

21. Gratuité de l'export

-

Étant donné que je demande un export de mes données

-

Quand l'export est généré et téléchargé

-

Alors aucun coût n'est facturé -Et l'export est entièrement gratuit -Et aucune inscription Premium n'est requise -Et le droit à la portabilité est accessible à tous les utilisateurs

-
-

22. Suivi du statut de génération

-

Étant donné que j'ai demandé un export

-

Quand j'ouvre "Paramètres > Confidentialité > Mes exports"

-

Alors je vois le statut actuel:

-
| statut | description |
-|---|---|
-| En cours de génération | Worker en train de générer l'archive |
-| Prêt au téléchargement | Lien de téléchargement disponible |
-| Expiré | Lien expiré (>7j), nouvel export requis |
-
-

Et la date de demande est affichée -Et la taille estimée de l'archive est visible

-
-
- -

Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)

-

21 scénarios

-
-
-

Contexte commun à tous les scénarios

-

Étant donné que je suis un utilisateur connecté -Et que j'ai utilisé l'application depuis plusieurs mois

-
-

1. Demande de suppression depuis les paramètres

-

Étant donné que je suis dans "Paramètres > Compte"

-

Quand je clique sur "Supprimer mon compte"

-

Alors une page d'avertissement s'affiche avec le message: -Et deux boutons sont disponibles: "Annuler" et "Confirmer la suppression"

-
-

2. Confirmation de suppression avec mot de passe

-

Étant donné que je clique sur "Confirmer la suppression"

-

Quand un formulaire de confirmation s'affiche

-

Alors je dois entrer mon mot de passe pour confirmer -Et je dois cocher "Je comprends que cette action est définitive" -Et un captcha peut être requis pour éviter les suppressions automatisées

-

Quand je valide le formulaire

-

Alors la suppression est initiée

-
-

3. Compte désactivé immédiatement après confirmation

-

Étant donné que j'ai confirmé la suppression de mon compte

-

Quand la demande est traitée

-

Alors mon compte est désactivé immédiatement -Et je suis déconnecté de toutes mes sessions -Et je ne peux plus me reconnecter -Et si j'essaie de me connecter, je reçois le message:

-
-

4. Contenus cachés pendant le grace period

-

Étant donné que mon compte est en cours de suppression

-

Quand un autre utilisateur recherche mes contenus

-

Alors mes contenus ne sont plus diffusés dans l'application -Et mes contenus n'apparaissent plus dans les recherches -Et mes contenus ne sont plus recommandés -Mais mes contenus ne sont pas encore supprimés définitivement

-
-

5. Email de confirmation envoyé immédiatement

-

Étant donné que j'ai confirmé la suppression de mon compte

-

Quand la demande est traitée

-

Alors je reçois un email avec le sujet "Confirmation de suppression de votre compte RoadWave" -Et l'email contient: -Et le lien d'annulation est valide 30 jours

-
-

6. Annulation de la suppression dans les 30 jours

-

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20 -Et que je reçois l'email de confirmation

-

Quand je clique sur le lien "Annuler la suppression" le 2025-02-05 (16 jours plus tard)

-

Alors mon compte est réactivé immédiatement -Et je peux me reconnecter normalement -Et mes contenus redeviennent visibles dans l'application -Et toutes mes données sont restaurées -Et je reçois un email de confirmation: "Votre compte a été réactivé"

-
-

7. Lien d'annulation expire après 30 jours

-

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20

-

Quand j'essaie de cliquer sur le lien d'annulation le 2025-02-25 (36 jours plus tard)

-

Alors le lien est expiré -Et je reçois un message "Ce lien a expiré. Votre compte a été définitivement supprimé." -Et la suppression effective a déjà eu lieu

-
-

8. Suppression effective sans annulation

-

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20 -Et que je n'ai pas cliqué sur le lien d'annulation

-

Quand la date atteint le 2025-02-19 (30 jours plus tard)

-

Alors un job automatique exécute la suppression définitive -Et toutes mes données personnelles sont supprimées

-
-

9. Liste des données supprimées définitivement

-

Étant donné que la suppression effective est exécutée

-

Quand le job de suppression se termine

-

Alors les données suivantes sont supprimées:

-
| données | supprimé |
-|---|---|
-| Compte utilisateur (email, mdp) | oui |
-| Profil (pseudo, bio, avatar) | oui |
-| Historique d'écoute | oui |
-| Historique GPS | oui |
-| Centres d'intérêt (jauges) | oui |
-| Sessions et tokens | oui |
-| Likes et abonnements | oui |
-| Notifications non lues | oui |
-| Historique consentements | oui |
-| Données de paiement | oui |
-
-

Et ces suppressions sont irréversibles

-
-

10. Anonymisation des contenus créés

-

Étant donné que j'ai créé 10 contenus audio

-

Quand la suppression effective est exécutée

-

Alors mes contenus audio restent disponibles dans l'application -Et le nom du créateur devient "Utilisateur supprimé" -Et mon pseudo n'est plus visible -Et les métadonnées (titre, description, tags, géolocalisation) sont conservées -Et les fichiers audio restent sur le CDN -Et les statistiques d'écoute sont conservées

-
-

11. Justification de l'anonymisation (intérêt légitime)

-

Étant donné que mes contenus sont conservés anonymement

-

Quand un auditeur RGPD vérifie la conformité

-

Alors la conservation est justifiée par l'intérêt légitime de la communauté -Et les contenus ne contiennent plus de données personnelles identifiables -Et la suppression complète nuirait à l'expérience des autres utilisateurs -Et cette pratique est conforme au RGPD si anonymisation réelle

-
-

12. Contenu anonymisé visible pour les autres utilisateurs

-

Étant donné que mon compte a été supprimé -Et que mes contenus ont été anonymisés

-

Quand un utilisateur consulte un de mes anciens contenus

-

Alors le créateur affiché est "Utilisateur supprimé" -Et le profil du créateur n'est plus accessible -Et le contenu reste écoutable normalement -Et les likes et statistiques sont conservés

-
-

13. Suppression de mes likes avec conservation des compteurs

-

Étant donné que j'avais liké 50 contenus

-

Quand la suppression effective est exécutée

-

Alors mes likes sont supprimés de la base de données -Mais les compteurs de likes sur les contenus sont préservés -Et les créateurs ne perdent pas leurs statistiques -Et seule la relation "user X a liké content Y" est supprimée

-
-

14. Suppression de mes abonnements

-

Étant donné que je suivais 20 créateurs

-

Quand la suppression effective est exécutée

-

Alors mes abonnements sont supprimés -Et les compteurs d'abonnés des créateurs sont décrémentés de 1 -Et les créateurs ne reçoivent pas de notification de désabonnement

-
-

15. Révocation de tous les tokens immédiatement

-

Étant donné que je suis connecté sur 3 appareils (mobile, tablette, web)

-

Quand je demande la suppression de mon compte

-

Alors tous mes tokens d'authentification sont révoqués immédiatement -Et je suis déconnecté de tous mes appareils -Et toute tentative de reconnexion échoue

-
-

16. Rappels par email pendant le grace period

-

Étant donné que j'ai demandé la suppression de mon compte le 2025-01-20

-

Quand le grace period s'écoule

-

Alors je reçois des emails de rappel:

-
| date | jours restants | sujet email |
-|---|---|---|
-| 2025-02-04 | 15 jours | Plus que 15 jours pour annuler la suppression |
-| 2025-02-12 | 7 jours | Dernière semaine pour annuler la suppression |
-| 2025-02-17 | 2 jours | Attention: suppression définitive dans 2 jours |
-
-

Et chaque email contient le lien d'annulation

-
-

17. Conformité droit à l'effacement

-

Étant donné que la suppression de mon compte est complète

-

Quand un auditeur RGPD vérifie la conformité

-

Alors le processus respecte l'article 17 du RGPD:

-
| exigence RGPD | respecté |
-|---|---|
-| Suppression de toutes les données personnelles | oui |
-| Délai raisonnable (30j grace period acceptable) | oui |
-| Possibilité d'annulation (bonne pratique) | oui |
-| Anonymisation des contenus (intérêt légitime) | oui |
-| Révocation des tokens et sessions | oui |
-| Suppression irréversible | oui |
-
-
-

18. Suppression d'un compte Premium

-

Étant donné que j'ai un abonnement Premium actif

-

Quand je demande la suppression de mon compte

-

Alors mon abonnement est annulé immédiatement -Et aucun remboursement n'est effectué (conformément aux CGV) -Et je reçois un email de confirmation d'annulation de l'abonnement -Et le reste du processus de suppression se déroule normalement

-
-

19. Suppression d'un compte créateur avec revenus en attente

-

Étant donné que je suis un créateur avec 75€ de revenus en attente de paiement

-

Quand je demande la suppression de mon compte

-

Alors un message m'informe: -Et je peux choisir "Recevoir le paiement et attendre" ou "Renoncer au paiement" -Et si je choisis "Recevoir le paiement", la suppression est repoussée de 7 jours

-
-

20. Suppression avec signalements de modération en cours

-

Étant donné que j'ai 2 signalements en cours de traitement

-

Quand je demande la suppression de mon compte

-

Alors les signalements sont automatiquement clôturés -Et les contenus signalés sont masqués immédiatement -Et aucune sanction n'est appliquée (compte déjà en suppression)

-
-

21. Log de la suppression pour traçabilité

-

Étant donné que la suppression effective est exécutée

-

Quand le job de suppression se termine

-

Alors un log est créé avec:

-
| champ | valeur |
-|---|---|
-| user_id | [ID anonymisé] |
-| deletion_requested_at | 2025-01-20T10:00:00Z |
-| deletion_executed_at | 2025-02-19T02:00:00Z |
-| deletion_cancelled | false |
-| data_deleted | [liste des tables] |
-| contents_anonymized | 10 |
-
-

Et ce log est conservé 5 ans pour audit RGPD -Et l'user_id est pseudonymisé pour anonymat

-
-
- - \ No newline at end of file diff --git a/output/documentation_complete.md b/output/documentation_complete.md deleted file mode 100644 index 16c18a6..0000000 --- a/output/documentation_complete.md +++ /dev/null @@ -1,37888 +0,0 @@ -# Documentation RoadWave - - ---- - - -## Table des matières - - -- [RoadWave](#roadwave) -- [RoadWave - Architecture Technique](#roadwave---architecture-technique) -- [ADR-001 : Langage Backend](#adr-001--langage-backend) -- [ADR-002 : Protocole de Streaming](#adr-002--protocole-de-streaming) -- [ADR-003 : Codec Audio](#adr-003--codec-audio) -- [ADR-004 : CDN](#adr-004--cdn) -- [ADR-005 : Base de Données](#adr-005--base-de-données) -- [ADR-006 : Chiffrement](#adr-006--chiffrement) -- [ADR-007 : Tests et Spécifications Exécutables](#adr-007--tests-et-spécifications-exécutables) -- [ADR-008 : Authentification et Gestion d'Identité](#adr-008--authentification-et-gestion-didentité) -- [ADR-009 : Solution de Paiement et Gestion des Abonnements](#adr-009--solution-de-paiement-et-gestion-des-abonnements) -- [ADR-010 : Commandes au volant et likes](#adr-010--commandes-au-volant-et-likes) -- [ADR-011 : Conformité App Stores et Plateformes Auto](#adr-011--conformité-app-stores-et-plateformes-auto) -- [ADR-012 : Architecture Backend](#adr-012--architecture-backend) -- [ADR-013 : ORM et Accès Données](#adr-013--orm-et-accès-données) -- [ADR-014 : Frontend Mobile](#adr-014--frontend-mobile) -- [ADR-015 : Stratégie Tests](#adr-015--stratégie-tests) -- [Règles métier RoadWave](#règles-métier-roadwave) -- [Annexe : Fonctionnalités reportées Post-MVP](#annexe--fonctionnalités-reportées-post-mvp) -- [Audio-guides multi-séquences pour piétons](#audio-guides-multi-séquences-pour-piétons) -- [Impact des abonnements sur l'algorithme](#impact-des-abonnements-sur-lalgorithme) -- [Limites d'abonnements et désabonnement](#limites-dabonnements-et-désabonnement) -- [Notifications contextuelles selon le mode de déplacement](#notifications-contextuelles-selon-le-mode-de-déplacement) -- [Création d'audio-guide multi-séquences](#création-daudio-guide-multi-séquences) -- [Intégration audio-guides avec autres fonctionnalités](#intégration-audio-guides-avec-autres-fonctionnalités) -- [Audio-guide mode piéton (navigation manuelle)](#audio-guide-mode-piéton-navigation-manuelle) -- [Audio-guide mode voiture (GPS automatique)](#audio-guide-mode-voiture-gps-automatique) -- [Audio-guides modes vélo et transport](#audio-guides-modes-vélo-et-transport) -- [Audio-guides Premium et monétisation](#audio-guides-premium-et-monétisation) -- [Sauvegarde et reprise de progression audio-guide](#sauvegarde-et-reprise-de-progression-audio-guide) -- [Classification des contenus par âge](#classification-des-contenus-par-âge) -- [Connexion utilisateur](#connexion-utilisateur) -- [Inscription utilisateur](#inscription-utilisateur) -- [Récupération de compte](#récupération-de-compte) -- [Gestion des sessions et tokens](#gestion-des-sessions-et-tokens) -- [Authentification à deux facteurs (2FA)](#authentification-à-deux-facteurs-2fa) -- [Vérification d'email](#vérification-demail) -- [Métadonnées et publication de contenu](#métadonnées-et-publication-de-contenu) -- [Modification et suppression de contenu](#modification-et-suppression-de-contenu) -- [Upload et encodage de contenu audio](#upload-et-encodage-de-contenu-audio) -- [Validation des 3 premiers contenus](#validation-des-3-premiers-contenus) -- [Élargissement automatique de zone quand aucun contenu n'est disponible](#élargissement-automatique-de-zone-quand-aucun-contenu-nest-disponible) -- [Gestion d'un contenu supprimé pendant l'écoute](#gestion-dun-contenu-supprimé-pendant-lécoute) -- [Mode dégradé sans géolocalisation](#mode-dégradé-sans-géolocalisation) -- [Gestion de la perte de réseau et buffering adaptatif](#gestion-de-la-perte-de-réseau-et-buffering-adaptatif) -- [Tests BDD - Documentation des fonctionnalités](#tests-bdd---documentation-des-fonctionnalités) -- [Pas de dégradation temporelle des jauges](#pas-de-dégradation-temporelle-des-jauges) -- [Évolution des jauges d'intérêt](#évolution-des-jauges-dintérêt) -- [Jauge initiale et cold start](#jauge-initiale-et-cold-start) -- [Synchronisation actions offline](#synchronisation-actions-offline) -- [Téléchargement de contenus offline](#téléchargement-de-contenus-offline) -- [Validité et renouvellement contenus offline](#validité-et-renouvellement-contenus-offline) -- [Modération préventive](#modération-préventive) -- [Sanctions et notifications de modération](#sanctions-et-notifications-de-modération) -- [Signalement de contenu inapproprié](#signalement-de-contenu-inapproprié) -- [Traitement des signalements par l'IA et les modérateurs](#traitement-des-signalements-par-lia-et-les-modérateurs) -- [Conditions d'activation de la monétisation](#conditions-dactivation-de-la-monétisation) -- [Contenus Premium exclusifs](#contenus-premium-exclusifs) -- [Désactivation et suspension monétisation](#désactivation-et-suspension-monétisation) -- [KYC et inscription à la monétisation](#kyc-et-inscription-à-la-monétisation) -- [Obligations fiscales](#obligations-fiscales) -- [Paiement des créateurs](#paiement-des-créateurs) -- [Sources de revenus créateurs](#sources-de-revenus-créateurs) -- [Actions complémentaires à l'arrêt](#actions-complémentaires-à-larrêt) -- [Commande "Précédent"](#commande-précédent) -- [Commandes vocales CarPlay et Android Auto](#commandes-vocales-carplay-et-android-auto) -- [Commandes au volant et interactions simplifiées](#commandes-au-volant-et-interactions-simplifiées) -- [File d'attente et commande "Suivant"](#file-dattente-et-commande-suivant) -- [Lecture en boucle et enchaînement automatique](#lecture-en-boucle-et-enchaînement-automatique) -- [Partage de contenu](#partage-de-contenu) -- [Avantages Premium](#avantages-premium) -- [Gestion abonnement Premium](#gestion-abonnement-premium) -- [Multi-devices et détection simultanée](#multi-devices-et-détection-simultanée) -- [Offre et tarification Premium](#offre-et-tarification-premium) -- [Profil créateur](#profil-créateur) -- [Création de campagnes publicitaires](#création-de-campagnes-publicitaires) -- [Caractéristiques et facturation des publicités](#caractéristiques-et-facturation-des-publicités) -- [Gestion du budget et alertes publicitaires](#gestion-du-budget-et-alertes-publicitaires) -- [Insertion et fréquence des publicités](#insertion-et-fréquence-des-publicités) -- [Métriques d'engagement et dashboard publicitaire](#métriques-dengagement-et-dashboard-publicitaire) -- [Validation et modération des publicités](#validation-et-modération-des-publicités) -- [Architecture technique radio live](#architecture-technique-radio-live) -- [Arrêt du live](#arrêt-du-live) -- [Comportement auditeur pendant un live](#comportement-auditeur-pendant-un-live) -- [Démarrage d'un live](#démarrage-dun-live) -- [Recherche de contenu](#recherche-de-contenu) -- [Classification de géo-pertinence des contenus](#classification-de-géo-pertinence-des-contenus) -- [Gestion du contenu politique (MVP simplifié)](#gestion-du-contenu-politique-mvp-simplifié) -- [Contenus géolocalisés en mode voiture](#contenus-géolocalisés-en-mode-voiture) -- [Gestion de l'historique et reproposition](#gestion-de-lhistorique-et-reproposition) -- [Médias traditionnels sur RoadWave](#médias-traditionnels-sur-roadwave) -- [Mode Kids pour utilisateurs 13-15 ans](#mode-kids-pour-utilisateurs-13-15-ans) -- [Paramétrabilité admin et A/B testing](#paramétrabilité-admin-et-ab-testing) -- [Paramétrabilité utilisateur et profils](#paramétrabilité-utilisateur-et-profils) -- [Formule de scoring et recommandation](#formule-de-scoring-et-recommandation) -- [Anonymisation des données GPS après 24h](#anonymisation-des-données-gps-après-24h) -- [Conformité administrative RGPD (Registre, Breach, DPO)](#conformité-administrative-rgpd-registre-breach-dpo) -- [Gestion du consentement RGPD](#gestion-du-consentement-rgpd) -- [Durée de conservation des données et purge automatique](#durée-de-conservation-des-données-et-purge-automatique) -- [Cookies et analytics avec Matomo self-hosted](#cookies-et-analytics-avec-matomo-self-hosted) -- [Mode dégradé avec GeoIP (sans GPS précis)](#mode-dégradé-avec-geoip-sans-gps-précis) -- [Portabilité des données (Article 20 RGPD)](#portabilité-des-données-article-20-rgpd) -- [Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)](#suppression-du-compte-utilisateur-article-17-rgpd---droit-à-leffacement) - - ---- - - -
- - -# RoadWave - -Réseau social audio géolocalisé pour les usagers de la route. - -## Concept - -RoadWave permet aux conducteurs d'écouter du contenu audio contextuel pendant leurs trajets. La navigation se fait par commandes au volant (suivant/précédent), inspirée des réseaux à scroll infini. - -Le contenu est diffusé en fonction de la position géographique de l'utilisateur et de ses centres d'intérêt. - ---- - -## Cas d'usage - -| Utilisateur | Scénario | -|-------------|----------| -| **Conducteur** | Écoute contenu audio en conduisant, navigation par commandes au volant (suivant/précédent), reçoit notifications géolocalisées en passant près de points d'intérêt | -| **Routier** | Écoute podcasts et radios live pendant ses trajets longue distance | -| **Touriste à pied** | Visite guidée audio d'un musée, monument ou ville : choisit parmi plusieurs guides, navigue entre séquences à son rythme (tactile/vocal), reçoit notification push quand un audio-guide est disponible à proximité | -| **Commerçant** | Diffuse une publicité audio ciblée GPS devant son commerce | -| **Passionné auto** | Découvre du contenu automobile près de circuits ou concessionnaires | -| **Habitant local** | Partage anecdotes ou bons plans géolocalisés dans son quartier | -| **Média traditionnel** | Le Monde, Le Parisien diffusent actualités géolocalisées ou nationales | - ---- - -## Utilisateurs - -Tout utilisateur peut écouter et créer du contenu (rôle flexible). - -| Rôle | Description | -|------|-------------| -| **Auditeur** | Écoute, like, s'abonne à des créateurs, signale des contenus | -| **Créateur** | Publie du contenu audio géolocalisé (individus, médias traditionnels) | -| **Publicitaire** | Diffuse des publicités ciblées géographiquement | -| **Modérateur** | Valide et modère les contenus signalés | - ---- - -## Types de contenu - -| Type | Description | -|------|-------------| -| **Contenu court** | Audio de quelques secondes à quelques minutes | -| **Podcast** | Épisodes plus longs, séries thématiques | -| **Radio live** | Diffusion en direct avec synchronisation approximative entre auditeurs | -| **Audio-guide** | Visite guidée multiséquence (musée, monument, ville) : plusieurs séquences numérotées, navigation manuelle entre pistes, liste complète visible, guidage vocal entre points d'intérêt | - ---- - -## Géolocalisation - -Le créateur définit la zone de diffusion de son contenu : - -| Niveau | Portée | -|--------|--------| -| **Point GPS** | Rayon précis autour d'une coordonnée | -| **Ville** | Diffusion dans une ville | -| **Département** | Diffusion départementale | -| **Région** | Diffusion régionale | -| **Pays** | Diffusion nationale | - -**Priorité de diffusion** : plus la zone est précise, plus le contenu a de chances d'être diffusé (GPS > ville > département > région > pays). - ---- - -## Algorithme de recommandation - -Le contenu proposé est calculé via un **score combiné** : - -- **Proximité géographique** : distance entre l'utilisateur et la zone du contenu -- **Pertinence des intérêts** : correspondance avec les centres d'intérêt de l'utilisateur - -Lorsque plusieurs contenus sont disponibles dans une zone, **seul le plus pertinent est diffusé**. - ---- - -## Centres d'intérêt - -Chaque utilisateur possède des **jauges d'intérêt** qui évoluent dynamiquement : - -### Catégories -- Automobile -- Voyage -- Famille -- Amour -- Musique -- Économie -- Cryptomonnaie -- Politique -- *... (extensible)* - -### Évolution des jauges - -| Action | Effet | -|--------|-------| -| Temps d'écoute long | Augmente la jauge | -| Like | Augmente la jauge | -| Abonnement | Augmente fortement la jauge | -| Skip rapide | Diminue la jauge | - -Les créateurs taguent leur contenu avec des centres d'intérêt. L'algorithme privilégie les correspondances mais n'exclut pas les utilisateurs sans correspondance. - ---- - -## Interactions - -### Commandes au volant (conduite) - -Interactions simplifiées pour sécurité routière maximale : - -| Commande | Action | -|----------|--------| -| **Suivant** | Passer au contenu suivant | -| **Précédent** | Revenir au contenu précédent | -| **Play/Pause** | Mettre en pause / reprendre la lecture | - -**Like automatique** : Le système détecte automatiquement vos préférences selon votre temps d'écoute : -- Écoute ≥80% du contenu → Like renforcé (+2 points jauge) -- Écoute 30-79% du contenu → Like standard (+1 point jauge) -- Skip après <10s → Signal négatif (-0.5 point) - -> Voir [ADR-010](#docs/adr/010-commandes-volant) pour les détails techniques - -### Actions complémentaires (application à l'arrêt) - -| Action | Description | -|--------|-------------| -| **Like explicite** | Bouton cœur pour liker manuellement | -| **S'abonner** | Suivre un créateur | -| **Signaler** | Signaler un contenu inapproprié | -| **Unlike** | Retirer un like | - ---- - -## Publicités - -- Insertion **entre deux contenus** uniquement (jamais d'interruption) -- Ciblage géographique : point GPS, ville, département, région ou national -- Interface dédiée pour les publicitaires - ---- - -## Radio live - -- Diffusion en direct par des créateurs -- **Buffering** pour garantir une écoute fluide -- **Synchronisation approximative** entre les auditeurs (quelques secondes de décalage possible) - ---- - -## Modération - -Approche hybride combinant participation communautaire, IA et modérateurs dédiés. - -### Contenus prohibés - -| Catégorie | Description | -|-----------|-------------| -| **Haine et violence** | Incitation à la haine, violence, discrimination | -| **Contenu sexuel** | Pornographie ou contenu sexuellement explicite | -| **Illégalité** | Apologie du terrorisme, actes criminels | -| **Désinformation dangereuse** | Fausses informations sur la santé, sécurité routière | -| **Harcèlement** | Menaces, intimidation, doxxing | -| **Droits d'auteur** | Violation de propriété intellectuelle | -| **Fraude** | Arnaques, escroqueries | - -### Rôles de modération - -| Rôle | Capacités | -|------|-----------| -| **Auditeur lambda** | Signaler un contenu (1 clic) | -| **Auditeur de confiance** | Signalements priorisés après historique positif | -| **Modérateur junior** | Traiter signalements simples (spam, contenu évident) | -| **Modérateur senior** | Cas complexes, appels, décisions de ban | -| **Admin modération** | Définir les règles, superviser l'équipe | - -### Flux de modération - -``` -1. Auditeur signale → File d'attente -2. IA pré-filtre → Cas évidents traités automatiquement -3. Modérateur junior → Traite 80% des cas restants -4. Modérateur senior → Cas complexes + recours -``` - -### Outils de modération automatique - -| Outil | Fonction | -|-------|----------| -| **Transcription audio** | Conversion automatique en texte pour analyse | -| **Analyse vocale IA** | Détection de ton agressif, cris, insultes | -| **Empreinte audio** | Détection de contenus déjà modérés (réupload) | -| **Détection droits d'auteur** | Identification automatique de musique protégée | -| **Filtrage mots-clés** | Liste noire de termes inappropriés | - -### Modération préventive - -- **Nouveaux créateurs** : validation manuelle des 3 premiers contenus -- **Score de confiance** : évolution selon l'historique du créateur -- **Publicités** : validation manuelle obligatoire avant diffusion - -### Système de strikes - -| Strike | Sanction | -|--------|----------| -| **Strike 1** | Avertissement + formation modération | -| **Strike 2** | Suspension 7 jours + contenu supprimé | -| **Strike 3** | Suspension 30 jours | -| **Strike 4** | Ban définitif | - -- **Réhabilitation** : -1 strike tous les 6 mois sans incident - -### Priorisation des signalements - -| Priorité | Type de contenu | -|----------|-----------------| -| **CRITIQUE** | Violence, suicide, mise en danger immédiate | -| **HAUTE** | Harcèlement, haine, désinformation | -| **MOYENNE** | Spam, contenu inapproprié | -| **BASSE** | Qualité audio, tags incorrects | - -### Transparence et recours - -- **Notification explicite** lors de suppression (raison détaillée) -- **Processus d'appel** : le créateur peut contester une décision -- **Délai de traitement** : 48-72h pour les recours -- **Historique** : tableau de bord des sanctions pour le créateur - -### Modération communautaire - -- **Utilisateurs de confiance** : signalements priorisés après historique positif -- **Récompenses** : badges, réduction premium pour signalements pertinents -- Lutte contre les signalements abusifs (sanctions possibles) - ---- - -## Modèle économique - -### Offres - -| Formule | Description | -|---------|-------------| -| **Gratuit** | Accès complet avec publicités entre les contenus | -| **Premium** | Sans publicité + accès aux contenus exclusifs | - -### Monétisation créateurs - -- **Partage des revenus pub** : rémunération basée sur le nombre d'écoutes -- **Pourboires** : les auditeurs peuvent faire des dons aux créateurs - ---- - -## Conformité RGPD - -### Données collectées - -| Donnée | Finalité | Base légale | -|--------|----------|-------------| -| **Position GPS** | Diffusion de contenu géolocalisé | Consentement | -| **Historique d'écoute** | Personnalisation des recommandations | Intérêt légitime | -| **Centres d'intérêt** | Algorithme de recommandation | Consentement | -| **Identité créateur** | Publication de contenu | Exécution du contrat | - -### Droits des utilisateurs - -- **Accès** : consulter toutes ses données personnelles -- **Rectification** : modifier ses informations -- **Suppression** : supprimer son compte et toutes ses données -- **Portabilité** : exporter ses données dans un format standard -- **Opposition** : désactiver le profilage publicitaire - -### Mesures techniques - -- Consentement explicite requis pour la géolocalisation -- Anonymisation des données de localisation après 24h (sauf historique personnel) -- Possibilité d'utiliser l'app en mode dégradé (sans géolocalisation précise) -- Données hébergées dans l'UE - - - - -
- - -# RoadWave - Architecture Technique - -> Les décisions techniques sont documentées dans [docs/adr/](#docs/adr/) - -## Stack Technologique - -| Composant | Technologie | ADR | -|-----------|-------------|-----| -| **Backend** | Go + Fiber | [ADR-001](#docs/adr/001-langage-backend) | -| **Architecture Backend** | Monolithe Modulaire | [ADR-012](#docs/adr/012-architecture-backend) | -| **Authentification** | Zitadel | [ADR-008](#docs/adr/008-authentification) | -| **Streaming** | HLS | [ADR-002](#docs/adr/002-protocole-streaming) | -| **Codec** | Opus | [ADR-003](#docs/adr/003-codec-audio) | -| **CDN** | Bunny CDN | [ADR-004](#docs/adr/004-cdn) | -| **Base de données** | PostgreSQL + PostGIS | [ADR-005](#docs/adr/005-base-de-donnees) | -| **ORM/Accès données** | sqlc | [ADR-013](#docs/adr/013-orm-acces-donnees) | -| **Cache** | Redis Cluster | [ADR-005](#docs/adr/005-base-de-donnees) | -| **Chiffrement** | TLS 1.3 | [ADR-006](#docs/adr/006-chiffrement) | -| **Live** | WebRTC | [ADR-002](#docs/adr/002-protocole-streaming) | -| **Frontend Mobile** | Flutter | [ADR-014](#docs/adr/014-frontend-mobile) | -| **Tests** | Testify + Godog (Gherkin) | [ADR-015](#docs/adr/015-strategie-tests), [ADR-007](#docs/adr/007-tests-bdd) | -| **Paiements** | Mangopay | [ADR-009](#docs/adr/009-solution-paiement) | -| **Commandes volant** | Like automatique | [ADR-010](#docs/adr/010-commandes-volant) | -| **Conformité stores** | CarPlay, Android Auto, App/Play Store | [ADR-011](#docs/adr/011-conformite-stores-carplay-android-auto) | - ---- - -## Streaming Audio - -### Protocole : HLS (HTTP Live Streaming) - -- Fonctionne à travers firewalls et réseaux mobiles instables -- Cache CDN natif (réduction des coûts) -- Bitrate adaptatif automatique (tunnels, zones rurales) -- Support natif iOS/Android - -### Codec : Opus - -Optimisé pour la voix en environnement bruyant (voiture). - -| Qualité | Bitrate | Usage | -|---------|---------|-------| -| Basse | 24 kbps | 2G/Edge | -| Standard | 48 kbps | 3G | -| Haute | 64 kbps | 4G/5G | - -Fallback AAC-LC pour appareils legacy. - -### Buffering Adaptatif - -| Réseau | Buffer min | Buffer cible | Buffer max | -|--------|------------|--------------|------------| -| WiFi | 5s | 30s | 120s | -| 4G/5G | 10s | 45s | 120s | -| 3G | 30s | 90s | 300s | - ---- - -## Sécurité - -### Chiffrement - -- **TLS 1.3** sur tous les endpoints (overhead ~1-2%) -- **DTLS-SRTP** pour WebRTC (radio live) -- Pas de DRM initialement (ajout si licences l'exigent) - -### Authentification - -- **Zitadel** (self-hosted) pour IAM -- JWT validation locale (zitadel-go SDK) -- OAuth2 PKCE pour mobile (iOS/Android) -- MFA et passkeys disponibles -- Rate limiting par IP et par utilisateur (Nginx + Zitadel) - ---- - -## Base de Données - -### PostgreSQL + PostGIS - -```sql --- Requête géolocalisée typique -SELECT id, ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance -FROM contents -WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000) -ORDER BY distance -LIMIT 20; -``` - -### Redis Geospatial (Cache) - -``` -GEOADD contents:geo longitude latitude content_id -GEORADIUS contents:geo user_lon user_lat 50 km WITHDIST COUNT 20 ASC -``` - -TTL cache : 5 minutes (le contenu ne bouge pas). - ---- - -## Architecture Services - -``` -┌─────────────────┐ -│ Bunny CDN │ Cache HLS, distribution globale -└────────┬────────┘ - │ -┌────────┴────────┐ -│ Nginx │ SSL, rate limiting, reverse proxy -└────────┬────────┘ - │ -┌────────┴────────┐ -│ API Gateway │ Go + Fiber -└────────┬────────┘ - │ - ┌────┴────┬─────────────┐ - │ │ │ -┌───▼───┐ ┌───▼───┐ ┌───────▼───────┐ -│ Auth │ │ User │ │ Content/Geo │ -│Service│ │Service│ │ Service │ -└───────┘ └───────┘ └───────────────┘ - │ │ │ - └─────────┴─────────────┘ - │ - ┌─────────┴─────────┐ - │ │ -┌───▼───┐ ┌─────▼─────┐ -│ Redis │ │ PostgreSQL│ -│Cluster│ │ + PostGIS │ -└───────┘ └───────────┘ -``` - ---- - -## Scaling 10M Utilisateurs - -### Stratégie par phase - -| Phase | Utilisateurs | Infra | Coût estimé | -|-------|--------------|-------|-------------| -| MVP | 0-100K | Monolithe Go, PostgreSQL managé + Zitadel, Bunny CDN/Storage | 50-150€/mois | -| Growth | 100K-1M | Kubernetes managé, replicas multi-région | 2-5K€/mois | -| Scale | 1M-10M | Multi-région, Nginx origin shield, Bunny CDN | 20-50K€/mois | - -### Métriques cibles - -| Métrique | Objectif | -|----------|----------| -| Latence API p99 | < 100ms | -| Temps de démarrage audio | < 3s | -| Disponibilité | 99.9% | -| Connexions/serveur | 100K+ | - ---- - -## Points de vigilance - -1. **Buffering mobile** : Pré-chargement agressif avant tunnels (détection GPS) -2. **Handoff réseau** : Buffer suffisant pour survivre aux changements de cellule -3. **Mode offline** : Téléchargement complet sur WiFi -4. **Bande passante** : 48 kbps Opus = ~20 MB/heure (faible consommation data) - ---- - -## Pourquoi pas UDP brut ? - -| UDP | HLS/TCP | -|-----|---------| -| Latence minimale | Latence acceptable (5-30s) | -| Problèmes NAT/firewall | Passe partout | -| Perte de paquets = artefacts | Retransmission automatique | -| Pas de cache CDN | Cache CDN = économies | -| Complexité++ | Standard de l'industrie | - -Pour du contenu non-interactif (podcasts, audio-guides), la latence HLS est acceptable. WebRTC réservé à la radio live uniquement. - - - - -
- - -# ADR-001 : Langage Backend - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé. - -## Décision - -**Go** avec le framework **Fiber**. - -## Alternatives considérées - -| Option | Performance | Simplicité | Écosystème | -|--------|-------------|------------|------------| -| **Go + Fiber** | 1M+ conn/serveur | Élevée | Excellent cloud-native | -| Rust + Tokio | 2M+ conn/serveur | Faible | Bon | -| Node.js | 100-500K conn | Élevée | Excellent | -| Elixir/Phoenix | 2M+ conn | Moyenne | Bon temps réel | - -## Justification - -- **Performance** : Go gère 1M+ connexions par serveur avec ~10KB/connexion -- **Simplicité** : Syntaxe claire, compilation rapide, facile à recruter -- **Écosystème** : First-class Kubernetes, tooling natif (profiling, race detection) -- **Équilibre** : Meilleur compromis performance/simplicité pour une startup - -## Conséquences - -- Formation équipe sur Go si nécessaire -- Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis - - - - -
- - -# ADR-002 : Protocole de Streaming - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire). - -## Décision - -**HLS** (HTTP Live Streaming) pour le contenu à la demande. -**WebRTC** réservé à la radio live. - -## Alternatives considérées - -| Option | Latence | Fiabilité mobile | Cache CDN | Complexité | -|--------|---------|------------------|-----------|------------| -| **HLS** | 5-30s | Excellente | Oui | Faible | -| DASH | 5-30s | Bonne | Oui | Moyenne | -| WebRTC | <500ms | Moyenne | Non | Élevée | -| UDP brut | Minimale | Faible | Non | Très élevée | - -## Justification - -- **Réseaux mobiles** : HLS gère les coupures et changements de cellule nativement -- **Cache CDN** : Segments .ts cachables = réduction des coûts -- **Compatibilité** : Support natif iOS/Android -- **Bitrate adaptatif** : Ajustement automatique selon la qualité réseau - -## Pourquoi pas UDP ? - -- Problèmes NAT/firewall sur réseaux mobiles -- Perte de paquets = artefacts audio -- Impossible à cacher sur CDN -- Complexité sans bénéfice pour du contenu non-interactif - -## Conséquences - -- Latence de 5-30s acceptable pour podcasts/audio-guides -- WebRTC à implémenter séparément pour la radio live - - - - -
- - -# ADR-003 : Codec Audio - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire. - -## Décision - -**Opus** comme codec principal, **AAC-LC** en fallback. - -## Profils d'encodage - -| Qualité | Bitrate | Usage | -|---------|---------|-------| -| Basse | 24 kbps | 2G/Edge | -| Standard | 48 kbps | 3G | -| Haute | 64 kbps | 4G/5G | - -## Alternatives considérées - -| Codec | Bitrate | Qualité voix | Support mobile | -|-------|---------|--------------|----------------| -| **Opus** | 24-64 kbps | Excellente | Android natif, iOS via libs | -| AAC-LC | 64-128 kbps | Bonne | Universel | -| AAC-HE v2 | 32-64 kbps | Très bonne | Bon | -| MP3 | 128-320 kbps | Correcte | Universel (legacy) | - -## Justification - -- **Environnement bruyant** : Opus intègre des algorithmes de résilience au bruit -- **Bande passante** : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix -- **Consommation data** : ~20 MB/heure à 48 kbps -- **Latence** : 2.5-60ms, idéal pour streaming adaptatif - -## Conséquences - -- Fallback AAC-LC pour appareils legacy -- Pipeline d'encodage à prévoir côté ingestion - - - - -
- - -# ADR-004 : CDN - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et indépendance vis-à-vis des géants du cloud. - -## Décision - -**Bunny CDN** comme CDN principal. - -## Alternatives considérées - -| Solution | Coût/mois (100TB) | Setup | Performance | Dépendance | -|----------|-------------------|-------|-------------|------------| -| **Bunny CDN** | ~1 000€ | 15 min | Très bon | Faible | -| Cloudflare | 0-5 000€ | 5 min | Excellent | Moyenne | -| CloudFront | ~9 750€ | 1h | Excellent | Forte (AWS) | -| Fastly | ~12-20 000€ | 2h | Exceptionnel | Moyenne | -| Nginx self-hosted | ~2-5 000€ | 1 jour | Excellent | Aucune | - -## Justification - -- **Coût** : 10x moins cher que CloudFront -- **HLS natif** : Support optimisé pour le streaming -- **Simplicité** : Setup en 15 minutes, zéro maintenance -- **Européen** : Conforme RGPD, 114 PoPs -- **Pas de lock-in** : Migration facile si besoin - -## Évolution prévue - -1. **Phase 1** (0-1M users) : Bunny CDN seul -2. **Phase 2** (1-5M users) : Ajout Nginx origin shield si nécessaire -3. **Phase 3** (5M+) : Évaluation multi-CDN - -## Conséquences - -- Configuration des règles de cache pour `.m3u8` (TTL court) et `.ts` (TTL long) -- Token authentication pour protéger les segments - - - - -
- - -# ADR-005 : Base de Données - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute. - -## Décision - -- **PostgreSQL + PostGIS** : Données persistantes et requêtes géospatiales -- **Redis Cluster** : Cache géolocalisation et sessions - -## Architecture - -``` -Requête → Redis Cache → [HIT] → Réponse - ↓ - [MISS] - ↓ - PostGIS → Cache → Réponse -``` - -## Alternatives considérées - -| Usage | Option choisie | Alternatives | -|-------|---------------|--------------| -| Données utilisateurs | PostgreSQL | MySQL, MongoDB | -| Géolocalisation | PostGIS | MongoDB Geo, Elasticsearch | -| Cache | Redis | Memcached, KeyDB | -| Analytics (futur) | ClickHouse | TimescaleDB | - -## Justification - -### PostgreSQL + PostGIS -- Requêtes géospatiales complexes et précises -- Index GIST pour performance -- ACID, fiabilité éprouvée -- Écosystème mature - -### Redis -- Cache géo natif (`GEORADIUS`) : 100K+ requêtes/sec -- Sessions utilisateurs -- Pub/sub pour temps réel - -## Exemple de requête - -```sql -SELECT id, name, - ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance -FROM contents -WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000) -ORDER BY distance -LIMIT 20; -``` - -## Conséquences - -- TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas) -- Index GIST sur colonnes géométriques -- Réplication read replicas pour scaling lecture - - - - -
- - -# ADR-006 : Chiffrement - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu. - -## Décision - -- **TLS 1.3** sur tous les endpoints -- **DTLS-SRTP** pour WebRTC (radio live) -- Pas de DRM au lancement - -## Alternatives considérées - -| Méthode | Overhead | Usage | -|---------|----------|-------| -| **TLS 1.3** | ~1-2% CPU | HTTPS streaming | -| DTLS-SRTP | ~3-5% CPU | WebRTC temps réel | -| AES-128-CBC | Minimal | Chiffrement segments HLS | -| Widevine/FairPlay | Modéré | DRM (si licences l'exigent) | - -## Justification - -### Pourquoi chiffrer ? - -- **RGPD** : Protection des données utilisateurs obligatoire -- **Confiance** : Standard attendu en 2025 -- **Intégrité** : Empêche injection de contenu par opérateurs -- **Overhead minimal** : TLS 1.3 optimisé, impact négligeable - -### Pourquoi pas de DRM ? - -- Contenu généré par utilisateurs (pas de licences) -- Complexité et coût d'intégration Widevine/FairPlay -- À reconsidérer si partenariats avec labels/éditeurs - -## Conséquences - -- Certificats SSL gérés par Bunny CDN ou Let's Encrypt -- Configuration TLS 1.3 sur Nginx/API -- DTLS-SRTP à implémenter pour le module radio live - - - - -
- - -# ADR-007 : Tests et Spécifications Exécutables - -**Statut** : Accepté -**Date** : 2025-01-17 - -## Contexte - -RoadWave nécessite une documentation des use cases qui soit à la fois lisible par tous les stakeholders et vérifiable automatiquement. Les scénarios utilisateurs (touriste, routier, commerçant) doivent être validés en continu. - -## Décision - -**Gherkin** pour les spécifications avec **Godog** comme runner de tests. - -## Alternatives considérées - -| Option | Lisibilité | Intégration Go | Maintenance | -|--------|------------|----------------|-------------| -| **Gherkin + Godog** | Excellente | Native | Faible | -| Gauge (Markdown) | Bonne | Plugin | Moyenne | -| Tests Go natifs | Faible (devs only) | Native | Faible | -| Concordion | Bonne | Java-centric | Élevée | - -## Justification - -- **Living Documentation** : Les fichiers `.feature` servent de documentation ET de tests -- **Accessibilité** : Syntaxe Given/When/Then lisible par PO, devs, testeurs -- **Cohérence stack** : Godog est le standard BDD pour Go -- **CI/CD** : Intégration simple dans les pipelines - -## Structure - -``` -features/ -├── recommendation/ -│ ├── geolocalisation.feature -│ └── interets.feature -├── streaming/ -│ ├── lecture.feature -│ └── buffering.feature -├── moderation/ -│ └── signalement.feature -└── steps/ - └── steps.go -``` - -## Exemple - -```gherkin -Feature: Recommandation géolocalisée - - Scenario: Touriste près d'un monument - Given un utilisateur avec l'intérêt "tourisme" à 80% - And une position GPS à 100m de la Tour Eiffel - When le système calcule les recommandations - Then l'audio guide "Histoire de la Tour Eiffel" est en première position -``` - -## Conséquences - -- Dépendance : `github.com/cucumber/godog` -- Les use cases du README doivent être traduits en `.feature` -- CI exécute `godog run` avant chaque merge - - - - -
- - -# ADR-008 : Authentification et Gestion d'Identité - -**Statut** : Accepté -**Date** : 2025-01-18 - -## Contexte - -RoadWave nécessite un système d'authentification sécurisé pour mobile (iOS/Android), scalable jusqu'à 10M utilisateurs, avec contraintes de coût réduit et conformité RGPD. - -## Décision - -**Zitadel** (self-hosted) pour l'IAM avec validation JWT locale côté API Go. - -## Alternatives considérées - -| Solution | Coût (10M users) | Performance | Simplicité | Intégration Go | -|----------|------------------|-------------|------------|----------------| -| **Zitadel** | 200-500€/mois | Excellente | Élevée | SDK natif | -| Supabase Auth | 32K€/mois | Excellente | Élevée | REST API | -| Keycloak | 200-800€/mois | Bonne | Faible | Lib tierce | -| Auth0 | 50K€+/mois | Excellente | Élevée | SDK natif | -| JWT Custom | 0€ (dev) | Excellente | Moyenne | Natif | - -## Justification - -- **Coût maîtrisé** : 100x moins cher que Supabase/Auth0 à 10M users -- **Performance** : JWT validation locale = 0 latence auth sur chaque requête API -- **Stack alignée** : Go + PostgreSQL + Redis (déjà dans RoadWave) -- **Scalabilité prouvée** : Clients avec 2.3M tenants, architecture event-sourced -- **RGPD natif** : Entreprise suisse, data residency EU, DPA fourni -- **Standards ouverts** : OpenID Connect certifié (pas de vendor lock-in) - -## Architecture - -``` -┌─────────────────┐ -│ Mobile Apps │ OAuth2 PKCE + Refresh tokens -└────────┬────────┘ - │ -┌────────▼────────┐ -│ Zitadel IdP │ PostgreSQL + Redis -│ (self-hosted) │ MFA, passkeys, SSO -└────────┬────────┘ - │ JWT token -┌────────▼────────┐ -│ Go + Fiber API │ Validation JWT locale -│ (RoadWave) │ github.com/zitadel/zitadel-go -└─────────────────┘ -``` - -## Exemple d'intégration - -```go -import "github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth" - -// Validation JWT locale haute performance -verifier := oauth.WithJWT(config) -app.Use(verifier.Middleware()) - -// Accès aux claims -userID := ctx.Locals("sub").(string) -``` - -## Conséquences - -- Déploiement Docker Compose pour MVP -- Migration vers Kubernetes HA en production -- Gestion refresh tokens (rotation automatique) -- MFA et passkeys disponibles out-of-the-box -- Rate limiting intégré à Zitadel - - - - -
- - -# ADR-009 : Solution de Paiement et Gestion des Abonnements - -**Statut** : Accepté -**Date** : 2025-01-19 - -## Contexte - -RoadWave nécessite une solution de paiement pour gérer les abonnements Premium (4.99€/mois) et reverser 70% des revenus aux créateurs de contenu. Besoin de marketplace natif (split payments), KYC automatique, conformité RGPD, et coûts maîtrisés. - -## Décision - -**Mangopay** (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements. - -## Alternatives considérées - -| Solution | Coût transaction | Marketplace | KYC | Souveraineté | -|----------|-----------------|-------------|-----|--------------| -| **Mangopay** | 1.8% + 0.18€ | ✅ Natif | ✅ Gratuit | 🇪🇺 France/LU | -| Stripe Connect | 2.9% + 0.30€ | ✅ Natif | ❌ 1.20€ | 🇺🇸 USA | -| Mollie | 2.9% + 0.29€ | ❌ Non | ❌ Non | 🇪🇺 Pays-Bas | -| Paddle | 5% + 0.50€ | ✅ Natif | ✅ Inclus | 🇬🇧 UK | - -## Justification - -- **38% moins cher** que Stripe (1.8% vs 2.9%) -- **Marketplace natif** : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits -- **KYC gratuit** : vérification d'identité incluse (vs 1.20€/créateur chez Stripe) -- **Souveraineté EU** : France/Luxembourg, régulé ACPR, RGPD natif -- **Conformité DAC7** : reporting fiscal automatique -- **Spécialisé marketplace** : utilisé par Vinted, Ulule, ManoMano - -## Architecture - -``` -┌────────────────────────┐ -│ Utilisateurs Premium │ 4.99€/mois -└───────────┬────────────┘ - │ - ┌───────▼───────┐ - │ Mangopay │ - Abonnements récurrents - │ │ - KYC créateurs (gratuit) - │ │ - E-wallets automatiques - └───────┬───────┘ - Payouts SEPA (gratuits) - │ - ┌─────────┼─────────┐ - │ │ │ -┌─▼───┐ ┌─▼───┐ ┌─▼────┐ -│Créa │ │Créa │ │Plate-│ -│teur │ │teur │ │forme │ -│ A │ │ B │ │(30%) │ -│(70%)│ │(70%)│ │ │ -└─────┘ └─────┘ └──────┘ -``` - -## Exemple intégration - -```go -// Abonnement récurrent -POST /v2.01/{ClientId}/recurringpayinregistrations -{ - "AuthorId": "{UserId}", - "FirstTransactionDebitedFunds": {"Currency": "EUR", "Amount": 499} -} - -// Transfer vers créateur (70%) -POST /v2.01/{ClientId}/transfers -{ - "DebitedWalletId": "{PlatformWalletId}", - "CreditedWalletId": "{CreatorWalletId}", - "DebitedFunds": {"Currency": "EUR", "Amount": 349} -} - -// Payout SEPA gratuit -POST /v2.01/{ClientId}/payouts/bankwire -``` - -## Conséquences - -- Solution tout-en-un : 1 seul prestataire vs 2-3 -- Économie de 2160€/an sur 1000 abonnés (vs Stripe) -- Délai activation compte : 2-5 jours -- Intégration Go via REST API (pas de SDK Go officiel) -- Apple/Google IAP gérés séparément (comme toute solution de paiement) - - - - -
- - -# ADR-010 : Commandes au volant et likes - -**Statut** : Accepté -**Date** : 2026-01-20 - -## Contexte - -RoadWave est utilisée en conduisant. Les utilisateurs doivent pouvoir liker du contenu pour améliorer les recommandations, mais les commandes au volant ont des limitations : -- 40% des véhicules n'ont que Suivant/Précédent/Mute -- iOS/Android ne supportent pas nativement les appuis longs ou doubles-appuis -- La sécurité impose des interactions minimales - -## Décision - -**Like automatique basé sur le temps d'écoute**. - -Règles : -- ≥80% d'écoute → Like renforcé (+2 points) -- 30-79% d'écoute → Like standard (+1 point) -- <30% d'écoute → Pas de like -- Skip <10s → Signal négatif (-0.5 point) - -## Alternatives considérées - -| Option | Compatibilité | Sécurité | Complexité | -|--------|---------------|----------|------------| -| **Like automatique** | 100% | Maximale | Faible | -| Double-tap Pause | ~80% | Moyenne | Moyenne | -| Appui long Suivant | ~95% | Faible | Élevée | -| Configuration paramétrable | 100% | Variable | Très élevée | - -## Justification - -- **Sécurité maximale** : Aucune action complexe en conduite -- **Compatibilité universelle** : Fonctionne sur 100% des véhicules -- **UX intuitive** : Comportement standard (Spotify, YouTube Music) -- **Engagement** : Tous les contenus génèrent des signaux -- **Simplicité** : Une seule logique à implémenter et maintenir - -## Conséquences - -- Tracking du temps d'écoute via le player audio -- Calcul du score côté backend basé sur `completion_rate` -- Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute" -- Possibilité de like manuel depuis l'app (à l'arrêt) -- Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs - - - - -
- - -# ADR-011 : Conformité App Stores et Plateformes Auto - -**Statut** : Accepté avec actions requises -**Date** : 2026-01-20 - -## Contexte - -RoadWave est une app audio géolocalisée utilisée en conduite (CarPlay/Android Auto) avec : -- Contenu généré par utilisateurs (UGC) -- Monétisation : publicités géolocalisées + Premium (4.99€ web / 5.99€ IAP) -- GPS en arrière-plan -- Partage de revenus avec créateurs (70/30) - -## Décision - -**Stratégie de conformité multi-plateforme** avec : -- Modération UGC robuste (IA + humain) -- Prix différenciés selon région (US/EU/Monde) -- GPS avec disclosure complète -- Paiements créateurs externes (Mangopay) - -## Plateformes analysées - -| Plateforme | Conformité | Points critiques | -|------------|------------|------------------| -| **Android Auto** | ✅ Conforme | API Level 35+ (Android 15+) | -| **CarPlay** | ✅ Conforme | Entitlement audio à demander | -| **Google Play** | ⚠️ Actions requises | Déclaration GPS + UGC modération | -| **App Store** | ⚠️ Actions requises | Prix différenciés US/EU | - -## Conformité détaillée - -### Android Auto / CarPlay ✅ -- 100% audio (pas de vidéo) -- Commandes standard au volant -- Aucun achat in-car -- Like automatique = sécurité maximale -- **Notifications géolocalisées** : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel) -- **Action** : Demander CarPlay Audio Entitlement (Apple) - -### Google Play ⚠️ - -**UGC (critique)** : -- Modération hybride IA + humain ✅ -- 3 premiers contenus validés manuellement ✅ -- Système de strikes (4 = ban) ✅ -- Signalement + blocage utilisateurs ✅ - -**GPS Background (critique)** : -- Permission "Always Location" = **OPTIONNELLE** -- Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides) -- Justification Play Console : - > "RoadWave permet aux utilisateurs de recevoir des alertes audio-guides lorsqu'ils passent à pied près de monuments/musées, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée dans les paramètres." -- In-app disclosure obligatoire (écran dédié avant demande permission) -- Si refusée : app fonctionne en mode voiture uniquement -- **Action** : Remplir formulaire background location Play Console avec justification - -**Réponses formulaire Play Console** : - -| Question | Réponse | -|----------|---------| -| Why does your app need background location? | "RoadWave offers optional pedestrian mode: users receive push notifications when passing near audio-guide points (museums, monuments) even when app is in background. This feature is opt-in and can be disabled in settings." | -| Is this feature core to your app? | "No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)." | -| What user value does this provide? | "Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app." | -| Does a less invasive alternative exist? | "Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience." | - -### App Store ⚠️ - -**Prix différenciés (légaux depuis 2025-2026)** : -- 🇺🇸 US : Lien externe autorisé (0% commission) -- 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite) -- 🌍 Monde : IAP obligatoire (30% commission) - -**UGC** : -- Mode Kids obligatoire (filtrage selon âge) ✅ -- Système de modération + signalement ✅ - -**GPS Background (critique)** : -- Permission "Always Location" = **OPTIONNELLE** -- Deux strings Info.plist requises : - - `NSLocationWhenInUseUsageDescription` : explication mode voiture - - `NSLocationAlwaysAndWhenInUseUsageDescription` : explication mode piéton (optionnel) -- In-app disclosure obligatoire avant demande "Always" -- Flux two-step : When In Use → Always (si user active mode piéton) -- Si refusée : app fonctionne en mode voiture uniquement -- **Action** : Voir strings détaillés dans [05-interactions-navigation.md](#../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides) - -### Revenus créateurs - -**Position** : Paiements créateurs = "services" (comme YouTube/Uber), pas IAP -- Paiement via Mangopay Connect (externe) -- Commission stores uniquement sur Premium (IAP) -- Comparables : YouTube AdSense, TikTok Creator Fund, Uber - -## Actions bloquantes avant soumission - -| Action | Plateforme | Deadline | Complexité | -|--------|-----------|----------|------------| -| Demander CarPlay Audio Entitlement | Apple | Avant soumission iOS | Faible | -| Remplir formulaire background location avec justification | Google Play | Avant soumission Android | Faible | -| Implémenter disclosure GPS (écran dédié mode piéton) | iOS + Android | MVP | Moyenne | -| Rendre permission "Always Location" optionnelle | iOS + Android | MVP | Moyenne | -| Désactiver overlay visuel notification en CarPlay/Android Auto | iOS + Android | MVP | Moyenne | -| Mettre à jour strings Info.plist avec justifications détaillées | iOS | MVP | Faible | -| Finaliser système modération UGC | Google + Apple | MVP | Élevée | - -**Estimation totale** : +5 jours développement avant soumission stores - -## Stratégie de lancement - -**Phase 1 - MVP** : -- IAP uniquement (5.99€/mois mondial) -- Modération UGC active -- GPS avec disclosure -- CarPlay/Android Auto basique - -**Phase 2 - Post-validation** : -- Prix différenciés US (lien externe 4.99€) -- Paiement externe EU (DMA) -- Monétisation créateurs (Mangopay) - -## Conséquences - -- Formation équipe sur politiques stores -- Suivi des métriques modération (% rejet, SLA) -- Migration iOS 26 SDK (Avril 2026) -- API Level 35 Android (2026) -- Communication transparente GPS/publicités - -## Sources - -- [Android Auto Media Apps](https://developer.android.com/training/cars/media) -- [CarPlay Developer Guide](https://developer.apple.com/carplay) -- [Google Play UGC Policy](https://support.google.com/googleplay/android-developer/answer/9876937) -- [App Store Guidelines](https://developer.apple.com/app-store/review/guidelines/) -- [Apple DMA Update EU](https://www.revenuecat.com/blog/growth/apple-eu-dma-update-june-2025/) -- [Google Background Location 2026](https://support.google.com/googleplay/android-developer/answer/9799150) - - - - -
- - -# ADR-012 : Architecture Backend - -**Statut** : Accepté -**Date** : 2025-01-20 - -## Contexte - -RoadWave nécessite une architecture backend évolutive tout en gardant la simplicité opérationnelle pour un MVP. Le système doit supporter une croissance progressive de 0 à 10M utilisateurs. - -## Décision - -**Monolithe modulaire** avec séparation claire en modules internes. - -## Alternatives considérées - -| Architecture | Complexité | Coûts infra | Time to market | Évolutivité | -|--------------|------------|-------------|----------------|-------------| -| **Monolithe modulaire** | Faible | Faible | Rapide | 0-1M users | -| Microservices | Élevée | Élevée | Lent | 1M+ users | -| Hybrid (Mono + Workers) | Moyenne | Moyenne | Moyen | 100K-5M users | - -## Justification - -- **Simplicité** : 1 seul binaire Go, déploiement trivial -- **Transactions** : Communications inter-modules en mémoire (pas de latence réseau) -- **Debugging** : Stack traces complètes, profiling unifié -- **Coûts** : 1 serveur suffit pour 100K users (vs N services) -- **Refactoring** : Modules internes bien séparés facilitent migration vers microservices si nécessaire - -## Structure modulaire - -``` -internal/ -├── auth/ # Validation JWT, intégration Zitadel -├── user/ # Profils, centres d'intérêt -├── content/ # CRUD contenus, métadonnées -├── geo/ # Recherche géospatiale, algorithme -├── streaming/ # Génération HLS, transcoding -├── moderation/ # Signalements, workflow -├── payment/ # Intégration Mangopay -└── analytics/ # Métriques écoute, jauges -``` - -Chaque module suit : `handler.go` → `service.go` → `repository.go`. - -## Conséquences - -- Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users) -- Transition vers microservices possible en phase 2 (extraction progressive des modules) -- Importance de maintenir découplage fort entre modules (interfaces claires) - - - - -
- - -# ADR-013 : ORM et Accès Données - -**Statut** : Accepté -**Date** : 2025-01-20 - -## Contexte - -RoadWave nécessite des requêtes SQL complexes (PostGIS géospatiales) avec performance optimale et type safety. Le choix entre ORM, query builder ou SQL brut impacte maintenabilité et performance. - -## Décision - -**sqlc** pour génération de code Go type-safe depuis SQL. - -## Alternatives considérées - -| Solution | Performance | Type Safety | Contrôle SQL | Courbe apprentissage | -|----------|-------------|-------------|--------------|----------------------| -| **sqlc** | Excellente | Très haute | Total | Faible | -| GORM | Moyenne | Moyenne | Limité | Faible | -| pgx + SQL brut | Excellente | Faible | Total | Moyenne | -| sqlx | Bonne | Faible | Total | Faible | - -## Justification - -- **Performance** : Génération compile-time, zero overhead runtime -- **Type safety** : Structs Go générées automatiquement, erreurs détectées à la compilation -- **Contrôle SQL** : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM) -- **Maintenabilité** : Modifications SQL → `sqlc generate` → code mis à jour -- **Simplicité** : Pas de magic, code généré lisible et debuggable - -## Workflow - -```sql --- queries/content.sql --- name: GetContentNearby :many -SELECT id, title, ST_Distance(location, $1::geography) as distance -FROM contents -WHERE ST_DWithin(location, $1::geography, $2) -ORDER BY distance -LIMIT $3; -``` - -```bash -sqlc generate -``` - -```go -// Code Go type-safe généré automatiquement -contents, err := q.GetContentNearby(ctx, location, radius, limit) -``` - -## Conséquences - -- Dépendance : `github.com/sqlc-dev/sqlc` -- Fichier `sqlc.yaml` à la racine pour configuration -- Migrations gérées séparément avec `golang-migrate` -- CI doit exécuter `sqlc generate` pour valider cohérence SQL/Go - - - - -
- - -# ADR-014 : Frontend Mobile - -**Statut** : Accepté -**Date** : 2025-01-20 - -## Contexte - -RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances. - -## Décision - -**Flutter** pour iOS et Android avec codebase unique. - -## Alternatives considérées - -| Framework | Codebase | Performance | Audio/CarPlay | Communauté | -|-----------|----------|-------------|---------------|------------| -| **Flutter** | Unique | Native | Excellente | Large | -| React Native | Unique | Bonne | Modules natifs requis | Très large | -| Native (Swift+Kotlin) | Double | Excellente | Native | Large | -| Ionic/Capacitor | Unique | Moyenne | Limitée | Moyenne | - -## Justification - -- **Codebase unique** : iOS + Android maintenus ensemble, vélocité développement x2 -- **Performance** : Dart compilé en code natif (pas de bridge JS) -- **Audio HLS** : Package `just_audio` mature avec support HLS, buffering adaptatif -- **CarPlay/Android Auto** : Support via packages communautaires (`flutter_carplay`, `android_auto_flutter`) -- **Géolocalisation** : `geolocator` robuste avec gestion permissions -- **Écosystème** : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod) - -## Packages clés - -```yaml -dependencies: - flutter_bloc: ^8.1.3 # State management - just_audio: ^0.9.36 # Lecture audio HLS - geolocator: ^11.0.0 # GPS temps réel (mode voiture) - geofence_service: ^5.2.0 # Geofencing arrière-plan (mode piéton) - flutter_local_notifications: ^17.0.0 # Notifications géolocalisées - dio: ^5.4.0 # HTTP client - flutter_secure_storage: ^9.0.0 # Tokens JWT - cached_network_image: ^3.3.1 # Cache images -``` - -**Nouveaux packages (contenus géolocalisés)** : - -- **`geofence_service`** : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton) - - Geofencing natif iOS/Android - - Minimise consommation batterie - - Supporte notifications push même app fermée - -- **`flutter_local_notifications`** : Notifications locales avec compteur dynamique - - Notification avec compteur décroissant (7→1) en mode voiture - - Icônes personnalisées selon type contenu - - Désactivation overlay en mode CarPlay/Android Auto (conformité) - -## Structure application - -``` -lib/ -├── core/ # Config, DI, routes -├── data/ # Repositories, API clients -├── domain/ # Models, business logic -├── presentation/ # UI (screens, widgets, blocs) -└── main.dart -``` - -## Conséquences - -- Équipe doit apprendre Dart (syntaxe proche Java/TypeScript) -- Taille binaire : 8-15 MB (acceptable) -- Tests : `flutter_test` pour widgets, `integration_test` pour E2E -- CI/CD : Fastlane pour déploiement stores - - - - -
- - -# ADR-015 : Stratégie Tests - -**Statut** : Accepté -**Date** : 2025-01-20 - -## Contexte - -RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité. - -## Décision - -Approche **multi-niveaux** : unitaires, intégration, BDD (Gherkin), E2E, load testing. - -## Stratégie par type - -| Type | Framework | Cible | Fréquence | -|------|-----------|-------|-----------| -| **Unitaires** | Testify | 80%+ couverture | Chaque commit | -| **Intégration DB** | Testify + Testcontainers | Repositories critiques | Avant merge PR | -| **BDD (Gherkin)** | Godog | User stories | Avant release | -| **E2E Mobile** | Flutter integration_test | Parcours critiques | Nightly | -| **Load** | k6 | N/A | Avant mise en prod | - -## Tests unitaires (Testify) - -```go -// internal/user/service_test.go -func TestGetUserByID(t *testing.T) { - mockRepo := new(MockRepository) - service := NewService(mockRepo) - - mockRepo.On("FindByID", "123").Return(&User{ID: "123"}, nil) - - user, err := service.GetByID("123") - - assert.NoError(t, err) - assert.Equal(t, "123", user.ID) - mockRepo.AssertExpectations(t) -} -``` - -**Couverture minimale** : 80% sur packages `internal/*/service.go` - -## Tests BDD (Gherkin + Godog) - -Voir [ADR-007](#007-tests-bdd) pour contexte complet. - -```gherkin -# features/recommendation.feature -Feature: Recommandation géolocalisée - - Scenario: Contenu proche prioritaire - Given je suis à Paris (48.8566, 2.3522) - And un contenu existe à 500m avec tag "tourisme" - And mon intérêt "tourisme" est à 85% - When je demande des recommandations - Then le contenu est en première position - And le score de pertinence est supérieur à 0.8 -``` - -**Couverture** : Tous les cas d'usage du [README.md](#../../README) traduits en `.feature`. - -## Tests intégration (Testcontainers) - -```go -// internal/geo/repository_integration_test.go -func TestFindContentNearby(t *testing.T) { - container := testcontainers.RunPostGISContainer(t) - defer container.Terminate() - - repo := NewRepository(container.DB()) - - // Insert test data - repo.CreateContent(testContent) - - // Query - results := repo.FindNearby(48.8566, 2.3522, 5000) - - assert.Len(t, results, 1) -} -``` - -## Tests E2E Mobile (Flutter) - -```dart -// integration_test/player_test.dart -testWidgets('Play audio and skip', (tester) async { - await tester.pumpWidget(MyApp()); - - await tester.tap(find.byIcon(Icons.play_arrow)); - await tester.pumpAndSettle(); - - expect(find.text('Now Playing'), findsOneWidget); - - await tester.tap(find.byIcon(Icons.skip_next)); - expect(find.text('Next Content'), findsOneWidget); -}); -``` - -## Load testing (k6) - -```javascript -// tests/load/streaming.js -import http from 'k6/http'; -import { check } from 'k6'; - -export let options = { - stages: [ - { duration: '2m', target: 1000 }, - { duration: '5m', target: 10000 }, - ], -}; - -export default function () { - let res = http.get('https://api.roadwave.com/v1/content/nearby'); - check(res, { 'status is 200': (r) => r.status === 200 }); -} -``` - -**Objectif** : API p99 < 100ms à 10K RPS. - -## CI/CD Pipeline - -```yaml -# .github/workflows/ci.yml -- name: Unit tests - run: go test -race -coverprofile=coverage.out ./... - -- name: BDD tests - run: godog run features/ - -- name: Integration tests - run: go test -tags=integration ./... - -- name: Coverage gate - run: | - coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') - if (( $(echo "$coverage < 80" | bc -l) )); then - echo "Coverage $coverage% < 80%" - exit 1 - fi -``` - -## Conséquences - -- Dépendances : - - `github.com/stretchr/testify` - - `github.com/cucumber/godog` - - `github.com/testcontainers/testcontainers-go` - - `grafana/k6` -- Temps CI : ~3-5 min (tests unitaires + BDD) -- Tests intégration/E2E : nightly builds (15-30 min) -- Load tests : avant chaque release majeure - - - - -
- - -# Règles métier RoadWave - -> Documentation complète des règles métier validées pour l'application RoadWave. -> Chaque section détaille les comportements, flux et décisions techniques. - ---- - -## 📋 Table des matières - -### [01. Authentification & Inscription](#01-authentification-inscription) - -**Contenu** : Inscription, connexion, récupération de compte - -- Inscription : email/password uniquement (pas d'OAuth tiers) -- Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j) -- Connexion : 5 tentatives max, blocage 15 min, refresh token 30j -- Récupération mot de passe : email, lien expire 1h - ---- - -### [02. Algorithme de recommandation](#02-algorithme-recommandation) - -**Contenu** : Scoring, géolocalisation, orientation politique, mode Kids - -- Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%) -- Engagement : 20%, Aléatoire : 10% -- Orientation politique : 5 niveaux, équilibre imposé (40/40/20) -- Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans -- Historique : >80% jamais reproposer, <10s ne pas reproposer - ---- - -### [03. Centres d'intérêt et jauges](#03-centres-interet-jauges) - -**Contenu** : Évolution jauges, valeurs initiales - -- Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1% -- Like explicite (manuel) : +2% (cumulable avec auto) -- Abonnement : +5% -- Skip rapide (<10s) : -0.5% -- Valeur initiale : 50% (neutre) -- Limites : 0-100% stricte, pas de dégradation temporelle - ---- - -### [04. Création et publication de contenu](#04-creation-publication-contenu) - -**Contenu** : Upload, métadonnées, validation, modification - -- Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h -- Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge -- Validation 3 premiers contenus : 24-48h (modération RoadWave) -- Modification : métadonnées uniquement, pas audio/zone/classification - ---- - -### [05. Interactions et navigation](#05-interactions-navigation) - -**Contenu** : Commandes Suivant/Précédent, interactions volant, lecture en boucle - -- Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min -- Précédent : <10s → contenu avant, ≥10s → replay début -- Commandes volant : Suivant, Précédent, Play/Pause uniquement -- Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point -- Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto) -- Passage auto après 2s (1s mode Kids) - ---- - -### [06. Publicités](#06-publicites) - -**Contenu** : Campagnes, fréquence, insertion, facturation - -- Interface self-service, budget min 50€, étalement paramétrable -- Fréquence : 1/5 contenus (gratuits uniquement) -- Durée : 10-60s (recommandé 15-30s), skippable après 5s -- Validation manuelle 24-48h, prépaiement Mangopay -- Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€ - ---- - -### [07. Radio live](#07-radio-live) - -**Contenu** : Démarrage, arrêt, comportement auditeur - -- Buffer 15s avant diffusion publique, durée max 8h -- Notification push abonnés dans zone géo uniquement -- Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s -- Enregistrement auto MP3 256 kbps → replay sous 5-10 min -- Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat - ---- - -### [08. Abonnements et notifications](#08-abonnements-notifications) - -**Contenu** : Impact algorithme, notifications, audio-guides, limites - -- Boost +30% au score final (pas priorité absolue) -- Détection contexte : <5 km/h piéton, >10 km/h voiture -- Voiture : in-app uniquement, Piéton : push actives -- Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h -- Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle -- Max 200 abonnements, +5% jauges tous tags créateur - ---- - -### [09. Monétisation créateurs](#09-monetisation-createurs) - -**Contenu** : Activation, KYC, sources revenus, paiement - -- Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j -- KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois -- Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub) -- Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute) -- Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay - ---- - -### [10. Premium](#10-premium) - -**Contenu** : Offre, multi-devices, avantages, gestion abonnement - -- Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif) -- Pas d'essai gratuit, pas de partage familial (MVP) -- Multi-devices : 1 seul stream actif, détection connexion simultanée -- Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité -- Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission) - ---- - -### [11. Mode offline](#11-mode-offline) - -**Contenu** : Téléchargement, validité, synchronisation - -- Zone géographique : choix manuel (autour de moi / ville / département / région) -- Nombre contenus : gratuit 50 max, Premium illimité -- WiFi par défaut, mobile avec confirmation + estimation volume -- Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours) -- Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max - ---- - -### [12. Gestion des erreurs](#12-gestion-erreurs) - -**Contenu** : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé - -- Aucun contenu : élargissement auto 50km → 100km → département → région → national -- Contenu supprimé : laisser terminer, passage auto suivant après 2s -- Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6× -- GPS désactivé : mode dégradé (contenu national + neutre + téléchargé) - ---- - -### [13. Conformité RGPD](#13-conformite-rgpd) - -**Contenu** : Consentements, anonymisation, export, suppression - -- Consentement : Tarteaucitron.js + PostgreSQL versioning -- GPS précis : 24h puis geohash 5 (~5km²) -- Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j -- Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé") -- Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers -- DPO : fondateur formé CNIL (non obligatoire <250 employés) - ---- - -### [14. Modération - Flows opérationnels](#14-moderation-flows) - -**Contenu** : Signalement, traitement, sanctions - -- Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre) -- IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min) -- SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h -- Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription) -- Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard) - ---- - -### [15. Autres comportements](#15-autres-comportements) - -**Contenu** : Partage, profil créateur, recherche - -- Partage : bouton partout, lien `roadwave.fr/share/c/[id]`, web player + deep link -- Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓ -- Badge vérifié : KYC validé OU célébrité OU >10K abonnés -- Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM) -- Filtres : type, durée, âge, géo, tags, date, premium (combinables) -- Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet - ---- - -### [16. Audio-guides multi-séquences](#16-audio-guides-multi-sequences) - -**Contenu** : Modes déplacement, navigation, déclenchement GPS, publicités - -- **4 modes** : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport -- **Mode Piéton** : pause auto après chaque séquence, user clique Suivant, navigation libre -- **Mode Voiture** : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h -- **Affichage voiture** : distance temps réel + ETA + direction (flèche) + vitesse -- **Rayons** : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m) -- **Publicités** : 1/5 séquences tous modes, auto-play, skippable 5s -- **Reprise** : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud) - ---- - -## 🗂️ Organisation - -Chaque fichier de règles métier suit la structure : - -1. **Décisions** : choix validés avec justifications -2. **Comportements détaillés** : flux utilisateur, cas limites -3. **Paramètres** : valeurs exactes, seuils, durées -4. **Points d'attention Gherkin** : éléments à tester - ---- - -## 🚀 Utilisation - -Ces documents servent de **référence unique** pour : - -- ✅ Développement backend/frontend -- ✅ Écriture des tests Gherkin (BDD) -- ✅ Validation QA -- ✅ Documentation produit - -**Prochaine étape** : Création des fichiers `.feature` Gherkin dans `features/` basés sur ces règles. - ---- - -## 📊 Statistiques - -- **16 sections** validées -- **~12 000 lignes** de spécifications détaillées -- **Coût infrastructure MVP** : ~50-250€/mois (hors salaires) -- **Technologies** : 100% open source (sauf Mangopay paiements) - ---- - -**Dernière mise à jour** : Janvier 2026 -**Statut** : ✅ Toutes sections validées - - - - -
- - -## 1. Authentification & Inscription - -### 1.1 Méthodes d'inscription - -**Décision** : Email/Password uniquement (pas d'OAuth tiers) - -- ❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine) -- ✅ Email + mot de passe -- ✅ 2FA (Two-Factor Authentication) disponible -- ✅ Option "Appareil de confiance" (skip 2FA pour 30 jours) - -**Justification** : -- Souveraineté : pas de dépendance externe -- RGPD : données 100% contrôlées -- Coût : 0€ (Zitadel intégré) - ---- - -### 1.2 Vérification email - -**Décision** : Différenciée selon le rôle utilisateur - -#### Pour les auditeurs (écoute uniquement) - -| État | Capacités | -|------|-----------| -| **Email non vérifié** | Lecture illimitée + création max 5 contenus | -| **Email vérifié** | Toutes fonctionnalités débloquées | - -**Paramètres** : -- Lien de vérification expire après **7 jours** -- Possibilité de renvoyer le lien (max 3 fois/jour) -- Rappel in-app après création du 3ème contenu - -**Justification** : -- Friction minimale à l'inscription -- Anti-spam sans bloquer l'essai du produit -- Incitation naturelle à vérifier (déblocage) - -#### Pour les créateurs (monétisation) - -**Vérification obligatoire sous 7 jours** pour : -- Accès au programme de monétisation -- KYC et reversement des revenus (conformité Mangopay) -- Publication illimitée de contenus - -**Justification** : -- **Conformité légale** : KYC obligatoire pour transferts financiers -- **Anti-fraude** : Vérification identité réelle pour paiements -- **Responsabilité** : RoadWave doit pouvoir prouver identité créateurs monétisés - ---- - -### 1.3 Données requises à l'inscription - -**Obligatoires** : -- ✅ Email (format validé) -- ✅ Mot de passe (voir règles ci-dessous) -- ✅ Pseudo (3-30 caractères, alphanumérique + underscore) -- ✅ Date de naissance (vérification âge minimum) - -**Optionnelles** : -- ❌ Nom complet (privacy by design) -- ❌ Photo de profil (avatar par défaut généré) -- ❌ Bio (ajout ultérieur) - -**Âge minimum** : -- **13 ans minimum** (conformité réglementation réseaux sociaux EU) -- Vérification à l'inscription via date de naissance -- Blocage inscription si <13 ans avec message explicite - -**Justification** : -- RGPD minimal data -- Friction réduite (4 champs max) -- Protection mineurs (obligation légale) - ---- - -### 1.4 Tranches d'âge des contenus - -**Décision** : Classification obligatoire des contenus - -**Catégories** : -- 🟢 **Tout public** (défaut) -- 🟡 **13+** : contenu mature léger (débats, actualité sensible) -- 🟠 **16+** : contenu mature (violence verbale, sujets sensibles) -- 🔴 **18+** : contenu adulte (langage explicite, sujets réservés) - -**Règles de diffusion** : -- Utilisateur 13-15 ans → contenus 🟢 uniquement -- Utilisateur 16-17 ans → contenus 🟢 🟡 -- Utilisateur 18+ → tous contenus - -**Modération** : -- Vérification obligatoire de la classification lors de la validation -- Reclassification possible par modérateurs -- Strike si classification volontairement incorrecte - -**Justification** : -- Protection mineurs (obligation légale) -- Responsabilité plateforme -- Coût : champ supplémentaire + règle algo - ---- - -### 1.5 Validation mot de passe - -**Règles** : -- ✅ Minimum **8 caractères** -- ✅ Au moins **1 majuscule** -- ✅ Au moins **1 chiffre** -- ❌ Pas de symbole obligatoire (simplicité) - -**Validation** : -- Côté client (feedback temps réel) -- Côté backend (sécurité) -- Message d'erreur explicite par règle non respectée - -**Justification** : -- Standard industrie -- Bloque 95% des mots de passe faibles -- UX acceptable (pas trop restrictif) - ---- - -### 1.6 Two-Factor Authentication (2FA) - -**Décision** : Optionnel mais recommandé - -**Méthodes disponibles** : -- ✅ TOTP (Time-based One-Time Password) via app (Google Authenticator, Authy) -- ✅ Email (code 6 chiffres, expire 10 min) -- ❌ SMS (coût élevé ~0.05€/SMS) - -**Appareil de confiance** : -- Option "Ne plus demander sur cet appareil" → bypass 2FA pendant **30 jours** -- Révocable depuis paramètres compte -- Liste des appareils de confiance visible - -**Justification** : -- Sécurité renforcée sans coût SMS -- UX : appareil de confiance évite friction quotidienne -- Zitadel natif (0€) - ---- - -### 1.7 Tentatives de connexion - -**Règles** : -- Maximum **5 tentatives** par période de **15 minutes** -- Blocage temporaire après 5 échecs -- Compteur reset automatique après 15 min -- Notification email si blocage (tentative suspecte) - -**Déblocage** : -- Automatique après 15 min -- Ou via lien "Mot de passe oublié" - -**Justification** : -- Anti brute-force -- Standard industrie (équilibre sécurité/UX) -- Zitadel natif (0€) - ---- - -### 1.8 Sessions et refresh tokens - -**Durée de vie** : -- **Access token** : 15 minutes -- **Refresh token** : 30 jours - -**Rotation** : -- Refresh token rotatif (nouveau token à chaque refresh) -- Ancien token invalidé immédiatement -- Détection token replay attack - -**Extension automatique** : -- Si app utilisée, session prolongée automatiquement -- Inactivité 30 jours → déconnexion - -**Justification** : -- Sécurité (token court-vie) -- UX (pas de reconnexion fréquente) -- Standard OAuth2/OIDC - ---- - -### 1.9 Multi-device - -**Décision** : Sessions simultanées illimitées - -**Gestion** : -- Liste des devices connectés visible (OS, navigateur, dernière connexion, IP/ville) -- Révocation individuelle possible -- Révocation globale "Déconnecter tous les appareils" - -**Alertes** : -- Notification push + email si connexion depuis nouveau device -- Détection localisation suspecte (IP pays différent) - -**Justification** : -- UX maximale (écoute voiture + tablette maison + web) -- Sécurité via transparence (utilisateur voit tout) -- Coût : table sessions PostgreSQL - ---- - -### 1.10 Récupération de compte - -**Méthode** : Email uniquement - -**Processus** : -1. Utilisateur clique "Mot de passe oublié" -2. Email avec lien de reset envoyé -3. Lien expire après **1 heure** -4. Page de reset : nouveau mot de passe (validation règles) -5. Confirmation + déconnexion tous devices (sauf celui en cours) - -**Notifications** : -- Email immédiat si changement mot de passe -- Push si changement depuis appareil non reconnu - -**Limite** : -- Maximum **3 demandes/heure** (anti-spam) - -**Justification** : -- Standard sécurité -- Pas de coût SMS -- Protection contre attaque sociale - ---- - -## Récapitulatif Section 1 - - - - -
- - -## 2. Algorithme de recommandation - -### 2.1 Classification de géo-pertinence - -**Décision** : 3 types de contenus selon leur pertinence géographique - -| Type | Description | Exemple | Pondération géo | -|------|-------------|---------|-----------------| -| **Géo-ancré** | Contenu lié à un lieu précis | Audio-guide monument, pub restaurant local | 70% | -| **Géo-contextuel** | Pertinent dans une zone | Actualité régionale, événement local | 50% | -| **Géo-neutre** | Universel, pas de lien géo | Podcast philosophie, musique | 20% | - -**Qui décide** : -- ✅ Créateur choisit le type à la publication -- ✅ Modération peut reclassifier après validation -- ✅ Modification possible après publication (tout le monde a le droit de se tromper) - -**Justification** : -- Différencie audio-guide (hyper-local) des podcasts génériques -- Algorithme adapte automatiquement la pondération -- Coût : champ supplémentaire en DB + règle algo - ---- - -### 2.2 Formule de scoring - -**Décision** : Score combiné dynamique selon type de contenu - -``` -score_final = (score_geo * poids_geo_type) - + (score_interets * poids_interets_type) - + (score_engagement * 0.2) - + (bonus_aleatoire) - -où : -- score_geo = 1 - (distance_km / distance_max_km) -- score_interets = moyenne des jauges utilisateur pour les tags du contenu -- score_engagement = (taux_completion * 0.5) + (ratio_likes * 0.3) + (ratio_abonnements * 0.2) -- bonus_aleatoire = 10% des recommandations tirées aléatoirement -``` - -**Pondérations par type** : - -| Type | Poids géo | Poids intérêts | -|------|-----------|----------------| -| Géo-ancré | 0.7 | 0.1 | -| Géo-contextuel | 0.5 | 0.3 | -| Géo-neutre | 0.2 | 0.6 | - -**Paramètres** : -- Distance max recommandée : **200 km** -- Dégradation : **linéaire** (1 - distance/200km) -- Rayon point GPS : **500m** (adapté au volume de contenu local) - -**Tous ces paramètres sont configurables à chaud via interface admin.** - -**Justification** : -- Flexibilité totale selon type de contenu -- Linéaire = rattrapage naturel du contenu viral ancien -- Auditable via métriques engagement (moyenne/médiane) - ---- - -### 2.3 Score d'engagement et popularité - -**Décision** : Intégration popularité avec poids 0.2 - -**Métriques** : -- **Taux de complétion** : écoutes >80% / total écoutes (poids 0.5) -- **Ratio likes** : likes / écoutes (poids 0.3) -- **Ratio abonnements** : nouveaux abonnés après écoute / écoutes (poids 0.2) - -**Seuil minimum** : -- Minimum **50 écoutes** avant de considérer l'engagement -- Contenu <50 écoutes : score engagement = 0.5 (neutre) - -**Contenu viral** : -- Un contenu viral à Paris **peut** être proposé à Marseille -- Score géo faible compensé par score engagement élevé -- Paramétrable admin - -**Dépréciation temporelle** : -- Pas de dépréciation automatique -- Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent - -**Justification** : -- Équilibre découverte / qualité -- Pas de pénalisation arbitraire des contenus anciens -- Coût : calculs sur métriques existantes - ---- - -### 2.4 Part d'aléatoire (exploration) - -**Décision** : 10% par défaut, paramétrable utilisateur - -**Fonctionnement** : -- 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté) -- Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max) - -**Curseur utilisateur** : -- 🎯 **0%** : Personnalisé max (recommandations strictes) -- ⚖️ **10%** : Équilibré (défaut) -- 🎲 **30%** : Découverte élevée -- 🌍 **50%** : Découverte max (équivaut à national = découverte) - -**Justification** : -- Évite la bulle de filtre -- Laisse l'utilisateur maître de son expérience -- Coût : variable aléatoire en algo - ---- - -### 2.5 Contenu politique (version MVP simplifiée) - -> ⚠️ **Note** : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir [ANNEXE-POST-MVP.md](#ANNEXE-POST-MVP) pour la version complète. - -**Décision MVP** : Tag simple "Politique" sans classification idéologique - -**Tagging** : -- Créateur peut taguer son contenu comme "Politique" (optionnel) -- Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc. -- **Pas de classification gauche/droite** -- **Pas d'équilibrage imposé** - -**Filtrage utilisateur** : -- Option paramètres : **"Masquer contenu politique"** -- Si activé → 0% de contenus tagués "Politique" dans le feed -- Par défaut : désactivé (tous contenus visibles) - -**Justification MVP** : -- **Simplicité** : Pas de modération politique coûteuse (~2000€/mois économisés) -- **Neutralité technique** : Aucun jugement éditorial sur orientation -- **Risque minimal** : Évite controverses et contentieux DSA au lancement -- **Fonctionnel** : Utilisateurs peuvent filtrer si souhaité - -**Post-MVP** : -- Classification avancée possible si forte demande utilisateurs -- Nécessite ressources modération dédiées et audit DSA - ---- - -### 2.6 Mode Kids (13-15 ans) - -**Décision** : Mode optionnel pour adolescents 13-15 ans uniquement - -> ⚠️ **Note** : Âge minimum d'inscription = **13 ans** (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme. - -**Tranche concernée** : - -| Tranche | Description | Contenus autorisés | Restrictions | -|---------|-------------|-------------------|--------------| -| **13-15 ans** | Collège | Contenus "Tous publics" uniquement | Filtrage 16+ et 18+ | - -**Activation** : -- ❌ **Pas d'activation automatique** (tous les utilisateurs ont ≥13 ans) -- ✅ **Activation manuelle** via toggle paramètres -- ✅ Parents peuvent activer pour leurs enfants 13-15 ans -- ✅ Utilisateur peut désactiver à tout moment - -**Filtrage quand Mode Kids activé** : -- ✅ Contenus "Tous publics" uniquement -- ❌ Exclusion contenus 16+ et 18+ -- ❌ Pas de contenu politique (automatiquement filtré) -- ❌ Pas de publicité (ou uniquement pub validée manuellement) - -**Interface** : -- Interface standard (pas d'interface dédiée enfants pour MVP) -- Filtrage algorithmique des contenus inappropriés - -**Justification** : -- **Conformité légale** : Âge minimum 13 ans (RGPD, DSA) -- **Simplicité MVP** : Un seul mode optionnel vs 4 tranches d'âge -- **Protection mineurs** : Filtrage contenus adultes pour 13-15 ans -- **Flexibilité** : Parents décident d'activer ou non - ---- - -### 2.7 Déclenchement géographique - -**Décision** : Notification au passage, pas d'anticipation - -**Fonctionnement** : -1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré) -2. **Notification sonore** (bip court) + **visuelle** (logo selon type) -3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement -4. Délai réaction utilisateur : **5 secondes** pour accepter (bouton volant ou commande vocale) -5. Si accepté → lecture immédiate -6. Si ignoré → contenu proposé normalement en file d'attente - -**Publicités** : -- ⚠️ **Jamais d'interruption** de contenu en cours -- Pub s'intercale **entre deux séquences** uniquement -- Notification pub : son différent (facultatif selon paramètres) - -**Gestion demi-tour** : -- Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé) -- Réinitialisation après 24h - -**Justification** : -- Respect écoute en cours (pas de coupure brutale) -- UX fluide (utilisateur garde contrôle) -- Simplicité technique (pas de prédiction trajectoire) - ---- - -### 2.8 Historique et repropositon - -**Décision** : Pas de reproposition sauf contenu partiel - -**Règles** : - -| État écoute | Completion | Action | -|-------------|------------|--------| -| **Écouté complètement** | >80% | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides) | -| **Skippé rapidement** | <10s | ❌ Ne pas reproposer | -| **Partiellement écouté** | 10-80% | ✅ Reproposer avec reprise position (`last_position_seconds`) | - -**Stockage historique** : -- Table `user_content_history` (user_id, content_id, completion_rate, last_position, listened_at) -- Historique **illimité** (PostgreSQL) -- Algorithme considère les **100 derniers** pour optimisation requêtes -- Export complet disponible (RGPD) - -**Justification** : -- Découverte maximale (pas de redites) -- Respect erreurs de clic (contenu partiel = 2nde chance) -- Coût stockage négligeable (PostgreSQL scalable) - ---- - -### 2.9 Paramétrabilité admin (interface dashboard) - -**Décision** : Tous paramètres scoring exposés + A/B testing - -**Paramètres configurables à chaud** : - -| Paramètre | Plage | Défaut | Unité | -|-----------|-------|--------|-------| -| `poids_geo_ancre` | 0.5 - 1.0 | 0.7 | % | -| `poids_geo_contextuel` | 0.3 - 0.7 | 0.5 | % | -| `poids_geo_neutre` | 0.0 - 0.4 | 0.2 | % | -| `poids_engagement` | 0.0 - 0.5 | 0.2 | % | -| `part_aleatoire_global` | 0.0 - 0.3 | 0.1 | % | -| `distance_max_km` | 50 - 500 | 200 | km | -| `rayon_gps_point_m` | 100 - 2000 | 500 | m | -| `seuil_min_ecoutes_engagement` | 10 - 200 | 50 | nb | - -**Application changements** : -- Immédiat : nouveaux calculs utilisent nouvelle config -- Aucun recalcul batch (coût CPU) -- Version config trackée (git-like) -- Rollback 1 clic - -**A/B Testing** : -- Création variantes (Config A vs Config B) -- Split utilisateurs 50/50 aléatoire -- Métriques comparatives : taux complétion, engagement, session duration -- Dashboard graphique temps réel - -**Audit engagement** : -- Métriques clés : moyenne/médiane temps d'écoute par session -- Graphiques : évolution engagement selon config -- Export CSV pour analyse externe - -**Justification** : -- Optimisation continue sans redéploiement -- Data-driven decisions (métriques objectives) -- Coût : dashboard admin à développer (one-time) - ---- - -### 2.10 Paramétrabilité utilisateur - -**Décision** : Curseurs avancés avec profils sauvegardables - -**Niveaux de personnalisation** : - -**Curseurs disponibles** : -- 📍 **Géolocalisation** : Local ← slider → National (découverte = national) -- 🎲 **Découverte** : 0% ← slider → 50% (part aléatoire) -- ⚖️ **Politique** : Masquer / Équilibré / Mes préférences - -**Profils sauvegardables** : -- 🚗 Trajet quotidien (boulot) : géo local, découverte 5%, politique masqué -- 🛣️ Road trip : géo régional, découverte 30%, politique équilibré -- 👶 Enfants : Mode Kids activé - -**Synchronisation** : -- ✅ Sync profils entre devices (cloud PostgreSQL) -- ❌ Pas de partage profils entre utilisateurs (famille) -- Auto-switch selon context (détection trajet récurrent via GPS) - -**Sécurité conduite** : -- ⚠️ **Blocage modification si vitesse GPS >10 km/h** -- Warning au lancement app : "Configurez avant de prendre la route" -- Modifications uniquement app arrêtée/passager - -**Justification** : -- Utilisateur maître de son expérience -- Contextes d'usage différents (quotidien vs voyage) -- Sécurité routière (pas de distraction) - ---- - -### 2.11 Médias traditionnels - -**Décision** : Ouverture aux médias établis - -**Médias autorisés** : -- Presse nationale : Le Monde, Le Parisien, Libération, Le Figaro, etc. -- Radios : France Inter, RTL, Europe 1, etc. -- Médias régionaux : Ouest-France, Sud-Ouest, etc. - -**Format contenus** : -- Flashs info géolocalisés (actualité régionale) -- Chroniques thématiques (culture, économie, sport) -- Éditos et débats (classification politique appliquée) - -**Validation** : -- Compte média vérifié (badge ✓) -- Pas de validation 3 premiers contenus (confiance établie) -- Modération a posteriori uniquement - -**Monétisation** : -- Partage revenus pub standard (même conditions créateurs) -- Possibilité sponsoring direct (pas via plateforme) - -**Justification** : -- Crédibilité plateforme (contenus professionnels) -- Diversité éditoriale -- Attractivité grand public (noms reconnus) - ---- - -## Récapitulatif Section 2 - - - - -
- - -## 3. Centres d'intérêt et jauges - -### 3.1 Évolution des jauges - -**Décision** : Système simple avec valeurs fixes - -| Action | Impact jauge | Justification | -|--------|--------------|---------------| -| **Like automatique renforcé (≥80% écoute)** | +2% | Signal fort d'intérêt (écoute quasi-complète) | -| **Like automatique standard (30-79% écoute)** | +1% | Signal modéré d'intérêt | -| **Like explicite (manuel)** | +2% | Signal fort, cumulable avec auto | -| **Abonnement créateur** | +5% sur tous ses tags | Signal très fort d'affinité | -| **Skip rapide (<10s)** | -0.5% | Désintérêt marqué | -| **Skip tardif (≥30%)** | 0% | Neutre (contenu essayé suffisamment) | - -**Paramètres techniques** : -- Les jauges sont bornées strictement entre **0% et 100%** -- Calcul immédiat à chaque action (pas de batch différé) -- Les tags du contenu sont définis par le créateur à la publication -- Si un contenu a plusieurs tags, chaque jauge correspondante est impactée - -**Exemple de calcul** : -``` -Contenu de 5 minutes tagué "Automobile" + "Voyage" - -Scénario 1 : Écoute 4min30 (90%) -→ Like automatique renforcé (+2%) -→ Jauge Automobile : 45% → 47% -→ Jauge Voyage : 60% → 62% - -Scénario 2 : Écoute 2min30 (50%) -→ Like automatique standard (+1%) -→ Jauge Automobile : 45% → 46% -→ Jauge Voyage : 60% → 61% - -Scénario 3 : Écoute 2min30 (50%) + Like manuel -→ Like auto +1% puis like manuel +2% = +3% total -→ Jauge Automobile : 45% → 48% -→ Jauge Voyage : 60% → 63% - -Scénario 4 : Skip après 5s -→ Signal négatif (-0.5%) -→ Jauge Automobile : 45% → 44.5% -→ Jauge Voyage : 60% → 59.5% -``` - -**Justification** : -- **Like automatique** : Reflète l'engagement réel (voir [ADR-010](#../adr/010-commandes-volant)) -- **Sécurité routière** : Pas d'action complexe en conduite -- **Prévisibilité** : Règles claires et déterministes -- **Coût minimal** : Calculs simples en backend -- **Fiabilité** : Pas d'edge cases complexes -- **Ajustable** : Valeurs modifiables via dashboard admin si besoin - ---- - -### 3.2 Jauge initiale - -**Décision** : Démarrage neutre à 50%, pas de questionnaire - -**À l'inscription** : -- Toutes les jauges d'intérêt sont initialisées à **50%** -- Pas de questionnaire onboarding (friction zéro) -- L'algorithme apprend naturellement via les premières écoutes - -**Catégories disponibles** : -- Automobile -- Voyage -- Famille -- Amour -- Musique -- Économie -- Cryptomonnaie -- Politique -- Culture générale -- Sport -- Technologie -- Santé -- *... (extensible)* - -**Cold start (premiers jours)** : -1. Nouvel utilisateur s'inscrit → toutes jauges à 50% -2. Écoute premier podcast "Automobile" → jauge Auto monte à 51% -3. Skip un contenu "Économie" → jauge Éco descend à 48% -4. Après 10-15 écoutes, profil commence à se dessiner clairement - -**Alternative optionnelle (post-MVP)** : -- Questionnaire **optionnel** proposé après 3 écoutes (in-app) -- Message : "Améliorez vos recommandations en sélectionnant vos centres d'intérêt" -- Si rempli : jauges sélectionnées passent à 70%, non sélectionnées à 30% -- Si skip : conserve 50% partout - -**Justification** : -- **Inscription ultra-rapide** : pas de questionnaire = moins de churn -- **Découverte naturelle** : l'algorithme apprend en quelques écoutes -- **Équitable** : pas de biais initial vers certains créateurs -- **Comportement déterministe** : facile à tester et débugger -- **Cold start acceptable** : à 50%, tous les contenus ont une chance égale initialement - ---- - -### 3.3 Dégradation temporelle - -**Décision** : Pas de dégradation automatique - -Les jauges **ne diminuent jamais** avec le temps de manière automatique. - -**Règle** : -- Une jauge ne change **que par les actions utilisateur** (like, écoute, skip) -- Pas de cron job de dégradation périodique -- Pas de "rafraîchissement" artificiel - -**Scénario illustratif** : -``` -Utilisateur aimait "Économie" (jauge 80%) il y a 1 an -→ Depuis, skip tous les contenus Éco -→ Jauge descend naturellement à 40% via les skips -→ Pas besoin de dégradation temporelle -``` - -**Si utilisateur inactif longtemps** : -- Utilisateur part en vacances 6 mois → jauges conservées -- Au retour : ses jauges reflètent toujours ses goûts d'avant -- Comportement cohérent et prévisible - -**Alternative utilisateur (contrôle explicite)** : -- Bouton "Réinitialiser mes centres d'intérêt" dans paramètres -- Action manuelle : remet toutes les jauges à 50% -- Permet nouveau départ si souhaité (changement de vie, etc.) - -**Justification** : -- **Principe KISS** (Keep It Simple, Stupid) -- **Coût 0** : pas de batch nocturne, pas de calculs temporels -- **Fiabilité maximale** : pas de bugs de fuseaux horaires, dates, etc. -- **UX prévisible** : jauge = reflet des actions, pas d'automatisme caché -- **Respect historique** : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ? -- **Évolution naturelle** : les actions récentes suffisent à faire évoluer les jauges - ---- - -## Récapitulatif Section 3 - - - - -
- - -## 4. Création et publication de contenu - -### 4.1 Upload et encodage - -**Décision** : Formats universels avec encodage asynchrone - -**Formats acceptés** : -- ✅ MP3 (`.mp3`) -- ✅ AAC (`.aac`, `.m4a`) -- ❌ WAV, FLAC (trop lourds, inutiles en voiture) - -**Limites** : - -| Paramètre | Valeur | Justification | -|-----------|--------|---------------| -| **Taille maximale** | 200 MB | ~4h de podcast à 128 kbps | -| **Durée maximale** | 4 heures | Suffisant pour podcasts longs | -| **Validation format** | Client + backend | Double sécurité | - -**Pipeline d'encodage** : - -``` -1. Upload fichier (MP3/AAC) → Bunny Storage temporaire -2. Job asynchrone (worker Go + FFmpeg) : - - Validation format et intégrité - - Réencodage Opus 3 profils (24/48/64 kbps) - - Génération segments HLS (.m3u8 + .ts) - - Génération image couverture par défaut -3. Suppression fichier original (économie stockage) -4. Notification créateur : "Contenu prêt à publier" -``` - -**Temps d'encodage estimé** : -- Contenu 5 min → ~30 secondes -- Podcast 1h → ~5 minutes -- Podcast 4h → ~20 minutes - -**Profils Opus générés** : - -| Qualité | Bitrate | Usage | -|---------|---------|-------| -| Basse | 24 kbps | 2G/Edge | -| Standard | 48 kbps | 3G (défaut) | -| Haute | 64 kbps | 4G/5G | - -**Écoute accélérée** : - -| Vitesse | Usage | -|---------|-------| -| 0.75x | Compréhension difficile (accent, technique) | -| 1.0x | Normal (défaut) | -| 1.25x | Gain léger | -| 1.5x | Podcasts longs | -| 2.0x | Survol rapide (modérateurs) | - -**Disponible pour** : -- ✅ Modérateurs (validation rapide : 30s → 15s à 2x) -- ✅ Auditeurs (tous les contenus) -- ✅ Standard industrie (YouTube, Spotify, Apple Podcasts) - -**Justification** : -- **Simplicité** : 2 formats couvrent 95% des cas d'usage -- **Coût optimisé** : pas de conversion WAV/FLAC lourds -- **Stockage réduit** : suppression original après encodage -- **Scalabilité** : workers horizontalement (Kubernetes jobs) -- **Productivité** : écoute accélérée = double productivité modération - ---- - -### 4.2 Métadonnées obligatoires - -**Décision** : Minimaliste pour réduire friction - -**Champs obligatoires** : - -| Champ | Format | Validation | -|-------|--------|------------| -| **Titre** | 5-100 caractères | Alphanumérique + ponctuation basique | -| **Type géo** | Enum | Ancré / Contextuel / Neutre | -| **Zone diffusion** | Composite | Voir détails ci-dessous | -| **Tags** | Enum | 1 à 3 parmi liste prédéfinie | -| **Classification âge** | Enum | Tout public / 13+ / 16+ / 18+ | - -**Zone de diffusion (obligatoire)** : - -Options mutuellement exclusives : -- **Point GPS** : latitude + longitude + rayon (100m à 10km) -- **Ville** : sélection dans référentiel INSEE -- **Département** : sélection liste -- **Région** : sélection liste -- **National** : France entière - -**Tags disponibles** (1 à 3 obligatoires) : -- Automobile -- Voyage -- Famille -- Amour -- Musique -- Économie -- Cryptomonnaie -- Politique -- Culture générale -- Sport -- Technologie -- Santé - -**Champs optionnels** : -- ❌ Description (ajout ultérieur) -- ❌ Image couverture (génération auto) - -**Image de couverture par défaut** : - -Génération automatique selon règles : -- Icône selon type géo : 📍 Ancré / 🌍 Contextuel / 🎧 Neutre -- Couleur selon tag principal : bleu (Auto), vert (Voyage), rouge (Musique), etc. -- Format 800×800px, PNG -- Personnalisable ultérieurement (post-MVP) - -**Exemple de publication** : -``` -Titre : "Histoire de la Tour Eiffel" -Type géo : Ancré -Zone : Point GPS (48.8584, 2.2945, rayon 500m) -Tags : Voyage, Culture générale -Classification : Tout public -→ Image auto : 📍 fond bleu-vert (Voyage) -``` - -**Justification** : -- **Friction minimale** : 5 champs max = 2 min de publication -- **Publication rapide** : pas de blocage sur description/image -- **Coût 0** : pas de génération IA au MVP -- **Évolutif** : champs optionnels ajoutables ultérieurement - ---- - -### 4.3 Validation des 3 premiers contenus - -**Décision** : Validation manuelle par équipe modération RoadWave - -**Processus nouveau créateur** : - -1. Créateur upload ses 3 premiers contenus -2. Contenus passent en **file d'attente modération** -3. Modérateur junior RoadWave : - - Écoute 30 secondes (ou 15s à 2x) - - Vérifie métadonnées - - Valide ou rejette avec raison -4. Si accepté : contenu publié + notification créateur -5. Si refusé : notification avec raison détaillée + lien vers règles -6. Après 3 contenus validés : créateur passe en **statut vérifié** - -**Critères de validation** : - -| Critère | Détails | -|---------|---------| -| **Qualité audio** | Compréhensible (pas de grésillement excessif) | -| **Respect règles** | Pas de contenu prohibé évident (haine, spam, illégal) | -| **Classification âge** | Cohérente avec contenu écouté | -| **Tags pertinents** | Correspondance minimale avec contenu | -| **Zone diffusion** | Cohérente (pas "Tour Eiffel" avec zone "National") | - -**Délai de validation** : -- Objectif : **24-48h** (jours ouvrés) -- Priorité : FIFO (First In First Out) -- Weekend : délai peut atteindre 72h -- Message au créateur : "Validation en cours, délai estimé 24-48h" - -**Notification créateur** : - -**Si accepté** : -- Email + push : "✅ Votre contenu '[Titre]' est en ligne !" -- Lien direct vers le contenu -- Compteur : "2/3 contenus validés pour devenir créateur vérifié" - -**Si refusé** : -- Email + push : "❌ Contenu '[Titre]' refusé" -- Raison détaillée : "Qualité audio insuffisante" / "Tags non pertinents" / "Classification incorrecte" / etc. -- Lien vers règles de publication -- Possibilité de correction + resoumission - -**Après 3 validations** : - -Créateur obtient **statut "Vérifié"** : -- Badge ✓ visible sur profil -- Contenus futurs publiés **immédiatement** (modération a posteriori uniquement) -- Modération seulement si signalé par utilisateurs - -**Outils modérateur** : -- Écoute accélérée (1.5x ou 2x) = double productivité -- Interface dédiée : queue de contenus à valider -- Raccourcis clavier : A (Accepter), R (Rejeter), Espace (Pause) -- Historique créateur visible (si déjà 1-2 contenus validés) - -**Modération communautaire (post-MVP)** : - -⚠️ **Non implémenté au MVP** (complexité juridique) - -Vision future (envisageable) : -- Créateurs établis peuvent opt-in "Modérateur communautaire" -- Formation obligatoire (30 min) + quiz (80%) -- Pré-validation uniquement (validation finale toujours par équipe RoadWave) -- Compensation : badges, premium offert -- Attribution aléatoire (pas de collusion) - -**Justification décision MVP** : -- **Responsabilité juridique** : plateforme reste responsable (DSA EU) -- **Qualité garantie** : modérateurs formés et mandatés -- **Anti-spam efficace** : bloque 95% des abus dès le début -- **Coût raisonnable** : 30s × 3 contenus = 1.5 min/créateur -- **UX acceptable** : délai 24-48h expliqué clairement -- **Pas de validation par pairs** au MVP = évite risques juridiques (collusion, compétence, conflits) - ---- - -### 4.4 Modification et suppression - -**Décision** : Modification métadonnées uniquement, suppression immédiate - -**Modification autorisée** : - -| Élément | Modifiable | Justification | -|---------|------------|---------------| -| **Titre** | ✅ | Correction coquilles | -| **Description** | ✅ | Si ajoutée ultérieurement | -| **Tags** | ✅ | Ajustement pertinence | -| **Image couverture** | ✅ | Personnalisation | -| **Audio** | ❌ | Intégrité contenu | -| **Zone diffusion** | ❌ | Évite manipulation algo | -| **Type géo** | ❌ | Évite manipulation algo | -| **Classification âge** | ❌ | Sécurité mineurs | - -**Raisons restrictions** : - -**Audio non modifiable** : -- Évite fraude : uploader contenu validé → remplacer par spam -- Intégrité : auditeurs doivent écouter ce qui a été validé - -**Zone/Type non modifiables** : -- Évite manipulation : créer "Local Paris" → changer en "National" pour boost visibilité -- Évite abus : créer "Neutre" (faible pondération géo) → changer en "Ancré" (forte pondération) - -**Classification non modifiable** : -- Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation -- Sécurité : garantit que classification a été vérifiée - -**Si besoin de changer audio/zone/classification** : -- Action : **Supprimer contenu + republier** -- Si créateur <3 contenus validés : retourne en file validation -- Si créateur ≥3 contenus validés : publication immédiate - -**Suppression de contenu** : - -| Aspect | Comportement | -|--------|--------------| -| **Délai** | Immédiat | Suppression BDD + CDN sous 5 min | -| **Réversibilité** | Non | Suppression définitive | -| **Historique auditeurs** | Marqué "Contenu supprimé par créateur" | Conserve écoute dans historique | -| **Analytics plateforme** | Anonymisé et conservé | Métriques globales (RGPD compliant) | -| **Fichiers CDN** | Supprimés sous 24h | Purge cache Bunny CDN | - -**Exemple scénario suppression** : -``` -Créateur supprime podcast écouté par 1000 personnes -→ CDN : fichiers purgés sous 24h -→ BDD : entrée marquée "deleted", auteur anonymisé -→ Historique auditeurs : "Contenu supprimé" (conserve durée écoute pour stats) -→ Analytics : métriques globales conservées (anonymes, RGPD OK) -``` - -**Notifications suppression** : -- Pas de notification aux auditeurs (pour éviter effet Streisand) -- Historique reste consultable : "Vous avez écouté ce contenu le [date]" -- Si auditeur tente de réécouter : "Ce contenu n'est plus disponible" - -**Justification** : -- **Simplicité** : règles claires et non-ambiguës -- **Sécurité** : évite manipulations algorithme et contournements modération -- **Contrôle créateur** : liberté totale de supprimer (RGPD) -- **Traçabilité** : historique conservé pour analytics (anonymisé) -- **Coût 0** : pas de revalidation métadonnées - ---- - -## Récapitulatif Section 4 - - - - -
- - -## 5. Interactions et navigation - -### 5.1 File d'attente et commande "Suivant" - -**Décision** : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques - -**File d'attente** : -- **5 contenus pré-calculés** en cache (Redis) -- Recalcul automatique si : - - Déplacement >10km - - Toutes les 10 minutes (rafraîchissement contenu) - - File d'attente <3 contenus restants - -**Insertion prioritaire géo-ancrée (mode voiture uniquement)** : - -**Détection** : -- Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android -- Notification déclenchée **7 secondes avant** d'arriver au point GPS -- Si vitesse < 5 km/h ET distance < 50m → notification immédiate -- ⚠️ **App doit être ouverte** (pas de détection en arrière-plan en mode voiture) - -**Notification** : -- **Sonore uniquement** : bip court ou son personnalisé RoadWave -- **Visuelle minimale** : icône selon type de contenu (🏛️ culture, 👨‍👩‍👧 famille, 🎵 musique, etc.) -- **Compteur visible** : 7...6...5...4...3...2...1 (décompte des secondes) -- **Pas de texte affiché** (éviter distraction conducteur) -- **Pas de bouton "Annuler"** : seul le bouton "Suivant" permet validation - -**Actions utilisateur** : -1. User entend notification sonore + voit icône et compteur -2. User appuie "Suivant" dans les 7 secondes → décompte 5s démarre -3. Pendant décompte : contenu actuel continue, compteur visible (5...4...3...2...1) -4. Si contenu actuel se termine pendant décompte → contenu suivant du buffer démarre -5. À la fin du décompte → contenu géolocalisé démarre (fade out/in 0.3s) - -**Si user n'appuie pas sur "Suivant"** : -- Notification disparaît après 7 secondes -- Contenu géolocalisé est perdu (pas d'insertion dans file) -- Pas de nouveau contenu géolocalisé pendant **10 minutes** (éviter spam) - -**Limitation anti-spam** : -- Maximum **6 contenus géolocalisés par heure** -- Timer reset toutes les heures (rolling window) -- Exception : séquences d'un même audio-guide multi-séquences (comptent comme 1) -- Si quota atteint : notifications suivantes ignorées jusqu'à libération du quota - -**Invalidation immédiate** : -- Utilisateur change ses préférences (curseurs géo/découverte/politique) - - ⚠️ **Modification bloquée si vitesse GPS >10 km/h** (sécurité routière) -- Live démarre d'un créateur suivi dans la zone - -**Implémentation** : -``` -Redis cache : - - Clé : user:{user_id}:queue - - Structure : [content_1, content_2, ..., content_5] - - Métadonnées : {last_lat, last_lon, computed_at, mode: "voiture"|"pieton"} - - TTL : 15 minutes - -Tracking GPS temps réel (mobile) : - - Vérification toutes les 1 seconde - - Calcul ETA vers points géolocalisés proches (rayon 500m) - - Si ETA ≤ 7s → trigger notification - - Historique GPS : 30 derniers points pour calcul vitesse moyenne - -Quota anti-spam (Redis) : - - Clé : user:{user_id}:geo_quota - - Structure : sorted set avec timestamps des 6 derniers contenus - - TTL : 1 heure - - Vérification avant notification : ZCOUNT pour compter contenus dernière heure - -Cooldown après ignorance (Redis) : - - Clé : user:{user_id}:geo_cooldown - - TTL : 10 minutes - - Set après notification ignorée -``` - -**Justification** : -- **Expérience fluide** : pas de latence au clic "Suivant" -- **Réactivité géo** : contenu local inséré immédiatement -- **Coût optimisé** : recalcul uniquement si nécessaire -- **Sécurité** : pas de modification en conduite - ---- - -### 5.1.2 Mode piéton (audio-guides) - -**Décision** : Notifications push en arrière-plan avec rayon large - -**Contexte** : -- Mode piéton détecté automatiquement si vitesse moyenne < 5 km/h -- Cas d'usage : visites à pied, musées, monuments, quartiers historiques -- User n'a pas besoin d'avoir l'app ouverte -- ⚠️ **Fonctionnalité optionnelle** : requiert permission "localisation en arrière-plan" (activée par user) - -**Détection** : -- App peut être en arrière-plan (si permission accordée) -- Rayon de détection : **200 mètres** autour du point GPS -- Geofencing iOS/Android pour minimiser consommation batterie -- Permission demandée uniquement si user active "Notifications audio-guides piéton" dans settings - -**Notification push système** : - -Format : -``` -Titre : "Audio-guide à proximité" -Body : "[Nom du contenu] - [Nom créateur]" -Action : Tap → ouvre app sur le contenu -``` - -Exemple : -``` -Audio-guide à proximité -Musée du Louvre : La Joconde - @paris_museum -``` - -**Permissions requises** : - -⚠️ **Important** : Permission "Always Location" est **optionnelle** et demandée uniquement si user active le mode piéton dans settings. - -iOS (`Info.plist`) : -```xml -NSLocationWhenInUseUsageDescription -RoadWave utilise votre position pour vous proposer des contenus audio géolocalisés adaptés à votre trajet en temps réel. - -NSLocationAlwaysAndWhenInUseUsageDescription -Si vous activez les notifications audio-guides piéton, RoadWave peut vous alerter lorsque vous passez près d'un monument ou musée, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée à tout moment dans les réglages. -``` - -Android (`AndroidManifest.xml`) : -```xml - - -``` - -**Disclosure avant demande permission** (Android requis, iOS recommandé) : - -Écran affiché avant demande permission "Always Location" : - -``` -┌────────────────────────────────────────┐ -│ 📍 Notifications audio-guides piéton │ -├────────────────────────────────────────┤ -│ Pour vous alerter d'audio-guides à │ -│ proximité même quand vous marchez avec │ -│ l'app fermée, RoadWave a besoin de │ -│ votre position en arrière-plan. │ -│ │ -│ Votre position sera utilisée pour : │ -│ ✅ Détecter monuments à 200m │ -│ ✅ Vous envoyer une notification │ -│ │ -│ Votre position ne sera jamais : │ -│ ❌ Vendue à des tiers │ -│ ❌ Utilisée pour de la publicité │ -│ │ -│ Cette fonctionnalité est optionnelle. │ -│ Vous pouvez utiliser RoadWave sans │ -│ cette permission. │ -│ │ -│ [Continuer] [Non merci] │ -│ │ -│ Plus d'infos : Politique confidentialité│ -└────────────────────────────────────────┘ -``` - -**Si user refuse** : -- Mode piéton désactivé (uniquement mode voiture disponible) -- App fonctionne normalement avec permission "When In Use" -- Audio-guides accessibles en mode manuel (user ouvre app, sélectionne contenu) - -**Comportement après tap sur notification** : -1. User tap notification push -2. App s'ouvre sur la page du contenu -3. User peut démarrer la lecture manuellement -4. Navigation libre (voir section 16.2 pour audio-guides piéton) - -**Basculement automatique voiture ↔ piéton** : - -Détection par vitesse GPS moyenne sur 30 secondes : -- Vitesse < 5 km/h (stable 10s) → mode piéton -- Vitesse ≥ 5 km/h (stable 10s) → mode voiture - -Changements de mode : - -| Mode actuel | Vitesse détectée | Nouveau mode | Effet | -|-------------|------------------|--------------|-------| -| Piéton | ≥ 5 km/h | Voiture | Notifications push → sonores + icône (app ouverte requise) | -| Voiture | < 5 km/h | Piéton | Notifications sonores → push arrière-plan | - -**Pas de popup confirmation** : -- Basculement transparent et automatique -- User n'a rien à faire -- Hysteresis (10s) pour éviter basculements intempestifs - -**Quota anti-spam mode piéton** : -- Même limitation que mode voiture : **6 contenus/heure** -- Cooldown 10 min si notification ignorée (app pas ouverte après tap) - -**Justification** : -- ✅ Expérience adaptée aux visites à pied (rayon large, pas de timing précis) -- ✅ Économie batterie (geofencing natif iOS/Android) -- ✅ User peut garder téléphone en poche -- ✅ Basculement automatique = pas de friction - ---- - -### 5.2 Commande "Précédent" - -**Décision** : Comportement smart selon progression écoute - -**Règles** : - -| Situation | Temps écouté | Action "Précédent" | -|-----------|--------------|-------------------| -| **Début de contenu** | <10 secondes | Retour au contenu précédent (position exacte) | -| **Milieu/fin** | ≥10 secondes | Replay contenu actuel depuis le début | -| **Premier de session** | N/A | Replay depuis début (rien avant) | - -**Historique de navigation** : -- **10 contenus maximum** en mémoire (Redis List) -- Structure : `[{content_id, position_seconds, listened_at}, ...]` -- FIFO : au-delà de 10, suppression du plus ancien - -**Exemple scénario** : -``` -Utilisateur écoute : -1. Contenu A → écoute 5s → "Suivant" -2. Contenu B → écoute 2min30 → "Suivant" -3. Contenu C → écoute 5s → "Précédent" - → Retour Contenu B à 2min30 (car >10s) -4. Sur Contenu B → "Précédent" - → Retour Contenu A à 5s (position exacte) -``` - -**Interface (responsabilité front)** : -- ❌ Pas de message UI -- ✅ Progress bar revient au début ou à position exacte -- ✅ Animation fluide (transition 0.3s) - -**Justification** : -- **UX intuitive** : comportement standard Spotify/YouTube -- **Pas de frustration** : si début, vraiment revenir en arrière -- **Simplicité** : règle unique (seuil 10s) - ---- - -### 5.3 Interactions au volant : Like automatique et engagement - -> ⚠️ **Architecture Decision Record** : Voir [ADR-010](#../adr/010-commandes-volant) pour les détails techniques complets - -**Décision** : Like automatique basé sur le temps d'écoute - -**Problème technique identifié** : -- iOS et Android ne supportent **pas nativement** les appuis longs ou doubles-appuis sur les commandes média -- Les commandes physiques au volant varient selon les véhicules (pas de bouton "Pause" dédié sur beaucoup de modèles) -- Système de double-appui/appui long = **non-intuitif** et **risques sécurité** (regarder écran pour feedback) - ---- - -#### Commandes au volant simplifiées - -**Actions disponibles** (100% compatibles tous véhicules) : - -| Commande physique | Action RoadWave | -|-------------------|-----------------| -| **Suivant** | Passer au contenu suivant | -| **Précédent** | Revenir au contenu précédent (règle 10s, voir section 5.2) | -| **Play/Pause** | Pause/reprise lecture (fade out 0.3s) | - -**Aucune action complexe au volant** → Sécurité routière maximale. - ---- - -#### Like automatique implicite - -**Principe** : Le système détecte automatiquement l'intérêt utilisateur selon le temps d'écoute. - -**Règles d'attribution** : - -| Durée écoutée | Action automatique | Points jauge | Justification | -|---------------|-------------------|--------------|---------------| -| **≥ 80% du contenu** | Like renforcé | +2.0 | Écoute quasi-complète = fort intérêt | -| **30-79% du contenu** | Like standard | +1.0 | Écoute significative = intérêt | -| **< 30% du contenu** | Pas de like | 0 | Écoute trop courte | -| **Skip après <10s** | Signal négatif | -0.5 | Désintérêt marqué | - -**Exemples concrets** : -``` -Contenu de 3 minutes (180s) : -- Écoute 2min30 (83%) → Like renforcé (+2 points) -- Écoute 1min15 (42%) → Like standard (+1 point) -- Écoute 30s (17%) puis skip → Pas de like -- Skip après 5s → Signal négatif (-0.5 point) - -Contenu de 15 minutes (900s) : -- Écoute 13min (87%) → Like renforcé (+2 points) -- Écoute 6min (40%) → Like standard (+1 point) -``` - ---- - -#### Actions complémentaires (app à l'arrêt) - -**Interface mobile** (véhicule arrêté uniquement) : - -| Action | Moyen | Effet | -|--------|-------|-------| -| **Like explicite** | Bouton cœur | +2 points jauge (même si déjà liké auto) | -| **Unlike** | Re-clic cœur (toggle) | -2 points jauge | -| **Abonnement** | Bouton "S'abonner" profil créateur | +5 points toutes jauges tags créateur | -| **Désabonnement** | Bouton "Se désabonner" | -5 points | -| **Signalement** | Menu contextuel "⋮" | Ouverture flux modération | - -**Feedback visuel** : -- **Like automatique** : Badge discret "♥ Ajouté à vos favoris" (2s, bas de l'écran) -- **Like explicite** : Animation cœur rouge + vibration courte -- **Abonnement** : Animation étoile dorée + badge "Abonné ✓" - ---- - -#### Commandes vocales (optionnel, si CarPlay/Android Auto) - -**Disponible uniquement avec** : -- Apple CarPlay (Siri) -- Android Auto (Google Assistant) -- ~30-40% du parc automobile EU (2026) - -**Exemples de commandes** : -``` -"Hey Siri, like ce podcast" -"OK Google, abonne-moi à ce créateur" -"Hey Siri, passe au contenu suivant" -"OK Google, signale ce contenu" -``` - -**Implémentation** : Intents iOS/Android personnalisés (Sprint 5, post-MVP) - ---- - -#### Gestion impacts jauges (algorithme) - -**Like automatique** : -- Like renforcé (≥80%) → **+2% jauges** de tous les tags du contenu -- Like standard (30-79%) → **+1% jauges** des tags du contenu -- Signal négatif (skip <10s) → **-0.5% jauges** des tags du contenu - -**Actions explicites** : -- Like manuel → **+2% jauges** (cumulable avec like auto) -- Unlike → **-2% jauges** -- Abonnement → **+5% toutes jauges** tags créateur -- Désabonnement → **-5% toutes jauges** - -**Persistance** : -- Événements stockés en base (table `listen_events`) -- Mise à jour jauges : **immédiate** (Redis) + **async batch** (PostgreSQL) - ---- - -#### Implémentation technique - -**Backend** (Go) : - -```go -type ListenEvent struct { - UserID string - ContentID string - StartedAt time.Time - StoppedAt time.Time - Duration int // secondes écoutées - ContentTotal int // durée totale contenu - Percentage float64 // duration / contentTotal * 100 - Action string // "completed", "skipped", "paused" -} - -func ProcessListenEvent(event ListenEvent) { - percentage := event.Percentage - - // Signal négatif fort - if event.Action == "skipped" && event.Duration < 10 { - UpdateJauges(event.UserID, event.ContentID, -0.5) - return - } - - // Like automatique - if percentage >= 80 { - AutoLike(event.UserID, event.ContentID, 2.0) // Renforcé - } else if percentage >= 30 { - AutoLike(event.UserID, event.ContentID, 1.0) // Standard - } - // < 30% : pas de like -} -``` - -**Mobile** (iOS/Android) : - -```swift -// iOS - Tracking écoute -class AudioPlayerManager { - var startTime: Date? - let contentDuration: TimeInterval - - func onPlay() { - startTime = Date() - } - - func onStop(action: String) { // "completed" | "skipped" | "paused" - guard let start = startTime else { return } - let duration = Date().timeIntervalSince(start) - let percentage = (duration / contentDuration) * 100 - - // API call - API.track(ListenEvent( - contentId: currentContentId, - duration: Int(duration), - percentage: percentage, - action: action - )) - } -} -``` - ---- - -#### Justification - -**Avantages** : -- ✅ **Sécurité routière maximale** : aucune action complexe au volant -- ✅ **UX intuitive** : comportement standard industrie (Spotify, YouTube Music, Deezer) -- ✅ **Compatibilité 100%** : fonctionne sur tous véhicules, tous OS -- ✅ **Engagement amélioré** : tous les contenus écoutés génèrent des signaux -- ✅ **Algorithme plus précis** : données granulaires (30%, 50%, 80%, 100%) -- ✅ **Simplicité développement** : pas de workarounds complexes iOS/Android - -**Inconvénients mitigés** : -- ⚠️ Pas de like explicite en conduite → **Mitigation** : like automatique + vocal (CarPlay/Android Auto) -- ⚠️ Pas d'abonnement en conduite → **Mitigation** : liste "Créateurs à découvrir" dans app -- ⚠️ Like automatique peut surprendre → **Mitigation** : onboarding clair + unlike possible - ---- - -#### Communication utilisateurs (onboarding) - -**Écran onboarding 1** : -``` -🚗 Conduite sécurisée - -RoadWave détecte automatiquement vos goûts -selon vos écoutes. - -Plus vous écoutez longtemps, plus -l'algorithme s'améliore ! - -[Suivant] -``` - -**Écran onboarding 2** : -``` -❤️ Likes automatiques - -Pas besoin de liker manuellement : -si vous écoutez >50% d'un contenu, -on comprend que vous aimez ! - -[Suivant] -``` - -**Écran onboarding 3** : -``` -⏸️ Commandes simples - -Utilisez les boutons au volant : -• Suivant → Prochain contenu -• Précédent → Contenu d'avant -• Pause → Mettre en pause - -[Commencer] -``` - ---- - -### 5.4 Lecture en boucle et enchaînement - -**Décision** : Passage automatique après 2s + insertion pub paramétrable - -**Fin de contenu** : -1. Audio termine → **Timer 2 secondes** démarre -2. UI overlay : "Contenu suivant dans 2s..." + barre décompte -3. Possibilité annuler : bouton "Rester sur ce contenu" (optionnel) -4. Timer atteint 0 → passage automatique au contenu suivant - -**Délai selon contexte** : - -| Mode | Délai | Justification | -|------|-------|---------------| -| **Standard** | 2 secondes | Temps réaction confortable | -| **Mode Kids** | 1 seconde | Attention courte enfants | -| **Live** | 0 seconde | Enchaînement immédiat | - -**Insertion publicité** : -- Pub s'insère **pendant le délai de 2s** (transition naturelle) -- Fréquence : **paramétrable admin** (défaut : 1 pub / 5 contenus) -- Message : "Publicité (15s)" puis lecture pub -- ⚠️ **Jamais d'interruption** d'un contenu en cours - -**Publicité skippable** : -- Durée minimale visionnage : **paramétrable** (défaut : 5 secondes) -- Bouton "Passer" apparaît après délai -- Métriques engagement : taux skip, durée écoute moyenne -- **Like et abonnement autorisés sur pub** (engagement créateur pub) - -**Si aucun contenu disponible** : -1. Message : "Aucun contenu disponible dans cette zone" -2. Proposition : "Élargir la zone de recherche ?" (bouton) -3. Si accepté → relance algo avec rayon +50km -4. Sinon → lecture en pause, attente action utilisateur - -**Gestion erreurs** : -- Échec chargement contenu suivant → **retry 3× avec backoff exponentiel** -- Si 3 échecs → message "Connexion instable, basculement mode offline" -- Mode offline → lecture contenus téléchargés uniquement - -**Justification** : -- **Fluidité** : enchaînement naturel sans action utilisateur -- **Contrôle** : possibilité annuler pendant délai -- **Paramétrabilité pub** : évite frustration excès publicité -- **Engagement pub** : like/abonnement autorisé = monétisation créateurs pub - ---- - -## Récapitulatif Section 5 - - - - -
- - -## 6. Publicités - -### 6.1 Système de campagnes publicitaires - -**Décision** : Interface self-service avec maîtrise budget et métriques détaillées - -**Fonctionnalités publicitaire** : - -#### Création de campagne - -**Paramètres configurables** : - -| Paramètre | Options | Justification | -|-----------|---------|---------------| -| **Budget total** | Montant libre (min 50€) | Maîtrise coût total | -| **Durée campagne** | Date début/fin + étalement | Ex: 300€ sur 2 semaines | -| **Ciblage géographique** | Point GPS / Ville / Département / Région / National | Précision selon besoin | -| **Ciblage horaire** | Plages horaires (ex: 7h-9h, 17h-19h) | Optimisation trajet domicile-travail | -| **Centres d'intérêt** | Tags (ex: Automobile, Voyage) | Ciblage thématique | -| **Tranche d'âge** | Tout public / 13+ / 16+ / 18+ | Respect classifications | - -**Étalement budget** : -``` -Exemple campagne : -- Budget : 300€ -- Durée : 14 jours -- Zone : Département du Var -- Horaires : 7h-9h + 17h-19h (rush) - -Calcul automatique : -→ Budget/jour = 300€ / 14 = 21.43€/jour -→ Diffusions/jour estimées : ~430 (0.05€/écoute) -→ Alerte si budget épuisé avant fin (réajustement possible) -``` - -**Mode de paiement** : -- ✅ Prépaiement obligatoire (évite impayés) -- ✅ Carte bancaire uniquement (Mangopay) -- ✅ Recharge automatique optionnelle (si budget <10%) - -#### Validation et modération - -**Processus** : -1. Publicitaire upload audio pub (formats : MP3, AAC) -2. **Validation manuelle obligatoire** (modérateur RoadWave) - - Délai : 24-48h ouvrées - - Critères : respect réglementation, qualité audio, classification correcte -3. Si accepté → campagne démarre à la date choisie -4. Si refusé → email avec raison + remboursement automatique - -**Contenus interdits en pub** : -- ❌ Alcool, tabac (réglementation française) -- ❌ Jeux d'argent -- ❌ Contenu politique (pendant campagnes électorales) -- ❌ Contenu sexuel ou violence -- ✅ Tous commerces/services légaux - -#### Dashboard métriques engagement - -**Indicateurs temps réel** : - -| Métrique | Description | Utilité | -|----------|-------------|---------| -| **Impressions** | Nombre de diffusions | Volume exposition | -| **Écoutes complètes** | Pub écoutée >80% | Engagement réel | -| **Taux de skip** | % skip après délai min | Qualité contenu | -| **Durée moyenne écoute** | Secondes écoutées | Rétention attention | -| **Likes** | Nombre de likes | Appréciation contenu | -| **Abonnements** | Abonnements au créateur pub | Conversion forte | -| **Coût par écoute** | Budget / écoutes complètes | ROI campagne | -| **Répartition géographique** | Heatmap diffusions | Validation ciblage | -| **Répartition horaire** | Graphique par heure | Optimisation horaires | - -**Métriques engagement avancées** : -- **Taux complétion par tranche d'âge** : identifier audience réceptive -- **Carte de chaleur GPS** : visualiser zones forte écoute -- **Comparatif campagnes** : A/B testing créatifs publicitaires - -**Export données** : -- ✅ CSV/Excel pour analyse externe -- ✅ Graphiques interactifs (Chart.js) -- ✅ Rapport PDF automatique fin de campagne - -#### Gestion budget et alertes - -**Suivi temps réel** : -- Dashboard : Budget restant, % consommé, jours restants -- Projection : "À ce rythme, budget épuisé dans X jours" -- Alerte email/push si : - - Budget consommé à 80% - - Budget consommé à 90% - - Budget épuisé - - Campagne terminée (rapport final) - -**Ajustements en cours** : -- ✅ Pause campagne (budget conservé) -- ✅ Prolonger campagne (recharge budget) -- ✅ Modifier ciblage horaire/géo (si <50% budget consommé) -- ❌ Modifier audio (nécessite nouvelle validation) - -#### Système d'enchères (post-MVP) - -**Optionnel future** : -- Enchère au CPM (coût pour 1000 impressions) -- Priorité selon prix : pub prix élevé → diffusion privilégiée -- Floor price : 2€ CPM minimum -- Évite surcharge pub : max 1 pub / 5 contenus stricte - -**Justification décision MVP** : -- Tarif fixe simple : 0.05€/écoute complète -- Pas de complexité enchères immédiatement -- Scalable : passage enchères ultérieur si demande forte - ---- - -### 6.2 Insertion et fréquence - -**Décision** : Paramétrable admin + respect expérience utilisateur - -**Fréquence d'insertion** : -- **Défaut : 1 pub / 5 contenus** (utilisateurs gratuits) -- **Paramétrable admin** : curseur 1/3 à 1/10 -- **Utilisateurs Premium** : 0 pub (modèle sans publicité) - -**Règles strictes** : -- ⚠️ **Jamais d'interruption** contenu en cours -- Pub s'insère uniquement **entre deux contenus** (pendant délai 2s) -- Rotation : même pub max **3 fois/jour** par utilisateur (évite saturation) -- Limite : max **6 pubs/heure** par utilisateur (évite spam) - -**Ciblage intelligent** : -- Géolocalisation prioritaire (point GPS > ville > département > région > national) -- Centres d'intérêt secondaires (tags utilisateur) -- Horaire (campagne 7h-9h → diffusion uniquement pendant plage) - -**Volume audio normalisé** : -- Pub normalisée à **-14 LUFS** (standard broadcast) -- Évite effet "pub trop forte" (frustration utilisateur) -- Validation automatique via FFmpeg lors encodage - ---- - -### 6.3 Caractéristiques publicités - -**Durée** : -- Minimum : **10 secondes** -- Maximum : **60 secondes** -- Recommandé : **15-30 secondes** (sweet spot engagement) - -**Skippable** : -- Délai minimum obligatoire : **5 secondes** (paramétrable admin : 3-10s) -- Bouton "Passer la publicité" apparaît après délai -- Durée minimale comptabilisée pour facturation - -**Facturation** : -- **Écoute complète** (>80%) : 0.05€ facturé publicitaire -- **Skip après délai min** : 0.02€ (exposition partielle) -- **Skip immédiat** (<5s) : 0€ (pas d'engagement) - -**Justification modèle tarif** : -- Incitatif qualité : pub engageante = coût réduit -- Équitable : publicitaire paie pour attention réelle -- Transparent : dashboard montre écoutes complètes vs skips - ---- - -## Récapitulatif Section 6 - - - - -
- - -## 7. Radio live - -### 7.1 Démarrage d'un live - -**Décision** : Buffer 15s + notification abonnés + limite 8h - -**Processus de démarrage** : - -1. Créateur appuie "Démarrer live" dans l'app -2. **Vérification pré-live** : - - Connexion ≥1 Mbps upload (warning si insuffisant) - - Micro autorisé - - Zone diffusion déjà définie (ville, département, région, national) -3. **Buffer initial 15 secondes** avant diffusion publique - - Créateur parle pendant 15s → accumulation buffer serveur - - Message créateur : "Live démarre dans 15s... Testez votre micro" - - Permet vérifier qualité audio avant diffusion -4. Après 15s → **Live public**, auditeurs peuvent rejoindre - -**Notification abonnés** : -- ✅ **Push notification immédiate** à tous les abonnés dans la zone géographique -- Message : "🔴 [Nom créateur] est en direct : [Titre live]" -- Tap notification → ouverture app + lecture live immédiate -- **Filtrage géographique** : si abonné hors zone, pas de notif (évite frustration) - -**Limite de durée** : -- **Maximum 8 heures** par session live -- Warning créateur à 7h30 : "Votre live se terminera dans 30 min" -- Si besoin continuer → arrêt + redémarrage nouveau live (évite abus ressources serveur) - -**Métadonnées obligatoires** : - -| Champ | Format | Validation | -|-------|--------|------------| -| **Titre** | 5-100 caractères | Ex: "Discussion politique en direct" | -| **Tags** | 1-3 centres d'intérêt | Sélection liste prédéfinie | -| **Classification âge** | Enum | Tout public / 13+ / 16+ / 18+ | -| **Zone diffusion** | Geo | Ville / Département / Région / National | - -**Contenus interdits en live** : - -| Type | Description | Sanction | -|------|-------------|----------| -| **Concert/spectacle** | Diffusion concert en direct depuis la salle | Strike 2 immédiat + ban temporaire | -| **Événement sportif payant** | Match, compétition avec droits TV | Strike 2 immédiat | -| **Œuvre protégée** | Film, série, musique en fond sans droits | Strike 1 + suppression live | -| **Contenu violent** | Agression, violence physique | Ban immédiat | -| **Contenu illégal** | Apologie terrorisme, pédopornographie | Ban définitif + signalement autorités | - -**Exemple usecase interdit** : -``` -❌ Utilisateur dans salle de concert diffuse live performance -→ Violation droits d'auteur + droits de diffusion -→ Détection : modération réactive (signalements) + IA audio fingerprint -→ Sanction : Strike 2 (suspension 7 jours) + suppression live + suppression replay -``` - -**Détection violations** : -- **Signalement utilisateurs** : bouton "Signaler" accessible pendant live -- **IA audio fingerprint** : détection musique protégée en arrière-plan (post-MVP) -- **Modération réactive** : modérateurs peuvent écouter lives signalés en temps réel -- **Coupure immédiate** : modérateur peut arrêter live si contenu illégal évident - -**Justification** : -- **Buffer 15s** : équilibre entre test qualité et friction minimale -- **Notification abonnés** : engagement maximal, valeur ajoutée live -- **8h max** : couvre 99% cas usage (podcasts longs, émissions radio) sans abus -- **Interdictions strictes** : protection juridique plateforme (DSA EU, droits d'auteur) -- **Coût** : WebRTC ingestion + HLS distribution (réutilise infra existante) - ---- - -### 7.2 Arrêt du live - -**Décision** : Compte à rebours 5s + tolérance déconnexion 60s + enregistrement auto - -**Fin manuelle créateur** : - -1. Créateur appuie "Arrêter live" -2. **Compte à rebours 5 secondes** affiché - - Message audio : "Ce live se termine dans 5... 4... 3... 2... 1" - - Permet au créateur de faire un outro propre - - Annulable pendant décompte (bouton "Annuler") -3. Timer atteint 0 → arrêt diffusion -4. **Traitement post-live automatique** démarre (voir ci-dessous) - -**Fin automatique si déconnexion** : - -| Durée coupure | Comportement | -|---------------|--------------| -| **<60 secondes** | Message auditeurs : "Connexion créateur perdue, reconnexion en cours..." | -| **≥60 secondes** | Arrêt automatique live + message : "Le live est terminé suite à une coupure de connexion" | - -**Enregistrement automatique** : - -✅ **Obligatoire et automatique** (valeur ajoutée énorme) - -**Processus** : -1. Pendant live : enregistrement continu serveur (format Opus raw) -2. Fin live → **job asynchrone** (worker Go + FFmpeg) : - - Conversion MP3 256 kbps (qualité optimale) - - Génération segments HLS (comme contenu classique) - - Normalisation volume -14 LUFS - - Détection silences prolongés (nettoyage) -3. **Publication automatique** du replay : - - Titre : "[REPLAY] [Titre live original]" - - Même zone diffusion, tags, classification - - Disponible sous **5-10 minutes** après fin live - - Type géo : automatiquement "Géo-neutre" (replay = contenu pérenne) - -**Options créateur** : - -| Option | Défaut | Description | -|--------|--------|-------------| -| **Publier replay automatiquement** | ✅ OUI | Désactivable avant démarrage live | -| **Supprimer replay après coup** | ✅ Possible | Suppression standard contenu | -| **Modifier replay** | ❌ Non | Intégrité enregistrement | - -**Conservation fichier source** : -- Opus raw conservé **7 jours** après fin live (backup) -- Suppression automatique après 7j (économie stockage) -- Si replay supprimé par créateur → fichier raw supprimé immédiatement - -**Justification** : -- **Compte à rebours 5s** : outro propre, pas de coupure brutale -- **Tolérance 60s** : évite arrêts intempestifs (tunnel, changement cellule) -- **Enregistrement auto** : valorisation contenu éphémère, génération contenu pérenne -- **MP3 256 kbps** : qualité optimale pour replay (vs 48 kbps live) -- **Coût** : stockage minimal (Opus → MP3 1× par live, puis suppression raw après 7j) - ---- - -### 7.3 Comportement auditeur - -**Décision** : Buffer 15s + continuation hors zone + reconnexion au live actuel + écoute passive uniquement - -**Buffer de synchronisation** : - -- **15 secondes** entre créateur et auditeurs -- Raisons : - - Stabilité réseau mobile (3G/4G fluctuant) - - Synchronisation approximative acceptable (pas besoin temps réel strict) - - Permet buffering anticiper coupures courtes (tunnels) - -**Comparaison buffers** : - -| Buffer | Avantages | Inconvénients | Décision | -|--------|-----------|---------------|----------| -| 5s | Quasi temps réel | Instable 3G, coupures fréquentes | ❌ | -| 10s | Bon compromis | Légèrement juste pour 3G | ❌ | -| **15s** | **Stabilité optimale 3G/4G** | Léger décalage acceptable | ✅ | -| 20s+ | Très stable | Décalage trop perceptible | ❌ | - -**Zone géographique pendant live** : - -- ✅ **Continuation si sortie de zone** -- Scénario : auditeur écoute live régional → sort du département → **live continue** -- Raisons : - - Pas de coupure brutale (mauvaise UX) - - Écoute engagée = terminer naturellement - - Après fin live → algo normal (pas de contenus hors zone) - -**Reconnexion après coupure réseau** : - -| Durée coupure | Comportement | -|---------------|--------------| -| **<90 secondes** | Reprend au live actuel (pas au buffer ancien) + saut temporel transparent | -| **≥90 secondes** | Message : "Live en cours perdu, passage au contenu suivant" + algo propose contenu normal | - -**Interactions disponibles** : - -**Décision ferme** : ❌ **Aucun chat en direct, ni maintenant ni dans le futur** - -**Raisons** : -- **Sécurité routière** : pas de distraction en voiture (focus UX) -- **Harcèlement** : évite contenu haineux, insultes, trolling -- **Modération** : pas de coût modération temps réel (impossible à scale) -- **Simplicité** : écoute passive = expérience uniforme - -**Actions autorisées pendant live** : - -| Action | Disponible | Effet | -|--------|------------|-------| -| **Like** | ✅ | Bouton cœur interface mobile (véhicule arrêté) | -| **Abonnement créateur** | ✅ | Bouton profil créateur (interface mobile) | -| **Skip** | ✅ | Passe au contenu suivant, sort du live | -| **Précédent** | ❌ | Pas de sens sur live (flux temps réel) | -| **Chat** | ❌ | Jamais implémenté (décision définitive) | -| **Réactions emoji** | ❌ | Jamais implémenté (décision définitive) | - -**Messages utilisateur** : -- "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement." - -**Justification décision définitive** : -- **UX cohérente** : RoadWave = écoute en conduisant, pas réseau social interactif -- **Bien-être** : évite toxicité, harcèlement, haine (fléau réseaux sociaux) -- **Juridique** : pas de risque contentieux modération chat (DSA EU) -- **Coût** : 0€ infra chat, 0€ modération temps réel -- **Différenciation** : positionnement "audio safe" vs plateformes toxiques - ---- - -### 7.4 Architecture technique - -**Stack** : - -``` -Créateur (App mobile) - ↓ WebRTC (OPUS 48 kbps) -Serveur Ingestion (Go + Pion WebRTC) - ↓ Conversion temps réel -Serveur HLS (segments .ts) - ↓ CDN (Bunny) -Auditeurs (App mobile, HLS natif) -``` - -**Flux détaillé** : -1. **Créateur** → WebRTC OPUS 48 kbps vers serveur Go -2. **Serveur Go** → Conversion temps réel OPUS → segments HLS (.m3u8 + .ts) -3. **Bunny CDN** → Distribution HLS avec cache -4. **Auditeurs** → Lecture HLS native iOS/Android (buffer 15s) -5. **Enregistrement parallèle** → Opus raw stocké temporairement -6. **Post-live** → Job async : Opus → MP3 256 kbps → Publication replay - -**Dépendances** : -- ✅ **Pion WebRTC** (Go library, open source, MIT license) -- ✅ **FFmpeg** (conversion audio, LGPL/GPL) -- ✅ **Bunny CDN** (distribution HLS, pas Google/Cloudflare) -- ✅ **PostgreSQL + Redis** (métadonnées live + cache) - -**Avantages** : -- ✅ Pas de dépendance Google/Facebook/Cloudflare (souveraineté) -- ✅ WebRTC standard ouvert (Pion = lib Go pure) -- ✅ Réutilise infra HLS existante (pas de doublon) -- ✅ CDN cache les segments (coût réduit) -- ✅ Scalable horizontalement (workers Go) - -**Coût estimé** : - -| Phase | Utilisateurs | Infra live | Coût/mois | -|-------|--------------|------------|-----------| -| **MVP** | 0-100K | 1 instance Go (ingestion 100 lives simultanés) | +50€ (serveur) + bande passante CDN | -| **Growth** | 100K-1M | 3-5 instances Go (500 lives simultanés) | +200€ + bande passante | -| **Scale** | 1M-10M | Kubernetes auto-scale (2000+ lives) | +1K€ + bande passante | - -**Bande passante** : -- Live : 48 kbps × nb_auditeurs (via CDN, cache segments) -- Exemple : 100 auditeurs = 4.8 Mbps = ~2 Go/heure via CDN -- Coût Bunny : ~0.01€/GB = 0.02€/heure pour 100 auditeurs - ---- - -## Récapitulatif Section 7 - - - - -
- - -## 8. Abonnements et notifications - -### 8.1 Impact sur l'algorithme - -**Décision** : Boost +30% au score + reste dans le mix - -**Boost de score abonnements** : -- **+30% au score final** pour contenus d'un créateur suivi -- Application : multiplicateur sur le score calculé - -``` -score_final_avec_boost = score_final × 1.3 -``` - -**Reste dans le mix** : -- ❌ **Pas de priorité absolue** (pas de file dédiée abonnements) -- ✅ Contenu suivi entre en **compétition avec autres contenus** -- ✅ Si créateur suivi publie contenu faible engagement → peut être battu par contenu viral non-suivi - -**Exemple concret** : -``` -Utilisateur à Paris, 2 contenus disponibles : - -Contenu A (créateur NON suivi) : -- Score géo : 0.9 (très proche) -- Score intérêts : 0.8 -- Score engagement : 0.7 -→ Score final : 0.80 - -Contenu B (créateur suivi) : -- Score géo : 0.5 (moyennement proche) -- Score intérêts : 0.6 -- Score engagement : 0.5 -→ Score final : 0.53 -→ Score avec boost : 0.53 × 1.3 = 0.69 - -→ Contenu A proposé en premier (0.80 > 0.69) -``` - -**Cas où abonnement fait la différence** : -``` -Contenu A (non suivi) : score 0.70 -Contenu B (suivi) : score 0.60 → avec boost 0.78 -→ Contenu B proposé (boost fait pencher la balance) -``` - -**Justification** : -- **Équilibre** : valorise abonnements sans enfermer utilisateur -- **Découverte** : contenus viraux/locaux peuvent toujours émerger -- **Prévisible** : boost fixe, pas de logique opaque -- **Coût 0** : multiplicateur simple dans l'algo - ---- - -### 8.2 Notifications contextuelles - -**Décision** : Push adapté selon contexte (voiture vs à pied) + limite 10/jour - -**Détection contexte utilisateur** : - -| Contexte | Détection | Comportement | -|----------|-----------|--------------| -| **En voiture** | Vitesse GPS >10 km/h | Notifications silencieuses (in-app uniquement) + commandes volant | -| **À pied** | Vitesse GPS <5 km/h | Notifications push actives + interface tactile/vocale | - -**Notifications activées** : - -#### En voiture (mode conduite) - -| Événement | Notification | Comportement | -|-----------|--------------|--------------| -| **Nouveau contenu créateur suivi** | In-app uniquement | Badge compteur, pas de push (sécurité) | -| **Live créateur suivi** | In-app uniquement | Badge compteur, pas de push | -| **Point d'intérêt proche** | Audio notification | Bip + annonce vocale : "Audio-guide disponible" | - -#### À pied (mode piéton) - -| Événement | Notification | Comportement | -|-----------|--------------|--------------| -| **Nouveau contenu créateur suivi** | ✅ Push | Si utilisateur dans zone géo du contenu | -| **Live créateur suivi** | ✅ Push | Si utilisateur dans zone géo | -| **Audio-guide disponible** | ✅ Push | "📍 Audio-guide disponible : [Lieu]" | -| **Séquence suivante suggérée** | Audio notification | Annonce vocale : "Pièce suivante disponible" | - -**Format notifications** : - -**Nouveau contenu** : -``` -🎧 [Nom créateur] a publié : "[Titre contenu]" -Tap pour écouter -``` - -**Live en direct** : -``` -🔴 [Nom créateur] est en direct : "[Titre live]" -Tap pour rejoindre -``` - -**Audio-guide à pied** : -``` -📍 Audio-guide disponible : [Nom du lieu] -Choisissez parmi 3 guides pour [Musée du Louvre] -Tap pour explorer -``` - -**Filtrage géographique** : -- Si contenu/live hors zone utilisateur → **pas de notification** -- Évite frustration : "notification pour contenu que je ne peux pas écouter" -- Exception : contenu national → notifie tous les abonnés - -**Fréquence maximale** : -- **Maximum 10 notifications push/jour** par utilisateur (tous types confondus) -- Si dépassement : notifications regroupées -- Message groupé : "🎧 3 nouveaux contenus de créateurs suivis" - -**Plages horaires** : -- **Mode silencieux** : 22h-8h (pas de push, sauf live) -- Paramétrable utilisateur (désactivation totale possible) -- Option "Notifications importantes uniquement" (lives uniquement) - -**Gestion préférences** : - -| Préférence | Défaut | Description | -|------------|--------|-------------| -| **Nouveaux contenus** | ✅ Activé | Push à chaque nouveau contenu (à pied uniquement) | -| **Lives** | ✅ Activé | Push au démarrage live (à pied uniquement) | -| **Audio-guides proximité** | ✅ Activé | Push quand audio-guide détecté à <100m | -| **Mode silencieux** | ✅ Activé (22h-8h) | Pas de push nocturne | -| **Limite quotidienne** | 10 | Modifiable 5-20 | - -**Justification** : -- **Sécurité routière** : pas de push en conduite (distraction) -- **Engagement piéton** : push actifs pour audio-guides (valeur ajoutée tourisme) -- **Pas de spam** : limite 10/jour + mode silencieux -- **Filtrage géo** : pertinence maximale (pas de notif inutiles) -- **Coût** : Firebase Cloud Messaging (gratuit jusqu'à volume élevé) - ---- - -### 8.3 Mode Audio-guide (piéton) - -**Décision** : Navigation manuelle multiséquence + choix parmi plusieurs guides - -**Fonctionnement** : - -#### Détection et proposition - -1. Utilisateur à pied (<5 km/h) passe à <**100m** d'un lieu avec audio-guides -2. **Notification push** : "📍 Audio-guide disponible : [Musée du Louvre]" -3. Tap notification → **Page de sélection** audio-guides - -#### Page de sélection - -**Affichage** : -``` -📍 Musée du Louvre - -Choisissez votre guide : - -┌─────────────────────────────────┐ -│ 🎨 Visite complète (45 min) │ -│ Par [Créateur A] • 12 séquences│ -│ ⭐ 4.8 • 1.2K écoutes │ -└─────────────────────────────────┘ - -┌─────────────────────────────────┐ -│ 🏛️ Œuvres majeures (20 min) │ -│ Par [Créateur B] • 5 séquences │ -│ ⭐ 4.9 • 3.5K écoutes │ -└─────────────────────────────────┘ - -┌─────────────────────────────────┐ -│ 👶 Visite famille (30 min) │ -│ Par [Créateur C] • 8 séquences │ -│ ⭐ 4.7 • 850 écoutes │ -└─────────────────────────────────┘ -``` - -#### Interface audio-guide - -**Après sélection** : -``` -🎨 Visite complète • Musée du Louvre - -Piste actuelle : 2/12 -"La Joconde - Histoire et mystères" -[████████────────────] 3:24 / 6:50 - -Liste des séquences : -✅ 1. Introduction et architecture -▶️ 2. La Joconde - Histoire et mystères -⏸️ 3. Vénus de Milo -⏸️ 4. Victoire de Samothrace -⏸️ 5. Peintures Renaissance -... -⏸️ 12. Conclusion et boutique -``` - -**Navigation** : - -| Action | Geste | Effet | -|--------|-------|-------| -| **Séquence suivante** | Tap "Suivant" ou commande vocale "Suivant" | Passe à séquence N+1 | -| **Séquence précédente** | Tap "Précédent" ou commande vocale "Précédent" | Revient à séquence N-1 | -| **Saut direct** | Tap séquence dans liste | Lecture séquence choisie | -| **Pause** | Tap bouton pause | Met en pause, reprise position exacte | -| **Quitter** | Tap "×" | Sauvegarde progression, sortie guide | - -**Guidage vocal automatique** : -- Entre 2 séquences : "Vous avez terminé la séquence 2. Dirigez-vous vers la Vénus de Milo pour la séquence 3." -- Si utilisateur s'éloigne (>50m de la prochaine pièce) : "Vous vous éloignez de la prochaine étape. Consultez le plan." - -**Sauvegarde progression** : -- Position dans guide sauvegardée automatiquement -- Retour ultérieur : "Reprendre à la séquence 5 ?" ou "Recommencer depuis le début" -- Historique : guide marqué "Terminé" si toutes séquences écoutées - -**Création audio-guide multiséquence** : - -**Processus créateur** : -1. Créateur upload **plusieurs fichiers audio** (1 par séquence) -2. Numérote les séquences : "Séquence 1", "Séquence 2", etc. -3. Titre chaque séquence : "Introduction", "La Joconde", etc. -4. Définit **point GPS unique** pour tout le guide (centre du lieu) -5. Métadonnées : durée totale calculée automatiquement - -**Format stockage** : -```json -{ - "guide_id": "abc123", - "title": "Visite complète Musée du Louvre", - "location": {"lat": 48.8606, "lon": 2.3376, "radius": 200}, - "sequences": [ - { - "sequence_number": 1, - "title": "Introduction et architecture", - "audio_url": "https://cdn.../seq1.mp3", - "duration_seconds": 180 - }, - { - "sequence_number": 2, - "title": "La Joconde - Histoire et mystères", - "audio_url": "https://cdn.../seq2.mp3", - "duration_seconds": 410 - }, - ... - ], - "total_duration_seconds": 2700, - "creator_id": "creator_xyz" -} -``` - -**Justification** : -- **UX piéton** : navigation tactile adaptée (pas de commandes volant) -- **Autonomie** : utilisateur maître de son rythme (pas d'enchaînement forcé) -- **Choix** : plusieurs guides = diversité styles (famille, expert, rapide) -- **Engagement** : sauvegarde progression = incitation terminer -- **Coût** : réutilise infra contenu standard (juste métadonnées séquences) - ---- - -### 8.4 Limites et désabonnement - -**Décision** : 200 abonnements max + désabonnement -5% jauges - -**Nombre maximum d'abonnements** : -- **200 créateurs maximum** par utilisateur -- Raisons : - - **Évite spam** : au-delà de 200, notifications ingérables - - **Usage réaliste** : 200 créateurs = déjà énorme (vs 100-150 sur YouTube/Twitter) - - **Performance** : requêtes SQL optimisées (index sur 200 max) - -**Si limite atteinte** : -- Message : "Vous suivez déjà 200 créateurs. Désabonnez-vous d'un créateur pour en suivre un nouveau." -- Liste triable : par date abonnement, nb contenus écoutés, dernière activité -- Suggestion : "Vous n'avez pas écouté [Créateur X] depuis 6 mois, le désabonner ?" - -**Abonnement initial** : -- Impact : **+5% toutes jauges tags du créateur** (défini en [ADR-010](#../adr/010-commandes-volant)) -- Action : Bouton "S'abonner" dans profil créateur (interface mobile) -- Immédiat à l'action - -**Désabonnement** : -- Impact : **-5% toutes jauges tags du créateur** (symétrique) -- Action : Bouton "Se désabonner" dans profil créateur -- Immédiat à l'action -- Pas de confirmation (action réversible) - -**Exemple** : -``` -Créateur tague ses contenus : Automobile, Voyage - -Abonnement : -→ Jauge Automobile : 60% → 65% (+5%) -→ Jauge Voyage : 55% → 60% (+5%) - -3 mois plus tard, désabonnement : -→ Jauge Automobile : 65% → 60% (-5%) -→ Jauge Voyage : 60% → 55% (-5%) -``` - -**Gestion multi-tags** : -- Si créateur a 3 tags → **+5% sur chacun des 3 tags** -- Logique : abonnement = signal fort d'affinité à TOUS les sujets du créateur - -**Abonnements réciproques** : -- ❌ **Pas d'abonnement mutuel visible** -- Créateur ne voit pas qui est abonné (privacy) -- Créateur voit uniquement : nombre total abonnés (métrique globale) - -**Justification** : -- **Limite 200** : équilibre entre liberté et gestion spam -- **Symétrie +5%/-5%** : cohérence mathématique, prévisibilité -- **Privacy** : pas de liste publique abonnés (évite stalking) -- **Coût** : table abonnements PostgreSQL standard - ---- - -## Récapitulatif Section 8 - - - - -
- - -## 9. Monétisation créateurs - -### 9.1 Pourboires - -**Décision** : ❌ Fonctionnalité abandonnée pour le MVP - -**Raisons** : -- Complexité juridique (collecte pour compte de tiers, TVA variable) -- Frais de transaction élevés sur petits montants (Mangopay ~1.8% + 0.18€) -- UX additionnelle à développer (wallet, transactions, confirmations) -- Charge comptable importante pour la plateforme - -**Post-MVP** : Possible réintégration avec crypto (Bitcoin/Lightning Network) si législation UE l'autorise clairement (régulation MiCA en cours). - ---- - -### 9.2 Conditions d'activation de la monétisation - -**Décision** : 5 critères cumulatifs obligatoires - -| Critère | Seuil | Justification | -|---------|-------|---------------| -| **Ancienneté** | Compte créé depuis ≥ 3 mois | Anti-fraude : temps de détecter comportements suspects | -| **Popularité** | ≥ 500 abonnés | Garantit audience réelle et engagée | -| **Engagement** | ≥ 10 000 écoutes complètes cumulées | Créateurs produisant du contenu de qualité | -| **Fiabilité** | Aucun strike actif, 0 contenu modéré dans les 6 derniers mois | Historique propre requis | -| **Régularité** | ≥ 5 contenus publiés dans les 90 derniers jours | Activité constante | - -**Vérification** : Automatique via requêtes SQL lors de la demande d'activation - -**Affichage** : -- Bouton "Demander la monétisation" dans profil créateur -- Si critères non remplis → affichage progression vers objectifs -- Si critères remplis → redirection vers KYC Mangopay - -**Justification** : -- **Anti-fraude** : Le délai de 3 mois permet de détecter les comptes suspects -- **Qualité** : Seuls les créateurs sérieux avec audience réelle sont monétisés -- **Coût administratif** : Réduit le nombre de comptes à gérer (KYC, comptabilité, virements) -- **Légitimité** : Audience organique prouvée - ---- - -### 9.3 KYC (Know Your Customer) et inscription - -**Décision** : Statut juridique professionnel obligatoire - -**Statuts acceptés** : -- Auto-entrepreneur (micro-BNC pour artistes/créateurs de contenu) -- SARL/SAS/SASU (sociétés) - -**Documents requis** : - -| Document | Obligatoire | Format | Validité | -|----------|-------------|--------|----------| -| **SIRET** | ✅ | 14 chiffres | Permanent | -| **RIB professionnel** | ✅ | IBAN FR | Permanent | -| **Pièce d'identité** | ✅ | CNI/Passeport | En cours de validité | -| **Numéro TVA intracommunautaire** | ⚠️ Si applicable | FR + 11 chiffres | Permanent | -| **Kbis <3 mois** | ⚠️ Si société | PDF | <3 mois | - -**Vérification** : Via Mangopay (KYC intégré + vérification bancaire) - -**Délai** : 24-72h si documents conformes - -**Rejet possible si** : -- Documents invalides/illisibles -- Identité ne correspond pas au compte RoadWave -- Liste noire anti-blanchiment (vérification automatique Mangopay) -- RIB non professionnel (particulier) - -**Base légale** : -- **Conformité fiscale** : L'État français impose déclaration revenus >1200€/an (DAS2) -- **Anti-blanchiment** : Directive EU 2018/843 (5ème directive LCB-FT) -- **RGPD** : Données hébergées EU via Mangopay (conforme) - -**Justification** : -- **Responsabilité légale** : RoadWave doit pouvoir prouver identité réelle créateurs monétisés -- **Automatisation** : Mangopay gère tout (KYC, vérifications, conformité, e-wallets) -- **KYC gratuit** : inclus dans l'offre Mangopay (vs 1.20€ chez Stripe) -- **Souveraineté EU** : Mangopay est européen (France/Luxembourg), régulé ACPR - ---- - -### 9.4 Sources de revenus créateurs - -#### A) Publicités (utilisateurs gratuits) - -**Formule** : **3€ / 1000 écoutes complètes** (CPM créateur) - -**Répartition économique** : - -``` -Publicité facturée par RoadWave : 0.05€/écoute complète = 50€ CPM -├─ Créateur touche : 3€ (6% du CA pub) -└─ Plateforme garde : 47€ (94%) - ├─ CDN + infrastructure : ~10-15€ - ├─ Modération + support : ~5-10€ - ├─ Développement + R&D : ~10-15€ - └─ Marge opérationnelle : ~10-15€ -``` - -**Exemple concret** : -- 10 000 écoutes/mois → créateur touche **30€** -- 50 000 écoutes/mois → créateur touche **150€** -- 100 000 écoutes/mois → créateur touche **300€** - -**Comparaison industrie** : -- YouTube : 3-5€/1000 vues -- Spotify : 3-4€/1000 écoutes -- RoadWave : 3€/1000 écoutes (aligné) - -**Règles comptabilisation** : -- ✅ Écoute complète = ≥80% du contenu écouté -- ✅ Utilisateur gratuit uniquement -- ❌ Écoutes Premium ne comptent pas ici (autre système) -- ❌ Bots détectés exclus (rate limiting + analyse patterns) - ---- - -#### B) Abonnés Premium - -**Formule** : **70% au créateur, 30% à la plateforme** - -**Répartition proportionnelle au temps d'écoute effectif** : - -``` -Utilisateur Premium = 4.99€/mois -├─ 3.49€ reversés aux créateurs (70%) -└─ 1.50€ gardés par plateforme (30%) - -Si l'utilisateur écoute 3 créateurs ce mois : -- Créateur A : 10h d'écoute (50%) → 1.75€ -- Créateur B : 6h d'écoute (30%) → 1.05€ -- Créateur C : 4h d'écoute (20%) → 0.70€ -``` - -**Calcul technique** : - -```sql --- Pour chaque utilisateur Premium -SELECT - creator_id, - SUM(listen_duration_seconds) AS total_seconds, - (SUM(listen_duration_seconds) / total_user_seconds) AS ratio, - (4.99 * 0.70 * ratio) AS revenue_euros -FROM premium_listens -WHERE user_id = :user_id - AND month = :current_month -GROUP BY creator_id; -``` - -**Comparaison industrie** : -- YouTube Premium : 70/30 -- Spotify : 70/30 -- Apple Music : 52/48 (moins avantageux) -- RoadWave : 70/30 (standard) - -**Justification** : -- **Standard industrie** : ratio équitable éprouvé -- **Incitation qualité** : créateurs les plus écoutés gagnent plus -- **Équité** : pas de "winner takes all", chaque créateur écouté reçoit sa part -- **Marge plateforme** : 30% couvre absence revenus pub sur Premium - ---- - -### 9.5 Paiement des créateurs - -**Seuil minimum** : 50€ - -- En dessous → solde reporté mois suivant -- Évite frais bancaires sur micro-sommes -- Standard industrie (YouTube/Twitch/Spotify = 50-100€) - -**Fréquence** : Mensuelle - -| Date | Action | -|------|--------| -| **Dernier jour du mois** (ex: 31 janvier) | Calcul revenus du mois via SQL | -| **1-14 du mois suivant** | Traitement contestations/fraudes éventuelles | -| **15 du mois suivant** (ex: 15 février) | Virement SEPA via Mangopay (Payout) | -| **16-18 du mois suivant** | Réception virement (1-3 jours ouvrés SEPA) | - -**Virement via Mangopay** : -- SEPA pour comptes EU (gratuit, 1-3 jours) -- Virement international hors EU (frais variables selon pays, rare en pratique) -- **E-wallets automatiques** : chaque créateur possède un wallet Mangopay où ses revenus sont transférés automatiquement - -**Tableau de bord créateur** (temps réel) : - -| Métrique | Description | Mise à jour | -|----------|-------------|-------------| -| **Revenus pub** | Écoutes × CPM | Temps réel | -| **Revenus premium** | Abonnés actifs × ratio écoute | Temps réel | -| **Solde disponible** | Total revenus mois en cours | Temps réel | -| **Solde en attente** | Revenus mois précédent (paiement le 15) | Figé fin de mois | -| **Historique virements** | Liste des paiements reçus | Permanent | -| **Export comptable CSV** | Données pour expert-comptable | Téléchargement | - -**Gestion échecs virement** : -1. Tentative 1 (15 du mois) → échec -2. Retry automatique J+3 -3. Retry automatique J+7 -4. Si 3 échecs → suspension monétisation + email créateur (RIB invalide) - ---- - -### 9.6 Contenus Premium exclusifs - -**Décision** : Créateur décide individuellement pour chaque contenu - -**Fonctionnement** : -- Toggle "Réservé Premium" lors création/édition contenu -- **Aucune limite imposée** : créateur peut mettre 0%, 50% ou 100% en premium -- Badge 👑 visible sur interface utilisateur - -**Comportement utilisateurs gratuits** : -- Contenu premium visible dans liste/algo -- Tentative lecture → overlay bloquant -- Message : "Ce contenu est réservé aux abonnés Premium" -- CTA : "Passez Premium pour 4.99€/mois" - -**Comportement algorithme** : -- Contenus premium inclus dans recommandations -- Si user gratuit → contenu skippé automatiquement (ne consomme pas de slot) -- Si user premium → diffusé normalement - -**Métadonnées** : -- Champ `is_premium` (boolean) en base -- Index sur ce champ pour requêtes rapides -- Cache Redis : `content:{id}:premium` (TTL 1h) - -**Justification** : -- **Liberté créateur** : chaque créateur choisit sa stratégie (freemium, tout gratuit, tout premium) -- **Incitation Premium** : contenu exclusif = argument fort pour s'abonner -- **Équité** : un petit créateur peut tout mettre en premium, un gros peut tout offrir gratuitement - ---- - -### 9.7 Obligations fiscales - -**RoadWave génère automatiquement** : - -| Document | Fréquence | Destinataire | Base légale | -|----------|-----------|--------------|-------------| -| **Relevé mensuel PDF** | Chaque mois | Créateur | Transparence | -| **Export CSV comptable** | À la demande | Créateur + expert-comptable | Facilitation déclarations | -| **DAS2 annuel** | Si >1200€/an | Impôts (DGFIP) | Obligation légale France | - -**Créateur responsable de** : -- Déclarer ses revenus à l'URSSAF (cotisations sociales auto-entrepreneur ou IS/IR) -- Déclarer ses revenus aux impôts (IR ou IS selon statut) -- Gérer sa TVA si applicable (franchise en base jusqu'à ~37K€/an en micro-BNC) -- Conserver justificatifs **10 ans** (obligation légale comptable) - -**Mangopay transmet automatiquement** : -- Données aux autorités fiscales EU via **DAC7** (directive 2021/514) -- Justificatif de chaque virement (preuve bancaire pour comptabilité créateur) - -**Exemple DAS2** : -``` -Si créateur a touché 2500€ en 2026 : -→ RoadWave envoie DAS2 aux impôts en janvier 2027 -→ Créateur reçoit copie par email -→ Créateur doit déclarer ces 2500€ dans sa déclaration annuelle -``` - -**Justification** : -- **Conformité légale** : RoadWave doit déclarer revenus versés (DAS2, DAC7) -- **Responsabilité fiscale** : Le créateur reste responsable de sa déclaration (impossible de gérer pour lui) -- **Automatisation** : Minimise charge administrative côtés créateur et plateforme - ---- - -### 9.8 Désactivation et suspension monétisation - -**Créateur peut** : -- Désactiver temporairement (vacances, pause création) -- Réactiver sans refaire KYC si données à jour (<2 ans) -- Solde conservé pendant désactivation - -**Plateforme suspend automatiquement si** : - -| Motif | Action | Réversible | -|-------|--------|------------| -| **Strike 3+ actif** | Suspension immédiate | Oui, après résolution strikes | -| **Compte bancaire invalide** | Suspension après 3 échecs virement | Oui, après mise à jour RIB | -| **Documents KYC expirés** | Suspension avec préavis 30j | Oui, après renouvellement docs | -| **Fraude détectée** | Suspension immédiate + enquête | Cas par cas | - -**Suppression définitive si** : -- Demande du créateur (solde versé sous 30 jours) -- Inactivité 24 mois + solde <50€ (purge RGPD) -- Ban définitif compte (Strike 4) - -**Notification** : -- Email + in-app pour toute suspension -- Raison explicite fournie -- Procédure de réactivation indiquée - -**Justification** : -- **Flexibilité** : créateur peut faire pause sans perdre statut -- **Sécurité** : plateforme doit pouvoir suspendre en cas problème légal/technique -- **RGPD** : suppression auto données inactives après délai raisonnable - ---- - -## Récapitulatif Section 9 - - - - -
- - -## 10. Premium - -### 10.1 Offre et tarification - -**Décision** : Deux formules sans essai gratuit - -| Formule | Prix | Économie | Prix effectif | -|---------|------|----------|---------------| -| **Mensuel** | 4.99€/mois | - | 4.99€/mois | -| **Annuel** | 49.99€/an | 2 mois offerts | 4.16€/mois | - -**❌ Pas d'essai gratuit** - -**Raisons** : -- **Anti-abus vacances** : évite inscriptions opportunistes (essai 14j avant road trip vacances, puis annulation) -- **Protection revenus créateurs** : les écoutes Premium rémunèrent créateurs dès jour 1 -- **Simplicité** : pas de gestion période trial + conversion -- **Engagement** : utilisateur qui paie dès début = plus engagé - -**❌ Pas de partage familial (MVP)** - -**Raisons** : -- Complexité technique (gestion invitations, validation liens, limite devices) -- Risque abus ("familles" de 6 inconnus) -- Coût dev/support élevé pour ROI incertain -- La plupart des users RoadWave sont individuels (conducteurs) -- **Post-MVP** : Si forte demande, offre "Famille" à 9.99€/mois pour 5 comptes - -**Justification tarif** : -- **Aligné marché bas** : Spotify = 10.99€, YouTube Premium = 11.99€, Apple Music = 10.99€ -- **Prix accessible** : cible conducteurs quotidiens (budget raisonnable) -- **Incitation annuel** : 2 mois offerts = engagement long terme + réduction churn - ---- - -### 10.2 Multi-devices et détection simultanée - -**Décision** : 1 seul stream actif par compte à tout moment - -**Détection connexion simultanée** : - -``` -User A écoute sur iPhone -→ User A lance sur iPad -→ Détection : session active iPhone existe -→ Action : Arrêt lecture iPhone (WebSocket close) -→ Message iPhone : "Lecture interrompue : votre compte est utilisé sur un autre appareil" -→ Lecture démarre iPad -``` - -**Implémentation technique** : - -``` -Redis : active_streams:{user_id} → {device_id, started_at} -TTL : 5 minutes (refresh à chaque heartbeat) - -Heartbeat toutes les 30s depuis app : -→ Si autre device détecté : kill session actuelle -→ Si pas de heartbeat pendant 5 min : considérer session morte -``` - -**Exceptions** : -- Contenus téléchargés (offline) ne comptent pas comme stream actif -- Transition rapide device (<10s) tolérée (changement voiture → maison) - -**Justification** : -- **Anti-partage compte** : empêche 2 personnes d'utiliser même compte Premium -- **Protection revenus créateurs** : 1 abonnement = 1 personne = 1 écoute -- **UX claire** : message explicite, pas de coupure brutale - ---- - -### 10.3 Contenus exclusifs Premium - -**Décision** : Créateur décide (déjà couvert section 9.6) - -**Rappel règles** : -- Toggle "Réservé Premium" par contenu -- Aucune limite de ratio gratuit/premium -- Badge 👑 visible -- Users gratuits : lecture bloquée avec CTA "Passez Premium" - -**Impact algorithme** : -- Contenus premium inclus dans recommandations -- Si user gratuit → skip automatique (ne consomme pas slot) -- Si user premium → diffusé normalement selon score - ---- - -### 10.4 Avantages Premium - -**Inclus dans l'abonnement** : - -| Avantage | Gratuit | Premium | -|----------|---------|---------| -| **Publicités** | 1/5 contenus | 0 (aucune) | -| **Contenus exclusifs** | ❌ Bloqués | ✅ Accès complet | -| **Qualité audio** | 48 kbps Opus | 64 kbps Opus | -| **Mode offline** | 50 contenus max | Illimité | -| **Historique écoute** | 100 derniers | Illimité | - -**Qualité audio** : -- Gratuit : 48 kbps Opus (~20 MB/h) = très correct pour voix -- Premium : 64 kbps Opus (~30 MB/h) = excellente qualité - -**Justification différences** : -- **0 pub** = argument principal (confort écoute) -- **Qualité audio** = avantage tangible audiophiles -- **Offline illimité** = use case road trips longs -- **Pas d'over-engineering** : pas de badges cosmétiques, fonctionnalités sociales, etc. (focus essentiel) - ---- - -### 10.5 Gestion abonnement - -**Souscription** : - -| Canal | Prestataire | Prix | Commission | -|-------|-------------|------|------------| -| **Web (desktop/mobile)** | Mangopay | 4.99€ | 1.8% + 0.18€ = 0.27€ | -| **iOS App** | Apple In-App Purchase | 5.99€ | 30% (Apple) | -| **Android App** | Google Play Billing | 5.99€ | 30% (Google) | - -**Majoration mobile (5.99€)** : -- Apple/Google prennent 30% de commission -- RoadWave majore prix de 20% pour compenser -- **Incitation web** : Email aux users "Abonnez-vous sur roadwave.com pour 4.99€/mois" (38% moins cher en frais !) - -**Renouvellement automatique** : -- Email rappel **7 jours avant** renouvellement -- Email confirmation **après** renouvellement réussi -- Retry automatique si échec paiement (3 tentatives sur 7 jours) -- Annulation automatique après 3 échecs - -**Annulation** : -- Self-service dans Settings app : "Abonnement > Annuler" -- Accès Premium maintenu jusqu'à **fin période payée** -- Pas de remboursement prorata (standard industrie) -- Email confirmation annulation avec date fin d'accès - -**Réabonnement** : -- Possibilité immédiate -- ❌ Pas de nouvelle période d'essai (pas d'essai du tout) - -**Architecture données** : - -```sql -CREATE TABLE subscriptions ( - id UUID PRIMARY KEY, - user_id UUID NOT NULL REFERENCES users(id) UNIQUE, - mangopay_recurring_payin_id VARCHAR(255), -- Null si IAP - mangopay_user_id VARCHAR(255), -- Null si IAP - apple_transaction_id VARCHAR(255), -- Null si Mangopay - google_purchase_token VARCHAR(255), -- Null si Mangopay - status VARCHAR(50) NOT NULL, -- 'active', 'cancelled', 'expired', 'past_due' - plan VARCHAR(50) NOT NULL, -- 'monthly', 'yearly' - current_period_start TIMESTAMP NOT NULL, - current_period_end TIMESTAMP NOT NULL, - cancelled_at TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT NOW() -); -``` - -**Vérification Premium en temps réel** : - -``` -Cache Redis : premium:{user_id} → boolean (TTL 1h) -Refresh via webhooks : -- Mangopay : PAYIN_NORMAL_SUCCEEDED, PAYIN_NORMAL_FAILED -- Apple : App Store Server Notifications -- Google : Real-time Developer Notifications -``` - ---- - -## Récapitulatif Section 10 - - - - -
- - -## 11. Mode offline - -### 11.1 Téléchargement - -**Zone géographique** : Choix manuel utilisateur - -**Options prédéfinies** : -- "Autour de moi" (rayon 50 km position actuelle) -- "Ma ville" (limite administrative détectée) -- "Mon département" (sélection liste) -- "Ma région" (sélection liste) -- Recherche manuelle : "Paris", "Lyon", "Marseille", etc. - -**Nombre de contenus téléchargeables** : - -| Statut | Limite | Affichage | -|--------|--------|-----------| -| **Gratuit** | 50 contenus max | "12/50 contenus téléchargés" | -| **Premium** | Illimité | "245 contenus (3.2 GB)" | - -**Calcul temps disponible** : -- 50 contenus × 5 min moyenne = 250 min = **4h d'écoute** (suffisant pour gratuits) -- Premium illimité = limité uniquement par espace disque device - -**Connexion WiFi/Mobile** : - -**Par défaut** : WiFi uniquement - -**Sur données mobiles** : -1. User clique "Télécharger" -2. Détection : pas de WiFi -3. Popup : "Vous n'êtes pas connecté en WiFi. Télécharger via données mobiles consommera environ **X MB**. Continuer ?" -4. Boutons : "Attendre WiFi" / "Continuer" - -**Calcul estimation** : -``` -Nombre contenus × durée moyenne × bitrate qualité -Exemple : 20 contenus × 5 min × 48 kbps = ~72 MB -``` - -**Qualité audio téléchargement** : - -| Qualité | Bitrate | Taille | Disponibilité | -|---------|---------|--------|---------------| -| **Basse** | 24 kbps | ~10 MB/h | Gratuit + Premium | -| **Standard** | 48 kbps | ~20 MB/h | Gratuit + Premium (défaut) | -| **Haute** | 64 kbps | ~30 MB/h | **Premium uniquement** | - -**Justification** : -- Standard = bon compromis qualité/taille (Opus 48 kbps = très correct pour voix) -- Haute réservée Premium = incitation upgrade -- User peut réduire à "basse" si espace limité - ---- - -### 11.2 Validité et renouvellement - -**Durée de validité** : 30 jours après téléchargement - -**Standard industrie** : -- Spotify : 30 jours -- YouTube Music : 30 jours -- Deezer : 30 jours - -**Renouvellement automatique** : - -``` -App détecte WiFi + contenus >25 jours -→ Requête API : GET /offline/contents/refresh -→ Backend vérifie pour chaque contenu : - - Abonnement Premium toujours actif ? - - Contenu pas modéré/supprimé ? - - Métadonnées à jour ? -→ Renouvelle validité à 30 jours supplémentaires -→ Mise à jour métadonnées (titre, créateur, statut) -→ Pas de re-téléchargement audio (sauf si fichier corrompu) -``` - -**Notification avant expiration** : -- **J-3** : "X contenus expirent dans 3 jours. Connectez-vous en WiFi pour les renouveler" -- **J-0** : Suppression automatique -- **J+0** : Toast "15 contenus expirés ont été supprimés" - -**Justification** : -- **Force reconnexion** : vérifier abonnement actif, contenus légaux -- **Évite stockage obsolète** : contenus supprimés/modérés ne restent pas -- **UX transparente** : renouvellement silencieux si WiFi régulier - ---- - -### 11.3 Synchronisation actions offline - -**Actions stockées localement (SQLite)** : -- Likes/unlikes -- Abonnements/désabonnements -- Signalements -- Progression audio-guides - -**Sync automatique à la reconnexion** : - -``` -1. App détecte reconnexion Internet -2. Récupération queue locale : SELECT * FROM pending_actions ORDER BY created_at -3. Envoi batch API : POST /sync/actions -4. Backend traite chaque action -5. Confirmation réception : DELETE FROM pending_actions WHERE id IN (...) -6. Toast : "3 likes et 1 abonnement synchronisés" -``` - -**Gestion erreurs sync** : -- Si échec après 3 tentatives → notification : "Impossible de synchroniser. Réessayez plus tard" -- Actions conservées jusqu'à sync réussie (pas de perte) -- **Rétention max 7 jours** : après = purge (évite queue infinie) - -**Conflits contenus supprimés** : - -``` -Backend retourne : {deleted_content_ids: [123, 456]} -→ App supprime fichiers locaux -→ Si contenu 123 en cours d'écoute : - - Attendre fin lecture actuelle - - Passage auto suivant après 2s -→ Toast : "1 contenu téléchargé a été retiré (violation règles)" -``` - -**Justification** : -- **Pas de conflit possible** : actions unilatérales user (likes/abonnements) -- **UX fluide** : pas de blocage offline -- **Batch = économie** : requêtes HTTP groupées -- **Conformité modération** : contenu illégal disparaît même offline - ---- - -## Récapitulatif Section 11 - -| Aspect | Décision | Valeur | -|--------|----------|--------| -| **Zone téléchargement** | Choix | Manuel (autour/ville/département/région/recherche) | -| **Limite gratuit** | Contenus | 50 max | -| **Limite Premium** | Contenus | Illimité (espace disque) | -| **Connexion** | Par défaut | WiFi (mobile avec confirmation) | -| **Qualité Standard** | Bitrate | 48 kbps Opus | -| **Qualité Haute** | Bitrate | 64 kbps (Premium uniquement) | -| **Validité** | Durée | 30 jours | -| **Renouvellement** | Mode | Automatique si WiFi | -| **Notification expiration** | Délai | J-3 | -| **Sync actions** | Mode | Batch automatique reconnexion | -| **Rétention queue** | Durée | 7 jours max | - ---- - - - - -
- - -## 12. Gestion des erreurs - -### 12.1 Aucun contenu disponible - -**Stratégie** : Élargissement automatique progressif - -**Flow** : - -``` -1. Recherche rayon 50 km → aucun résultat -2. Élargissement auto 100 km -3. Si toujours rien → département -4. Si toujours rien → région -5. Dernier recours → contenu national (toujours disponible) -``` - -**Messages adaptatifs** : - -| Cas | Message | -|-----|---------| -| **Trouvé à 100 km** | "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)" | -| **Trouvé département** | "Aucun contenu local disponible. Voici du contenu dans votre département" | -| **Contenu national** | "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser" | - -**Justification** : -- **UX fluide** : pas de message d'erreur bloquant "Aucun contenu" -- **User ne reste jamais sans contenu** -- **Contenu national = filet de sécurité** : actualités Le Monde, podcasts génériques - ---- - -### 12.2 Contenu signalé/supprimé pendant l'écoute - -**Décision** : Pas d'interruption brutale - -**Flow** : - -``` -1. Contenu supprimé côté backend (modération) -2. Si contenu en écoute → laisser terminer lecture en cours -3. Après fin lecture → désactiver bouton "Précédent" pour ce contenu -4. Passage automatique suivant après 2s -5. Toast notification discrète : "Contenu précédent retiré (violation règles)" -``` - -**Si tentative "Précédent" manuellement** : -- Message : "Ce contenu n'est plus disponible" -- Retour au contenu actuel - -**Justification** : -- **Sécurité routière** : pas d'interruption brutale pendant conduite -- **User informé mais pas alarmé** : message discret -- **Empêche réécoute** : contenu modéré inaccessible - ---- - -### 12.3 Perte de réseau - -**Buffer adaptatif** (cf. TECHNICAL.md) : - -| Réseau | Buffer min | Buffer cible | Buffer max | -|--------|------------|--------------|------------| -| **WiFi** | 5s | 30s | 120s | -| **4G/5G** | 10s | 45s | 120s | -| **3G** | 30s | 90s | 300s | - -**Comportement détaillé** : - -**Phase 1 : Connexion instable** (latence élevée, paquets perdus) -- Aucun message immédiat -- Lecture continue sur buffer -- Si > 10s latence : toast discret "Connexion instable" - -**Phase 2 : Perte totale réseau** -- Lecture continue jusqu'à épuisement buffer -- Toast : "Hors ligne, lecture sur buffer (30s restantes)" -- Compte à rebours visible - -**Phase 3 : Buffer épuisé sans reconnexion** -- Pause automatique -- Overlay : "Connexion perdue. Reconnexion en cours..." -- Retry automatique toutes les 5s (max 6 tentatives = 30s) - -**Phase 4 : Basculement mode offline** (après 30s échec) -- Popup : "Voulez-vous continuer avec vos contenus téléchargés ?" -- Boutons : "Réessayer" / "Mode offline" -- Si "Mode offline" → lecture contenus téléchargés - -**Reconnexion réussie** : -- Reprise automatique lecture au point d'arrêt exact -- Toast : "Connexion rétablie" - -**Justification** : -- **Expérience fluide zones blanches** (tunnels, campagne) -- **Buffer généreux** : absorbe fluctuations réseau mobile -- **Mode offline secours** : si coupure prolongée - ---- - -### 12.4 Géolocalisation désactivée - -**Mode dégradé automatique** - -**Contenu disponible** : - -| Type contenu | Disponible | -|--------------|-----------| -| **Contenu national** (podcasts, actualités) | ✅ | -| **Contenu téléchargé** (offline) | ✅ | -| **Contenus "Neutre"** géographiquement | ✅ | -| **Contenu géolocalisé** (Ancré/Contextuel) | ❌ | -| **Audio-guides** | ❌ | -| **Notifications push géo-déclenchées** | ❌ | - -**Popup au lancement** : -- **Apparition** : Premier lancement après refus géolocalisation -- **Message** : "RoadWave fonctionne mieux avec la géolocalisation activée. Sans elle, seul le contenu national sera disponible." -- **Boutons** : - - "Activer" → Redirection paramètres OS - - "Continuer sans" → Mode dégradé -- **Checkbox** : "Ne plus me demander" - -**Banner permanent si refus** : -- Bandeau haut écran : "Mode limité : géolocalisation désactivée. [Activer]" -- Pas intrusif mais rappel constant -- Disparaît si géolocalisation réactivée - -**Justification** : -- **App reste fonctionnelle** sans GPS (pas de blocage) -- **Incitation forte** à activer (meilleure UX) -- **Respecte choix user** (RGPD : consentement libre) - ---- - -## Récapitulatif Section 12 - - - - -
- - -## 13. Conformité RGPD - -### 13.1 Gestion du consentement - -**Décision** : Tarteaucitron.js + PostgreSQL backend - -**Implémentation web** : -- ✅ Tarteaucitron.js (opensource, self-hosted) -- ✅ Banner RGPD français, customisable -- ✅ Granularité : fonctionnel / analytique / marketing - -**Implémentation backend** : -- Table `user_consents` avec versioning -- Champs : user_id, consent_type, version, accepted, timestamp -- Historique complet conservé (preuve légale) - -**Consentements requis** : -- **Géolocalisation précise** : obligatoire (banner + permission OS) -- **Analytics** : optionnel (Matomo) -- **Notifications push** : optionnel (permission OS) - -**Justification** : -- Opensource, 0€, conformité RGPD garantie -- Historique backend = preuve légale en cas de contrôle -- Granularité conforme recommandations CNIL - ---- - -### 13.2 Anonymisation des données GPS - -**Décision** : Geohash après 24h - -**Processus** : -1. Données précises conservées **24h** (recommandation personnalisée) -2. Après 24h : conversion en geohash précision 5 (~5km²) -3. Coordonnées originales supprimées définitivement - -**Implémentation PostGIS** : -```sql --- Job quotidien -UPDATE location_history -SET location = ST_SetSRID(ST_GeomFromGeoHash(ST_GeoHash(location::geography, 5)), 4326)::geography, - anonymized = true -WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false; -``` - -**Exceptions** : -- ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif -- ❌ Analytics globales : uniquement geohash anonyme - -**Justification** : -- Vraie anonymisation RGPD (CNIL compliant) -- Permet analytics agrégées (heatmaps trafic) -- PostGIS natif, 0€ - ---- - -### 13.3 Export des données (portabilité) - -**Décision** : JSON + HTML + ZIP, génération asynchrone - -**Contenu de l'export** : -``` -export-roadwave-[user_id]-[date].zip -├── export.json # Machine-readable -├── index.html # Human-readable (stylé) -├── audio/ -│ ├── content-123.opus -│ ├── content-456.opus -│ └── ... -└── README.txt # Instructions -``` - -**Données exportées** : -- Profil utilisateur (email, pseudo, date inscription, bio) -- Historique d'écoute (titres, dates, durées) -- Contenus créés (audio + métadonnées) -- Abonnements et likes -- Centres d'intérêt (jauges) -- Historique consentements - -**Processus** : -1. Demande via paramètres compte -2. Génération asynchrone (worker background) -3. Email avec lien download (expire **7 jours**) -4. Délai : **48h maximum** (conformité RGPD) - -**Limite** : -- Maximum **1 export/mois** (anti-abus) - -**Justification** : -- Conformité article 20 RGPD (portabilité) -- Double format (human + machine) -- Worker asynchrone évite timeout - ---- - -### 13.4 Suppression du compte - -**Décision** : Grace period 30j + anonymisation contenus - -**Processus** : -1. Utilisateur clique "Supprimer mon compte" -2. Compte désactivé immédiatement (login impossible) -3. Contenus cachés pendant 30 jours (non diffusés) -4. Email confirmation + lien annulation (valide 30j) -5. Après 30j sans annulation : suppression effective - -**Suppression effective** : -- ✅ Compte utilisateur supprimé (données personnelles) -- ✅ Historique d'écoute supprimé -- ✅ GPS historique supprimé -- ✅ Sessions et tokens révoqués -- ⚠️ Contenus créés **anonymisés** (créateur = "Utilisateur supprimé") -- ⚠️ Likes et abonnements supprimés (mais compteurs préservés) - -**Contenus conservés anonymement** : -- Audio files (CDN) -- Métadonnées (titre, description, tags, géolocalisation) -- Statistiques d'écoute - -**Justification** : -- Grace period évite suppressions impulsives -- Anonymisation contenus = intérêt légitime communauté -- Conforme RGPD si créateur = donnée supprimée - ---- - -### 13.5 Mode dégradé (sans GPS précis) - -**Décision** : GeoIP par défaut, GPS optionnel - -**Niveaux de précision** : - -| Niveau | Technologie | Contenus accessibles | Consentement | -|--------|-------------|---------------------|--------------| -| **Pays** | Aucune géoloc | Contenus nationaux uniquement | ❌ Non requis | -| **Ville** | GeoIP (MaxMind) | Contenus régionaux/ville | ❌ Non requis | -| **Précis** | GPS | Tous contenus (hyperlocaux inclus) | ✅ Requis | - -**Implémentation** : -- Démarrage app : GeoIP automatique (IP → ville) -- Banner in-app : "Activez la géolocalisation pour découvrir du contenu près de chez vous" -- Upgrade volontaire vers GPS - -**API GeoIP** : -- MaxMind GeoLite2 (gratuit, self-hosted) -- Update DB mensuelle automatique -- Précision ~80% au niveau ville - -**Justification** : -- RGPD : pas de consentement requis pour GeoIP (pas de donnée personnelle) -- UX dégradée acceptable (contenus disponibles) -- Progressive disclosure (upgrade optionnel) - ---- - -### 13.6 Durée de conservation des données - -**Décision** : 5 ans inactivité → purge automatique - -**Règles** : - -| Type de compte | Seuil inactivité | Action | -|----------------|------------------|--------| -| **Auditeur uniquement** | 5 ans sans connexion | Suppression automatique | -| **Créateur avec contenus actifs** | Jamais (tant qu'écoutes) | Conservation indéfinie | -| **Créateur inactif** | 5 ans sans connexion + 2 ans sans écoute | Suppression automatique | - -**Notifications avant suppression** : -- Email + push : **90 jours** avant -- Email + push : **30 jours** avant -- Email + push : **7 jours** avant -- Toute connexion = reset compteur inactivité - -**Contenu conservé** : -- Contenus créés par comptes supprimés (anonymisés) : conservation indéfinie - -**Justification** : -- Conformité principe minimisation RGPD -- 5 ans = équilibre raisonnable (standard industrie) -- Exception créateurs actifs = intérêt légitime plateforme - ---- - -### 13.7 Cookies et trackers web - -**Décision** : Matomo self-hosted, zéro cookie tiers - -**Cookies utilisés** : - -| Cookie | Type | Durée | Finalité | Consentement | -|--------|------|-------|----------|--------------| -| `session` | Technique | 30j | Authentification | ❌ Non requis | -| `refresh_token` | Technique | 30j | Session persistante | ❌ Non requis | -| `_pk_id` | Analytique | 13 mois | Matomo (IP anonyme) | ✅ Requis | - -**Analytics : Matomo self-hosted** : -- Hébergé sur nos serveurs (Docker) -- IP anonymisées automatiquement (2 derniers octets) -- Pas de cookie si consentement refusé -- Alternative : Plausible (SaaS EU, 9€/mois) - -**Trackers interdits** : -- ❌ Google Analytics -- ❌ Facebook Pixel -- ❌ Hotjar, Mixpanel, etc. - -**Justification** : -- Souveraineté données (pas de transfert US) -- Conformité RGPD max (CNIL compatible) -- Matomo = opensource, 0€ infra - ---- - -### 13.8 Registre des traitements - -**Décision** : Document Markdown versionné Git (MVP) - -**Emplacement** : -- `docs/rgpd/registre-traitements.md` -- Versionné Git (historique modifications) - -**Contenu obligatoire par traitement** : -- Nom et finalité du traitement -- Catégories de données collectées -- Base légale (consentement / contrat / intérêt légitime) -- Durée de conservation -- Destinataires (sous-traitants, CDN, etc.) -- Transferts hors UE (aucun prévu) - -**Responsable** : -- DPO / Fondateur -- Review trimestrielle obligatoire -- Update immédiate si nouveau traitement - -**Migration future** : -- Si > 100K utilisateurs : interface admin PostgreSQL - -**Justification** : -- Obligation RGPD Article 30 -- Markdown = simple, versionné, auditable -- 0€ - ---- - -### 13.9 Notification violations de données (breach) - -**Décision** : Monitoring + alertes + runbook - -**Détection automatique** : - -| Événement | Outil | Alerte | -|-----------|-------|--------| -| Erreurs backend critiques | Sentry | Discord/Slack immédiat | -| Pic requêtes anormal | Grafana | Email équipe | -| Accès non autorisé DB | PostgreSQL logs | SMS fondateur | -| Authentification suspecte | Zitadel alerts | Email équipe | - -**Procédure breach** : -- Runbook : `docs/rgpd/procedure-breach.md` -- Checklist 72h CNIL : - 1. H+0 : Détection et confinement - 2. H+24 : Évaluation gravité (données concernées, utilisateurs impactés) - 3. H+48 : Notification CNIL si risque pour utilisateurs - 4. H+72 : Notification utilisateurs si risque élevé - -**Contact CNIL** : -- Email pré-rédigé (template) -- Formulaire en ligne (account CNIL créé) - -**Justification** : -- Obligation RGPD Article 33 (notification 72h) -- Monitoring proactif évite découverte tardive -- Sentry gratuit < 5K events/mois - ---- - -### 13.10 DPO (Délégué à la Protection des Données) - -**Décision** : Fondateur = DPO temporaire (MVP) - -**Raison légale** : -- Non obligatoire si : - - < 250 employés - - Pas de traitement à grande échelle de données sensibles - - RoadWave : données localisation = sensible MAIS échelle MVP - -**Formation** : -- CNIL : formation gratuite en ligne (4h) -- Certification CNIL "Atelier RGPD" (gratuit) - -**Contact** : -- Email : dpo@roadwave.fr -- Publié dans CGU et mentions légales -- Délai réponse : **1 mois** (RGPD) - -**Migration future** : -- Si > 100K utilisateurs : DPO externe mutualisé (~200€/mois) -- Ou recrutement DPO interne si > 10 employés - -**Justification** : -- Conforme RGPD (non obligatoire en phase MVP) -- 0€, contrôle total -- Bonne pratique : avoir un contact identifié - ---- - -## Récapitulatif Section 13 - -| Mesure | Implémentation | Coût | -|--------|----------------|------| -| **Consentement** | Tarteaucitron.js + PostgreSQL | 0€ | -| **Anonymisation GPS** | Geohash PostGIS (24h) | 0€ | -| **Export données** | JSON+HTML+ZIP asynchrone | 0€ | -| **Suppression compte** | Grace period 30j + anonymisation | 0€ | -| **Mode dégradé** | GeoIP MaxMind + GPS optionnel | 0€ | -| **Conservation** | Purge auto 5 ans inactivité | 0€ | -| **Analytics** | Matomo self-hosted | ~5€/mois | -| **Registre traitements** | Markdown Git | 0€ | -| **Breach detection** | Sentry + Grafana + runbook | 0€ | -| **DPO** | Fondateur formé CNIL | 0€ | - -**Coût total RGPD : ~5€/mois** - ---- - -## Points d'attention pour Gherkin - -- Tester consentement géolocalisation (accept/refuse → contenus différents) -- Tester anonymisation GPS après 24h (job cron) -- Tester export données (génération complète + vérification contenu) -- Tester grace period suppression (annulation possible) -- Tester mode GeoIP (ville détectée correctement) -- Tester purge automatique (5 ans inactivité) -- Tester notifications avant purge (90j/30j/7j) - - - - -
- - -## 14. Modération - Flows opérationnels - -### 14.1 Signalement - -**Décision** : Formulaire simple avec 7 catégories prédéfinies - -#### 14.1.1 Catégories de signalement - -Liste déroulante avec 7 options : - -| Catégorie | Description | -|-----------|-------------| -| 🚫 **Haine & violence** | Incitation à la haine, discrimination, menaces | -| 🔞 **Contenu sexuel** | Pornographie, contenu explicite | -| ⚖️ **Illégalité** | Terrorisme, apologie de crimes | -| 🎵 **Droits d'auteur** | Musique/contenu protégé non autorisé | -| 📧 **Spam** | Publicité non sollicitée, répétition | -| ❌ **Fausse information** | Désinformation sur santé, sécurité routière | -| 🔧 **Autre** | Champ texte obligatoire si sélectionné | - -**Justification** : -- Équilibre entre simplicité (pas trop de choix) et précision (aide les modérateurs) -- Coût : 0€ (liste déroulante standard) - ---- - -#### 14.1.2 Commentaire du signaleur - -**Décision** : Optionnel avec incitation - -- Champ texte libre (0-500 caractères) -- Placeholder : "Décrivez le problème (optionnel mais recommandé)" -- Non bloquant : le signalement peut être envoyé sans commentaire - -**Justification** : -- Encourage la qualité des signalements sans créer de friction -- Aide les modérateurs à comprendre le contexte -- Pas de risque d'abandon du processus - ---- - -#### 14.1.3 Confirmation après signalement - -**Décision** : Toast in-app avec lien historique - -**Affichage** : -- Toast notification : "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." -- Durée affichage : 5 secondes -- Bouton optionnel "Voir mes signalements" (accès historique) - -**Historique personnel** : -- Liste des signalements envoyés par l'utilisateur -- Statut : En cours / Traité / Rejeté -- Notification in-app si action prise (contenu retiré, signalement rejeté) - -**Justification** : -- Transparence maximale -- Coût : 0€ (aucun email automatique) -- Bonne UX - ---- - -### 14.2 Traitement des signalements - -#### 14.2.1 IA pré-filtre (transcription + analyse) - -**Décision** : OpenAI Whisper open source + NLP - -**Stack technique** : - -| Composant | Technologie | Hébergement | -|-----------|-------------|-------------| -| **Transcription** | Whisper large-v3 | Self-hosted (CPU MVP, GPU scale) | -| **Analyse sentiment** | distilbert-base-uncased | Self-hosted | -| **Détection haine** | facebook/roberta-hate-speech | Self-hosted | -| **Mots-clés** | Liste noire FR/EN + regex | PostgreSQL | - -**Processus** : -1. Signalement reçu → ajout file d'attente asynchrone -2. Transcription audio (1-10 minutes selon durée) -3. Analyse automatique : - - Score de confiance : 0-100% - - Catégorie détectée - - Timestamps des passages problématiques -4. Priorisation automatique selon score - -**Délais** : -- Audio <5 min : 1-3 minutes -- Audio 5-30 min : 3-10 minutes -- Audio >30 min : 10-20 minutes - -**Coût** : -- **MVP** : 0€ (CPU standard, processing asynchrone) -- **Scale** : 50-200€/mois (GPU VPS si >1000 signalements/jour) - -**Justification** : -- 100% open source, pas de dépendance GAFAM -- Coût maîtrisé (scaling progressif) -- Gain productivité modérateurs ×3-5 - ---- - -#### 14.2.2 Délais de traitement (SLA) - -**Décision** : SLA progressif selon priorité - -| Priorité | Délai cible | Traitement | -|----------|-------------|------------| -| **CRITIQUE** | <2h (24/7) | Violence, suicide, mise en danger → Astreinte modérateur senior | -| **HAUTE** | <24h (jours ouvrés) | Haine, harcèlement, désinformation → Modérateur junior/senior | -| **MOYENNE** | <24h (jours ouvrés) | Spam, contenu inapproprié → Modérateur junior | -| **BASSE** | <72h (jours ouvrés) | Qualité audio, tags incorrects → Modérateur junior | - -**Traitement automatique** : -- Score IA >95% + catégorie évidente (ex: spam répété) → Action automatique immédiate -- Notification créateur + possibilité d'appel - -**Justification** : -- Réaliste et conforme DSA (Digital Services Act) -- Scalable : priorisation automatique -- Ressources humaines optimisées - ---- - -#### 14.2.3 Priorisation automatique - -**Décision** : File d'attente intelligente basée sur score IA - -**Calcul de priorité** : - -``` -Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1) -``` - -**Détails** : -- **Score_IA** : 0-100% (confiance analyse automatique) -- **Signalements_cumulés** : nombre de signalements du même contenu (boost priorité) -- **Fiabilité_signaleur** : score utilisateur (historique signalements pertinents) - -**Classification résultante** : -- Priorité ≥90 → **CRITIQUE** (traitement immédiat) -- Priorité 70-89 → **HAUTE** (file prioritaire) -- Priorité 40-69 → **MOYENNE** (file normale) -- Priorité <40 → **BASSE** (file différée) - -**Justification** : -- Optimise le temps des modérateurs -- Traite les cas graves en priorité -- Coût : 0€ (algorithme simple) - ---- - -### 14.3 Sanctions - -#### 14.3.1 Notification au créateur - -**Décision** : Multi-canal (email + push + in-app) - -**Canaux utilisés** : - -| Canal | Timing | Contenu | -|-------|--------|---------| -| **Push notification** | Immédiat | Alerte courte : "Votre contenu a été modéré" | -| **In-app** | Au prochain lancement | Popup détaillée avec bouton "Voir détails" | -| **Email** | Dans l'heure | Notification complète avec lien vers formulaire d'appel | - -**Contenu email** : -``` -Objet : Modération de votre contenu "[Titre du contenu]" - -Bonjour [Pseudo], - -Votre contenu "[Titre]" publié le [Date] a été modéré. - -Catégorie violée : [Catégorie] -Raison : [Explication détaillée] -Sanction : [Strike X / Suspension X jours / Suppression contenu] - -Extrait audio concerné : [Timestamp] -Transcription : "[Passage problématique surligné]" - -Vous pouvez contester cette décision sous 7 jours : -[Lien formulaire d'appel] - -L'équipe RoadWave -``` - -**Coût** : -- Email : ~0.001€/notification (Brevo, Resend) -- Push : 0€ (Firebase Cloud Messaging / APNs) -- In-app : 0€ - -**Justification** : -- Conformité DSA (transparence obligatoire) -- Multi-canal garantit réception -- Coût négligeable - ---- - -#### 14.3.2 Détail de la sanction - -**Décision** : Notification complète avec preuves - -**Éléments inclus obligatoirement** : - -1. **Catégorie violée** : référence précise CGU (ex: "Article 3.2 - Haine & violence") -2. **Raison détaillée** : explication en langage clair (non juridique) -3. **Extrait audio** : timestamp exact du passage problématique (ex: "3:42-4:15") -4. **Transcription** : texte problématique surligné en rouge -5. **Gravité** : Strike actuel + conséquences (ex: "Strike 2/4 - Suspension 7 jours") -6. **Recours** : lien direct vers formulaire d'appel + délai (7 jours) - -**Exemple visuel in-app** : -``` -┌─────────────────────────────────────┐ -│ ⚠️ Contenu modéré │ -├─────────────────────────────────────┤ -│ Titre : "Mon podcast #42" │ -│ Publié le : 15/01/2026 │ -│ │ -│ Catégorie violée : │ -│ 🚫 Haine & violence (Article 3.2) │ -│ │ -│ Passage problématique : 3:42-4:15 │ -│ "[Transcription surlignée]" │ -│ │ -│ Sanction : Strike 2/4 │ -│ Suspension : 7 jours │ -│ │ -│ [Contester cette décision] │ -└─────────────────────────────────────┘ -``` - -**Justification** : -- Transparence maximale (obligation DSA) -- Créateur comprend l'erreur → amélioration future -- Réduit les appels non fondés - ---- - -#### 14.3.3 Processus d'appel - -**Décision** : Formulaire in-app structuré - -**Accès** : -- Bouton "Contester cette décision" dans notification -- Section "Mes sanctions" dans profil créateur - -**Formulaire d'appel** : - -| Champ | Type | Obligatoire | -|-------|------|-------------| -| **Sanction contestée** | Pré-rempli (non modifiable) | ✅ | -| **Raison de l'appel** | Texte libre (50-1000 caractères) | ✅ | -| **Arguments** | Zone texte enrichie | ✅ | -| **Preuves** | Upload fichiers (max 5, 10 MB total) | ❌ | - -**Après soumission** : -- Génération numéro de ticket unique (ex: `#MOD-2026-00142`) -- Email confirmation : "Votre appel sera traité sous 72h" -- Statut visible dans l'app : "En cours d'examen" - -**Délai de soumission** : -- Maximum **7 jours** après notification de sanction -- Après 7 jours : appel automatiquement refusé - -**Justification** : -- Professionnel et traçable -- Intégration complète avec système modération -- Coût : 0€ (formulaire custom backend) - ---- - -#### 14.3.4 Délai de réponse pour appel - -**Décision** : SLA 72h garanti - -**Délais** : - -| Type d'appel | Délai | Responsable | -|--------------|-------|-------------| -| **Standard** | 72h max (3 jours ouvrés) | Modérateur senior | -| **Complexe** | 5 jours ouvrés + notification intermédiaire J+3 | Modérateur senior + Admin modération | -| **Critique** | 24h (cas suspension longue/ban) | Admin modération | - -**Notification intermédiaire** (si délai >72h) : -- Email J+3 : "Votre appel #MOD-XXX est en cours d'examen approfondi. Réponse sous 2 jours." - -**Réponse finale** : - -Email détaillé avec : -1. **Décision** : Maintien / Annulation / Réduction de sanction -2. **Justification** : explication de la décision d'appel -3. **Actions** : Strike retiré / Suspension annulée / Contenu rétabli (si applicable) -4. **Définitif** : mention "Cette décision est définitive" (pas de second appel) - -**Suivi in-app** : -- Mise à jour statut : "Appel accepté ✓" ou "Appel rejeté ✗" -- Badge notification - -**Justification** : -- Équilibre entre rapidité et qualité de traitement -- Conforme pratiques industrie (YouTube, TikTok : 5-7 jours) -- Ressources humaines réalistes - ---- - -### 14.4 Outils modérateurs - -**Stack technique complète** : - -| Outil | Technologie | Fonction | -|-------|-------------|----------| -| **Dashboard** | React + TanStack Table | Interface modération | -| **File signalements** | PostgreSQL + Redis | Priorisation temps réel | -| **Player audio** | Wavesurfer.js | Lecture avec waveform + annotations | -| **Transcription** | Whisper large-v3 | Conversion audio → texte | -| **Historique créateur** | Vue 360° | Contenus, strikes, appels, métriques | -| **Actions rapides** | Shortcuts clavier | Approuver (A), Rejeter (R), Escalade (E) | -| **Logs audit** | PostgreSQL + export | Traçabilité complète (DSA) | -| **Collaboration** | Système de commentaires | Modérateurs peuvent s'entraider sur cas complexes | - -**Fonctionnalités clés** : - -1. **Lecture accélérée** : 0.75x à 2x (gain productivité) -2. **Marqueurs temporels** : annotation directe sur waveform -3. **Historique créateur** : vue rapide contenus précédents + strikes -4. **Statistiques** : signalements traités/jour, temps moyen, précision -5. **Fil d'activité** : actions récentes équipe (temps réel) - -**Coût infrastructure** : -- MVP : 0-50€/mois (serveur CPU) -- Scale : 50-200€/mois (GPU + Redis Cluster) - ---- - -### 14.5 Modération préventive (rappel) - -**Nouveaux créateurs** : -- Validation manuelle des **3 premiers contenus** -- Délai : 24-48h (jours ouvrés) -- Transcription automatique pour aide modérateur - -**Score de confiance** : -- Évolution dynamique selon historique -- Créateur fiable (0 strike depuis 6 mois) → validation automatique -- Créateur suspect (strikes récents) → validation manuelle systématique - -**Publicités** : -- Validation manuelle obligatoire 24-48h (responsabilité juridique) -- Transcription + analyse métadonnées (ciblage, durée, volume) - -**Justification** : -- Prévention > réaction (économie modération) -- Qualité plateforme préservée dès le début - ---- - -## Récapitulatif Section 14 - -| Point | Décision | Coût | -|-------|----------|------| -| **Catégories signalement** | 7 catégories prédéfinies + champ libre | 0€ | -| **Commentaire signaleur** | Optionnel avec incitation | 0€ | -| **Confirmation** | Toast in-app + historique personnel | 0€ | -| **IA pré-filtre** | Whisper (CPU MVP, GPU scale) + NLP open source | 0-200€/mois | -| **Délais traitement** | SLA progressif : 2h/24h/72h selon priorité | Dépend équipe | -| **Priorisation** | File intelligente basée score IA | 0€ | -| **Notification sanction** | Email + push + in-app (multi-canal) | ~0.001€/notif | -| **Détail sanction** | Complet : raison + extrait + transcription | 0€ | -| **Processus appel** | Formulaire in-app structuré | 0€ | -| **Délai appel** | 72h garanti (standard) | Dépend équipe | -| **Outils modérateurs** | Dashboard React + Whisper + Wavesurfer.js | 0-200€/mois | - -**Coût total MVP** : **0-200€/mois** (infrastructure IA optionnelle) - -**Conformité** : -- ✅ DSA (Digital Services Act) : transparence, traçabilité, délais -- ✅ RGPD : données modération anonymisées après 3 ans -- ✅ Logs audit : toutes actions tracées (obligation légale plateforme) - -**Scalabilité** : -- 0-1000 signalements/mois : équipe 1-2 modérateurs junior + 1 senior -- 1000-10K signalements/mois : équipe 5-10 modérateurs + IA GPU -- 10K+ signalements/mois : équipe dédiée + IA optimisée + modération communautaire - ---- - -**Prochaine section à clarifier** : Section 11 (Mode offline) ou Section 12 (Gestion des erreurs) - - - - -
- - -## 15. Autres comportements - -### 15.1 Partage de contenu - -**Décision** : Système de partage complet avec web player - -#### 15.1.1 Bouton "Partager" - -**Disponibilité** : Partout dans l'application - -**Emplacements** : -- Player en lecture (bouton dans contrôles) -- Page profil créateur (sur chaque contenu) -- Liste de recherche (menu contextuel) -- Historique personnel - -**Icône** : ⬆️ (universelle iOS/Android) - -**Menu options** : -- Copier le lien -- WhatsApp -- Email -- SMS -- Plus... (sheet natif OS) - -**Justification** : -- Viralité = croissance organique gratuite -- Aucune friction, partage universel - ---- - -#### 15.1.2 Comportement du lien partagé - -**Format URL** : `https://roadwave.fr/share/c/[content_id]` - -**Comportement multi-plateforme** : - -``` -User clique lien partagé - ↓ -Page web responsive - ↓ -┌─────────────────────────────────┐ -│ Si app installée │ -│ → Deep link (ouverture directe) │ -└─────────────────────────────────┘ - ↓ -┌─────────────────────────────────┐ -│ Si app non installée │ -│ → Web player + CTA téléchargement│ -└─────────────────────────────────┘ -``` - -**Contenu de la page web** : - -```html -┌───────────────────────────────────────┐ -│ RoadWave │ -├───────────────────────────────────────┤ -│ [Image cover 16:9] │ -│ │ -│ 📻 Titre du contenu │ -│ Par @créateur · 12 min · 🎧 2.3K │ -│ │ -│ 📍 Paris 5e · Ancré │ -│ 🏷️ #Voyage #Histoire │ -│ │ -│ Description : Lorem ipsum... │ -│ │ -│ [▶️ Écouter maintenant] │ -│ (Player HTML5 si contenu public) │ -│ │ -│ ────────────────────────────────── │ -│ │ -│ 📱 Télécharger l'app RoadWave │ -│ [App Store] [Google Play] │ -│ │ -│ [Voir le profil de @créateur] │ -└───────────────────────────────────────┘ -``` - -**Métadonnées Open Graph (SEO)** : - -```html - - - - - - - - -``` - -**Deep linking** : -- iOS : Universal Links (configuration `apple-app-site-association`) -- Android : App Links (configuration `assetlinks.json`) -- URL scheme : `roadwave://content/[content_id]` - -**Justification** : -- Meilleure viralité (partage social optimisé) -- SEO (contenus indexés Google) -- UX optimale (web + app) -- Coût : 0€ (backend simple + CDN existant) - ---- - -#### 15.1.3 Contenus Premium partagés - -**Décision** : Preview 30 secondes + paywall - -**Comportement** : - -1. User clique lien contenu Premium partagé -2. Page web affiche badge "👑 Contenu Premium" -3. Player démarre automatiquement -4. Après **30 secondes exactement** : - - Fade out audio (2 secondes) - - Overlay apparaît : - -``` -┌─────────────────────────────────┐ -│ 👑 Contenu réservé Premium │ -│ │ -│ Profitez de ce contenu complet │ -│ et de milliers d'autres │ -│ sans publicité │ -│ │ -│ [Passer Premium - 4.99€/mois] │ -│ [Télécharger l'app] │ -└─────────────────────────────────┘ -``` - -5. Utilisateur peut : - - S'abonner Premium (redirection web Mangopay) - - Télécharger l'app (redirection stores) - - Rejouer les 30 premières secondes (illimité) - -**Tracking** : -- Métriques créateur : "Partages Premium" + "Conversions Premium" -- Créateur touche sa part si conversion (70%) - -**Justification** : -- Équilibre viralité / monétisation -- 30s = assez pour donner envie, pas assez pour satisfaire -- Protège revenus créateurs - ---- - -### 15.2 Profil créateur - -**Décision** : Profil public complet et transparent - -#### 15.2.1 Structure de la page profil - -**URL** : `https://roadwave.fr/@[pseudo]` - -**Layout** : - -``` -┌────────────────────────────────────────┐ -│ [Photo profil 120×120] │ -│ @pseudo ✓ │ -│ [Badge vérifié si applicable] │ -│ │ -│ Bio : Lorem ipsum dolor sit amet... │ -│ (300 caractères max) │ -│ │ -│ 🎧 1.2K abonnés │ -│ 📻 42 contenus │ -│ ⏱️ 18h de contenu créé │ -│ 🔊 54K écoutes totales │ -│ │ -│ [S'abonner] [Partager profil] [•••] │ -│ │ -│ ──────────────────────────────────── │ -│ │ -│ Contenus ▼ [Plus récents ▼] │ -│ │ -│ ┌──────────────────────────────────┐ │ -│ │ [Cover] Titre contenu 1 │ │ -│ │ 12 min · 🎧 2.3K · 📍 Paris │ │ -│ │ [▶️] │ │ -│ └──────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────┐ │ -│ │ [Cover] Titre contenu 2 │ │ -│ │ 8 min · 🎧 5.1K · 📍 Lyon │ │ -│ │ [▶️] │ │ -│ └──────────────────────────────────┘ │ -│ │ -│ [Charger plus] │ -└────────────────────────────────────────┘ -``` - -**Informations affichées** : - -| Élément | Visibilité | Détails | -|---------|------------|---------| -| **Photo + pseudo** | ✅ Public | Identité visuelle | -| **Badge vérifié ✓** | ✅ Public (si applicable) | Compte authentique | -| **Bio** | ✅ Public | 0-300 caractères, markdown basique (gras, italique, liens) | -| **Nombre abonnés** | ✅ Public | Arrondi si >1000 (ex: 1.2K, 54K) | -| **Nombre contenus** | ✅ Public | Exact | -| **Durée totale créée** | ✅ Public | Arrondi en heures (ex: 18h, 142h) | -| **Écoutes totales** | ✅ Public | Arrondi (ex: 54K, 1.2M) | -| **Liste abonnés** | ❌ Privé | Protection vie privée (RGPD) | -| **Revenus** | ❌ Privé | Confidentialité financière | -| **Localisation précise** | ❌ Privé | Sécurité | -| **Email** | ❌ Privé | Anti-spam | - -**Tri des contenus** : - -| Option | Comportement | -|--------|--------------| -| **Plus récents** | Date publication DESC (défaut) | -| **Plus populaires** | Écoutes complètes × (1 + (date_publication - now) / 90 jours) | -| **Plus anciens** | Date publication ASC | -| **Par tag** | Filtre multi-sélection tags | - -**Recherche locale** : -- Barre recherche dans profil : "Rechercher dans les contenus de @pseudo" -- Recherche full-text sur titres + descriptions - -**Actions menu [•••]** : -- Partager profil -- Signaler profil (spam, usurpation) -- Bloquer créateur (masque tous ses contenus) - ---- - -#### 15.2.2 Statistiques publiques - -**Décision** : Stats arrondies et motivantes - -**Affichage public** : - -| Métrique | Format affichage | Exemple | -|----------|------------------|---------| -| **Abonnés** | Exact si <1000, arrondi sinon | 342 / 1.2K / 54K / 1.2M | -| **Écoutes totales** | Arrondi dès 1000 | 842 / 5.4K / 142K / 2.1M | -| **Contenus publiés** | Exact | 42 contenus | -| **Durée totale** | Arrondi en heures | 18h / 142h de contenu | - -**Métriques PRIVÉES (créateur uniquement)** : - -| Métrique | Disponible dans dashboard créateur | -|----------|-------------------------------------| -| **Taux complétion moyen** | 78% (écoutes >80% / écoutes totales) | -| **Évolution abonnés** | Graphique 30j / 90j / 1 an | -| **Écoutes par contenu** | Tableau détaillé | -| **Revenus** | Dashboard monétisation dédié | -| **Taux conversion Premium** | Partages → conversions | -| **Démographie** | Âge / zone géo (agrégée, anonymisée) | - -**Justification** : -- Arrondi = évite comparaisons anxiogènes -- Preuve sociale pour nouveaux auditeurs (trust) -- Gamification douce (motivation créateurs) -- Privacy by design - ---- - -#### 15.2.3 Badge vérifié - -**Décision** : Badge unique ✓ (vérifié officiel) - -**Critères d'attribution** (au moins UN des critères) : - -1. **KYC monétisation validé** : identité vérifiée via Mangopay KYC -2. **Célébrité / Média officiel** : validation manuelle équipe RoadWave -3. **Communauté significative** : ≥10K abonnés + compte actif >6 mois - -**Affichage** : -- Badge bleu **✓** accolé au pseudo (partout : profil, player, recherche) -- Tooltip au survol/appui long : "Compte vérifié" - -**Processus d'obtention** : - -| Type | Processus | -|------|-----------| -| **Automatique (KYC)** | Badge attribué dès validation documents Mangopay | -| **Manuel (célébrité)** | Formulaire demande → équipe vérifie identité → validation 48-72h | -| **Automatique (10K)** | Badge attribué automatiquement à 10K abonnés si compte >6 mois | - -**Retrait du badge** : -- Suspension monétisation → badge retiré temporairement -- Strikes multiples → badge retiré définitivement -- Usurpation identité détectée → ban + retrait - -**Justification** : -- Combat usurpations d'identité -- Trust auditeurs (surtout pour médias/personnalités) -- Simplicité (1 seul badge, pas de gamification excessive) -- Coût : 0€ (champ boolean `verified` en DB) - ---- - -### 15.3 Recherche - -**Décision** : Recherche full-text + géo + filtres avancés - -#### 15.3.1 Recherche par mot-clé - -**Implémentation** : PostgreSQL full-text search (français) - -**Configuration technique** : - -```sql --- Index full-text optimisé français -CREATE INDEX idx_content_search ON contents -USING GIN( - to_tsvector('french', - coalesce(title, '') || ' ' || - coalesce(description, '') || ' ' || - coalesce(creator_pseudo, '') - ) -); - --- Recherche avec ranking -SELECT - c.*, - ts_rank( - to_tsvector('french', c.title || ' ' || c.description), - plainto_tsquery('french', $search_query) - ) AS rank -FROM contents c -WHERE to_tsvector('french', c.title || ' ' || c.description) - @@ plainto_tsquery('french', $search_query) -ORDER BY rank DESC, listen_count DESC -LIMIT 20; -``` - -**Champs indexés** : -- Titre du contenu (poids × 3) -- Description (poids × 1) -- Pseudo créateur (poids × 2) -- Tags (poids × 1.5) - -**Fonctionnalités** : - -| Feature | Description | -|---------|-------------| -| **Stemming français** | "voyages" trouve "voyage", "voyager", etc. | -| **Correction auto** | Suggestion si 0 résultat | -| **Recherches populaires** | "Essayez plutôt : balade paris, audio-guide louvre" | -| **Historique personnel** | 10 dernières recherches sauvegardées | -| **Autocomplete** | Suggestions pendant frappe (top 5) | - -**Coût** : 0€ (PostgreSQL natif) - -**Migration future** : -- Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois) -- Si >1M contenus : Elasticsearch cluster - -**Justification** : -- PostgreSQL full-text = performant jusqu'à 500K contenus -- Stemming français natif -- 0€, aucune dépendance externe - ---- - -#### 15.3.2 Recherche géographique - -**Décision** : Recherche lieu + rayon paramétrable - -**Interface utilisateur** : - -``` -┌─────────────────────────────────────┐ -│ 🔍 Recherche contenu... │ -├─────────────────────────────────────┤ -│ �� Lieu │ -│ [Paris, France ▼] │ -│ · Autour de moi (GPS actuel) │ -│ · Entrer une adresse/ville │ -│ │ -│ 📏 Rayon de recherche │ -│ [●─────────────────] 50 km │ -│ (curseur 5 km → 500 km) │ -│ │ -│ 🗺️ [Afficher sur carte] │ -└─────────────────────────────────────┘ -``` - -**Géocodage** : - -| Service | Usage | Coût | -|---------|-------|------| -| **Nominatim (OSM)** | MVP (API publique) | 0€ (rate limit 1 req/s) | -| **Nominatim self-hosted** | Scale (Docker) | 20-50€/mois VPS | -| **Mapbox Geocoding** | Fallback premium | 0.50€ / 1000 requêtes | - -**Processus de recherche géo** : - -1. User tape "Louvre" ou "Paris" -2. Autocomplete via Nominatim → liste suggestions -3. User sélectionne → récupération coordonnées (lat, lon) -4. Requête PostGIS : - -```sql -SELECT c.*, - ST_Distance(c.location::geography, ST_Point($lon, $lat)::geography) AS distance -FROM contents c -WHERE ST_DWithin( - c.location::geography, - ST_Point($lon, $lat)::geography, - $radius_meters -) -ORDER BY distance ASC; -``` - -**Affichage résultats** : -- Tri par défaut : distance croissante -- Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km" -- Option carte : markers cliquables (clustering si >50 résultats) - -**Coût** : -- MVP : 0€ (Nominatim public) -- Scale : 20-50€/mois (Nominatim self-hosted Docker) - -**Justification** : -- Essentiel pour tourisme / planification trajet -- OpenStreetMap = pas de dépendance Google -- PostGIS = performant (index GIST natif) - ---- - -#### 15.3.3 Filtres avancés - -**Décision** : 7 catégories de filtres combinables - -**Interface filtres** : - -``` -┌─────────────────────────────────────┐ -│ Filtres [×] │ -├─────────────────────────────────────┤ -│ Type de contenu │ -│ ☐ Contenu court (<5 min) │ -│ ☐ Podcast (>5 min) │ -│ ☐ Radio live │ -│ ☐ Audio-guide │ -│ │ -│ Durée │ -│ ○ Toutes durées │ -│ ○ <5 min │ -│ ○ 5-15 min │ -│ ○ 15-30 min │ -│ ○ >30 min │ -│ │ -│ Classification âge │ -│ ☐ Tout public │ -│ ☐ 13+ │ -│ ☐ 16+ │ -│ ☐ 18+ │ -│ │ -│ Géo-pertinence │ -│ ☐ Ancré (lieu précis) │ -│ ☐ Contextuel (zone large) │ -│ ☐ Neutre (national) │ -│ │ -│ Tags (multi-sélection) │ -│ ☐ Automobile ☐ Voyage │ -│ ☐ Famille ☐ Histoire │ -│ ☐ Économie ☐ Sciences │ -│ ... (liste complète tags) │ -│ │ -│ Date de publication │ -│ ○ Toutes dates │ -│ ○ Dernières 24h │ -│ ○ Cette semaine │ -│ ○ Ce mois │ -│ ○ Cette année │ -│ │ -│ Abonnement │ -│ ○ Tous les contenus │ -│ ○ Gratuits uniquement │ -│ ○ Premium uniquement 👑 │ -│ │ -│ ────────────────────────────── │ -│ [Réinitialiser] [Appliquer] │ -└─────────────────────────────────────┘ -``` - -**Options de tri** : - -| Tri | Algorithme | -|-----|-----------| -| **Pertinence** | Score recherche × (1 + log(listen_count + 1)) | -| **Popularité** | Écoutes complètes derniers 30j DESC | -| **Récent** | Date publication DESC | -| **Proximité** | Distance GPS ASC (si recherche géo active) | -| **Durée** | Durée audio ASC ou DESC | - -**Sauvegarde de recherches** : - -- Bouton "💾 Sauvegarder cette recherche" -- Nom personnalisable : "Podcasts voyage Paris" -- Maximum **5 recherches sauvegardées** -- Accès rapide : onglet "Recherches sauvegardées" dans page recherche -- Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'" - -**Performances** : - -```sql --- Index composites pour filtres -CREATE INDEX idx_content_filters ON contents ( - content_type, - duration, - age_rating, - geo_type, - published_at -); - --- Index GIN pour tags -CREATE INDEX idx_content_tags ON contents USING GIN(tags); -``` - -**Coût** : 0€ (PostgreSQL + index standards) - -**Justification** : -- Filtres essentiels pour découvrabilité -- Combinables = puissance maximale -- Sauvegarde = gain temps utilisateurs réguliers - ---- - -#### 15.3.4 Page de résultats - -**Décision** : Liste avec previews enrichies - -**Layout résultats** : - -``` -┌─────────────────────────────────────────┐ -│ 🔍 "voyage paris" │ -│ 42 résultats · Tri : Pertinence ▼ │ -│ [Filtres] [Carte] │ -├─────────────────────────────────────────┤ -│ ┌─────────────────────────────────────┐ │ -│ │ [Cover ] Balade à Paris │ │ -│ │ [16:9 ] @paris_stories ✓ │ │ -│ │ [Image ] 12 min · 🎧 2.3K │ │ -│ │ 📍 Paris 5e · Ancré │ │ -│ │ 🏷️ #Voyage #Histoire │ │ -│ │ [▶️ Écouter] [⋮] │ │ -│ └─────────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────┐ │ -│ │ [Cover ] Secrets Montmartre │ │ -│ │ [16:9 ] @explore_paris │ │ -│ │ [Image ] 8 min · 🎧 5.1K │ │ -│ │ 📍 Paris 18e · Guide │ │ -│ │ 🏷️ #Voyage #Art │ │ -│ │ [▶️ Écouter] [⋮] │ │ -│ └─────────────────────────────────────┘ │ -│ │ -│ [Charger plus] (20 suivants) │ -└─────────────────────────────────────────┘ -``` - -**Informations par résultat** : - -| Élément | Affichage | -|---------|-----------| -| **Cover image** | 16:9, 120×68 px, lazy loading | -| **Titre** | Tronqué 2 lignes max | -| **Créateur** | @pseudo + badge ✓ si vérifié, cliquable → profil | -| **Durée** | Format : "3 min" / "12 min" / "1h 24 min" | -| **Écoutes** | Arrondi : "2.3K" / "54K" / "1.2M" | -| **Localisation** | Ville + type géo (Ancré/Contextuel/Neutre) | -| **Tags** | Maximum 3 premiers tags | -| **Badge Premium** | 👑 si contenu premium | -| **Distance** | Si recherche géo : "À 2.3 km" | - -**Actions contextuelles [⋮]** : -- Partager -- Ajouter à une playlist (future feature) -- Télécharger (offline) -- Signaler - -**Pagination** : -- **20 résultats** par page -- Infinite scroll (charger automatiquement si scroll >80%) -- Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé) - -**Vue carte (alternative)** : -- Bouton toggle "Liste / Carte" -- Map Leaflet (OpenStreetMap) -- Markers cliquables → popup avec preview -- Clustering si >50 résultats proches - -**Coût** : 0€ (Leaflet open source + OSM tiles gratuit) - -**Justification** : -- Équilibre information / compacité -- Lazy loading = performances -- Infinite scroll = UX moderne - ---- - -## Récapitulatif Section 15 - -| Point | Décision | Coût | Complexité | -|-------|----------|------|------------| -| **15.1.1** Bouton partager | Disponible partout (⬆️), menu natif OS | 0€ | Faible | -| **15.1.2** Lien partagé | Web player + deep link + Open Graph SEO | 0€ | Moyenne | -| **15.1.3** Premium partagé | Preview 30s + paywall overlay | 0€ | Faible | -| **15.2.1** Page profil | Profil public complet (stats + bio + contenus + tri) | 0€ | Faible | -| **15.2.2** Stats publiques | Arrondies (abonnés, écoutes, durée totale) | 0€ | Faible | -| **15.2.3** Badge vérifié | ✓ si KYC/célébrité/>10K abonnés | 0€ | Faible | -| **15.3.1** Recherche texte | PostgreSQL full-text french + stemming | 0€ | Moyenne | -| **15.3.2** Recherche géo | Lieu + rayon (Nominatim OSM) | 0-50€/mois | Moyenne | -| **15.3.3** Filtres | 7 catégories combinables + sauvegarde recherches | 0€ | Moyenne | -| **15.3.4** Page résultats | Liste enrichie + vue carte Leaflet + infinite scroll | 0€ | Moyenne | - -**Coût total MVP : 0-50€/mois** (Nominatim self-hosted optionnel) - ---- - -## Points d'attention pour Gherkin - -- Tester partage contenu public vs Premium (preview 30s) -- Tester deep linking iOS/Android (ouverture app si installée) -- Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook) -- Tester profil public (stats arrondies, badge vérifié) -- Tester recherche full-text français (stemming, accents) -- Tester recherche géo + rayon (PostGIS distance) -- Tester combinaison filtres multiples (AND logic) -- Tester sauvegarde recherches (max 5) -- Tester pagination infinite scroll + fallback bouton -- Tester vue carte Leaflet (clustering, markers cliquables) - - - - -
- - -## 16. Audio-guides multi-séquences - -### 16.1 Types d'audio-guides et modes de déplacement - -**Décision** : 4 modes distincts avec détection automatique - -#### 16.1.1 Classification par mode - -| Mode | Vitesse détection | Déclenchement | Use case | -|------|-------------------|---------------|----------| -| **🚶 Piéton** | <5 km/h | Manuel (bouton "Suivant") | Musées, visites urbaines, monuments | -| **🚗 Voiture** | >10 km/h | Auto GPS + Manuel possible | Safari-parc, routes touristiques, circuits auto | -| **🚴 Vélo** | 5-25 km/h | Auto GPS + Manuel possible | Pistes cyclables, circuits vélo, parcours nature | -| **🚌 Transport** | Variable | Auto GPS + Manuel possible | Bus touristiques, trains panoramiques | - -**Détection automatique** : -- Vitesse moyenne calculée sur 30 secondes -- Suggestion mode au démarrage : "Détection : 🚗 Voiture. Est-ce correct ? [Oui] [Changer]" -- User peut forcer mode manuellement (settings) - -**Justification** : -- Flexibilité maximale créateurs et utilisateurs -- Expériences optimisées par type de déplacement -- Gestion cas limites (vélo lent vs piéton rapide) - ---- - -#### 16.1.2 Création d'un audio-guide (côté créateur) - -**Formulaire création** : - -``` -┌────────────────────────────────────────┐ -│ Nouvel audio-guide multi-séquences │ -├────────────────────────────────────────┤ -│ Titre : [Safari du Paugre] │ -│ Description : [Découvrez les animaux │ -│ du parc en voiture...] │ -│ │ -│ Mode de déplacement : *obligatoire │ -│ ○ 🚶 Piéton (navigation manuelle) │ -│ ● 🚗 Voiture (GPS auto + manuel) │ -│ ○ 🚴 Vélo (GPS auto + manuel) │ -│ ○ 🚌 Transport (GPS auto + manuel) │ -│ │ -│ Vitesse recommandée : 30-50 km/h │ -│ (si voiture/vélo/transport) │ -│ │ -│ ──────────────────────────────────── │ -│ │ -│ Séquences (ordre lecture) : │ -│ │ -│ 1. [📍] Introduction - Point d'accueil │ -│ Lat: 43.1234, Lon: 2.5678 │ -│ Rayon déclenchement : 30m │ -│ Durée : 2:15 │ -│ [🎵 Audio uploadé] [✏️] [🗑️] │ -│ │ -│ 2. [📍] Enclos des lions │ -│ Lat: 43.1245, Lon: 2.5690 │ -│ Rayon déclenchement : 30m │ -│ Durée : 3:42 │ -│ [📤 Upload audio] [✏️] [🗑️] │ -│ │ -│ 3. [📍] Enclos des girafes │ -│ [+ Ajouter point GPS] │ -│ │ -│ [+ Ajouter séquence] │ -│ │ -│ 📊 Statistiques : │ -│ · 2 séquences complètes │ -│ · 5:57 durée totale │ -│ · 320m distance totale │ -│ │ -│ [🗺️ Aperçu sur carte] │ -│ [✅ Publier audio-guide] │ -└────────────────────────────────────────┘ -``` - -**Métadonnées obligatoires** : - -| Champ | Requis | Détails | -|-------|--------|---------| -| **Titre audio-guide** | ✅ | 5-100 caractères | -| **Description** | ✅ | 10-500 caractères | -| **Mode déplacement** | ✅ | Piéton / Voiture / Vélo / Transport | -| **Nombre séquences** | ✅ | Minimum 2, maximum 50 | -| **Point GPS par séquence** | ✅ (sauf piéton) | Latitude, longitude (WGS84) | -| **Rayon déclenchement** | ✅ (sauf piéton) | 10-100m selon mode | -| **Vitesse recommandée** | ❌ | Optionnel, affichée utilisateur | -| **Tags** | ✅ | 1-3 parmi liste prédéfinie | -| **Classification âge** | ✅ | Tout public / 13+ / 16+ / 18+ | -| **Zone diffusion** | ✅ | Polygon géographique | - -**Wizard de création** : -- Étape 1 : Infos générales (titre, description, mode) -- Étape 2 : Ajout séquences une par une -- Étape 3 : Preview carte (trace + points) -- Étape 4 : Validation modération (3 premiers audio-guides) - -**Justification** : -- Contrôle total créateur sur expérience -- Carte preview aide visualiser parcours -- Wizard guidé = réduction friction création - ---- - -### 16.2 Mode Piéton (manuel) - -**Décision** : Navigation manuelle avec pub auto-play - -#### 16.2.1 Passage entre séquences - -**Séquence normale (sans pub)** : - -1. Séquence 1 se termine -2. Player se met en **pause automatique** -3. Message affiché : "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt." -4. User appuie sur [▶|] → Séquence 2 démarre immédiatement - -**Séquence avec publicité** (1 pub / 5 séquences) : - -1. Séquence 2 se termine -2. **Publicité s'enchaîne automatiquement** (pas d'attente bouton) -3. Pub se lit (skippable après 5s) -4. Pub se termine → Player se met en **pause automatique** -5. Message : "Séquence 3 prête. Appuyez sur Suivant." -6. User appuie sur [▶|] → Séquence 3 démarre - -**Schéma flux** : -``` -Séquence 1 [fin] → PAUSE → User clique → Séquence 2 [fin] → PUB AUTO-PLAY → PAUSE → User clique → Séquence 3 -``` - -**Fréquence pub** : -- Gratuits : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10) -- Premium : 0 pub - -**Justification** : -- Pub s'insère naturellement (pas d'attente utilisateur pour déclencher) -- User garde contrôle rythme visite (pause après pub) -- Monétisation effective créateurs -- Premium reste attractif (0 interruption) - ---- - -#### 16.2.2 Navigation et contrôles - -**Décision** : Liberté totale utilisateur - -**Contrôles disponibles** : - -| Bouton | Fonction | Comportement | -|--------|----------|--------------| -| **[▶\|] Suivant** | Passe séquence suivante | Immédiat, même si séquence actuelle pas terminée | -| **[\|◀] Précédent** | Retour séquence précédente | Saut direct séquence avant (pas de logique "replay si >10s") | -| **[⏸️] Pause** | Pause temporaire | Reprend à position exacte | -| **[▶️] Play** | Reprend lecture | Continue position actuelle | -| **Liste séquences** | Navigation libre | Tap séquence → saut direct (même séquences non écoutées) | - -**Interface liste séquences** : - -``` -┌────────────────────────────────────────┐ -│ 🚶 Audio-guide Piéton │ -│ Musée du Louvre │ -├────────────────────────────────────────┤ -│ [Cover image] │ -│ │ -│ ▶️ 0:00 ──●────────── 3:42 │ -│ │ -│ Séquence 3/12 : La Joconde │ -│ │ -│ [|◀] [⏸️] [▶|] │ -│ │ -│ ──────────────────────────────────── │ -│ │ -│ 📋 Liste des séquences │ -│ │ -│ ✅ 1. Introduction (2:15) │ -│ Écouté le 15/01/2026 │ -│ │ -│ ✅ 2. Pyramide du Louvre (1:48) │ -│ Écouté le 15/01/2026 │ -│ │ -│ ▶️ 3. La Joconde (3:42) - EN COURS │ -│ ──●──────────── 1:22/3:42 │ -│ │ -│ ⭕ 4. Vénus de Milo (2:58) │ -│ │ -│ ⭕ 5. Code d'Hammurabi (4:12) │ -│ │ -│ ⭕ 6. Victoire de Samothrace (3:25) │ -│ │ -│ ... +6 séquences │ -│ │ -│ [Tout afficher ▼] │ -└────────────────────────────────────────┘ -``` - -**Navigation libre** : -- User peut sauter séquences déjà connues -- User peut revenir en arrière à tout moment -- User peut aller directement à séquence 8 (même si 4-7 non écoutées) - -**Sauvegarde progression** : -- Checkmarks ✅ sur séquences écoutées >80% -- Position exacte sauvegardée dans séquence en cours - -**Justification** : -- Utilisateur contrôle 100% son rythme -- Adapté musées : visitor peut voir physiquement une œuvre lointaine et vouloir écouter sa description -- Pas de frustration (liberté totale) - ---- - -### 16.3 Mode Voiture (GPS automatique) - -**Décision** : GPS auto avec navigation manuelle conservée - -#### 16.3.1 Déclenchement et contrôles - -**Distinction audio-guides vs contenus géolocalisés simples** : - -⚠️ **Important** : Les audio-guides multi-séquences fonctionnent différemment des contenus géolocalisés simples. - -| Type | Séquences | Déclenchement | Notification | Enchaînement | Comptabilité quota | -|------|-----------|---------------|--------------|--------------|-------------------| -| **Contenu géolocalisé simple** | 1 séquence unique | Notification 7s avant (temps ETA) | Sonore + icône | Fin → retour buffer normal | 1 contenu = 1 quota | -| **Audio-guide multi-séquences** | 2 à 50 séquences | Au point GPS exact (distance 30m) | Ding + toast 2s | Séquences s'enchaînent auto | 1 audio-guide = 1 quota (toutes séquences) | - -**Fonctionnement GPS automatique** : - -1. User démarre audio-guide en voiture (voir section 16.1 pour démarrage) -2. Séquence 1 démarre automatiquement au point GPS défini (rayon 30m) -3. Séquence 1 se termine -4. **Affichage progress bar** : distance temps réel + ETA jusqu'au prochain point -5. User roule vers point GPS suivant -6. Arrivée au point GPS suivant (rayon 30m) → **déclenchement automatique** séquence suivante -7. Notification sonore discrète : "Ding" (0.3s) + toast 2s : "Enclos des girafes" -8. Séquence suivante démarre immédiatement (pas de décompte) - -**Pas de système "7 secondes avant" pour les audio-guides** : -- Contrairement aux contenus géolocalisés simples (voir [05-interactions-navigation.md](#05-interactions-navigation.md#511-file-dattente-et-commande-suivant)) -- Les séquences se déclenchent **au point GPS exact** (rayon 30m) -- Raison : expérience guidée continue, user sait qu'il suit un parcours - -**Navigation manuelle CONSERVÉE** : - -| Bouton | État | Comportement | -|--------|------|--------------| -| **[▶\|] Suivant** | ✅ Toujours actif | Passe séquence suivante immédiatement (même hors point GPS) | -| **[\|◀] Précédent** | ✅ Toujours actif | Retour séquence précédente (même hors point GPS) | -| **[⏸️] Pause** | ✅ | Pause temporaire | -| **Liste séquences** | ✅ | Saut direct possible | - -**Use cases navigation manuelle** : - -| Situation | Solution manuelle | -|-----------|-------------------| -| Embouteillage (séquence finie, point GPS loin) | User clique Suivant → avance manuellement | -| Point GPS inaccessible (route fermée) | User clique Suivant → skip point | -| Envie réécouter séquence précédente | User clique Précédent → retour | -| Passager manipule l'app | Passager navigue librement | - -**Avertissement sécurité** : - -- Si vitesse **>10 km/h** ET user clique bouton (Suivant/Précédent) : - - Toast 3 secondes : "⚠️ Manipulation en conduite détectée. Pour votre sécurité, demandez à un passager." - - **Action quand même exécutée** (pas de blocage) -- Justification : sensibilisation sans bloquer (passager peut légitimement manipuler) - -**Schéma flux** : -``` -Point GPS 1 (30m) → Séquence 1 AUTO → User roule → Distance affichée → Point GPS 2 (30m) → Séquence 2 AUTO - ↓ - User clique Suivant (manuel) → Séquence 2 immédiate -``` - -**Justification** : -- Flexibilité maximale : GPS optimise expérience MAIS user garde contrôle -- Gestion cas limites : routes fermées, détours, embouteillages -- Sécurité : warning sensibilise sans bloquer (passager légitime) - ---- - -#### 16.3.2 Affichage distance et guidage - -**Décision** : Distance + direction (PAS de carte miniature) - -**Interface en conduite** : - -``` -┌────────────────────────────────────────┐ -│ 🚗 Audio-guide Voiture │ -│ Safari du Paugre │ -├────────────────────────────────────────┤ -│ │ -│ ▶️ 0:00 ──●────────── 2:15 │ -│ │ -│ Séquence 2/8 : Les lions │ -│ │ -│ ──────────────────────────────────── │ -│ │ -│ 📍 Prochain point │ -│ │ -│ Enclos des girafes │ -│ │ -│ ┌────────────────────────────────┐ │ -│ │ │ │ -│ │ ↗️ │ │ -│ │ (direction) │ │ -│ │ │ │ -│ │ 320 mètres │ │ -│ │ ≈ 40 secondes │ │ -│ │ │ │ -│ └────────────────────────────────┘ │ -│ │ -│ Vitesse actuelle : 28 km/h │ -│ Vitesse recommandée : 20-30 km/h │ -│ │ -│ [|◀] [⏸️] [▶|] [📋 Liste] │ -└────────────────────────────────────────┘ -``` - -**Affichage entre deux séquences** : - -Quand une séquence se termine et qu'il reste un point GPS suivant, l'interface bascule en mode "attente prochain point" : - -``` -┌────────────────────────────────────────┐ -│ 🚗 Audio-guide Voiture │ -│ Safari du Paugre │ -├────────────────────────────────────────┤ -│ │ -│ ✅ Séquence 2/8 terminée │ -│ Les lions │ -│ │ -│ ──────────────────────────────────── │ -│ │ -│ 📍 Prochain point │ -│ │ -│ Enclos des girafes │ -│ │ -│ ┌────────────────────────────────┐ │ -│ │ [Progress bar] │ │ -│ │ ████████░░░░░░░░░ 65% │ │ -│ │ │ │ -│ │ ↗️ │ │ -│ │ (direction) │ │ -│ │ │ │ -│ │ 320 mètres │ │ -│ │ ≈ 40 secondes │ │ -│ │ │ │ -│ └────────────────────────────────┘ │ -│ │ -│ Vitesse actuelle : 28 km/h │ -│ │ -│ [|◀] [▶️ Rejouer séq.] [▶|] │ -└────────────────────────────────────────┘ -``` - -**Progress bar dynamique** : -- Se remplit au fur et à mesure qu'on se rapproche du point -- Calcul : `progress = 100 - (distance_actuelle / distance_initiale * 100)` -- Exemple : distance initiale 500m, distance actuelle 175m → progress = 65% -- Couleur : vert (#4CAF50) pour la partie remplie, gris (#E0E0E0) pour le reste - -**Bouton "Rejouer séq."** : -- Permet de réécouter la séquence qui vient de se terminer -- User clique → séquence actuelle redémarre depuis 0:00 -- Utile si distraction pendant l'écoute - ---- - -**Informations affichées** : - -| Info | Mise à jour | Format | -|------|-------------|--------| -| **Distance** | Chaque seconde | "320 m" / "1.2 km" | -| **ETA** | Chaque seconde | "≈ 40 secondes" / "≈ 2 minutes" | -| **Direction** | Chaque 5s | Flèche indique direction (8 directions : ↑ ↗ → ↘ ↓ ↙ ← ↖) | -| **Vitesse actuelle** | Chaque seconde | "28 km/h" | -| **Vitesse recommandée** | Statique | "20-30 km/h" (définie par créateur) | -| **Progress bar** | Chaque seconde | Pourcentage parcouru vers prochain point | - -**Calcul direction** : - -```javascript -// Calcul angle entre position actuelle et prochain point -const currentGPS = getCurrentLocation(); -const nextPoint = audioGuide.sequences[currentIndex + 1].location; - -const angle = calculateBearing(currentGPS, nextPoint); // 0-360° - -// Conversion en flèche (8 directions) -const arrows = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖']; -const index = Math.round(angle / 45) % 8; -const direction = arrows[index]; -``` - -**Calcul ETA** : - -```javascript -const distance = calculateDistance(currentGPS, nextPoint); // mètres -const currentSpeed = getCurrentSpeed(); // km/h - -if (currentSpeed > 5) { - const eta = (distance / 1000) / currentSpeed * 3600; // secondes - return formatETA(eta); // "≈ 40 secondes" ou "≈ 2 minutes" -} else { - return "En attente de déplacement"; -} -``` - -**Justification** : -- Distance + ETA = info essentielle sans surcharge visuelle -- Direction (flèche) = aide se repérer sans carte complexe -- Simplicité = moins distraction conducteur -- Économie batterie (pas de rendu carte) - ---- - -#### 16.3.3 Rayon de déclenchement et tolérance - -**Décision** : Rayon configurable créateur avec défauts intelligents - -**Rayons par défaut** : - -| Mode | Rayon déclenchement | Rayon "point manqué" | Justification | -|------|---------------------|----------------------|---------------| -| **🚗 Voiture** | 30 mètres | 100 mètres | Vitesse élevée = anticipation | -| **🚴 Vélo** | 50 mètres | 75 mètres | Vitesse variable, arrêts fréquents | -| **🚌 Transport** | 100 mètres | 150 mètres | Arrêts bus/train, moins précis | - -**Configuration créateur** : - -- Curseur rayon : **10m → 200m** -- Défaut pré-sélectionné selon mode choisi -- Preview visuel : cercle sur carte (lors création) -- Suggestion auto : "Recommandé : 30m pour voiture à 30 km/h" - -**Gestion point manqué** : - -``` -User passe à 110m du point GPS -(hors rayon déclenchement 30m MAIS dans rayon tolérance 100m) - ↓ -Toast : "⚠️ Point manqué : Enclos des girafes" - ↓ -Popup 5 secondes : -┌────────────────────────────────────┐ -│ Point manqué │ -│ │ -│ "Enclos des girafes" │ -│ Vous êtes passé à 110m du point │ -│ │ -│ [🔊 Écouter quand même] │ -│ [⏭️ Passer au suivant] │ -│ [🔙 Faire demi-tour] │ -└────────────────────────────────────┘ -``` - -**Actions popup** : - -| Bouton | Comportement | -|--------|--------------| -| **Écouter quand même** | Lance séquence immédiatement (même hors zone) | -| **Passer au suivant** | Skip séquence, continue vers prochain point | -| **Faire demi-tour** | Lance navigation GPS externe (Google Maps / Waze) vers point manqué | - -**Si user au-delà rayon tolérance (>100m)** : -- Aucun popup (point trop loin, probablement hors itinéraire) -- User peut naviguer manuellement (bouton Suivant) - -**Justification** : -- Flexibilité créateur (ajuste selon terrain, vitesse prévue) -- Gestion intelligente imprévus (détours, routes fermées) -- User pas bloqué (toujours moyen avancer) - ---- - -### 16.4 Modes Vélo et Transport - -**Décision** : Même logique voiture avec tolérances ajustées - -**Différences par rapport à mode voiture** : - -| Paramètre | Voiture | Vélo | Transport | -|-----------|---------|------|-----------| -| **Rayon déclenchement** | 30m | 50m | 100m | -| **Rayon tolérance "point manqué"** | 100m | 75m | 150m | -| **Vitesse recommandée affichée** | 20-50 km/h | 10-25 km/h | Variable (selon ligne) | -| **Warning sécurité** | >10 km/h | >5 km/h | Désactivé | - -**Mode Vélo spécificités** : - -- Rayon plus large : vitesse variable, nombreux arrêts (feux, piétons) -- Warning sécurité dès 5 km/h (vélo en mouvement) -- Tolérance GPS moins stricte (tracé moins prévisible qu'auto) - -**Mode Transport spécificités** : - -- Rayon très large : arrêts fréquents (bus, train), ligne fixe -- Pas de warning sécurité (user = passager, pas conducteur) -- Vitesse recommandée = "Selon ligne" (pas de valeur fixe) -- Tolérance horaire : si bus en retard, point peut se déclencher avec 2-3 min de délai - -**Comportement identique voiture** : - -- Navigation manuelle conservée (boutons actifs) -- Affichage distance + ETA + direction -- Gestion point manqué -- Pub entre séquences - -**Justification** : -- Vélo : moins de contrôle qu'auto (obstacles, arrêts), nécessite tolérance -- Transport : moins de contrôle utilisateur (suit ligne fixe), rayon large compense -- Même UX globale = cohérence - ---- - -### 16.5 Publicités dans audio-guides - -**Décision** : Pub auto-play entre séquences TOUS modes - -#### 16.5.1 Règles universelles - -**Insertion publicité** : - -- Fréquence : **1 pub toutes les 5 séquences** (paramétrable admin 1/3 à 1/10) -- Gratuits uniquement, **Premium 0 pub** -- Pub s'enchaîne **automatiquement** après séquence -- Skippable après **5 secondes** (règle standard RoadWave) -- Volume normalisé -14 LUFS (comme pubs normales) - -**Comportement MODE PIÉTON** : - -``` -Séquence 2 [fin] - → Pub AUTO-PLAY - → Pub se termine - → PAUSE AUTO - → Message "Séquence 3 prête. Appuyez sur Suivant." - → User clique [▶|] - → Séquence 3 démarre -``` - -**Comportement MODE VOITURE/VÉLO/TRANSPORT** : - -``` -Séquence 2 [fin] - → Pub AUTO-PLAY - → Pub se termine - → ATTENTE point GPS suivant OU user clique Suivant - → Séquence 3 démarre -``` - -**Schéma complet** : - -| Mode | Après séquence normale | Après pub | -|------|------------------------|-----------| -| **Piéton** | Pause + attente user | Pause + attente user | -| **Voiture** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant | -| **Vélo** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant | -| **Transport** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant | - -**Justification** : -- Monétisation équitable créateurs (tous modes participent) -- Pub s'insère naturellement (auto-play, pas d'attente utilisateur) -- User garde contrôle : piéton clique Suivant, voiture peut skip manuel -- Premium reste attractif (expérience 0 interruption) -- Modèle économique viable - ---- - -#### 16.5.2 Métriques pub audio-guides - -**Dashboard créateur** : - -| Métrique | Affichage | -|----------|-----------| -| **Impressions pub** | Nombre de pubs insérées dans audio-guides | -| **Écoutes complètes pub** | Nombre de pubs écoutées >80% | -| **Taux skip pub** | % pubs skippées avant 5s vs après | -| **Revenus pub audio-guides** | 3€ / 1000 écoutes complètes (6% CA pub) | - -**Distinction contenus normaux vs audio-guides** : -- Dashboard sépare : "Revenus contenus classiques" / "Revenus audio-guides" -- Permet créateur voir performance par type - -**Justification** : -- Transparence créateur (comprend revenus) -- Incite création audio-guides (nouvelle source revenus) - ---- - -### 16.6 Reprise et sauvegarde progression - -**Décision** : Sauvegarde complète automatique avec popup intelligente - -#### 16.6.1 Sauvegarde automatique - -**Données sauvegardées** : - -| Info | Détail | Utilité | -|------|--------|---------| -| **Audio-guide ID** | Identifiant unique | Retrouver audio-guide | -| **Séquence actuelle** | Index (ex: 3/12) | Reprise position | -| **Position dans séquence** | Timestamp exact (ex: 1:42/3:20) | Reprise exacte | -| **Séquences écoutées** | Liste avec checkmarks ✅ | Historique progression | -| **Date dernière écoute** | Timestamp | Proposer reprise si <30j | -| **GPS dernière position** | Coordonnées optionnelles | Info contextuelle (non utilisée pour reprise) | - -**Stockage** : - -| Environnement | Technologie | Utilité | -|---------------|-------------|---------| -| **Local** | SQLite mobile | Fonctionnement offline | -| **Cloud** | PostgreSQL (sync auto) | Multi-device (reprendre sur autre appareil) | - -**Synchronisation** : -- Sauvegarde locale : chaque fin de séquence + chaque 30s -- Sync cloud : à la reconnexion réseau (batch) - -**Justification** : -- Expérience fluide (pas de perte progression) -- Multi-device (démarrer sur iPhone, continuer sur iPad) -- Offline-first (fonctionne sans réseau) - ---- - -#### 16.6.2 Interface de reprise - -**Conditions popup** : -- Dernière écoute **<30 jours** -- Progression **>0%** et **<100%** (pas terminé) - -**Popup reprise** : - -``` -┌────────────────────────────────────────┐ -│ Reprendre l'audio-guide ? │ -├────────────────────────────────────────┤ -│ 🚗 Safari du Paugre │ -│ @safari_createur │ -│ │ -│ Progression : 3/8 séquences écoutées │ -│ Dernière écoute : il y a 2 jours │ -│ │ -│ Vous étiez à : │ -│ "Les lions" (1:42/3:20) │ -│ │ -│ [▶️ Reprendre] [🔄 Recommencer] │ -│ [📋 Voir toutes les séquences] │ -└────────────────────────────────────────┘ -``` - -**Actions** : - -| Bouton | Comportement | -|--------|--------------| -| **Reprendre** | Continue séquence 3 à position 1:42 exacte | -| **Recommencer** | Reset progression, démarre séquence 1 depuis 0:00 | -| **Voir séquences** | Affiche liste complète, user choisit séquence départ | - -**Expiration progression** : -- Progression conservée **30 jours** -- Après 30j : popup "Audio-guide expiré. Recommencez depuis le début ?" -- Suppression données progression (mais historique "écouté" préservé) - -**Justification** : -- Contexte clair : user sait exactement où il en est -- Flexibilité : reprendre OU recommencer (choix utilisateur) -- 30 jours = raisonnable pour tourisme multi-jours ou retour ultérieur - ---- - -#### 16.6.3 Multi-device - -**Scénario** : - -1. User démarre audio-guide sur iPhone (séquences 1-3) -2. Progression sync cloud -3. Lendemain : user ouvre app sur iPad -4. Popup : "Reprendre Safari du Paugre sur cet appareil ?" -5. User clique Reprendre → continue séquence 4 - -**Conflit de version** : -- Si modifications simultanées 2 appareils (rare) : **dernière modification gagne** -- Toast : "Progression mise à jour depuis votre autre appareil" - -**Justification** : -- Confort utilisateur (change d'appareil librement) -- Use case réel : planning trajet sur tablette, écoute sur smartphone en voiture - ---- - -## Récapitulatif Section 16 - -| Point | Décision | Coût | Complexité | -|-------|----------|------|------------| -| **16.1** Types audio-guides | 4 modes (piéton/voiture/vélo/transport) avec détection auto | 0€ | Moyenne | -| **16.1.2** Création | Formulaire séquences + GPS + rayon + wizard guidé | 0€ | Moyenne | -| **16.2.1** Piéton - Passages | Manuel AVEC pub auto-play entre séquences, pause après | 0€ | Faible | -| **16.2.2** Piéton - Navigation | Liberté totale (skip, retour, saut direct liste) | 0€ | Faible | -| **16.3.1** Voiture - Déclenchement | GPS auto + boutons manuels actifs (warning sécurité si >10 km/h) | 0€ | Moyenne | -| **16.3.2** Voiture - Affichage | Distance + ETA + direction (flèche) + vitesse (PAS de carte) | 0€ | Faible | -| **16.3.3** Voiture - Rayon | Configurable créateur (défauts 30m/50m/100m selon mode) | 0€ | Faible | -| **16.4** Vélo & Transport | Mêmes règles avec tolérances ajustées + warning adapté | 0€ | Faible | -| **16.5** Publicités | 1/5 séquences, auto-play TOUS modes, skippable 5s | 0€ | Faible | -| **16.6.1** Sauvegarde | Complète (séquence + position + historique) local + cloud | 0€ | Faible | -| **16.6.2** Reprise | Popup intelligente avec choix (reprendre/recommencer), expiration 30j | 0€ | Faible | -| **16.6.3** Multi-device | Sync cloud PostgreSQL (reprendre sur autre appareil) | 0€ | Faible | - -**Coût total MVP : 0€** (GPS natif, calcul distance PostGIS) - ---- - -## Points d'attention pour Gherkin - -- Tester 4 modes audio-guides (détection vitesse auto) -- Tester création séquences avec points GPS + rayon configurable -- Tester mode piéton : pause après séquence + pub auto-play + pause après pub + clic Suivant -- Tester navigation libre piéton (skip, retour, saut direct liste) -- Tester mode voiture : déclenchement GPS auto rayon 30m -- Tester navigation manuelle voiture : boutons actifs + warning si vitesse >10 km/h -- Tester affichage distance + ETA + direction (flèche 8 directions) -- Tester rayon tolérance "point manqué" (popup 3 actions) -- Tester mode vélo (rayon 50m) et transport (rayon 100m) -- Tester insertion pub 1/5 séquences tous modes avec auto-play -- Tester sauvegarde progression locale + sync cloud -- Tester popup reprise (3 boutons : reprendre/recommencer/voir liste) -- Tester expiration progression 30 jours -- Tester multi-device : démarrer iPhone, continuer iPad -- Tester gestion conflit progression simultanée 2 appareils - - - - -
- - -# Annexe : Fonctionnalités reportées Post-MVP - -**Date** : 2026-01-19 -**Statut** : Fonctionnalités validées mais reportées après le MVP - ---- - -## Sommaire - -1. [Classification politique et équilibre éditorial](##1-classification-politique-et-équilibre-éditorial) -2. [Système de pourboires créateurs](##2-système-de-pourboires-créateurs) - ---- - -## 1. Classification politique et équilibre éditorial - -> ⚠️ **Reporté post-MVP** pour raisons de coût, complexité et risques juridiques. - -### Contexte du report - -**Raisons** : -- **Coût modération** : Classification manuelle humaine très coûteuse (~2000€/mois pour 1-2 modérateurs senior full-time) -- **Risque juridique** : Accusations de biais éditorial, contentieux DSA -- **Complexité technique** : Dashboard audit, logs 3 ans, alertes déséquilibre -- **Controverse** : Peut créer polémique dès le lancement -- **Pas essentiel MVP** : L'application fonctionne sans ce système - -**Version MVP** (actuelle) : -- Tag "Politique" simple (comme "Économie", "Sport") -- Pas de classification gauche/droite -- Pas d'équilibrage imposé -- Option utilisateur "Masquer politique" → 0% contenus politiques - ---- - -### Spécifications complètes (future implémentation) - -**Échelle de classification** (5 niveaux) : -- 🔴 **Extrême gauche** (anticapitalisme radical, révolution) -- 🟠 **Gauche** (écologie, social, critique capitalisme modérée) -- ⚪ **Centre/Neutre** (pas de positionnement politique clair) -- 🔵 **Droite** (sécurité, tradition, économie libérale) -- 🟣 **Extrême droite** (nationalisme radical, conservatisme extrême) -- 🟢 **Non politique** (enfants, musique, fiction, culture générale) - -**Qui classifie** : -- ❌ Pas de classification automatique IA (outil informatif uniquement, jamais décisionnaire) -- ✅ Modérateurs senior après transcription -- ✅ Créateur peut contester via processus d'appel - -**Affichage** : -- Badge politique visible : **au choix de l'utilisateur** (paramètre "Afficher orientation politique") -- Par défaut : badges masqués (UX neutre) - -**Règles de diffusion (équilibre imposé)** : - -| Préférence utilisateur | Répartition | Justification | -|------------------------|-------------|---------------| -| **Équilibré** (défaut) | 35% gauche / 35% droite / 30% centre-neutre | Neutralité plateforme | -| **Plutôt gauche** | 50% gauche / 20% droite / 30% centre-neutre | Préférence respectée avec minimum opposition | -| **Plutôt droite** | 50% droite / 20% gauche / 30% centre-neutre | Préférence respectée avec minimum opposition | -| **Masquer politique** | 0% gauche / 0% droite / 100% centre-neutre + non politique | Option apolitique | - -**Audit et conformité DSA** : -- Rapport hebdomadaire automatique : % gauche/droite/centre diffusé par utilisateur -- Alerte si déséquilibre global plateforme (>55% d'un bord) -- Logs conservés **3 ans** (exigence Digital Services Act EU) -- Dashboard admin : visualisation répartition temps réel - -**Sanctions mauvaise classification** : -- Classification volontairement incorrecte = Strike 1 -- Récidive = Strike 2 (suspension 7j) -- Détection via signalements utilisateurs + audit modération - -**Justification** : -- **Conformité juridique DSA** (obligation neutralité plateforme EU) -- Protection contre accusations de biais éditorial -- Transparence auditable -- Coût : temps modération humaine (incompressible) - ---- - -### Conditions de réintégration - -**Prérequis** : -1. Base utilisateurs stable et revenus suffisants pour financer modération -2. Équipe modération dédiée (2+ modérateurs senior formés) -3. Dashboard admin audit DSA opérationnel -4. Système de logs et archivage 3 ans en place -5. Validation juridique du processus de classification - -**Chronologie estimée** : -- Phase 1 (Post-MVP+3 mois) : Validation demande utilisateurs via sondages -- Phase 2 (Post-MVP+6 mois) : Recrutement modérateurs + développement dashboard -- Phase 3 (Post-MVP+9 mois) : Tests bêta avec utilisateurs volontaires -- Phase 4 (Post-MVP+12 mois) : Déploiement progressif si résultats positifs - ---- - -## 2. Système de pourboires créateurs - -> ⚠️ **Reporté post-MVP** - Fonctionnalité crypto (Lightning Network) prévue ultérieurement. - -### Contexte du report - -**Raisons** : -- **Complexité technique** : Intégration Lightning Network, gestion wallets crypto -- **Réglementation** : Incertitude juridique crypto en EU (MiCA 2025) -- **Focus MVP** : Priorité sur monétisation via abonnements Premium et publicités -- **Adoption utilisateurs** : Nécessite éducation et adoption crypto préalables - -**Version MVP** (actuelle) : -- Monétisation créateurs via : - - Partage revenus publicités (3€ CPM) - - 70% revenus abonnements Premium - ---- - -### Spécifications complètes (future implémentation) - -**Système prévu** : Micro-dons via Lightning Network (Bitcoin Layer 2) - -**Fonctionnement** : -1. Auditeur peut envoyer pourboire pendant ou après écoute -2. Montants suggérés : 0.10€, 0.50€, 1€, 5€ (personnalisable) -3. Transaction instantanée via Lightning Network (frais <0.01€) -4. Créateur reçoit directement dans wallet Lightning -5. Conversion EUR/BTC automatique (optionnelle) - -**Avantages Lightning Network** : -- ✅ Frais quasi-nuls (<1%) vs 1.8% Mangopay -- ✅ Transactions instantanées (<1 seconde) -- ✅ Micropaiements possibles (dès 0.01€) -- ✅ International sans frais supplémentaires -- ✅ Pas d'intermédiaire (peer-to-peer) - -**Contraintes** : -- ❌ Adoption crypto limitée (2-5% population EU en 2026) -- ❌ Volatilité BTC (nécessite conversion EUR immédiate) -- ❌ UX complexe pour utilisateurs non-crypto -- ❌ Réglementation MiCA en évolution - -**Alternatives étudiées** : -- Ko-fi / Buy Me a Coffee : simple mais frais 5% -- PayPal/Stripe : frais 2.9% + 0.30€ (non viable pour micropaiements) -- Mangopay : déjà utilisé, mais frais élevés pour petits montants - ---- - -### Conditions de réintégration - -**Prérequis** : -1. Réglementation MiCA stabilisée et conforme -2. Adoption crypto suffisante dans la base utilisateurs (>10%) -3. Intégration Lightning Network validée techniquement -4. UX simplifiée pour utilisateurs non-crypto (onboarding dédié) -5. Demande créateurs confirmée via sondages - -**Chronologie estimée** : -- Phase 1 (Post-MVP+6 mois) : Étude de marché et demande utilisateurs -- Phase 2 (Post-MVP+12 mois) : Développement intégration Lightning -- Phase 3 (Post-MVP+15 mois) : Tests bêta avec créateurs volontaires -- Phase 4 (Post-MVP+18 mois) : Déploiement public si résultats positifs - ---- - -## Autres fonctionnalités candidates Post-MVP - -Liste non exhaustive de fonctionnalités évoquées mais non encore spécifiées : - -- **Mode offline avancé** : Téléchargement automatique zones fréquentes -- **Playlists collaboratives** : Co-création de playlists géolocalisées -- **API publique créateurs** : Intégration RSS, podcasts existants -- **Gamification** : Badges, défis géolocalisés, leaderboards -- **Mode nuit** : Interface sombre automatique -- **Statistiques avancées créateurs** : Démographie, retention, heatmaps GPS - -Ces fonctionnalités seront spécifiées et priorisées selon les retours utilisateurs MVP. - ---- - -## Suivi et validation - -**Responsable** : Product Owner -**Révision** : Trimestrielle -**Critères de priorisation** : -1. Demande utilisateurs (votes, sondages) -2. Impact business (revenus, rétention) -3. Faisabilité technique (complexité, ressources) -4. Conformité légale (RGPD, DSA, MiCA) -5. Différenciation concurrentielle - - - - -
- - -# Audio-guides multi-séquences pour piétons -> *En tant qu'auditeur à pied* -> *Je veux profiter d'audio-guides structurés lors de mes visites* -> *Afin de découvrir des lieux de manière autonome et à mon rythme* - -**29 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'auditeur -> **Et** que je suis en mode piéton (vitesse <5 km/h) -## 1. Détection d'audio-guide à proximité - -**Étant donné** que je me trouve à 80 mètres du Musée du Louvre -**Et** que 3 audio-guides sont disponibles pour ce lieu - -**Quand** le système détecte ma position - -**Alors** je reçois une notification push: - ---- - -## 2. Rayon de détection de 100m - -**Étant donné** qu'un audio-guide est centré aux coordonnées GPS du Louvre - -**Quand** je suis à exactement 100m du centre - -**Alors** la notification est déclenchée -**Et** quand je suis à 101m, aucune notification n'est envoyée - ---- - -## 3. Page de sélection des audio-guides - -**Étant donné** que j'ai tapé sur la notification audio-guide - -**Quand** la page de sélection s'affiche - -**Alors** je vois une liste de guides disponibles: - - | titre | créateur | nb_sequences | durée | note | écoutes | - |---|---|---|---|---|---| - | Visite complète | Créateur A | 12 | 45 min | 4.8 | 1.2K | - | Œuvres majeures | Créateur B | 5 | 20 min | 4.9 | 3.5K | - | Visite famille | Créateur C | 8 | 30 min | 4.7 | 850 | - - ---- - -## 4. Sélection d'un audio-guide - -**Étant donné** que je suis sur la page de sélection - -**Quand** je tape sur "Visite complète (45 min)" - -**Alors** l'interface de lecture d'audio-guide s'ouvre -**Et** la séquence 1 commence automatiquement -**Et** je vois la liste complète des 12 séquences - ---- - -## 5. Interface de lecture audio-guide - -**Étant donné** que j'ai sélectionné un audio-guide de 12 séquences - -**Quand** l'interface s'affiche - -**Alors** je vois: - - | élément | exemple | - |---|---| - | Titre guide | 🎨 Visite complète • Musée du Louvre | - | Piste actuelle | Piste 2/12 | - | Titre séquence | "La Joconde - Histoire et mystères" | - | Barre de progression | 3:24 / 6:50 | - | Liste séquences | ✅ 1. Intro, ▶️ 2. Joconde, ⏸️ 3. Vénus... | - | Boutons navigation | Précédent, Play/Pause, Suivant | - - ---- - -## 6. Navigation vers séquence suivante - -**Étant donné** que j'écoute la séquence 2 - -**Quand** je tape sur "Suivant" - -**Alors** la séquence 3 commence immédiatement -**Et** le titre de la séquence s'affiche: "Vénus de Milo" -**Et** la barre de progression se réinitialise - ---- - -## 7. Navigation vers séquence précédente - -**Étant donné** que j'écoute la séquence 5 - -**Quand** je tape sur "Précédent" - -**Alors** la séquence 4 recommence depuis le début -**Et** je peux réécouter cette séquence - ---- - -## 8. Saut direct à une séquence spécifique - -**Étant donné** que j'écoute la séquence 2 -**Et** que la liste des séquences est affichée - -**Quand** je tape sur "7. Peintures Renaissance" - -**Alors** la séquence 7 démarre immédiatement -**Et** je passe directement de la séquence 2 à la 7 - ---- - -## 9. Commande vocale "Suivant" - -**Étant donné** que j'écoute la séquence 3 - -**Quand** je dis "Suivant" via la commande vocale - -**Alors** la séquence 4 démarre -**Et** la commande vocale fonctionne même si l'écran est verrouillé - ---- - -## 10. Commande vocale "Précédent" - -**Étant donné** que j'écoute la séquence 6 - -**Quand** je dis "Précédent" via la commande vocale - -**Alors** la séquence 5 démarre depuis le début - ---- - -## 11. Pause et reprise à la position exacte - -**Étant donné** que j'écoute la séquence 4 à la position 2:30 - -**Quand** je mets en pause -**Et** que j'attends 5 minutes -**Et** que je reprends la lecture - -**Alors** la séquence reprend exactement à 2:30 -**Et** aucune donnée n'est perdue - ---- - -## 12. Guidage vocal automatique entre séquences - -**Étant donné** que la séquence 2 se termine - -**Quand** la transition vers la séquence 3 se produit - -**Alors** j'entends un message vocal: -**Et** la séquence 3 ne démarre pas automatiquement (navigation manuelle) - ---- - -## 13. Avertissement si éloignement du point d'intérêt - -**Étant donné** que je suis dans le guide du Louvre -**Et** que je devrais être devant la Vénus de Milo (séquence 3) - -**Quand** je m'éloigne de plus de 50m de ce point - -**Alors** j'entends un message vocal: -**Et** un bouton "Voir le plan" apparaît dans l'interface - ---- - -## 14. Sauvegarde automatique de la progression - -**Étant donné** que j'écoute la séquence 5 à la position 1:45 - -**Quand** je ferme l'application brutalement -**Et** que je la rouvre 10 minutes plus tard - -**Alors** je vois une popup "Reprendre la visite du Musée du Louvre ?" -**Et** si je choisis "Reprendre", je retourne à la séquence 5 à 1:45 - ---- - -## 15. Option de recommencer depuis le début - -**Étant donné** que j'ai une progression sauvegardée à la séquence 7 - -**Quand** je rouvre le guide - -**Alors** je vois 2 options: - - | option | action | - |---|---| - | Reprendre à la séquence 7 | Reprend à la position exacte | - | Recommencer depuis le début | Retourne à la séquence 1 | - - ---- - -## 16. Expiration de la sauvegarde après 30 jours - -**Étant donné** que j'ai une progression sauvegardée depuis 30 jours - -**Quand** j'essaie de reprendre le guide - -**Alors** la sauvegarde est considérée comme expirée -**Et** je recommence depuis la séquence 1 -**Et** je vois le message "Votre précédente visite date de plus de 30 jours. Recommençons depuis le début." - ---- - -## 17. Synchronisation multi-device de la progression - -**Étant donné** que j'écoute un guide sur mon iPhone à la séquence 4 - -**Quand** je ferme l'app et ouvre sur mon iPad - -**Alors** je vois la progression synchronisée -**Et** je peux reprendre à la séquence 4 sur l'iPad - ---- - -## 18. Marquage "Terminé" après toutes les séquences - -**Étant donné** que j'écoute la dernière séquence (12/12) - -**Quand** cette séquence se termine - -**Alors** le guide est marqué "✅ Terminé" dans mon historique -**Et** je vois un message de félicitation: -**Et** le créateur gagne les statistiques d'écoute complète - ---- - -## 19. Création d'audio-guide par un créateur - -**Étant donné** que je suis un créateur - -**Quand** je crée un nouvel audio-guide - -**Alors** je dois: - - | étape | détail | - |---|---| - | Uploader plusieurs fichiers | 1 fichier MP3 par séquence | - | Numéroter les séquences | Séquence 1, Séquence 2, etc. | - | Titrer chaque séquence | "Introduction", "La Joconde", etc. | - | Définir point GPS unique | Centre du lieu (ex: Louvre) | - | Définir rayon de détection | Par défaut 100m | - -**Et** la durée totale est calculée automatiquement - ---- - -## 20. Structure JSON de stockage audio-guide - -**Étant donné** qu'un créateur publie un audio-guide du Louvre - -**Quand** les métadonnées sont stockées en base - -**Alors** le format JSON contient: - ---- - -## 21. Limitation du nombre de séquences - -**Étant donné** que je crée un audio-guide - -**Quand** j'essaie d'ajouter plus de 50 séquences - -**Alors** je vois le message "Maximum 50 séquences par audio-guide" -**Et** je dois structurer mon contenu différemment ou créer plusieurs guides - ---- - -## 22. Quitter le guide et sauvegarder - -**Étant donné** que j'écoute la séquence 6 - -**Quand** je tape sur le bouton "×" (fermer) - -**Alors** je vois une confirmation: -**Et** si je confirme, la progression est enregistrée -**Et** je retourne à l'écran principal - ---- - -## 23. Statistiques créateur pour audio-guides - -**Étant donné** que je suis créateur d'un audio-guide - -**Quand** je consulte mes statistiques - -**Alors** je vois: - - | métrique | exemple valeur | - |---|---| - | Nombre de démarrages | 1250 | - | Nombre de complétions (100%) | 387 (31%) | - | Séquence la plus skippée | Séquence 8 | - | Durée moyenne d'écoute | 28 min (sur 45) | - - ---- - -## 24. Audio-guide multilingue (post-MVP) - -**Étant donné** qu'un créateur peut publier plusieurs versions linguistiques - -**Quand** un touriste anglophone visite le Louvre - -**Alors** il voit les guides disponibles en anglais -**Et** peut choisir parmi les guides traduits -**Mais** cette fonctionnalité n'est pas disponible en MVP - ---- - -## 25. Publicité entre séquences d'audio-guide - -**Étant donné** que je suis un utilisateur gratuit -**Et** que j'écoute un audio-guide - -**Quand** je passe de la séquence 5 à la séquence 6 - -**Alors** une publicité peut être insérée (1 pub toutes les 5 séquences) -**Et** la publicité est skippable après 5 secondes -**Et** les utilisateurs Premium ne voient pas de publicité - ---- - -## 26. Audio-guide en mode offline - -**Étant donné** que j'ai téléchargé un audio-guide complet - -**Quand** je visite le lieu sans connexion internet - -**Alors** toutes les séquences sont disponibles hors ligne -**Et** la navigation fonctionne normalement -**Et** seule la sauvegarde cloud est différée jusqu'à reconnexion - ---- - -## 27. Notation d'un audio-guide après écoute - -**Étant donné** que j'ai terminé un audio-guide - -**Quand** je ferme l'interface - -**Alors** je vois une popup "Notez cette visite" -**Et** je peux donner une note de 1 à 5 étoiles -**Et** cette note contribue à la note globale visible par les autres utilisateurs - ---- - -## 28. Filtrage par langue dans la page de sélection - -**Étant donné** que plusieurs audio-guides sont disponibles en différentes langues - -**Quand** j'accède à la page de sélection - -**Alors** je peux filtrer par langue -**Et** par défaut, les guides dans ma langue système sont affichés en premier - ---- - -## 29. Réutilisation de l'infrastructure existante - -**Étant donné** qu'un audio-guide est techniquement un contenu structuré - -**Alors** il réutilise: - - | composant | usage | - |---|---| - | Stockage Bunny | Hébergement fichiers MP3 séquences | - | Streaming HLS | Diffusion audio adaptative | - | Cache Redis | Métadonnées guides + progressions | - | PostgreSQL | Stockage structure JSON guides | - -**Et** aucune infrastructure dédiée n'est nécessaire - ---- - - - - - -
- - -# Impact des abonnements sur l'algorithme -> *En tant qu'auditeur* -> *Je veux que les contenus de mes créateurs suivis soient favorisés* -> *Afin de ne pas rater leurs publications tout en découvrant de nouveaux contenus* - -**16 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'auditeur -> **Et** que je suis abonné au créateur "JeanDupont" -## 1. Boost de +30% appliqué au score final - -**Étant donné** un contenu du créateur "JeanDupont" avec: - - | score_geo | 0.5 | - |---|---| - | score_interet | 0.6 | - | score_engage | 0.5 | - - -**Quand** le score final est calculé - -**Alors** le score de base est 0.53 -**Et** le boost abonnement de +30% est appliqué -**Et** le score final avec boost est 0.69 - ---- - -## 2. Contenu non-suivi peut battre contenu suivi - -**Étant donné** que je suis à Paris -**Et** que 2 contenus sont disponibles: - - | contenu | createur_suivi | score_geo | score_interet | score_engage | score_final_base | score_avec_boost | - |---|---|---|---|---|---|---| - | Contenu A | Non | 0.9 | 0.8 | 0.7 | 0.80 | 0.80 | - | Contenu B | Oui | 0.5 | 0.6 | 0.5 | 0.53 | 0.69 | - - -**Quand** l'algorithme sélectionne le prochain contenu - -**Alors** le Contenu A est proposé en premier - ---- - -## 3. Contenu suivi remporte grâce au boost - -**Étant donné** que je suis à Paris -**Et** que 2 contenus sont disponibles: - - | contenu | createur_suivi | score_final_base | score_avec_boost | - |---|---|---|---| - | Contenu A | Non | 0.70 | 0.70 | - | Contenu B | Oui | 0.60 | 0.78 | - - -**Quand** l'algorithme sélectionne le prochain contenu - -**Alors** le Contenu B est proposé en premier - ---- - -## 4. Contenu suivi avec faible engagement ne domine pas - -**Étant donné** que je suis abonné au créateur "CreateurMoyen" -**Et** qu'il publie un contenu avec très faible engagement (score 0.30) -**Et** qu'un contenu viral d'un créateur non-suivi a un score de 0.85 - -**Quand** l'algorithme sélectionne le prochain contenu - -**Alors** le contenu viral est proposé en premier (0.85) - ---- - -## 5. Pas de file dédiée aux abonnements - -**Étant donné** que je suis abonné à 50 créateurs - -**Quand** l'algorithme génère ma file d'attente de 5 contenus - -**Alors** les contenus suivis et non-suivis sont mélangés -**Et** tous entrent en compétition selon leurs scores (avec boost si abonnement) -**Et** il n'y a pas de section séparée "Contenus de vos abonnements" - ---- - -## 6. Vérification du calcul du boost - -**Étant donné** un contenu d'un créateur suivi -**Et** que le score final de base est calculé à 0.65 - -**Quand** le boost abonnement est appliqué - -**Alors** le multiplicateur utilisé est exactement 1.3 -**Et** le score final avec boost est 0.845 (0.65 × 1.3) -**Et** le résultat est arrondi à 2 décimales: 0.85 - ---- - -## 7. Boost appliqué à tous les contenus du créateur suivi - -**Étant donné** que je suis abonné au créateur "JeanDupont" -**Et** qu'il a publié 10 contenus différents - -**Quand** l'algorithme évalue chacun de ces contenus - -**Alors** le boost de +30% est appliqué à tous les 10 contenus -**Et** chaque contenu bénéficie du même multiplicateur 1.3 - ---- - -## 8. Plusieurs créateurs suivis en compétition - -**Étant donné** que je suis abonné à "Créateur A" et "Créateur B" -**Et** que les 2 ont des contenus disponibles dans ma zone: - - | createur | score_base | score_avec_boost | - |---|---|---| - | Créateur A | 0.70 | 0.91 | - | Créateur B | 0.65 | 0.85 | - - -**Quand** l'algorithme sélectionne le prochain contenu - -**Alors** le contenu du Créateur A est proposé en premier (0.91 > 0.85) -**Et** les 2 bénéficient du boost, mais le meilleur score gagne - ---- - -## 9. Contenu national d'un créateur suivi - -**Étant donné** que je suis abonné à "MediaNational" -**Et** qu'il publie un contenu de type "National" (score_geo 0.2) - -**Quand** le score est calculé avec: - - | score_geo | score_interet | score_engage | - |---|---|---| - | 0.2 | 0.7 | 0.6 | - - -**Alors** le score de base est environ 0.50 -**Et** avec le boost abonnement, le score devient 0.65 -**Et** le contenu peut être proposé malgré son score géo faible - ---- - -## 10. Transparence du boost dans les paramètres - -**Quand** j'accède aux paramètres de l'algorithme de recommandation - -**Alors** je vois l'information: "Les contenus de vos créateurs suivis bénéficient d'un boost de +30%" -**Et** je comprends que ce n'est pas une priorité absolue -**Et** que la découverte de nouveaux contenus reste possible - ---- - -## 11. Boost désactivé si désabonnement - -**Étant donné** que je suis abonné au créateur "JeanDupont" -**Et** qu'un de ses contenus bénéficiait du boost +30% - -**Quand** je me désabonne de "JeanDupont" - -**Alors** ses contenus n'ont plus le boost -**Et** leur score revient au score de base sans multiplicateur - ---- - -## 12. Contenu d'un créateur nouvellement suivi - -**Étant donné** que je viens de m'abonner à "NouveauCreateur" -**Et** qu'il a publié un contenu il y a 2 jours - -**Quand** l'algorithme recalcule les scores - -**Alors** le boost de +30% est immédiatement appliqué à ce contenu -**Et** il peut apparaître dans ma prochaine file d'attente - ---- - -## 13. Impact sur le taux de contenu suivi dans le feed - -**Étant donné** que je suis abonné à 30 créateurs -**Et** que j'écoute 100 contenus sur une semaine - -**Quand** j'analyse la répartition - -**Alors** environ 40-50% des contenus proviennent de créateurs suivis -**Et** 50-60% proviennent de créateurs non-suivis (découverte) - ---- - -## 14. Contenu suivi hors zone géographique - -**Étant donné** que je suis à Paris -**Et** que je suis abonné à un créateur de Marseille -**Et** qu'il publie un contenu ancré à Marseille (hors de portée) - -**Quand** l'algorithme évalue ce contenu - -**Alors** le score géo est quasi nul (0.05) -**Et** même avec boost +30%, le score reste très faible -**Et** le contenu n'est probablement pas proposé - ---- - -## 15. Performance de calcul du boost - -**Étant donné** que je suis abonné à 100 créateurs -**Et** que l'algorithme évalue 1000 contenus potentiels - -**Quand** le calcul des scores avec boost est effectué - -**Alors** le temps de calcul reste inférieur à 50ms -**Et** la requête SQL utilise un JOIN sur la table abonnements - ---- - -## 16. Boost combiné avec d'autres facteurs - -**Étant donné** un contenu d'un créateur suivi -**Et** que le contenu bénéficie aussi de: - - | facteur | impact | - |---|---| - | Score d'engagement élevé | +20% | - | Contenu récent (<24h) | +10% | - | Boost abonnement | +30% | - - -**Quand** le score final est calculé - -**Alors** le boost abonnement s'applique au score final (après tous les autres calculs) -**Et** les boosts ne s'additionnent pas, le boost abonnement est un multiplicateur final - ---- - - - - - -
- - -# Limites d'abonnements et désabonnement -> *En tant qu'auditeur* -> *Je veux gérer mes abonnements de manière équilibrée* -> *Afin de suivre mes créateurs préférés sans être submergé* - -**27 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'auditeur -## 1. Limite maximale de 200 abonnements - -**Étant donné** que je suis abonné à 199 créateurs - -**Quand** j'essaie de m'abonner à un 200ème créateur - -**Alors** l'abonnement réussit -**Et** je suis maintenant abonné à 200 créateurs - ---- - -## 2. Impossible de dépasser 200 abonnements - -**Étant donné** que je suis déjà abonné à 200 créateurs - -**Quand** j'essaie de m'abonner à un nouveau créateur - -**Alors** l'action échoue -**Et** je vois le message: - ---- - -## 3. Suggestion de désabonnement de créateurs inactifs - -**Étant donné** que je suis abonné à 200 créateurs -**Et** que j'essaie de m'abonner à un nouveau créateur - -**Quand** je vois le message de limite atteinte - -**Alors** je vois aussi une suggestion: -**Et** un bouton "Désabonner" est proposé pour ce créateur - ---- - -## 4. Liste triable des abonnements - -**Étant donné** que je suis abonné à 150 créateurs - -**Quand** j'accède à ma liste d'abonnements - -**Alors** je peux trier par: - - | critère | ordre | - |---|---| - | Date d'abonnement | Plus récent / Plus ancien | - | Nombre de contenus écoutés | Plus écoutés / Moins écoutés | - | Dernière activité créateur | Plus récent / Plus ancien | - | Ordre alphabétique | A-Z / Z-A | - - ---- - -## 5. Abonnement initial augmente les jauges de +5% - -**Étant donné** que mes jauges d'intérêt sont: - - | catégorie | valeur initiale | - |---|---| - | Automobile | 60% | - | Voyage | 55% | - -**Et** qu'un créateur tague ses contenus "Automobile" et "Voyage" - -**Quand** je m'abonne à ce créateur - -**Alors** mes jauges évoluent: - - | catégorie | nouvelle valeur | - |---|---| - | Automobile | 65% (+5%) | - | Voyage | 60% (+5%) | - - ---- - -## 6. Abonnement avec créateur ayant 3 tags - -**Étant donné** qu'un créateur tague ses contenus: - - | tags | - |---| - | Automobile, Voyage, Technologie | - -**Et** que mes jauges sont toutes à 50% - -**Quand** je m'abonne à ce créateur - -**Alors** les 3 jauges augmentent de +5%: - - | catégorie | nouvelle valeur | - |---|---| - | Automobile | 55% | - | Voyage | 55% | - | Technologie | 55% | - - ---- - -## 7. Désabonnement diminue les jauges de -5% - -**Étant donné** que je suis abonné à un créateur avec tags "Politique" et "Économie" -**Et** que mes jauges sont: - - | catégorie | valeur actuelle | - |---|---| - | Politique | 70% | - | Économie | 65% | - - -**Quand** je me désabonne de ce créateur - -**Alors** mes jauges évoluent: - - | catégorie | nouvelle valeur | - |---|---| - | Politique | 65% (-5%) | - | Économie | 60% (-5%) | - - ---- - -## 8. Désabonnement sans confirmation - -**Étant donné** que je consulte le profil d'un créateur suivi - -**Quand** je clique sur "Se désabonner" - -**Alors** le désabonnement est immédiat -**Et** aucune popup de confirmation n'apparaît - ---- - -## 9. Réabonnement possible immédiatement - -**Étant donné** que je viens de me désabonner d'un créateur - -**Quand** je consulte à nouveau son profil - -**Alors** le bouton "S'abonner" est affiché -**Et** je peux me réabonner immédiatement -**Et** mes jauges augmentent à nouveau de +5% - ---- - -## 10. Effet symétrique abonnement/désabonnement - -**Étant donné** qu'un créateur a les tags "Musique" et "Culture" -**Et** que ma jauge Musique est à 50% - -**Quand** je m'abonne puis me désabonne immédiatement - -**Alors** ma jauge revient exactement à 50% -**Et** il n'y a pas de perte ou gain net - ---- - -## 11. Abonnement ne dépasse pas 100% de jauge - -**Étant donné** que ma jauge Automobile est à 97% -**Et** qu'un créateur tague ses contenus "Automobile" - -**Quand** je m'abonne à ce créateur - -**Alors** ma jauge Automobile passe à 100% (limite max) -**Et** l'augmentation effective est de +3% seulement - ---- - -## 12. Désabonnement ne descend pas sous 0% - -**Étant donné** que ma jauge Politique est à 3% -**Et** que je suis abonné à un créateur avec tag "Politique" - -**Quand** je me désabonne de ce créateur - -**Alors** ma jauge Politique passe à 0% (limite min) -**Et** la diminution effective est de -3% seulement - ---- - -## 13. Créateur ne voit pas qui est abonné (privacy) - -**Étant donné** que je suis abonné au créateur "JeanDupont" - -**Quand** "JeanDupont" consulte ses statistiques - -**Alors** il voit le nombre total d'abonnés (ex: "1,247 abonnés") -**Mais** il ne voit pas la liste des utilisateurs abonnés -**Et** mon identité reste privée - ---- - -## 14. Créateur voit uniquement le nombre total d'abonnés - -**Étant donné** que je suis créateur -**Et** que j'ai 523 abonnés - -**Quand** je consulte mes statistiques - -**Alors** je vois "523 abonnés" -**Mais** je ne peux pas: - - | action interdite | - |---| - | Voir la liste des abonnés | - | Contacter mes abonnés individuellement | - | Voir leurs profils | - - ---- - -## 15. Pas d'abonnement mutuel visible - -**Étant donné** que je suis abonné au créateur "Alice" -**Et** qu'"Alice" est abonnée à mon compte créateur - -**Quand** je consulte le profil d'"Alice" - -**Alors** je ne vois pas d'indication qu'elle est abonnée à moi -**Et** il n'y a pas de badge "Abonné mutuellement" - ---- - -## 16. Performance avec 200 abonnements - -**Étant donné** que je suis abonné à 200 créateurs - -**Quand** l'algorithme calcule ma recommandation - -**Alors** la requête SQL utilise un JOIN sur la table abonnements -**Et** la table est indexée sur user_id et creator_id -**Et** le temps de calcul reste inférieur à 50ms - ---- - -## 17. Impact sur la recommandation avec beaucoup d'abonnements - -**Étant donné** que je suis abonné à 150 créateurs très actifs -**Et** qu'ils publient collectivement 100 contenus par jour - -**Quand** l'algorithme génère ma file de 5 contenus - -**Alors** environ 60-70% des contenus proviennent de créateurs suivis (grâce au boost +30%) -**Mais** 30-40% proviennent de nouveaux créateurs (découverte) - ---- - -## 18. Notification de désabonnement au créateur (non implémenté) - -**Étant donné** que je me désabonne d'un créateur - -**Alors** le créateur ne reçoit aucune notification -**Et** il ne peut pas savoir qui s'est désabonné - ---- - -## 19. Statistiques d'abonnements pour l'utilisateur - -**Étant donné** que je suis abonné à 87 créateurs - -**Quand** j'accède à mes statistiques d'abonnements - -**Alors** je vois: - - | métrique | exemple valeur | - |---|---| - | Nombre total d'abonnements | 87 / 200 | - | Créateurs les plus écoutés | Top 10 avec % écoute | - | Créateurs non écoutés depuis 6 mois | 12 créateurs | - | Nouveaux contenus non écoutés | 23 contenus | - - ---- - -## 20. Recherche dans la liste d'abonnements - -**Étant donné** que je suis abonné à 120 créateurs - -**Quand** j'accède à ma liste d'abonnements - -**Alors** je peux chercher par nom de créateur -**Et** les résultats sont filtrés en temps réel -**Et** je trouve rapidement un créateur spécifique - ---- - -## 21. Export de la liste d'abonnements (RGPD) - -**Étant donné** que je demande l'export de mes données - -**Quand** l'export est généré - -**Alors** la liste de mes abonnements est incluse: - ---- - -## 22. Suppression compte utilisateur et impact sur abonnements - -**Étant donné** que je suis abonné à 50 créateurs - -**Quand** je supprime définitivement mon compte - -**Alors** tous mes abonnements sont supprimés -**Et** le compteur d'abonnés de chaque créateur est décrémenté de -1 -**Et** les jauges n'existent plus (données supprimées) - ---- - -## 23. Suppression compte créateur et impact sur abonnés - -**Étant donné** que je suis abonné au créateur "Bob" - -**Quand** "Bob" supprime son compte créateur - -**Alors** je suis automatiquement désabonné -**Et** mes jauges diminuent de -5% pour les tags de "Bob" -**Et** je ne vois plus "Bob" dans ma liste d'abonnements - ---- - -## 24. Limite 200 justifiée par usage réaliste - -**Étant donné** que la moyenne d'abonnements sur YouTube est de ~50-100 chaînes -**Et** que Twitter limite à 5000 follows (mais moyenne ~150) - -**Quand** RoadWave fixe la limite à 200 - -**Alors** cela couvre largement 99% des utilisateurs -**Et** évite les abus (comptes spam suivant tout le monde) - ---- - -## 25. Table PostgreSQL optimisée pour abonnements - -**Étant donné** la structure de table subscriptions: - -**Alors** les requêtes d'abonnements sont O(1) avec index -**Et** le count d'abonnés par créateur est rapide -**Et** la vérification "est abonné ?" est instantanée - ---- - -## 26. Détection d'abonnements abusifs - -**Étant donné** qu'un utilisateur s'abonne à 200 créateurs en moins de 5 minutes - -**Quand** le système détecte cette activité suspecte - -**Alors** un rate limiting est appliqué (max 10 abonnements/minute) -**Et** l'utilisateur voit "Trop d'actions rapides. Veuillez réessayer dans 1 minute" -**Et** cela prévient les bots de spam - ---- - -## 27. Badge créateur vérifié visible dans abonnements - -**Étant donné** que je suis abonné à 3 créateurs dont 1 vérifié - -**Quand** je consulte ma liste d'abonnements - -**Alors** le créateur vérifié a un badge ✓ bleu -**Et** les créateurs non vérifiés n'ont pas de badge - ---- - - - - - -
- - -# Notifications contextuelles selon le mode de déplacement -> *En tant qu'auditeur* -> *Je veux recevoir des notifications adaptées à mon contexte* -> *Afin d'être informé sans être distrait en conduisant* - -**28 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'auditeur -> **Et** que j'ai activé les notifications -## 1. Détection automatique du contexte en voiture - -**Étant donné** que ma vitesse GPS est de 50 km/h - -**Quand** le système détecte mon contexte - -**Alors** je suis identifié comme "En voiture" -**Et** les notifications push sont désactivées -**Et** seules les notifications in-app sont actives - ---- - -## 2. Détection automatique du contexte à pied - -**Étant donné** que ma vitesse GPS est de 3 km/h - -**Quand** le système détecte mon contexte - -**Alors** je suis identifié comme "À pied" -**Et** les notifications push sont activées -**Et** l'interface tactile et vocale sont disponibles - ---- - -## 3. Zone de transition 5-10 km/h - -**Étant donné** que ma vitesse GPS varie entre 5 et 10 km/h - -**Quand** le système détecte mon contexte - -**Alors** un algorithme de lissage est appliqué sur 30 secondes -**Et** le mode est déterminé selon la vitesse moyenne -**Et** les changements de mode ne sont pas trop fréquents - ---- - -## 4. Nouveau contenu créateur suivi - Mode voiture - -**Étant donné** que je suis en voiture (vitesse >10 km/h) -**Et** que je suis abonné au créateur "JeanDupont" - -**Quand** "JeanDupont" publie un nouveau contenu dans ma zone - -**Alors** je ne reçois pas de notification push -**Mais** je vois un badge compteur in-app -**Et** le contenu apparaît dans ma file avec boost +30% - ---- - -## 5. Nouveau contenu créateur suivi - Mode piéton - -**Étant donné** que je suis à pied (vitesse <5 km/h) -**Et** que je suis abonné au créateur "JeanDupont" -**Et** que je suis situé en Île-de-France - -**Quand** "JeanDupont" publie un contenu géolocalisé en Île-de-France - -**Alors** je reçois une notification push: - ---- - -## 6. Live créateur suivi - Mode voiture - -**Étant donné** que je suis en voiture -**Et** que je suis abonné au créateur "RadioLive" - -**Quand** "RadioLive" démarre un live dans ma zone - -**Alors** je ne reçois pas de notification push -**Mais** je vois un badge compteur in-app -**Et** le live peut apparaître dans ma recommandation automatiquement - ---- - -## 7. Live créateur suivi - Mode piéton - -**Étant donné** que je suis à pied -**Et** que je suis abonné au créateur "RadioLive" -**Et** que je suis situé dans la zone du live - -**Quand** "RadioLive" démarre un live - -**Alors** je reçois une notification push: - ---- - -## 8. Audio-guide disponible à proximité - Mode piéton - -**Étant donné** que je suis à pied - -**Quand** je passe à moins de 100m d'un lieu avec audio-guides - -**Alors** je reçois une notification push: - ---- - -## 9. Audio-guide disponible à proximité - Mode voiture - -**Étant donné** que je suis en voiture - -**Quand** je passe à moins de 100m d'un lieu avec audio-guides - -**Alors** je reçois une notification audio (bip) -**Et** une annonce vocale: "Audio-guide disponible" -**Mais** pas de notification push (sécurité) - ---- - -## 10. Filtrage géographique des notifications - -**Étant donné** que je suis abonné au créateur "CreateurMarseille" -**Et** que je suis situé à Paris - -**Quand** "CreateurMarseille" publie un contenu ancré à Marseille - -**Alors** je ne reçois pas de notification -**Et** cela évite la frustration de contenus non écoutables - ---- - -## 11. Contenu national notifie tous les abonnés - -**Étant donné** que je suis abonné au créateur "MediaNational" -**Et** que je suis situé n'importe où en France - -**Quand** "MediaNational" publie un contenu de type "National" - -**Alors** je reçois une notification (si mode piéton) - ---- - -## 12. Limite de 10 notifications push par jour - -**Étant donné** que je suis abonné à 50 créateurs actifs -**Et** que j'ai déjà reçu 10 notifications push aujourd'hui - -**Quand** un 11ème contenu est publié - -**Alors** je ne reçois pas de notification push individuelle -**Mais** une notification groupée: "🎧 3 nouveaux contenus de créateurs suivis" - ---- - -## 13. Paramétrage de la limite quotidienne - -**Étant donné** que la limite par défaut est de 10 notifications/jour - -**Quand** j'accède aux paramètres de notifications - -**Alors** je peux modifier la limite entre 5 et 20 -**Et** si je choisis 15, je recevrai jusqu'à 15 notifications/jour - ---- - -## 14. Mode silencieux nocturne par défaut - -**Étant donné** que le mode silencieux est activé de 22h à 8h par défaut -**Et** qu'il est 23h30 - -**Quand** un créateur suivi publie un contenu - -**Alors** je ne reçois pas de notification push -**Mais** les notifications sont empilées -**Et** je les vois le lendemain matin à 8h01 - ---- - -## 15. Exception du mode silencieux pour les lives - -**Étant donné** que le mode silencieux est activé (22h-8h) -**Et** qu'il est 23h00 -**Et** que j'ai activé "Notifications importantes uniquement" (lives uniquement) - -**Quand** un créateur suivi démarre un live - -**Alors** je reçois quand même la notification push du live - ---- - -## 16. Désactivation complète des notifications - -**Étant donné** que j'accède aux paramètres de notifications - -**Quand** je désactive toutes les notifications - -**Alors** je ne reçois plus aucune notification push -**Et** les badges in-app sont également désactivés -**Et** seule la recommandation algorithmique reste active - ---- - -## 17. Notification "Nouveaux contenus" activée par défaut - -**Étant donné** que je crée un nouveau compte -**Et** que je m'abonne à mon premier créateur - -**Quand** je consulte les préférences de notifications - -**Alors** "Nouveaux contenus" est activé par défaut -**Et** "Lives" est activé par défaut -**Et** "Audio-guides proximité" est activé par défaut - ---- - -## 18. Désactivation sélective par type de notification - -**Étant donné** que j'ai activé toutes les notifications - -**Quand** je désactive uniquement "Nouveaux contenus" - -**Alors** je ne reçois plus de notifications pour nouveaux contenus -**Mais** je reçois toujours les notifications de lives -**Et** les notifications d'audio-guides restent actives - ---- - -## 19. Notification groupée après limite dépassée - -**Étant donné** que j'ai reçu 10 notifications push aujourd'hui -**Et** que 5 nouveaux contenus sont publiés dans l'heure suivante - -**Quand** la 11ème notification devrait être envoyée - -**Alors** les 5 contenus sont regroupés en une seule notification: - ---- - -## 20. Détail de la notification groupée - -**Étant donné** que j'ai reçu une notification groupée "3 nouveaux contenus" - -**Quand** je tape sur la notification - -**Alors** l'app s'ouvre sur une liste des 3 contenus: - - | créateur | titre | - |---|---| - | JeanDupont | "Actualité du jour" | - | MarieDurand | "Podcast économie" | - | PaulMartin | "Anecdote historique" | - -**Et** je peux choisir lequel écouter en premier - ---- - -## 21. Personnalisation des plages horaires du mode silencieux - -**Étant donné** que le mode silencieux est 22h-8h par défaut - -**Quand** j'accède aux paramètres - -**Alors** je peux modifier les heures: par exemple 23h-7h -**Et** le mode silencieux s'applique dans la nouvelle plage horaire - ---- - -## 22. Format notification nouveau contenu complet - -**Étant donné** que je suis à pied -**Et** qu'un créateur suivi publie un contenu - -**Quand** je reçois la notification push - -**Alors** elle contient: - - | élément | exemple | - |---|---| - | Emoji | 🎧 | - | Créateur | JeanDupont | - | Action | a publié | - | Titre | "Les secrets du Louvre" | - | CTA | Tap pour écouter | - - ---- - -## 23. Format notification live complet - -**Étant donné** que je suis à pied -**Et** qu'un créateur suivi démarre un live - -**Quand** je reçois la notification push - -**Alors** elle contient: - - | élément | exemple | - |---|---| - | Emoji | 🔴 | - | Créateur | RadioLive | - | Action | est en direct | - | Titre | "Débat politique ce soir" | - | CTA | Tap pour rejoindre | - - ---- - -## 24. Notification disparaît si contenu supprimé - -**Étant donné** que j'ai reçu une notification pour un contenu -**Et** que je n'ai pas encore tapé dessus - -**Quand** le créateur supprime le contenu - -**Alors** la notification est automatiquement retirée de mon centre de notifications -**Et** si je tape dessus par erreur, je vois "Contenu non disponible" - ---- - -## 25. Badge compteur in-app en mode voiture - -**Étant donné** que je suis en voiture -**Et** que 5 créateurs suivis publient des contenus - -**Quand** j'ouvre l'application - -**Alors** je vois un badge "5" sur l'onglet "Nouveautés" -**Et** en consultant l'onglet, je vois les 5 nouveaux contenus -**Et** le badge disparaît après consultation - ---- - -## 26. Coût des notifications push Firebase - -**Étant donné** que je reçois 10 notifications push par jour -**Et** que je suis actif 365 jours par an - -**Quand** le système calcule le coût - -**Alors** 3650 notifications/an sont envoyées -**Et** Firebase Cloud Messaging est gratuit jusqu'à plusieurs millions de notifications -**Et** le coût reste 0€ pour le volume MVP/Growth - ---- - -## 27. Deep link depuis notification push - -**Étant donné** que je reçois une notification push pour un contenu - -**Quand** je tape sur la notification - -**Alors** l'app s'ouvre directement sur le contenu -**Et** la lecture démarre automatiquement (si j'étais à pied) - ---- - -## 28. Notification refusée si permissions désactivées au niveau OS - -**Étant donné** que j'ai désactivé les notifications dans les paramètres iOS/Android - -**Quand** un créateur suivi publie un contenu - -**Alors** aucune notification push n'est envoyée -**Et** l'app propose de réactiver les permissions dans les paramètres -**Mais** les badges in-app continuent de fonctionner - ---- - - - - - -
- - -# Création d'audio-guide multi-séquences -> *En tant que créateur de contenu* -> *Je veux créer des audio-guides avec plusieurs séquences géolocalisées* -> *Afin d'offrir des expériences guidées adaptées aux différents modes de déplacement* - -**35 scénarios** (32 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que le créateur "guide@example.com" est connecté -> **Et** que son compte est vérifié -## 1. 📋 Plan: Détection automatique du mode selon la vitesse - -**Étant donné** que l'utilisateur se déplace à km/h - -**Quand** la vitesse est calculée sur 30 secondes - -**Alors** le mode est suggéré automatiquement - -**📊 Exemples de données:** - -| vitesse | mode | -|---|---| -| 3 | Piéton | -| 15 | Vélo | -| 35 | Voiture | -| 50 | Voiture | - ---- - -## 2. Suggestion de mode au démarrage avec confirmation - -**Étant donné** qu'un audio-guide "Safari du Paugre" est disponible -**Et** que l'utilisateur se déplace à 35 km/h - -**Quand** l'audio-guide démarre - -**Alors** une popup s'affiche: - ---- - -## 3. Changement manuel du mode détecté - -**Étant donné** que le mode "Voiture" est suggéré automatiquement - -**Quand** l'utilisateur clique sur "Changer" - -**Alors** les 4 modes sont proposés: - - | mode | emoji | - |---|---| - | Piéton | 🚶 | - | Voiture | 🚗 | - | Vélo | 🚴 | - | Transport | 🚌 | - - ---- - -## 4. 📋 Plan: Caractéristiques par mode de déplacement - -**Étant donné** un audio-guide configuré en mode - -**Alors** les paramètres suivants sont appliqués: - - | paramètre | valeur | - |---|---| - | Vitesse détection | | - | Déclenchement | | - - -**📊 Exemples de données:** - -| mode | vitesse_detection | declenchement | -|---|---|---| -| Piéton | <5 km/h | Manuel (bouton Suivant) | -| Voiture | >10 km/h | Auto GPS + Manuel | -| Vélo | 5-25 km/h | Auto GPS + Manuel | -| Transport | Variable | Auto GPS + Manuel | - ---- - -## 5. Accès au formulaire de création d'audio-guide - -**Étant donné** que le créateur est sur son dashboard - -**Quand** il clique sur "Créer un audio-guide" - -**Alors** le formulaire de création s'affiche -**Et** le wizard guidé en 4 étapes est visible: - - | étape | description | - |---|---| - | 1 | Infos générales | - | 2 | Ajout séquences | - | 3 | Preview carte | - | 4 | Validation modération | - - ---- - -## 6. Étape 1 - Informations générales obligatoires - -**Étant donné** que le créateur est sur l'étape 1 du wizard - -**Quand** il complète le formulaire - -**Alors** les champs suivants sont obligatoires: - - | champ | contrainte | - |---|---| - | Titre | 5-100 caractères | - | Description | 10-500 caractères | - | Mode de déplacement | Choix parmi 4 | - | Tags | 1-3 tags | - | Classification âge | Tout public/13+/16+/18+ | - - ---- - -## 7. Sélection du mode de déplacement - -**Étant donné** que le créateur crée un audio-guide - -**Quand** il sélectionne le mode "🚗 Voiture (GPS auto + manuel)" - -**Alors** le champ "Vitesse recommandée" s'affiche -**Et** la plage suggérée est "30-50 km/h" - ---- - -## 8. Validation du titre - -**Étant donné** que le créateur entre un titre - -**Quand** le titre contient moins de 5 caractères - -**Alors** un message d'erreur "Minimum 5 caractères" s'affiche -**Et** le bouton "Suivant" est désactivé - ---- - -## 9. Validation de la description - -**Étant donné** que le créateur entre une description - -**Quand** la description contient 520 caractères - -**Alors** un message d'erreur "Maximum 500 caractères" s'affiche -**Et** les 20 caractères en trop sont surlignés en rouge - ---- - -## 10. Étape 2 - Ajout de la première séquence - -**Étant donné** que le créateur est sur l'étape 2 "Ajout séquences" - -**Quand** il clique sur "Ajouter séquence" - -**Alors** le formulaire de séquence s'affiche avec: - - | champ | requis | note | - |---|---|---| - | Titre séquence | ✅ | 5-80 caractères | - | Audio | ✅ | Upload MP3/AAC, max 200 MB | - | Point GPS | ✅* | *Sauf mode piéton | - | Rayon déclenchement | ✅* | *Sauf mode piéton, 10-200m | - - ---- - -## 11. Ajout du point GPS pour une séquence - -**Étant donné** que le créateur ajoute une séquence en mode "Voiture" - -**Quand** il clique sur "📍 Ajouter point GPS" - -**Alors** une carte s'affiche -**Et** il peut: - - | action | - |---| - | Cliquer sur la carte | - | Entrer coordonnées manuelles | - | Utiliser sa position actuelle | - - ---- - -## 12. Configuration du rayon de déclenchement avec preview - -**Étant donné** qu'un point GPS est défini à (43.1234, 2.5678) - -**Quand** le créateur ajuste le curseur de rayon - -**Alors** le rayon varie de 10m à 200m -**Et** un cercle visuel est affiché sur la carte -**Et** la valeur actuelle s'affiche "30m" - ---- - -## 13. 📋 Plan: Rayon par défaut selon le mode - -**Étant donné** un audio-guide en mode - -**Quand** le créateur ajoute un point GPS - -**Alors** le rayon par défaut est - -**📊 Exemples de données:** - -| mode | rayon_defaut | -|---|---| -| Voiture | 30m | -| Vélo | 50m | -| Transport | 100m | - ---- - -## 14. Suggestion intelligente du rayon - -**Étant donné** un audio-guide en mode "Voiture" avec vitesse recommandée 30 km/h - -**Quand** le créateur ajoute un point GPS - -**Alors** une suggestion s'affiche: "Recommandé : 30m pour voiture à 30 km/h" - ---- - -## 15. Upload audio pour une séquence - -**Étant donné** que le créateur crée une séquence "Introduction" - -**Quand** il upload un fichier audio de 5 MB - -**Alors** le fichier est vérifié: - - | vérification | règle | - |---|---| - | Format | MP3, AAC, M4A | - | Taille max | 200 MB | - | Durée max | 15 minutes | - - ---- - -## 16. Ordre des séquences modifiable - -**Étant donné** un audio-guide avec 5 séquences: - - | ordre | titre | - |---|---| - | 1 | Introduction | - | 2 | Les lions | - | 3 | Les girafes | - | 4 | Les éléphants | - | 5 | Conclusion | - - -**Quand** le créateur glisse "Les éléphants" en position 2 - -**Alors** l'ordre devient: - - | ordre | titre | - |---|---| - | 1 | Introduction | - | 2 | Les éléphants | - | 3 | Les lions | - | 4 | Les girafes | - | 5 | Conclusion | - - ---- - -## 17. Nombre minimum de séquences requis - -**Étant donné** un audio-guide avec seulement 1 séquence - -**Quand** le créateur tente de passer à l'étape suivante - -**Alors** un message d'erreur s'affiche: "Minimum 2 séquences requis" -**Et** le bouton "Suivant" est désactivé - ---- - -## 18. Nombre maximum de séquences - -**Étant donné** un audio-guide avec 50 séquences - -**Quand** le créateur tente d'ajouter une 51ème séquence - -**Alors** un message d'erreur s'affiche: "Maximum 50 séquences par audio-guide" -**Et** le bouton "+ Ajouter séquence" est désactivé - ---- - -## 19. Étape 3 - Preview carte avec tracé et points - -**Étant donné** un audio-guide avec 5 séquences géolocalisées - -**Quand** le créateur accède à l'étape 3 "Preview carte" - -**Alors** une carte Leaflet s'affiche -**Et** les éléments suivants sont visibles: - - | élément | description | - |---|---| - | Markers numérotés | 1, 2, 3, 4, 5 sur chaque point | - | Tracé entre points | Ligne pointillée connectant les points | - | Cercles de déclenchement | Rayon visuel autour de chaque point | - - ---- - -## 20. Statistiques du parcours - -**Étant donné** un audio-guide avec les séquences suivantes: - - | séquence | durée | distance_au_suivant | - |---|---|---| - | 1 | 2:15 | 150m | - | 2 | 3:42 | 200m | - | 3 | 4:10 | 320m | - - -**Quand** les statistiques sont calculées - -**Alors** le résumé suivant est affiché: - - | métrique | valeur | - |---|---| - | Séquences | 3 complètes | - | Durée totale | 10:07 | - | Distance totale | 670m | - - ---- - -## 21. Modification d'une séquence depuis la carte - -**Étant donné** que la preview carte est affichée - -**Quand** le créateur clique sur le marker "2" - -**Alors** une popup s'affiche avec: - - | information | - |---| - | Titre: "Les lions" | - | Durée: 3:42 | - | Rayon: 30m | - | [✏️ Modifier] | - | [🗑️ Supprimer] | - - ---- - -## 22. Zone de diffusion géographique - -**Étant donné** un audio-guide avec des points dans Paris - -**Quand** le créateur définit la zone de diffusion - -**Alors** il peut choisir: - - | type | exemple | - |---|---| - | Polygon | Tracé manuel sur carte | - | Ville | Paris (API Nominatim) | - | Département | 75 - Paris | - | Région | Île-de-France | - - ---- - -## 23. Étape 4 - Publication et validation modération - -**Étant donné** un créateur qui publie ses 3 premiers audio-guides - -**Quand** il clique sur "✅ Publier audio-guide" - -**Alors** un message s'affiche: - ---- - -## 24. Publication directe pour créateurs expérimentés - -**Étant donné** un créateur ayant publié 5 audio-guides validés -**Et** aucun strike actif - -**Quand** il publie un nouvel audio-guide - -**Alors** l'audio-guide est publié immédiatement -**Et** il devient visible pour les utilisateurs -**Et** aucune validation manuelle n'est requise - ---- - -## 25. Mode piéton sans points GPS obligatoires - -**Étant donné** un audio-guide en mode "🚶 Piéton" - -**Quand** le créateur ajoute une séquence - -**Alors** le champ "Point GPS" est optionnel -**Et** le champ "Rayon déclenchement" est masqué -**Et** un message info s'affiche: "Mode manuel : les séquences se déclenchent au clic utilisateur" - ---- - -## 26. Sauvegarde brouillon automatique - -**Étant donné** que le créateur édite un audio-guide depuis 5 minutes - -**Quand** il ajoute une nouvelle séquence - -**Alors** l'audio-guide est sauvegardé en brouillon automatiquement -**Et** un toast "Brouillon sauvegardé" s'affiche brièvement - ---- - -## 27. Reprise d'un brouillon - -**Étant donné** un audio-guide en brouillon "Safari du Paugre" -**Et** qu'il contient 3 séquences complètes - -**Quand** le créateur retourne sur son dashboard - -**Alors** le brouillon est visible avec le statut "📝 Brouillon" -**Et** un bouton "Continuer" est disponible -**Et** la progression "3/5 séquences" est affichée - ---- - -## 28. Suppression d'un brouillon - -**Étant donné** un audio-guide en brouillon - -**Quand** le créateur clique sur "🗑️ Supprimer" - -**Alors** une confirmation s'affiche: - ---- - -## 29. Modification d'un audio-guide publié - -**Étant donné** un audio-guide publié "Safari du Paugre" - -**Quand** le créateur clique sur "✏️ Modifier" - -**Alors** il peut modifier: - - | élément modifiable | élément non modifiable | - |---|---| - | Titre | Mode de déplacement | - | Description | Points GPS | - | Tags | Rayons déclenchement | - | Séquences (ordre) | | - -**Et** un avertissement s'affiche: "Les modifications structurelles nécessitent une nouvelle publication" - ---- - -## 30. Duplication d'un audio-guide existant - -**Étant donné** un audio-guide publié "Visite Paris" - -**Quand** le créateur clique sur "📋 Dupliquer" - -**Alors** une copie est créée avec le titre "Visite Paris (copie)" -**Et** toutes les séquences sont copiées -**Et** le statut est "📝 Brouillon" -**Et** le créateur peut modifier avant publication - ---- - -## 31. Upload audio échoue (format non supporté) - -**Étant donné** que le créateur upload un fichier "audio.wav" - -**Quand** le format est vérifié - -**Alors** un message d'erreur s'affiche: "Format non supporté. Utilisez MP3, AAC ou M4A" -**Et** le fichier est rejeté - ---- - -## 32. Upload audio échoue (taille trop grande) - -**Étant donné** que le créateur upload un fichier de 250 MB - -**Quand** la taille est vérifiée - -**Alors** un message d'erreur s'affiche: "Fichier trop volumineux. Maximum 200 MB" -**Et** le fichier est rejeté - ---- - -## 33. Points GPS trop éloignés (alerte cohérence) - -**Étant donné** un audio-guide en mode "Piéton" -**Et** une séquence au Louvre (Paris) - -**Quand** le créateur ajoute une séquence à Lyon - -**Alors** un avertissement s'affiche: - ---- - -## 34. Pas de connexion lors de la sauvegarde - -**Étant donné** que le créateur édite un audio-guide -**Et** que la connexion réseau est perdue - -**Quand** il tente de sauvegarder - -**Alors** le brouillon est sauvegardé localement -**Et** un message s'affiche: "Sauvegarde locale. Sera synchronisée à la reconnexion" -**Et** une icône "☁️ Hors ligne" s'affiche - ---- - -## 35. Reprise après perte de connexion - -**Étant donné** un brouillon sauvegardé localement - -**Quand** la connexion réseau est rétablie - -**Alors** le brouillon est synchronisé automatiquement -**Et** un toast "✅ Audio-guide synchronisé" s'affiche - ---- - - - - - -
- - -# Intégration audio-guides avec autres fonctionnalités -> *En tant qu'utilisateur* -> *Je veux utiliser les audio-guides avec toutes les fonctionnalités de l'app* -> *Afin d'avoir une expérience complète et cohérente* - -**39 scénarios** (38 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté -## 1. Téléchargement complet d'un audio-guide - -**Étant donné** un audio-guide "Visite du Louvre" avec 12 séquences - -**Quand** l'utilisateur clique sur "⬇️ Télécharger pour écouter hors ligne" - -**Alors** toutes les 12 séquences sont téléchargées -**Et** les métadonnées (titres, descriptions, GPS) sont sauvegardées -**Et** les images (cover, miniatures) sont mises en cache - ---- - -## 2. Affichage de la progression du téléchargement - -**Étant donné** qu'un téléchargement d'audio-guide est en cours - -**Quand** l'utilisateur consulte l'état - -**Alors** la progression s'affiche: - ---- - -## 3. Téléchargement uniquement en WiFi (par défaut) - -**Étant donné** que l'option "Télécharger uniquement en WiFi" est activée - -**Quand** l'utilisateur lance un téléchargement sur réseau mobile - -**Alors** un avertissement s'affiche: - ---- - -## 4. Gestion de l'espace de stockage - -**Étant donné** que l'appareil a 500 MB d'espace libre -**Et** qu'un audio-guide pèse 380 MB - -**Quand** l'utilisateur lance le téléchargement - -**Alors** un avertissement s'affiche: - ---- - -## 5. Liste des audio-guides téléchargés - -**Étant donné** que l'utilisateur a téléchargé 3 audio-guides - -**Quand** il accède à "Bibliothèque > Téléchargés" - -**Alors** il voit: - - | audio_guide | taille | date_telechargement | - |---|---|---| - | Visite du Louvre | 380 MB | 2026-01-20 | - | Safari du Paugre | 245 MB | 2026-01-18 | - | Circuit Loire à Vélo | 520 MB | 2026-01-15 | - - ---- - -## 6. Lecture hors connexion complète - -**Étant donné** qu'un audio-guide est téléchargé -**Et** que l'utilisateur active le mode avion - -**Quand** il lance l'audio-guide - -**Alors** toutes les séquences sont lisibles -**Et** les métadonnées sont accessibles -**Et** les images s'affichent normalement -**Et** la progression est sauvegardée localement - ---- - -## 7. GPS fonctionne en mode avion (mode voiture) - -**Étant donné** qu'un audio-guide voiture est téléchargé -**Et** que le mode avion est activé (avec GPS actif) - -**Quand** l'utilisateur se déplace - -**Alors** les déclenchements GPS fonctionnent normalement -**Et** la distance/ETA sont calculés - ---- - -## 8. Suppression d'audio-guide téléchargé - -**Étant donné** qu'un audio-guide téléchargé pèse 380 MB - -**Quand** l'utilisateur clique sur "🗑️ Supprimer téléchargement" - -**Alors** une confirmation s'affiche -**Et** si confirmé, les 380 MB sont libérés -**Et** l'audio-guide reste accessible en streaming - ---- - -## 9. Mise à jour automatique si nouvelle version - -**Étant donné** qu'un audio-guide téléchargé a été mis à jour par le créateur - -**Quand** l'utilisateur se connecte en WiFi - -**Alors** une notification s'affiche: - ---- - -## 10. Ajout d'audio-guide à une playlist - -**Étant donné** que l'utilisateur consulte un audio-guide - -**Quand** il clique sur "➕ Ajouter à une playlist" - -**Alors** ses playlists s'affichent: - - | playlist | - |---| - | 🗺️ Voyages en France | - | 🏛️ Musées parisiens | - | + Créer nouvelle playlist | - - ---- - -## 11. Comportement audio-guide dans une playlist - -**Étant donné** une playlist contenant 2 audio-guides et 1 podcast - -**Quand** la lecture atteint un audio-guide - -**Alors** l'audio-guide démarre à la séquence 1 (ou progression sauvegardée) -**Et** les séquences se jouent normalement - -**Quand** l'audio-guide se termine (dernière séquence) - -**Alors** le contenu suivant de la playlist démarre - ---- - -## 12. Audio-guide marqué comme "Favori" - -**Étant donné** qu'un utilisateur aime un audio-guide - -**Quand** il clique sur "⭐ Ajouter aux favoris" - -**Alors** l'audio-guide est ajouté à la section "Favoris" -**Et** il est facilement accessible depuis le menu principal - ---- - -## 13. Collections thématiques d'audio-guides - -**Étant donné** que RoadWave propose des collections éditoriales - -**Quand** l'utilisateur accède à "Collections" - -**Alors** il voit des collections comme: - - | collection | nombre_audio_guides | - |---|---| - | 🏛️ Musées de France | 12 | - | 🦁 Parcs animaliers | 8 | - | 🚴 Circuits vélo | 15 | - | 🚗 Routes touristiques | 10 | - - ---- - -## 14. Bouton partager sur page audio-guide - -**Étant donné** qu'un utilisateur consulte un audio-guide - -**Quand** il clique sur "⬆️ Partager" - -**Alors** le menu de partage natif s'ouvre -**Et** le lien généré est "https://roadwave.fr/share/ag/louvre_123" - ---- - -## 15. Page web de partage pour audio-guide - -**Étant donné** qu'un lien d'audio-guide partagé est ouvert sur le web - -**Quand** la page se charge - -**Alors** elle affiche: - - | élément | exemple | - |---|---| - | Cover image 16:9 | Photo du Louvre | - | Titre | "Visite du Louvre" | - | Créateur | "@art_guide ✓" | - | Badge type | "🎧 Audio-guide • 12 séquences" | - | Durée totale | "45 minutes" | - | Mode | "🚶 Piéton" | - | Description | Texte complet | - | Preview séquence 1 | Player HTML5 (séquence intro) | - | Carte avec points GPS | Leaflet avec 12 markers | - | CTA téléchargement | Boutons App Store / Google Play | - - ---- - -## 16. Deep link vers audio-guide spécifique - -**Étant donné** que l'app est installée -**Et** qu'un lien "https://roadwave.fr/share/ag/louvre_123" est cliqué - -**Quand** le système détecte l'app - -**Alors** l'app s'ouvre directement sur l'audio-guide -**Et** l'utilisateur peut démarrer immédiatement - ---- - -## 17. Partage avec séquence spécifique - -**Étant donné** qu'un utilisateur est sur la séquence 5 "La Joconde" - -**Quand** il partage l'audio-guide - -**Alors** le lien généré est "https://roadwave.fr/share/ag/louvre_123?seq=5" -**Et** le destinataire est dirigé vers la séquence 5 directement - ---- - -## 18. Note globale de l'audio-guide - -**Étant donné** qu'un utilisateur termine un audio-guide - -**Quand** la dernière séquence se termine - -**Alors** une popup de notation s'affiche: - ---- - -## 19. Note moyenne affichée sur la page - -**Étant donné** qu'un audio-guide a reçu 150 notes -**Et** que la moyenne est 4.3/5 - -**Quand** la page est affichée - -**Alors** la note "⭐ 4.3 (150 avis)" est visible - ---- - -## 20. Commentaires triés par pertinence - -**Étant donné** qu'un audio-guide a 50 commentaires - -**Quand** l'utilisateur consulte les avis - -**Alors** les commentaires sont triés par défaut selon: - - | critère | poids | - |---|---| - | Note élevée | 30% | - | Récent | 30% | - | Likes reçus | 40% | - - ---- - -## 21. Réponse du créateur aux commentaires - -**Étant donné** qu'un utilisateur laisse un commentaire négatif - -**Quand** le créateur consulte son dashboard - -**Alors** il peut répondre au commentaire -**Et** sa réponse apparaît en dessous avec badge "Créateur" - ---- - -## 22. Audio-guides similaires recommandés - -**Étant donné** qu'un utilisateur termine "Visite du Louvre" - -**Quand** il consulte les recommandations - -**Alors** l'algorithme suggère des audio-guides basés sur: - - | critère | exemple | - |---|---| - | Tags similaires | #Art #Histoire #Musée | - | Créateur identique | Autres audio-guides de @art_guide | - | Localisation proche | Autres musées parisiens | - | Mode de déplacement | Autres audio-guides piéton | - - ---- - -## 23. Suggestion géographique contextuelle - -**Étant donné** qu'un utilisateur est à Paris (GPS détecté) - -**Quand** il ouvre l'onglet "Audio-guides" - -**Alors** les audio-guides parisiens sont mis en avant -**Et** un filtre "🗺️ Autour de moi" est pré-appliqué - ---- - -## 24. Badge "Populaire dans votre région" - -**Étant donné** qu'un audio-guide a >100 écoutes dans la région Île-de-France -**Et** que l'utilisateur est en Île-de-France - -**Quand** l'audio-guide est affiché - -**Alors** un badge "🔥 Populaire près de chez vous" est visible - ---- - -## 25. Préchargement de la séquence suivante - -**Étant donné** que la séquence 3 est en cours à 2:30/3:42 - -**Quand** il reste 60 secondes de lecture - -**Alors** la séquence 4 est préchargée en arrière-plan -**Et** la transition est instantanée (0 latence) - ---- - -## 26. Buffer adaptatif selon connexion - -**Étant donné** qu'un utilisateur est sur réseau 4G - -**Quand** la séquence démarre - -**Alors** 30 secondes d'audio sont bufferisées initialement -**Et** le buffering continue en arrière-plan - ---- - -## 27. 📋 Plan: Buffer selon qualité réseau - -**Étant donné** qu'un utilisateur est sur réseau - -**Quand** une séquence démarre - -**Alors** secondes sont bufferisées - -**📊 Exemples de données:** - -| reseau | buffer_secondes | -|---|---| -| WiFi | 60 | -| 5G | 45 | -| 4G | 30 | -| 3G | 20 | - ---- - -## 28. Compression audio adaptative - -**Étant donné** qu'un utilisateur est sur connexion lente (3G) - -**Quand** une séquence est streamée - -**Alors** le CDN sert la version 64 kbps (au lieu de 128 kbps) -**Et** la qualité reste acceptable pour la voix - ---- - -## 29. Cache intelligent des séquences jouées - -**Étant donné** qu'un utilisateur a écouté les séquences 1-5 - -**Quand** il clique sur "Précédent" pour réécouter la séquence 4 - -**Alors** la séquence 4 est chargée depuis le cache local -**Et** le chargement est instantané (pas de stream) - ---- - -## 30. Nettoyage automatique du cache - -**Étant donné** que le cache audio occupe 500 MB -**Et** que la limite configurée est 300 MB - -**Quand** le nettoyage automatique s'exécute - -**Alors** les séquences les plus anciennes (non téléchargées) sont supprimées -**Et** le cache revient à 280 MB - ---- - -## 31. Tracking des événements clés - -**Étant donné** qu'un utilisateur écoute un audio-guide - -**Quand** il interagit avec l'application - -**Alors** les événements suivants sont trackés: - - | événement | données | - |---|---| - | audio_guide_started | audio_guide_id, mode, user_id | - | sequence_completed | sequence_id, completion_rate, duration | - | audio_guide_completed | audio_guide_id, total_time, sequences_count | - | point_gps_triggered | point_id, distance, auto_or_manual | - | point_gps_missed | point_id, distance, action_taken | - | paywall_displayed | audio_guide_id, sequence_number | - | premium_conversion | source: audio_guide_paywall | - - ---- - -## 32. Heatmap des abandons par séquence - -**Étant donné** qu'un audio-guide a été écouté 1000 fois - -**Quand** le créateur consulte la heatmap - -**Alors** il voit pour chaque séquence: - - | sequence | starts | completions | abandon_rate | - |---|---|---|---| - | 1 | 1000 | 950 | 5% | - | 2 | 950 | 920 | 3% | - | 3 | 920 | 850 | 8% | - | ... | ... | ... | ... | - | 12 | 650 | 580 | 11% | - - ---- - -## 33. Attribution GPS auto vs manuel - -**Étant donné** un audio-guide voiture avec 8 points GPS - -**Quand** les statistiques sont calculées - -**Alors** le créateur voit: - - | mode_declenchement | nombre | - |---|---| - | GPS automatique | 542 | - | Manuel | 123 | - | Point manqué | 89 | - - ---- - -## 34. Audio-guide avec une seule séquence (edge case) - -**Étant donné** un audio-guide avec seulement 1 séquence - -**Quand** il est publié - -**Alors** un avertissement s'affiche: - ---- - -## 35. Séquence manquante ou corrompue - -**Étant donné** qu'une séquence 5 a un fichier audio corrompu - -**Quand** l'utilisateur tente de la lire - -**Alors** un message d'erreur s'affiche -**Et** un bouton "⏭️ Passer à la suivante" est disponible -**Et** le créateur reçoit une notification de l'erreur - ---- - -## 36. GPS désactivé puis réactivé en cours de route - -**Étant donné** un audio-guide voiture en cours -**Et** que l'utilisateur désactive le GPS - -**Quand** il le réactive 10 minutes plus tard - -**Alors** le déclenchement automatique reprend -**Et** les points GPS manqués entre-temps ne déclenchent pas de popup - ---- - -## 37. Modification d'audio-guide avec utilisateurs en cours - -**Étant donné** qu'un audio-guide a 50 utilisateurs en cours d'écoute - -**Quand** le créateur modifie une séquence - -**Alors** les utilisateurs actuels conservent l'ancienne version -**Et** les nouveaux utilisateurs obtiennent la nouvelle version -**Et** un message informe les utilisateurs lors de la prochaine ouverture - ---- - -## 38. Suppression d'audio-guide par le créateur - -**Étant donné** qu'un audio-guide a 20 utilisateurs avec progression - -**Quand** le créateur supprime l'audio-guide - -**Alors** une confirmation stricte est demandée -**Et** si confirmé, les progressions utilisateurs sont archivées (30 jours) -**Et** l'audio-guide devient inaccessible - ---- - -## 39. Signalement d'audio-guide pour contenu inapproprié - -**Étant donné** qu'un utilisateur signale un audio-guide - -**Quand** le signalement est modéré -**Et** jugé valide - -**Alors** l'audio-guide est dépublié temporairement -**Et** le créateur reçoit une notification d'explication -**Et** il peut corriger puis republier - ---- - - - - - -
- - -# Audio-guide mode piéton (navigation manuelle) -> *En tant qu'utilisateur à pied* -> *Je veux naviguer manuellement entre les séquences d'un audio-guide* -> *Afin de contrôler mon rythme de visite* - -**29 scénarios** (28 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté (gratuit) -> **Et** qu'un audio-guide piéton "Visite du Louvre" est disponible avec 12 séquences -## 1. Fin de séquence normale avec pause automatique - -**Étant donné** que la séquence 1 "Introduction" est en cours de lecture - -**Quand** la séquence se termine à 2:15 - -**Alors** le player se met en pause automatiquement -**Et** le message suivant s'affiche: "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt." -**Et** la barre de progression indique "1/12 complétée" - ---- - -## 2. Passage manuel à la séquence suivante - -**Étant donné** que la séquence 1 est terminée et le player en pause - -**Quand** l'utilisateur appuie sur le bouton [▶|] "Suivant" - -**Alors** la séquence 2 "Pyramide du Louvre" démarre immédiatement -**Et** aucune latence n'est observée - ---- - -## 3. Séquence avec publicité (1/5 séquences) - -**Étant donné** que la séquence 5 se termine -**Et** que c'est la 5ème séquence (1 pub toutes les 5) - -**Quand** la séquence se termine - -**Alors** la publicité s'enchaîne automatiquement (sans attente bouton) -**Et** la publicité se lit normalement -**Et** elle est skippable après 5 secondes - ---- - -## 4. Fin de publicité avec pause automatique - -**Étant donné** qu'une publicité est en cours de lecture - -**Quand** la publicité se termine - -**Alors** le player se met en pause automatiquement -**Et** le message suivant s'affiche: "Séquence 6 prête. Appuyez sur Suivant." -**Et** l'utilisateur doit cliquer sur [▶|] pour continuer - ---- - -## 5. Flux complet séquence → pub → séquence - -**Étant donné** que la séquence 5 démarre - -**Quand** la séquence 5 se termine - -**Alors** la publicité démarre automatiquement - -**Quand** la publicité se termine - -**Alors** le player se met en pause - -**Quand** l'utilisateur clique sur [▶|] - -**Alors** la séquence 6 démarre - ---- - -## 6. 📋 Plan: Fréquence de publicité configurable - -**Étant donné** que l'utilisateur gratuit écoute un audio-guide -**Et** que la fréquence pub est configurée à - -**Quand** il termine la séquence - -**Alors** une publicité est insérée : - -**📊 Exemples de données:** - -| frequence | numero_sequence | pub_inseree | -|---|---|---| -| 1/5 | 5 | Oui | -| 1/5 | 10 | Oui | -| 1/5 | 4 | Non | -| 1/3 | 3 | Oui | -| 1/3 | 6 | Oui | - ---- - -## 7. Utilisateur Premium sans publicités - -**Étant donné** que l'utilisateur "premium@example.com" est abonné Premium -**Et** qu'il écoute un audio-guide piéton - -**Quand** il termine la séquence 5 - -**Alors** aucune publicité n'est insérée -**Et** le player se met en pause immédiatement -**Et** le message "Séquence 6 prête. Appuyez sur Suivant." s'affiche - ---- - -## 8. Boutons de contrôle disponibles en mode piéton - -**Étant donné** qu'un audio-guide piéton est en lecture - -**Quand** l'utilisateur consulte les contrôles - -**Alors** les boutons suivants sont visibles: - - | bouton | fonction | - |---|---| - | [▶\ | ] Suivant | Passe à la séquence suivante | - | [\ | ◀] Précédent | Retour à la séquence précédente | - | [⏸️] Pause | Pause temporaire | - | [▶️] Play | Reprend la lecture | - | [📋] Liste | Affiche toutes les séquences | - - ---- - -## 9. Passage à la séquence suivante pendant la lecture - -**Étant donné** que la séquence 3 "La Joconde" est en cours à 1:42/3:42 - -**Quand** l'utilisateur clique sur [▶|] "Suivant" - -**Alors** la séquence 4 "Vénus de Milo" démarre immédiatement -**Et** la séquence 3 n'est pas marquée comme écoutée (car <80%) - ---- - -## 10. Retour à la séquence précédente (saut direct) - -**Étant donné** que la séquence 5 est en cours de lecture - -**Quand** l'utilisateur clique sur [|◀] "Précédent" - -**Alors** la séquence 4 démarre depuis le début (0:00) -**Et** il n'y a pas de logique "replay si >10s" (contrairement au contenu classique) - ---- - -## 11. Pause et reprise pendant une séquence - -**Étant donné** que la séquence 2 est en cours à 1:15/1:48 - -**Quand** l'utilisateur clique sur [⏸️] "Pause" - -**Alors** la lecture se met en pause -**Et** la position 1:15 est conservée - -**Quand** l'utilisateur clique sur [▶️] "Play" - -**Alors** la lecture reprend exactement à 1:15 - ---- - -## 12. Interface liste des séquences - -**Étant donné** qu'un audio-guide de 12 séquences est en cours - -**Quand** l'utilisateur clique sur [📋] "Liste séquences" - -**Alors** une liste complète s'affiche avec: - - | élément | exemple | - |---|---| - | Numéro et titre | "3. La Joconde" | - | Durée | (3:42) | - | État | ✅ Écouté / ▶️ En cours / ⭕ À écouter | - | Date écoute (si écouté) | "Écouté le 15/01/2026" | - - ---- - -## 13. Séquence en cours dans la liste - -**Étant donné** que la séquence 3 est en cours à 1:22/3:42 - -**Quand** la liste des séquences est affichée - -**Alors** la séquence 3 affiche: - ---- - -## 14. Navigation libre vers séquence non encore écoutée - -**Étant donné** que l'utilisateur est sur la séquence 3 -**Et** que les séquences 4 à 12 n'ont pas été écoutées - -**Quand** l'utilisateur clique sur "8. Les Appartements de Napoléon" - -**Alors** la séquence 8 démarre immédiatement depuis 0:00 -**Et** les séquences 4 à 7 restent marquées ⭕ "À écouter" - ---- - -## 15. Retour à une séquence déjà écoutée - -**Étant donné** que la séquence 2 "Pyramide du Louvre" a été écoutée à 100% -**Et** qu'elle est marquée ✅ "Écouté" - -**Quand** l'utilisateur clique dessus dans la liste - -**Alors** la séquence 2 démarre depuis 0:00 -**Et** le statut ✅ est conservé - ---- - -## 16. Checkmarks sur séquences écoutées >80% - -**Étant donné** que l'utilisateur écoute la séquence 2 de durée 1:48 - -**Quand** il écoute jusqu'à 1:30 (83% de complétion) -**Et** qu'il passe à la séquence suivante - -**Alors** la séquence 2 est marquée ✅ "Écouté" -**Et** la date d'écoute est enregistrée - ---- - -## 17. Pas de checkmark si séquence écoutée <80% - -**Étant donné** que l'utilisateur écoute la séquence 3 de durée 3:42 - -**Quand** il écoute jusqu'à 1:30 (40% de complétion) -**Et** qu'il passe à la séquence suivante - -**Alors** la séquence 3 reste marquée ⭕ "À écouter" - ---- - -## 18. Bouton "Tout afficher" si plus de 6 séquences - -**Étant donné** un audio-guide avec 12 séquences - -**Quand** la liste est affichée - -**Alors** seules les 6 premières séquences sont visibles initialement -**Et** un bouton "Tout afficher ▼" est présent - -**Quand** l'utilisateur clique sur "Tout afficher ▼" - -**Alors** les 6 séquences restantes sont affichées - ---- - -## 19. Saut vers séquence spécifique depuis la barre de progression - -**Étant donné** qu'un audio-guide est en cours - -**Quand** l'utilisateur clique sur "3/12" dans la barre de progression - -**Alors** la liste des séquences s'ouvre -**Et** la séquence en cours (3) est mise en surbrillance - ---- - -## 20. Position exacte sauvegardée automatiquement - -**Étant donné** que la séquence 5 est en cours à 2:34/4:10 - -**Quand** l'utilisateur quitte l'application - -**Alors** la position 2:34 dans la séquence 5 est sauvegardée -**Et** la sauvegarde est effectuée localement (SQLite) -**Et** la sauvegarde est synchronisée sur le cloud (PostgreSQL) - ---- - -## 21. Reprise après fermeture de l'application - -**Étant donné** que l'utilisateur a quitté l'app à la séquence 5 position 2:34 - -**Quand** il rouvre l'audio-guide - -**Alors** une popup de reprise s'affiche - -**Quand** il clique sur "▶️ Reprendre" - -**Alors** la lecture reprend à la séquence 5 position 2:34 exacte - ---- - -## 22. Visiteur qui connaît déjà certaines œuvres - -**Étant donné** qu'un visiteur du Louvre démarre l'audio-guide -**Et** qu'il connaît déjà "La Joconde" (séquence 3) - -**Quand** il arrive à la séquence 3 -**Et** qu'il clique sur [▶|] "Suivant" après 10 secondes - -**Alors** la séquence 4 démarre immédiatement -**Et** la séquence 3 n'est pas marquée comme écoutée - ---- - -## 23. Visiteur qui veut voir une œuvre éloignée - -**Étant donné** qu'un visiteur est à la séquence 2 -**Et** qu'il aperçoit "La Victoire de Samothrace" (séquence 8) physiquement - -**Quand** il ouvre la liste et clique sur la séquence 8 - -**Alors** la séquence 8 démarre immédiatement -**Et** il peut écouter la description même si les séquences 3-7 ne sont pas écoutées - ---- - -## 24. Visiteur qui prend une pause café - -**Étant donné** qu'un visiteur écoute la séquence 6 - -**Quand** il clique sur [⏸️] "Pause" -**Et** qu'il ferme l'application pendant 30 minutes -**Quand** il rouvre l'application - -**Alors** la séquence 6 reprend à la position exacte où il s'était arrêté - ---- - -## 25. Visiteur qui revient le lendemain - -**Étant donné** qu'un visiteur a écouté les séquences 1-5 hier -**Et** qu'il revient au musée aujourd'hui - -**Quand** il ouvre l'audio-guide - -**Alors** une popup propose "▶️ Reprendre" (séquence 6) -**Et** les séquences 1-5 sont marquées ✅ "Écouté" - ---- - -## 26. Séquence audio corrompue ou indisponible - -**Étant donné** que la séquence 7 a un fichier audio corrompu - -**Quand** l'utilisateur tente de la lire - -**Alors** un message d'erreur s'affiche: - ---- - -## 27. Perte de connexion pendant le chargement - -**Étant donné** que l'utilisateur lance la séquence 4 -**Et** que la connexion réseau est perdue - -**Quand** le chargement échoue - -**Alors** un message s'affiche: "Connexion perdue. Vérifiez votre réseau." -**Et** un bouton "🔄 Réessayer" est disponible - ---- - -## 28. Batterie faible en cours de visite - -**Étant donné** que la batterie de l'appareil est à 5% - -**Quand** l'utilisateur écoute une séquence - -**Alors** une notification système s'affiche: "Batterie faible. Progression sauvegardée." -**Et** la position est sauvegardée localement toutes les 10 secondes - ---- - -## 29. Mode piéton sans points GPS (pas d'alerte localisation) - -**Étant donné** un audio-guide en mode piéton -**Et** que le GPS est désactivé - -**Quand** l'utilisateur démarre l'audio-guide - -**Alors** aucune alerte GPS ne s'affiche -**Et** l'audio-guide fonctionne normalement (navigation 100% manuelle) - ---- - - - - - -
- - -# Audio-guide mode voiture (GPS automatique) -> *En tant qu'utilisateur en voiture* -> *Je veux que les séquences se déclenchent automatiquement selon ma position GPS* -> *Afin de profiter d'une expérience guidée hands-free* - -**45 scénarios** (40 standards, 5 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté (gratuit) -> **Et** qu'un audio-guide voiture "Safari du Paugre" est disponible avec 8 séquences -> **Et** que le GPS est activé -## 1. Distinction audio-guides vs contenus géolocalisés simples - -**Étant donné** que l'utilisateur est en mode voiture - -**Quand** il écoute un contenu géolocalisé simple (1 séquence unique) - -**Alors** une notification avec compteur 7→1 est affichée 7s avant le point -**Et** il doit valider avec "Suivant" + décompte 5s -**Et** ce contenu compte 1/6 dans le quota horaire - -**Quand** il démarre un audio-guide multi-séquences - -**Alors** les séquences se déclenchent au point GPS exact (rayon 30m) -**Et** aucun compteur 7s n'est affiché (juste notification "Ding" + toast 2s) -**Et** l'audio-guide entier compte 1/6 dans le quota - ---- - -## 2. Démarrage automatique au premier point GPS - -**Étant donné** que l'utilisateur démarre l'audio-guide "Safari du Paugre" -**Et** que le point de départ est à (43.1234, 2.5678) avec rayon 30m - -**Quand** l'utilisateur entre dans le rayon de 30m - -**Alors** la séquence 1 "Introduction - Point d'accueil" démarre automatiquement -**Et** une notification sonore "Ding" est jouée (non intrusif) -**Et** un toast s'affiche brièvement pendant 2s: "Introduction - Point d'accueil" -**Et** aucun compteur 7→1 n'est affiché (contrairement aux contenus géolocalisés simples) - ---- - -## 3. Déclenchement automatique séquence suivante - -**Étant donné** que la séquence 1 est terminée -**Et** que l'utilisateur se déplace vers le point GPS 2 (43.1245, 2.5690) - -**Quand** l'utilisateur entre dans le rayon de 30m du point 2 - -**Alors** la séquence 2 "Enclos des lions" démarre automatiquement -**Et** une notification "Ding" + toast "Enclos des lions" s'affiche - ---- - -## 4. Navigation manuelle conservée (bouton Suivant actif) - -**Étant donné** que la séquence 1 est en cours -**Et** que l'utilisateur est encore loin du point GPS 2 (distance 500m) - -**Quand** l'utilisateur clique sur [▶|] "Suivant" - -**Alors** la séquence 2 démarre immédiatement -**Et** aucune vérification GPS n'est effectuée - ---- - -## 5. Navigation manuelle conservée (bouton Précédent actif) - -**Étant donné** que la séquence 3 est en cours - -**Quand** l'utilisateur clique sur [|◀] "Précédent" - -**Alors** la séquence 2 démarre depuis le début -**Et** aucune vérification GPS n'est effectuée - ---- - -## 6. Tous les boutons de contrôle restent actifs - -**Étant donné** qu'un audio-guide voiture est en cours - -**Quand** l'utilisateur consulte les contrôles - -**Alors** les boutons suivants sont actifs: - - | bouton | état | comportement | - |---|---|---| - | [▶\ | ] Suivant | ✅ | Passe séquence suivante immédiate | - | [\ | ◀] Précédent | ✅ | Retour séquence précédente | - | [⏸️] Pause | ✅ | Pause temporaire | - | [📋] Liste | ✅ | Saut direct possible | - - ---- - -## 7. Use case - Embouteillage (séquence finie, point GPS loin) - -**Étant donné** que la séquence 3 "Enclos des girafes" est terminée -**Et** que le point GPS 4 est à 2 km de distance (embouteillage) - -**Quand** l'utilisateur clique manuellement sur [▶|] "Suivant" - -**Alors** la séquence 4 démarre immédiatement -**Et** l'utilisateur peut continuer l'expérience sans attendre d'atteindre le point GPS - ---- - -## 8. Use case - Route fermée (point GPS inaccessible) - -**Étant donné** que le point GPS 5 est sur une route fermée -**Et** que l'utilisateur ne peut pas s'en approcher - -**Quand** l'utilisateur clique sur [▶|] "Suivant" - -**Alors** la séquence 5 démarre quand même -**Et** l'audio-guide continue normalement - ---- - -## 9. Use case - Passager manipule l'application - -**Étant donné** que l'utilisateur est passager (non conducteur) -**Et** que la vitesse du véhicule est 45 km/h - -**Quand** le passager clique sur [▶|] "Suivant" - -**Alors** la séquence suivante démarre -**Et** un avertissement s'affiche pendant 3 secondes - ---- - -## 10. Avertissement sécurité si vitesse >10 km/h - -**Étant donné** que la vitesse actuelle est 35 km/h - -**Quand** l'utilisateur clique sur un bouton (Suivant ou Précédent) - -**Alors** l'action est exécutée immédiatement (pas de blocage) -**Et** un toast s'affiche pendant 3 secondes: - ---- - -## 11. 📋 Plan: Avertissement selon la vitesse - -**Étant donné** que la vitesse actuelle est km/h - -**Quand** l'utilisateur clique sur un bouton de navigation - -**Alors** l'avertissement est affiché : - -**📊 Exemples de données:** - -| vitesse | avertissement | -|---|---| -| 5 | Non | -| 10 | Non | -| 11 | Oui | -| 35 | Oui | -| 90 | Oui | - ---- - -## 12. Affichage entre deux séquences avec progress bar - -**Étant donné** que la séquence 2 "Les lions" vient de se terminer -**Et** que le prochain point GPS 3 "Enclos des girafes" est à 500m - -**Quand** l'interface bascule en mode "attente prochain point" - -**Alors** l'écran affiche: - - | élément | description | - |---|---| - | Statut séquence | "✅ Séquence 2/8 terminée" | - | Nom séquence | "Les lions" | - | Progress bar | Barre dynamique remplie selon distance (0%) | - | Distance prochain point | "500 mètres" | - | ETA | "≈ 1 minute 30" | - | Direction | ↗️ | - | Vitesse actuelle | "28 km/h" | - | Bouton "Rejouer séq." | Permet de réécouter la séquence qui vient de finir | - - ---- - -## 13. Progress bar dynamique vers le prochain point - -**Étant donné** que la distance initiale vers le prochain point était 500m -**Et** que la séquence précédente est terminée - -**Quand** l'utilisateur se rapproche du prochain point -**Et** que la distance actuelle est 175m - -**Alors** la progress bar affiche "65%" remplie -**Et** le calcul est: 100 - (175 / 500 * 100) = 65% -**Et** la barre se met à jour chaque seconde - ---- - -## 14. Bouton "Rejouer séq." pour réécouter - -**Étant donné** que la séquence 3 vient de se terminer -**Et** que l'interface "attente prochain point" est affichée - -**Quand** l'utilisateur clique sur [▶️ Rejouer séq.] - -**Alors** la séquence 3 redémarre depuis 0:00 -**Et** l'utilisateur peut la réécouter (utile si distraction) - ---- - -## 15. Interface en conduite avec distance et ETA - -**Étant donné** que la séquence 2 est en cours -**Et** que le prochain point GPS 3 "Enclos des girafes" est à 320m -**Et** que la vitesse actuelle est 28 km/h - -**Quand** l'interface est affichée - -**Alors** les informations suivantes sont visibles: - - | information | valeur | - |---|---| - | Nom prochain point | "Enclos des girafes" | - | Distance | "320 mètres" | - | ETA | "≈ 40 secondes" | - | Direction | ↗️ (flèche direction) | - | Vitesse actuelle | "28 km/h" | - | Vitesse recommandée | "20-30 km/h" | - - ---- - -## 16. Mise à jour de la distance en temps réel - -**Étant donné** que la distance au prochain point est 500m - -**Quand** 10 secondes s'écoulent et que l'utilisateur se rapproche - -**Alors** la distance est mise à jour chaque seconde -**Et** la nouvelle distance "450m" s'affiche - ---- - -## 17. Mise à jour de l'ETA en temps réel - -**Étant donné** que l'ETA est "≈ 2 minutes" -**Et** que la vitesse est constante à 30 km/h - -**Quand** l'utilisateur se rapproche du point - -**Alors** l'ETA est recalculé chaque seconde -**Et** il diminue progressivement: "≈ 1 minute 50", "≈ 1 minute 40", etc. - ---- - -## 18. 📋 Plan: Format d'affichage de la distance - -**Étant donné** que la distance au prochain point est - -**Quand** l'interface est mise à jour - -**Alors** la distance affichée est "" - -**📊 Exemples de données:** - -| distance_metres | affichage | -|---|---| -| 50 | 50 m | -| 320 | 320 m | -| 980 | 980 m | -| 1200 | 1.2 km | -| 5400 | 5.4 km | - ---- - -## 19. 📋 Plan: Format d'affichage de l'ETA - -**Étant donné** que l'ETA calculé est secondes - -**Quand** l'interface est mise à jour - -**Alors** l'ETA affiché est "" - -**📊 Exemples de données:** - -| secondes | affichage | -|---|---| -| 30 | ≈ 30 secondes | -| 75 | ≈ 1 minute | -| 150 | ≈ 2 minutes | -| 400 | ≈ 6 minutes | - ---- - -## 20. Calcul de la direction (flèche 8 directions) - -**Étant donné** que la position actuelle est (43.1234, 2.5678) -**Et** que le prochain point est au nord-est (angle 45°) - -**Quand** la direction est calculée - -**Alors** la flèche "↗" est affichée - ---- - -## 21. 📋 Plan: Flèches de direction selon l'angle - -**Étant donné** que l'angle vers le prochain point est ° - -**Quand** la direction est calculée - -**Alors** la flèche "" est affichée - -**📊 Exemples de données:** - -| angle | fleche | -|---|---| -| 0 | ↑ | -| 45 | ↗ | -| 90 | → | -| 135 | ↘ | -| 180 | ↓ | -| 225 | ↙ | -| 270 | ← | -| 315 | ↖ | - ---- - -## 22. Mise à jour de la direction toutes les 5 secondes - -**Étant donné** que la direction actuelle est ↑ (nord) -**Et** que l'utilisateur tourne vers l'est - -**Quand** 5 secondes s'écoulent - -**Alors** la direction est recalculée -**Et** la nouvelle flèche ↗ (nord-est) s'affiche - ---- - -## 23. Message "En attente de déplacement" si vitesse <5 km/h - -**Étant donné** que la vitesse actuelle est 2 km/h (arrêté) - -**Quand** l'ETA est calculé - -**Alors** le message "En attente de déplacement" s'affiche -**Et** l'ETA n'est pas calculé (car vitesse insuffisante) - ---- - -## 24. Simplicité de l'interface (pas de carte miniature) - -**Étant donné** qu'un audio-guide voiture est en cours - -**Quand** l'interface est affichée - -**Alors** aucune carte miniature n'est présente -**Et** seuls les éléments essentiels sont affichés: - - | élément | - |---| - | Distance | - | ETA | - | Direction (flèche) | - | Vitesse | - | Contrôles audio | - - ---- - -## 25. Rayon de déclenchement par défaut en mode voiture - -**Étant donné** un audio-guide voiture - -**Quand** un point GPS est défini - -**Alors** le rayon de déclenchement est 30 mètres par défaut -**Et** le rayon de tolérance "point manqué" est 100 mètres - ---- - -## 26. Déclenchement dans le rayon (30m) - -**Étant donné** que le point GPS 3 est défini avec rayon 30m - -**Quand** l'utilisateur entre à 25m du point - -**Alors** la séquence 3 se déclenche automatiquement - ---- - -## 27. Pas de déclenchement hors rayon - -**Étant donné** que le point GPS 3 a un rayon de 30m - -**Quand** l'utilisateur passe à 45m du point - -**Alors** la séquence 3 ne se déclenche pas automatiquement - ---- - -## 28. Point manqué dans rayon de tolérance (100m) - -**Étant donné** que l'utilisateur passe à 60m du point GPS 4 (hors rayon 30m) -**Et** que 60m < 100m (rayon tolérance) - -**Quand** le point est détecté comme manqué - -**Alors** un toast s'affiche: "⚠️ Point manqué : Enclos des éléphants" -**Et** une popup s'affiche pendant 5 secondes avec 3 options - ---- - -## 29. Popup "Point manqué" avec 3 actions - -**Étant donné** qu'un point GPS a été manqué (distance 60m) - -**Quand** la popup s'affiche - -**Alors** les options suivantes sont disponibles: - - | bouton | icône | comportement | - |---|---|---| - | Écouter quand même | 🔊 | Lance séquence immédiatement (même hors zone) | - | Passer au suivant | ⏭️ | Skip séquence, continue vers prochain point | - | Faire demi-tour | 🔙 | Ouvre GPS externe (Google Maps/Waze) vers point | - - ---- - -## 30. Action "Écouter quand même" - -**Étant donné** qu'un point GPS est manqué - -**Quand** l'utilisateur clique sur "🔊 Écouter quand même" - -**Alors** la séquence correspondante démarre immédiatement -**Et** l'utilisateur peut continuer sa route - ---- - -## 31. Action "Passer au suivant" - -**Étant donné** qu'un point GPS 5 est manqué - -**Quand** l'utilisateur clique sur "⏭️ Passer au suivant" - -**Alors** la séquence 5 est ignorée (non écoutée) -**Et** l'application attend le point GPS 6 -**Et** la distance vers le point 6 s'affiche - ---- - -## 32. Action "Faire demi-tour" - -**Étant donné** qu'un point GPS est manqué à (43.1250, 2.5700) - -**Quand** l'utilisateur clique sur "🔙 Faire demi-tour" - -**Alors** l'application détecte l'app GPS installée (Google Maps ou Waze) -**Et** ouvre la navigation GPS externe vers (43.1250, 2.5700) - ---- - -## 33. Point manqué au-delà du rayon de tolérance (>100m) - -**Étant donné** que l'utilisateur passe à 150m du point GPS 6 - -**Quand** la distance est détectée - -**Alors** aucune popup ne s'affiche (point trop loin) -**Et** l'utilisateur peut naviguer manuellement avec [▶|] - ---- - -## 34. 📋 Plan: Gestion selon la distance au point - -**Étant donné** un point GPS avec rayon 30m et tolérance 100m - -**Quand** l'utilisateur passe à du point - -**Alors** le comportement est - -**📊 Exemples de données:** - -| distance | comportement | -|---|---| -| 20m | Déclenchement automatique séquence | -| 40m | Rien (hors rayon, pas encore tolérance) | -| 60m | Popup "Point manqué" avec 3 options | -| 110m | Rien (trop loin, hors tolérance) | - ---- - -## 35. Configuration rayon personnalisé par le créateur - -**Étant donné** qu'un créateur définit un rayon de 50m (au lieu de 30m) - -**Quand** un utilisateur entre à 45m du point - -**Alors** la séquence se déclenche automatiquement -**Et** le rayon personnalisé est respecté - ---- - -## 36. Rayon minimum et maximum configurables - -**Étant donné** qu'un créateur configure un rayon - -**Quand** il ajuste le curseur - -**Alors** les valeurs disponibles sont de 10m à 200m -**Et** le rayon par défaut suggéré est 30m pour la voiture - ---- - -## 37. Safari-parc avec déclenchement automatique fluide - -**Étant donné** qu'un utilisateur roule dans un safari à 20 km/h - -**Quand** il passe devant "Enclos des lions" (point GPS 2) - -**Alors** la séquence 2 démarre automatiquement sans intervention -**Et** il peut se concentrer sur la conduite et l'observation - ---- - -## 38. Détour imprévu (travaux sur la route) - -**Étant donné** qu'un utilisateur prend un détour à cause de travaux -**Et** que le point GPS 4 devient inaccessible - -**Quand** il est loin du point (>100m) -**Et** qu'il clique manuellement sur [▶|] - -**Alors** la séquence 4 démarre quand même -**Et** l'expérience continue sans blocage - ---- - -## 39. Passager qui navigue librement - -**Étant donné** qu'un passager utilise l'application -**Et** que le conducteur roule à 50 km/h - -**Quand** le passager clique sur "Précédent" pour réécouter - -**Alors** l'action est exécutée immédiatement -**Et** un warning apparaît brièvement (sensibilisation) - ---- - -## 40. Embouteillage prolongé - -**Étant donné** que la séquence 3 est terminée depuis 10 minutes -**Et** que l'utilisateur est bloqué dans un embouteillage -**Et** que le point GPS 4 est encore à 1.5 km - -**Quand** l'utilisateur clique sur [▶|] - -**Alors** la séquence 4 démarre immédiatement -**Et** l'utilisateur peut passer le temps en écoutant - ---- - -## 41. GPS désactivé en mode voiture - -**Étant donné** qu'un audio-guide voiture est démarré -**Et** que le GPS est désactivé - -**Quand** l'application détecte l'absence de GPS - -**Alors** une alerte s'affiche: - ---- - -## 42. Action "Passer en mode Manuel" - -**Étant donné** que le GPS est désactivé - -**Quand** l'utilisateur clique sur "Passer en mode Manuel" - -**Alors** l'audio-guide bascule en navigation 100% manuelle -**Et** les boutons [▶|] et [|◀] permettent de naviguer -**Et** aucun déclenchement GPS n'est tenté - ---- - -## 43. Précision GPS insuffisante - -**Étant donné** que le signal GPS a une précision de ±150m - -**Quand** l'utilisateur approche d'un point GPS avec rayon 30m - -**Alors** un avertissement s'affiche: - ---- - -## 44. Perte signal GPS en cours de route - -**Étant donné** qu'un audio-guide voiture est en cours - -**Quand** le signal GPS est perdu (tunnel, parking souterrain) - -**Alors** un toast s'affiche: "Signal GPS perdu. Navigation manuelle active." -**Et** les boutons de navigation restent actifs - -**Quand** le signal GPS revient - -**Alors** un toast s'affiche: "Signal GPS rétabli" -**Et** le déclenchement automatique est réactivé - ---- - -## 45. Dépassement de la vitesse recommandée - -**Étant donné** qu'un audio-guide recommande 20-30 km/h -**Et** que l'utilisateur roule à 65 km/h - -**Quand** la vitesse est détectée - -**Alors** l'affichage vitesse est en orange: "⚠️ 65 km/h" -**Et** un message info s'affiche: "Vitesse élevée. Risque de manquer des points." - ---- - - - - - -
- - -# Audio-guides modes vélo et transport -> *En tant qu'utilisateur à vélo ou en transport en commun* -> *Je veux profiter d'un guidage GPS adapté à mon mode de déplacement* -> *Afin d'avoir une expérience optimisée avec tolérances appropriées* - -**27 scénarios** (24 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté -> **Et** que le GPS est activé -## 1. 📋 Plan: Paramètres par mode de déplacement - -**Étant donné** un audio-guide configuré en mode - -**Alors** les paramètres suivants sont appliqués: - - | paramètre | valeur | - |---|---| - | Rayon déclenchement | | - | Rayon tolérance "point manqué" | | - | Vitesse recommandée | | - | Seuil warning sécurité | | - - -**📊 Exemples de données:** - -| mode | rayon_declenchement | rayon_tolerance | vitesse_recommandee | seuil_warning | -|---|---|---|---|---| -| Voiture | 30m | 100m | 20-50 km/h | >10 km/h | -| Vélo | 50m | 75m | 10-25 km/h | >5 km/h | -| Transport | 100m | 150m | Variable | Désactivé | - ---- - -## 2. Déclenchement automatique avec rayon 50m (mode vélo) - -**Étant donné** un audio-guide vélo "Circuit des châteaux de la Loire" -**Et** que le point GPS 3 a un rayon de 50m - -**Quand** l'utilisateur à vélo entre à 45m du point - -**Alors** la séquence 3 "Château de Chambord" se déclenche automatiquement - ---- - -## 3. Rayon plus large justifié pour le vélo - -**Étant donné** qu'un cycliste roule sur piste cyclable -**Et** que sa vitesse varie entre 8 et 22 km/h (arrêts fréquents) -**Et** que le tracé est moins prévisible qu'en voiture - -**Quand** un point GPS avec rayon 50m est défini - -**Alors** le rayon plus large compense la variabilité de trajectoire - ---- - -## 4. Warning sécurité dès 5 km/h en vélo - -**Étant donné** un audio-guide vélo en cours -**Et** que la vitesse actuelle est 12 km/h - -**Quand** l'utilisateur clique sur [▶|] "Suivant" - -**Alors** l'action est exécutée -**Et** un warning s'affiche: "⚠️ Manipulation en déplacement détecté. Pour votre sécurité, arrêtez-vous." - ---- - -## 5. 📋 Plan: Warning vélo selon la vitesse - -**Étant donné** que la vitesse actuelle à vélo est km/h - -**Quand** l'utilisateur clique sur un bouton de navigation - -**Alors** le warning est affiché : - -**📊 Exemples de données:** - -| vitesse | warning | -|---|---| -| 0 | Non | -| 4 | Non | -| 6 | Oui | -| 15 | Oui | -| 25 | Oui | - ---- - -## 6. Tolérance GPS moins stricte en vélo - -**Étant donné** qu'un cycliste passe à 65m du point GPS 4 -**Et** que le rayon de déclenchement est 50m -**Et** que le rayon de tolérance est 75m - -**Quand** la distance est détectée - -**Alors** la popup "Point manqué" s'affiche avec 3 options -**Et** le système tolère l'écart (trajectoire vélo moins prévisible) - ---- - -## 7. Affichage adapté au vélo - -**Étant donné** un audio-guide vélo en cours - -**Quand** l'interface est affichée - -**Alors** les informations suivantes sont visibles: - - | information | valeur | - |---|---| - | Icône mode | 🚴 | - | Distance prochain point | "450 m" | - | ETA | "≈ 2 minutes" | - | Direction | ↗️ | - | Vitesse actuelle | "18 km/h" | - | Vitesse recommandée | "10-25 km/h" | - - ---- - -## 8. Cas d'usage - Piste cyclable avec arrêts fréquents - -**Étant donné** qu'un cycliste suit un circuit nature -**Et** qu'il s'arrête régulièrement (feux, photos, fatigue) - -**Quand** il s'arrête à 40m d'un point GPS (rayon 50m) - -**Alors** la séquence se déclenche automatiquement -**Et** le rayon large permet le déclenchement malgré l'arrêt - ---- - -## 9. Cas d'usage - Circulation mixte piétons/vélos - -**Étant donné** qu'un cycliste roule sur voie partagée -**Et** qu'il doit ralentir fréquemment pour éviter les piétons - -**Quand** sa vitesse varie entre 5 et 20 km/h - -**Alors** le système s'adapte avec le rayon 50m -**Et** le déclenchement reste fiable - ---- - -## 10. Déclenchement automatique avec rayon 100m (mode transport) - -**Étant donné** un audio-guide transport "Ligne touristique Paris" -**Et** que le point GPS "Tour Eiffel" a un rayon de 100m - -**Quand** le bus touristique entre à 85m du point - -**Alors** la séquence "Tour Eiffel" se déclenche automatiquement - ---- - -## 11. Rayon très large justifié pour le transport - -**Étant donné** qu'un bus touristique suit une ligne fixe -**Et** qu'il effectue des arrêts fréquents (stations) -**Et** que l'utilisateur n'a aucun contrôle sur la trajectoire - -**Quand** un point GPS avec rayon 100m est défini - -**Alors** le rayon large compense les arrêts et la ligne fixe - ---- - -## 12. Pas de warning sécurité en mode transport - -**Étant donné** un audio-guide transport en cours -**Et** que le bus roule à 50 km/h - -**Quand** l'utilisateur clique sur [▶|] "Suivant" - -**Alors** l'action est exécutée immédiatement -**Et** aucun warning n'est affiché - ---- - -## 13. Vitesse recommandée "Selon ligne" - -**Étant donné** un audio-guide transport - -**Quand** l'interface est affichée - -**Alors** la vitesse recommandée indique "Selon ligne" -**Et** aucune valeur fixe n'est affichée (car ligne de transport varie) - ---- - -## 14. Tolérance horaire pour retards - -**Étant donné** qu'un bus touristique est en retard de 3 minutes -**Et** qu'il arrive au point GPS "Musée du Louvre" avec retard - -**Quand** il entre dans le rayon de 100m - -**Alors** la séquence se déclenche normalement -**Et** le système tolère le retard (pas de pénalité temporelle) - ---- - -## 15. Tolérance spatiale très large (150m) - -**Étant donné** qu'un bus passe à 120m du point GPS "Arc de Triomphe" -**Et** que le rayon de déclenchement est 100m -**Et** que le rayon de tolérance est 150m - -**Quand** la distance est détectée - -**Alors** la popup "Point manqué" s'affiche avec 3 options - ---- - -## 16. Affichage adapté au transport - -**Étant donné** un audio-guide transport en cours - -**Quand** l'interface est affichée - -**Alors** les informations suivantes sont visibles: - - | information | valeur | - |---|---| - | Icône mode | 🚌 | - | Distance prochain point | "1.2 km" | - | ETA | "≈ 3 minutes" | - | Direction | → | - | Vitesse actuelle | "35 km/h" | - | Vitesse recommandée | "Selon ligne" | - - ---- - -## 17. Cas d'usage - Bus touristique hop-on hop-off - -**Étant donné** un bus touristique "Paris Open Tour" -**Et** qu'il suit un circuit fixe avec 15 arrêts - -**Quand** il approche de chaque arrêt - -**Alors** la séquence correspondante se déclenche automatiquement -**Et** l'utilisateur n'a rien à faire (expérience passive) - ---- - -## 18. Cas d'usage - Train panoramique - -**Étant donné** un train touristique "Ligne des Alpes" -**Et** qu'il roule à vitesse variable (20-80 km/h) - -**Quand** il passe près de points d'intérêt - -**Alors** les séquences se déclenchent avec rayon 100m -**Et** le système compense la vitesse élevée - ---- - -## 19. Navigation manuelle conservée (vélo et transport) - -**Étant donné** un audio-guide en mode - -**Quand** l'utilisateur clique sur [▶|] ou [|◀] - -**Alors** les boutons manuels fonctionnent normalement -**Et** aucune vérification GPS n'est effectuée - ---- - -## 20. Affichage distance + ETA + direction (tous modes) - -**Étant donné** un audio-guide en mode - -**Quand** l'interface est affichée - -**Alors** les informations distance, ETA et direction sont affichées -**Et** le format est identique au mode voiture - ---- - -## 21. Gestion "Point manqué" identique - -**Étant donné** un audio-guide en mode - -**Quand** un point GPS est manqué (dans rayon tolérance) - -**Alors** la popup avec 3 options s'affiche: - - | option | - |---| - | 🔊 Écouter quand même | - | ⏭️ Passer au suivant | - | 🔙 Faire demi-tour | - - ---- - -## 22. 📋 Plan: Insertion publicité dans tous les modes - -**Étant donné** un utilisateur gratuit écoute un audio-guide en mode - -**Quand** la séquence 5 se termine (1 pub / 5 séquences) - -**Alors** la publicité s'enchaîne automatiquement -**Et** elle est skippable après 5 secondes - -**📊 Exemples de données:** - -| mode | -|---| -| Voiture | -| Vélo | -| Transport | -| Piéton | - ---- - -## 23. GPS imprécis en forêt (vélo) - -**Étant donné** un cycliste dans une forêt dense -**Et** que la précision GPS est ±80m - -**Quand** il approche d'un point GPS avec rayon 50m - -**Alors** un avertissement s'affiche: - ---- - -## 24. Bus dévié de son itinéraire (transport) - -**Étant donné** un bus touristique avec déviation -**Et** que plusieurs points GPS deviennent inaccessibles - -**Quand** l'utilisateur est informé - -**Alors** un message s'affiche: - ---- - -## 25. Changement de mode en cours de route - -**Étant donné** un audio-guide démarré en mode "Vélo" - -**Quand** l'utilisateur décide de continuer à pied -**Et** qu'il ouvre les paramètres - -**Alors** il peut changer le mode vers "Piéton" -**Et** les rayons sont reconfigurés automatiquement -**Et** une confirmation s'affiche: - ---- - -## 26. Détection automatique incohérente - -**Étant donné** qu'un utilisateur marche rapidement (7 km/h) -**Et** que le système détecte "Vélo" par erreur - -**Quand** la suggestion s'affiche - -**Alors** l'utilisateur peut cliquer sur "Changer" -**Et** sélectionner manuellement "Piéton" - ---- - -## 27. Batterie en mode vélo longue distance - -**Étant donné** un circuit vélo de 50 km avec 20 séquences -**Et** que l'utilisateur roule pendant 3 heures - -**Quand** la batterie atteint 15% - -**Alors** une notification suggère: - ---- - - - - - -
- - -# Audio-guides Premium et monétisation -> *En tant que créateur* -> *Je veux pouvoir proposer des audio-guides Premium* -> *Afin de monétiser mon contenu de qualité* - -**31 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que le créateur "guide@example.com" est connecté et vérifié -## 1. Création d'un audio-guide Premium - -**Étant donné** que le créateur crée un audio-guide "Visite VIP Versailles" - -**Quand** il accède aux paramètres de monétisation (étape 4) - -**Alors** il peut choisir: - - | option | description | - |---|---| - | Gratuit | Accessible à tous (avec pubs) | - | Premium | Réservé abonnés Premium | - - ---- - -## 2. Badge Premium visible sur l'audio-guide - -**Étant donné** un audio-guide configuré en Premium - -**Quand** il est affiché dans les résultats de recherche - -**Alors** un badge "👑 Premium" est visible -**Et** la cover image a un cadre doré subtil - ---- - -## 3. Preview 3 premières séquences pour utilisateurs gratuits - -**Étant donné** un audio-guide Premium "Visite VIP Versailles" avec 15 séquences -**Et** qu'un utilisateur gratuit ouvre l'audio-guide - -**Quand** il consulte la liste des séquences - -**Alors** les séquences affichent: - - | séquence | état | - |---|---| - | 1 | ✅ Accessible (preview) | - | 2 | ✅ Accessible (preview) | - | 3 | ✅ Accessible (preview) | - | 4 | 🔒 Réservé Premium | - | 5-15 | 🔒 Réservé Premium | - - ---- - -## 4. Écoute des 3 premières séquences sans blocage - -**Étant donné** un utilisateur gratuit -**Et** un audio-guide Premium avec preview - -**Quand** il écoute les séquences 1, 2 et 3 - -**Alors** aucune publicité n'est insérée (preview = teasing) -**Et** l'écoute est fluide - ---- - -## 5. Paywall après la 3ème séquence - -**Étant donné** qu'un utilisateur gratuit termine la séquence 3 - -**Quand** la séquence se termine - -**Alors** un overlay paywall s'affiche immédiatement: - ---- - -## 6. Bouton "Passer Premium" vers tunnel d'abonnement - -**Étant donné** que l'overlay paywall Premium est affiché - -**Quand** l'utilisateur clique sur "Passer Premium" - -**Alors** il est redirigé vers la page d'abonnement Mangopay -**Et** l'audio-guide actuel est marqué en "pending" (reprise après souscription) - ---- - -## 7. Reprise automatique après souscription Premium - -**Étant donné** qu'un utilisateur s'est abonné Premium depuis un paywall audio-guide - -**Quand** l'abonnement est activé - -**Alors** il est redirigé vers l'audio-guide automatiquement -**Et** la séquence 4 démarre immédiatement -**Et** un toast de bienvenue s'affiche: "✨ Bienvenue Premium ! Profitez de votre audio-guide" - ---- - -## 8. Utilisateur Premium - Accès complet immédiat - -**Étant donné** qu'un utilisateur Premium ouvre un audio-guide Premium - -**Quand** il consulte la liste des séquences - -**Alors** toutes les 15 séquences sont accessibles -**Et** aucun paywall ne s'affiche -**Et** aucune publicité n'est insérée - ---- - -## 9. Pas de preview si l'audio-guide a <3 séquences - -**Étant donné** un audio-guide Premium avec seulement 2 séquences - -**Quand** un utilisateur gratuit tente de l'ouvrir - -**Alors** un paywall s'affiche immédiatement (avant lecture) -**Et** aucune preview n'est disponible - ---- - -## 10. Rémunération créateur pour audio-guide Premium - -**Étant donné** un créateur avec un audio-guide Premium -**Et** que 50 utilisateurs Premium ont écouté l'audio-guide ce mois - -**Quand** la répartition des revenus est calculée - -**Alors** le créateur reçoit 70% des revenus proportionnels -**Et** la formule est: (Écoutes créateur / Total écoutes Premium) × 70% pool Premium - ---- - -## 11. Dashboard revenus par audio-guide - -**Étant donné** qu'un créateur a 3 audio-guides Premium publiés - -**Quand** il consulte son dashboard revenus - -**Alors** il voit pour chaque audio-guide: - - | audio_guide | ecoutes_mois | revenus_estime | - |---|---|---| - | Visite VIP Versailles | 142 | 45.20 € | - | Secrets du Louvre | 89 | 28.50 € | - | Châteaux de la Loire | 203 | 64.80 € | - - ---- - -## 12. Comparaison gratuit vs Premium - -**Étant donné** qu'un créateur a publié 2 audio-guides: - - | titre | type | ecoutes_mois | revenus | - |---|---|---|---| - | Tour de Paris | Gratuit | 1200 | 12.50 € | - | Visite VIP Versailles | Premium | 142 | 45.20 € | - - -**Quand** il consulte son dashboard - -**Alors** il peut comparer les performances -**Et** constater que Premium génère plus de revenus par écoute - ---- - -## 13. Seuil minimum de paiement (20€) - -**Étant donné** qu'un créateur a généré 18€ de revenus ce mois - -**Quand** le paiement mensuel est traité - -**Alors** le montant est reporté au mois suivant -**Et** un message s'affiche: "Seuil minimum non atteint (20€). Montant reporté." - ---- - -## 14. Paiement automatique mensuel - -**Étant donné** qu'un créateur a généré 138.50€ de revenus en janvier - -**Quand** le 5 février arrive - -**Alors** le paiement est initié automatiquement via Mangopay -**Et** le créateur reçoit une notification: "Paiement de 138.50€ en cours" -**Et** les fonds arrivent sous 2-3 jours ouvrés - ---- - -## 15. Insertion publicité toutes les 5 séquences (gratuit) - -**Étant donné** un audio-guide gratuit avec 12 séquences -**Et** un utilisateur gratuit - -**Quand** il termine la séquence 5 - -**Alors** une publicité démarre automatiquement - -**Quand** il termine la séquence 10 - -**Alors** une deuxième publicité démarre - ---- - -## 16. Publicité après séquence en mode piéton (avec pause) - -**Étant donné** un audio-guide piéton gratuit - -**Quand** la séquence 5 se termine - -**Alors** la publicité démarre automatiquement (pas d'attente bouton) -**Et** la pub est skippable après 5 secondes - -**Quand** la publicité se termine - -**Alors** le player se met en pause -**Et** l'utilisateur doit cliquer sur [▶|] pour continuer - ---- - -## 17. Publicité en mode voiture/vélo/transport (automatique) - -**Étant donné** un audio-guide voiture gratuit - -**Quand** la séquence 5 se termine - -**Alors** la publicité démarre automatiquement - -**Quand** la publicité se termine - -**Alors** la séquence 6 démarre automatiquement (pas de pause) - ---- - -## 18. Publicités géolocalisées dans audio-guides - -**Étant donné** un audio-guide dans la région "Île-de-France" - -**Quand** une publicité doit être insérée - -**Alors** l'API publicitaire filtre par: - - | critère | valeur | - |---|---| - | Géolocalisation | Île-de-France | - | Catégorie | Tourisme, Culture | - | Langue | Français | - - ---- - -## 19. Comptabilisation des impressions pub pour créateur - -**Étant donné** qu'un audio-guide gratuit génère 200 écoutes complètes -**Et** que chaque écoute complète = 2 publicités (séq. 5 et 10) - -**Quand** les revenus pub sont calculés - -**Alors** 400 impressions sont comptabilisées -**Et** le créateur reçoit 0.80€ (400 × 0.002€) - ---- - -## 20. CTA Premium après audio-guide gratuit complété - -**Étant donné** qu'un utilisateur gratuit complète un audio-guide gratuit - -**Quand** il termine la dernière séquence - -**Alors** un overlay s'affiche: - ---- - -## 21. Recommandations d'audio-guides Premium après gratuit - -**Étant donné** qu'un utilisateur termine un audio-guide gratuit "Tour de Paris" - -**Quand** l'overlay de fin s'affiche - -**Alors** 3 audio-guides Premium similaires sont suggérés: - - | titre | type | créateur | - |---|---|---| - | Secrets de Montmartre | Premium | @paris_stories | - | Visite VIP Musée d'Orsay | Premium | @art_guide | - | Paris hors des sentiers | Premium | @explore_paris | - - ---- - -## 22. Badge "Premium recommandé" sur audio-guides populaires - -**Étant donné** un audio-guide Premium avec >500 écoutes et note >4.5/5 - -**Quand** il est affiché dans les résultats de recherche - -**Alors** un badge "⭐ Premium recommandé" est visible -**Et** il est mis en avant dans les résultats - ---- - -## 23. Conversion tracking pour attribution créateur - -**Étant donné** qu'un utilisateur découvre Premium via un audio-guide créateur - -**Quand** il s'abonne - -**Alors** la conversion est trackée: - - | donnée | valeur | - |---|---| - | source_conversion | audio_guide_paywall | - | audio_guide_id | visite_vip_versailles_123 | - | creator_id | guide_versailles_456 | - -**Et** le créateur bénéficie d'un bonus de conversion - ---- - -## 24. Essai gratuit 7 jours Premium via audio-guide - -**Étant donné** qu'un utilisateur gratuit atteint le paywall d'un audio-guide Premium -**Et** qu'il n'a jamais essayé Premium - -**Quand** l'overlay s'affiche - -**Alors** une offre d'essai est proposée: - ---- - -## 25. Activation immédiate après essai gratuit - -**Étant donné** qu'un utilisateur démarre un essai gratuit 7 jours - -**Quand** l'essai est activé - -**Alors** l'audio-guide Premium démarre immédiatement -**Et** toutes les séquences sont débloquées -**Et** aucune publicité n'est insérée - ---- - -## 26. Rappel 2 jours avant fin d'essai - -**Étant donné** qu'un utilisateur a démarré un essai gratuit le 15/01 - -**Quand** le 20/01 arrive (J-2) - -**Alors** une notification est envoyée: - ---- - -## 27. Créateur mix gratuit + Premium - -**Étant donné** qu'un créateur a publié 5 audio-guides: - - | titre | type | - |---|---| - | Découverte de Paris | Gratuit | - | Visite VIP Louvre | Premium | - | Balade Montmartre | Gratuit | - | Secrets Versailles | Premium | - | Visite express Orsay | Gratuit | - - -**Quand** un utilisateur découvre son profil - -**Alors** les audio-guides gratuits servent de teasing -**Et** les audio-guides Premium sont mis en avant avec badge - ---- - -## 28. Utilisateur hésite à s'abonner - -**Étant donné** qu'un utilisateur atteint le paywall d'un audio-guide Premium -**Et** qu'il clique sur "Découvrir d'autres audio-guides gratuits" - -**Quand** il revient 2 jours plus tard sur le même audio-guide - -**Alors** le paywall s'affiche à nouveau -**Et** une réduction temporaire est proposée: "Offre spéciale : -20% premier mois" - ---- - -## 29. Échec du paiement Premium via paywall - -**Étant donné** qu'un utilisateur tente de s'abonner Premium - -**Quand** le paiement Mangopay échoue - -**Alors** un message d'erreur s'affiche: - ---- - -## 30. Abonnement Premium expiré pendant écoute - -**Étant donné** qu'un utilisateur Premium écoute un audio-guide Premium -**Et** que son abonnement expire pendant l'écoute (séquence 8/15) - -**Quand** l'expiration est détectée - -**Alors** l'écoute continue jusqu'à la fin de la séquence en cours -**Et** un overlay s'affiche ensuite: - ---- - -## 31. Créateur change audio-guide de gratuit à Premium - -**Étant donné** qu'un audio-guide gratuit a 50 utilisateurs avec progression - -**Quand** le créateur le passe en Premium - -**Alors** les utilisateurs ayant déjà commencé gardent l'accès complet -**Et** seuls les nouveaux utilisateurs sont soumis au paywall -**Et** un message de transparence s'affiche: - ---- - - - - - -
- - -# Sauvegarde et reprise de progression audio-guide -> *En tant qu'utilisateur* -> *Je veux que ma progression soit sauvegardée automatiquement* -> *Afin de pouvoir reprendre mon audio-guide là où je me suis arrêté* - -**32 scénarios** (31 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté -## 1. Sauvegarde automatique toutes les 10 secondes - -**Étant donné** qu'un audio-guide "Visite du Louvre" est en cours -**Et** que la séquence 3 est à la position 1:24 - -**Quand** 10 secondes s'écoulent - -**Alors** la progression est sauvegardée automatiquement: - - | donnée | valeur | - |---|---| - | audio_guide_id | louvre_123 | - | sequence_actuelle | 3 | - | position_audio | 1:24 | - | timestamp | 2026-01-22 14:35:42 | - | sequences_ecoutees | [1, 2] | - - ---- - -## 2. Sauvegarde locale (SQLite) pour rapidité - -**Étant donné** qu'une sauvegarde est déclenchée - -**Quand** la progression est enregistrée - -**Alors** les données sont écrites en SQLite local -**Et** l'écriture prend moins de 50ms -**Et** l'application reste fluide - ---- - -## 3. Synchronisation cloud en arrière-plan - -**Étant donné** qu'une sauvegarde locale est effectuée - -**Quand** 30 secondes s'écoulent - -**Alors** la progression est synchronisée vers PostgreSQL cloud -**Et** la synchronisation s'effectue en arrière-plan -**Et** elle n'impacte pas les performances - ---- - -## 4. Sauvegarde immédiate lors de la fermeture - -**Étant donné** qu'un audio-guide est en cours à la séquence 4 position 2:15 - -**Quand** l'utilisateur ferme l'application - -**Alors** la progression est sauvegardée immédiatement (local + cloud) -**Et** les données sont écrites avant la fermeture complète - ---- - -## 5. Sauvegarde des séquences complétées - -**Étant donné** qu'un audio-guide de 12 séquences est en cours -**Et** que les séquences 1, 2, 4, 5 ont été écoutées à >80% - -**Quand** la progression est sauvegardée - -**Alors** les séquences complétées sont enregistrées: - ---- - -## 6. Historique des écoutes pour statistiques - -**Étant donné** qu'un utilisateur a écouté 3 séquences d'un audio-guide - -**Quand** les données sont sauvegardées - -**Alors** l'historique d'écoute inclut: - - | sequence_id | started_at | completed_at | completion_rate | - |---|---|---|---| - | 1 | 2026-01-22 14:10:00 | 2026-01-22 14:12:15 | 100% | - | 2 | 2026-01-22 14:12:20 | 2026-01-22 14:14:08 | 100% | - | 3 | 2026-01-22 14:14:15 | 2026-01-22 14:17:45 | 92% | - - ---- - -## 7. Popup de reprise au redémarrage - -**Étant donné** que l'utilisateur a quitté l'app à la séquence 6 position 2:34 - -**Quand** il rouvre l'audio-guide "Visite du Louvre" - -**Alors** une popup s'affiche: - ---- - -## 8. Action "Reprendre" - Position exacte restaurée - -**Étant donné** qu'une popup de reprise est affichée - -**Quand** l'utilisateur clique sur "▶️ Reprendre" - -**Alors** la séquence 6 "Vénus de Milo" se charge -**Et** la position exacte 2:34 est restaurée -**Et** la lecture démarre automatiquement après 1 seconde - ---- - -## 9. Action "Recommencer" - Réinitialisation complète - -**Étant donné** qu'une popup de reprise est affichée - -**Quand** l'utilisateur clique sur "🔄 Recommencer" - -**Alors** l'audio-guide redémarre depuis la séquence 1 position 0:00 -**Et** toutes les séquences sont marquées ⭕ "À écouter" -**Et** l'historique d'écoute est réinitialisé pour cette session - ---- - -## 10. Reprise après 7 jours d'inactivité - -**Étant donné** qu'un utilisateur a arrêté un audio-guide le 15/01/2026 -**Et** qu'il le rouvre le 22/01/2026 (7 jours plus tard) - -**Quand** l'audio-guide se charge - -**Alors** la popup de reprise s'affiche normalement -**Et** toutes les données de progression sont conservées - ---- - -## 11. Reprise sur un autre appareil (synchronisation cloud) - -**Étant donné** qu'un utilisateur écoute un audio-guide sur iPhone -**Et** qu'il quitte à la séquence 4 position 1:20 - -**Quand** il ouvre le même audio-guide sur iPad - -**Alors** la popup de reprise s'affiche avec la progression iPhone -**Et** il peut reprendre exactement où il s'était arrêté - ---- - -## 12. Conflit de synchronisation (dernier appareil gagne) - -**Étant donné** qu'un utilisateur écoute sur iPhone à la séquence 3 -**Et** simultanément sur iPad à la séquence 7 - -**Quand** les deux appareils synchronisent - -**Alors** la progression la plus récente (timestamp) est conservée -**Et** l'appareil avec ancienne progression affiche une notification: - ---- - -## 13. Mode hors-ligne - Sauvegarde locale uniquement - -**Étant donné** qu'un utilisateur écoute un audio-guide hors connexion -**Et** qu'il atteint la séquence 5 - -**Quand** la progression est sauvegardée - -**Alors** les données sont écrites localement (SQLite) -**Et** une icône "☁️ Non synchronisé" s'affiche discrètement - ---- - -## 14. Synchronisation automatique à la reconnexion - -**Étant donné** que l'utilisateur a écouté hors ligne jusqu'à la séquence 8 -**Et** que 5 progressions locales ne sont pas synchronisées - -**Quand** la connexion réseau est rétablie - -**Alors** les 5 progressions sont synchronisées automatiquement -**Et** un toast s'affiche brièvement: "✅ Progression synchronisée" - ---- - -## 15. Suppression de la progression (recommencer proprement) - -**Étant donné** qu'un utilisateur est à la séquence 10/12 - -**Quand** il ouvre les paramètres de l'audio-guide -**Et** qu'il clique sur "🔄 Réinitialiser progression" - -**Alors** une confirmation s'affiche: -**Et** si confirmé, la progression est effacée - ---- - -## 16. Taux de complétion global de l'audio-guide - -**Étant donné** un audio-guide de 12 séquences -**Et** que l'utilisateur a écouté complètement 8 séquences -**Et** partiellement 1 séquence (45%) - -**Quand** les statistiques sont calculées - -**Alors** le taux de complétion affiché est "67%" (8/12) - ---- - -## 17. Badge "Audio-guide complété" à 100% - -**Étant donné** un audio-guide de 12 séquences - -**Quand** l'utilisateur écoute la 12ème séquence à 100% - -**Alors** un badge "✅ Audio-guide complété" s'affiche -**Et** une notification de félicitations est envoyée -**Et** le statut "Complété le 22/01/2026" est visible dans l'historique - ---- - -## 18. Temps total passé sur l'audio-guide - -**Étant donné** qu'un utilisateur a écouté un audio-guide sur 2 sessions: - - | session | durée | - |---|---| - | 1 | 25 min | - | 2 | 18 min | - - -**Quand** les statistiques sont calculées - -**Alors** le temps total est "43 minutes" -**Et** il est affiché dans l'historique personnel - ---- - -## 19. Liste des audio-guides "En cours" dans le profil - -**Étant donné** qu'un utilisateur a 3 audio-guides en cours: - - | audio_guide | progression | - |---|---| - | Visite du Louvre | 6/12 | - | Safari du Paugre | 3/8 | - | Circuit Loire à Vélo | 12/15 | - - -**Quand** il consulte son profil "Audio-guides" - -**Alors** la section "📍 En cours" affiche les 3 audio-guides -**Et** chaque élément montre la progression sous forme de barre - ---- - -## 20. Liste des audio-guides "Complétés" dans le profil - -**Étant donné** qu'un utilisateur a complété 2 audio-guides: - - | audio_guide | date_completion | - |---|---| - | Tour de Paris | 2026-01-15 | - | Découverte de Lyon | 2026-01-20 | - - -**Quand** il consulte son profil "Audio-guides" - -**Alors** la section "✅ Complétés" affiche les 2 audio-guides -**Et** la date de complétion est visible - ---- - -## 21. Badge "Complétiste" pour 10 audio-guides complétés - -**Étant donné** qu'un utilisateur complète son 10ème audio-guide - -**Quand** la complétion est enregistrée - -**Alors** un badge "🏆 Complétiste" est débloqué -**Et** il apparaît sur son profil -**Et** une notification est envoyée: - ---- - -## 22. 📋 Plan: Niveaux de badges selon nombre d'audio-guides complétés - -**Étant donné** qu'un utilisateur complète audio-guides - -**Quand** le badge est attribué - -**Alors** il reçoit le badge "" - -**📊 Exemples de données:** - -| nombre | badge | -|---|---| -| 1 | 🎧 Premier audio-guide | -| 5 | 🗺️ Explorateur | -| 10 | 🏆 Complétiste | -| 25 | 🌟 Expert | -| 50 | 💎 Maître audio-guideur | - ---- - -## 23. Dashboard créateur - Statistiques par audio-guide - -**Étant donné** qu'un créateur a publié l'audio-guide "Visite du Louvre" - -**Quand** il consulte son dashboard - -**Alors** les métriques suivantes sont affichées: - - | métrique | valeur | - |---|---| - | Écoutes totales | 1542 | - | Écoutes complètes (>80%) | 892 | - | Taux de complétion moyen | 58% | - | Temps d'écoute total | 423h | - | Séquence la plus écoutée | Séq. 3 | - | Séquence la moins écoutée | Séq. 11 | - - ---- - -## 24. Graphique de complétion par séquence - -**Étant donné** un audio-guide de 12 séquences - -**Quand** le créateur consulte les statistiques détaillées - -**Alors** un graphique en barres affiche: - - | séquence | taux_completion | - |---|---| - | 1 | 100% | - | 2 | 95% | - | 3 | 89% | - | ... | ... | - | 12 | 58% | - - ---- - -## 25. Détection des points d'abandon - -**Étant donné** qu'un audio-guide a un taux de complétion de 58% -**Et** que 35% des utilisateurs abandonnent à la séquence 7 - -**Quand** le créateur consulte les insights - -**Alors** un avertissement s'affiche: - ---- - -## 26. Heatmap géographique des écoutes - -**Étant donné** un audio-guide géolocalisé - -**Quand** le créateur consulte la heatmap - -**Alors** une carte affiche: - - | élément | description | - |---|---| - | Densité d'écoutes | Zones rouge/orange/jaune selon écoutes | - | Points GPS | Marqueurs sur chaque point | - | Statistiques par point | Nombre d'écoutes par zone | - - ---- - -## 27. Temps moyen par séquence - -**Étant donné** qu'un créateur analyse son audio-guide - -**Quand** il consulte les statistiques temporelles - -**Alors** il voit pour chaque séquence: - - | séquence | durée_audio | temps_ecoute_moyen | ecart | - |---|---|---|---| - | 1 | 2:15 | 2:10 | -5s | - | 2 | 1:48 | 1:30 | -18s | - | 3 | 3:42 | 3:40 | -2s | - - ---- - -## 28. Notification créateur pour milestone - -**Étant donné** qu'un audio-guide atteint 1000 écoutes - -**Quand** le seuil est franchi - -**Alors** une notification est envoyée au créateur: - ---- - -## 29. Corruption de données de sauvegarde - -**Étant donné** qu'une sauvegarde locale (SQLite) est corrompue - -**Quand** l'application tente de charger la progression - -**Alors** une récupération depuis le cloud est tentée -**Et** si réussie, les données cloud sont restaurées -**Et** la base locale est reconstruite - ---- - -## 30. Échec de synchronisation cloud - -**Étant donné** que l'API cloud est indisponible - -**Quand** une tentative de synchronisation est effectuée - -**Alors** l'application continue avec sauvegarde locale uniquement -**Et** un retry automatique est programmé dans 5 minutes -**Et** l'icône "☁️ Non synchronisé" reste affichée - ---- - -## 31. Suppression accidentelle de progression (récupération) - -**Étant donné** qu'un utilisateur réinitialise un audio-guide par erreur - -**Quand** il contacte le support dans les 7 jours - -**Alors** l'équipe peut restaurer la progression depuis les backups -**Et** les données sont récupérables (backup quotidien conservé 30 jours) - ---- - -## 32. Nettoyage automatique des vieilles progressions - -**Étant donné** qu'une progression n'a pas été mise à jour depuis 6 mois - -**Quand** le nettoyage automatique s'exécute - -**Alors** la progression est archivée (mais pas supprimée) -**Et** l'utilisateur peut la restaurer via l'historique - ---- - - - - - -
- - -# Classification des contenus par âge -> *En tant que plateforme responsable* -> *Je veux classifier les contenus par tranche d'âge* -> *Afin de protéger les mineurs et respecter les obligations légales* - -**13 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -## 1. Créateur doit classifier son contenu à la publication - -**Étant donné** que je suis un créateur connecté - -**Quand** je crée un nouveau contenu audio - -**Alors** je dois obligatoirement choisir une classification d'âge parmi: - - | classification | description | - |---|---| - | Tout public | Contenu adapté à tous les âges | - | 13+ | Contenu mature léger | - | 16+ | Contenu mature | - | 18+ | Contenu adulte | - - ---- - -## 2. Publication impossible sans classification - -**Étant donné** que je crée un contenu audio - -**Quand** j'essaie de publier sans sélectionner de classification - -**Alors** la publication échoue -**Et** je vois le message "Vous devez sélectionner une classification d'âge" - ---- - -## 3. Utilisateur 13-15 ans voit uniquement du contenu "Tout public" - -**Étant donné** que je suis un utilisateur de 14 ans -**Et** qu'il existe des contenus avec les classifications suivantes: - - | classification | nombre | - |---|---| - | Tout public | 20 | - | 13+ | 15 | - | 16+ | 10 | - | 18+ | 5 | - - -**Quand** je demande des recommandations - -**Alors** je vois uniquement les 20 contenus "Tout public" -**Et** les autres contenus ne sont jamais proposés - ---- - -## 4. Utilisateur 16-17 ans voit "Tout public" et "13+" - -**Étant donné** que je suis un utilisateur de 17 ans -**Et** qu'il existe des contenus avec les classifications suivantes: - - | classification | nombre | - |---|---| - | Tout public | 20 | - | 13+ | 15 | - | 16+ | 10 | - | 18+ | 5 | - - -**Quand** je demande des recommandations - -**Alors** je vois 35 contenus (Tout public + 13+) -**Et** les contenus 16+ et 18+ ne sont pas proposés - ---- - -## 5. Utilisateur 18+ voit tous les contenus - -**Étant donné** que je suis un utilisateur de 25 ans -**Et** qu'il existe des contenus avec toutes les classifications - -**Quand** je demande des recommandations - -**Alors** je vois tous les contenus sans restriction -**Et** aucun filtre d'âge n'est appliqué - ---- - -## 6. Mode Kids activé automatiquement pour les moins de 13 ans - -**Étant donné** que je m'inscris avec une date de naissance "2013-01-21" - -**Alors** le mode Kids est activé automatiquement -**Et** je vois uniquement du contenu "Tout public" -**Et** des protections supplémentaires sont appliquées - ---- - -## 7. Modérateur reclassifie un contenu mal catégorisé - -**Étant donné** qu'un contenu est publié avec la classification "Tout public" -**Et** que ce contenu contient du langage inapproprié détecté en modération - -**Quand** le modérateur reclassifie ce contenu en "16+" - -**Alors** la nouvelle classification est appliquée immédiatement -**Et** le contenu n'est plus visible pour les utilisateurs de moins de 16 ans -**Et** le créateur reçoit une notification de reclassification - ---- - -## 8. Strike si classification volontairement incorrecte - -**Étant donné** qu'un créateur a publié un contenu "18+" classifié comme "Tout public" -**Et** que ce contenu a été signalé - -**Quand** le modérateur confirme la mauvaise classification volontaire - -**Alors** le créateur reçoit 1 strike -**Et** le contenu est reclassifié en "18+" -**Et** le créateur reçoit une notification explicative - ---- - -## 9. Créateur peut voir la distribution d'âge de son audience - -**Étant donné** que je suis un créateur -**Et** que j'ai publié des contenus avec différentes classifications - -**Quand** je consulte mes statistiques - -**Alors** je vois la répartition des âges de mes auditeurs: - - | tranche_age | pourcentage | - |---|---| - | 13-15 ans | 15% | - | 16-17 ans | 20% | - | 18+ ans | 65% | - - ---- - -## 10. Recherche filtrée par classification d'âge - -**Étant donné** que je suis un utilisateur de 16 ans - -**Quand** je recherche des contenus - -**Alors** les résultats incluent uniquement: - - | classification | - |---| - | Tout public | - | 13+ | - -**Et** je ne vois pas les contenus 16+ et 18+ dans les résultats - ---- - -## 11. Notification si tentative d'accès à contenu non autorisé - -**Étant donné** que je suis un utilisateur de 14 ans -**Et** qu'un contenu "16+" est partagé avec moi via un lien direct - -**Quand** j'essaie d'accéder au contenu - -**Alors** l'accès est refusé -**Et** je vois le message "Ce contenu est réservé aux utilisateurs de 16 ans et plus" - ---- - -## 12. Validation obligatoire des 3 premiers contenus inclut la classification - -**Étant donné** que je suis un nouveau créateur -**Et** que je publie mon premier contenu classifié "18+" - -**Quand** le modérateur valide mon contenu - -**Alors** il vérifie que la classification "18+" est appropriée -**Et** peut la modifier si nécessaire avant validation - ---- - -## 13. Statistiques de classification dans l'interface créateur - -**Étant donné** que je suis un créateur - -**Quand** je consulte mes contenus publiés - -**Alors** je vois pour chaque contenu: - - | information | exemple | - |---|---| - | Classification actuelle | 13+ | - | Nombre de signalements | 2 | - | Reclassifications | Aucune / 1× par modérateur | - - ---- - - - - - -
- - -# Connexion utilisateur -> *En tant qu'utilisateur existant* -> *Je veux me connecter à mon compte* -> *Afin d'accéder à mes contenus et paramètres* - -**11 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur existe avec: - | email | mot_de_passe | - |---|---| - | user@test.fr | Password123 | - - -## 1. Connexion réussie avec identifiants valides - -**Quand** je me connecte avec: - - | email | mot_de_passe | - |---|---| - | user@test.fr | Password123 | - - -**Alors** je suis connecté avec succès -**Et** je reçois un access token valide pour 15 minutes -**Et** je reçois un refresh token valide pour 30 jours - ---- - -## 2. Connexion échouée avec email inexistant - -**Quand** je me connecte avec l'email "inexistant@test.fr" - -**Alors** la connexion échoue -**Et** je vois le message "Email ou mot de passe incorrect" - ---- - -## 3. Connexion échouée avec mot de passe incorrect - -**Quand** je me connecte avec: - - | email | mot_de_passe | - |---|---| - | user@test.fr | MauvaisPass1 | - - -**Alors** la connexion échoue -**Et** je vois le message "Email ou mot de passe incorrect" - ---- - -## 4. Blocage après 5 tentatives échouées - -**Étant donné** que j'ai échoué 4 tentatives de connexion - -**Quand** j'échoue une 5ème tentative de connexion - -**Alors** mon compte est temporairement bloqué -**Et** je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées" -**Et** je reçois un email de notification de blocage - ---- - -## 5. Tentative de connexion pendant le blocage - -**Étant donné** que mon compte est bloqué suite à 5 tentatives échouées -**Et** que seulement 5 minutes se sont écoulées - -**Quand** j'essaie de me connecter avec les bons identifiants - -**Alors** la connexion échoue -**Et** je vois le message "Compte bloqué. Réessayez dans 10 minutes" - ---- - -## 6. Déblocage automatique après 15 minutes - -**Étant donné** que mon compte est bloqué suite à 5 tentatives échouées -**Et** que 15 minutes se sont écoulées - -**Quand** je me connecte avec les bons identifiants - -**Alors** je suis connecté avec succès -**Et** le compteur de tentatives est réinitialisé - ---- - -## 7. Reset du compteur après connexion réussie - -**Étant donné** que j'ai échoué 3 tentatives de connexion - -**Quand** je me connecte avec les bons identifiants - -**Alors** je suis connecté avec succès -**Et** le compteur de tentatives est remis à 0 - ---- - -## 8. Reset automatique du compteur après 15 minutes sans blocage - -**Étant donné** que j'ai échoué 3 tentatives de connexion -**Et** que 15 minutes se sont écoulées sans nouvelle tentative - -**Quand** je consulte mon compteur de tentatives - -**Alors** le compteur est réinitialisé à 0 - ---- - -## 9. Déblocage via lien "Mot de passe oublié" - -**Étant donné** que mon compte est bloqué suite à 5 tentatives échouées - -**Quand** j'utilise la fonction "Mot de passe oublié" -**Et** que je réinitialise mon mot de passe - -**Alors** le blocage est levé immédiatement -**Et** je peux me connecter avec le nouveau mot de passe - ---- - -## 10. Email de notification lors d'un blocage - -**Étant donné** que j'ai échoué 5 tentatives de connexion - -**Alors** je reçois un email avec: - - | sujet | Tentatives de connexion suspectes détectées | - |---|---| - | contenu_contient | Votre compte a été temporairement bloqué | - | lien_mot_de_passe | présent | - - ---- - -## 11. Connexion multi-device simultanée autorisée - -**Étant donné** que je suis connecté sur un appareil iOS - -**Quand** je me connecte également sur un appareil Android - -**Alors** les deux sessions sont actives simultanément -**Et** je peux utiliser l'application sur les deux appareils - ---- - - - - - -
- - -# Inscription utilisateur -> *En tant que nouvel utilisateur* -> *Je veux créer un compte avec email et mot de passe* -> *Afin d'accéder à l'application RoadWave* - -**15 scénarios** (14 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que Zitadel est configuré -## 1. Inscription réussie avec données valides - -**Étant donné** que l'email "nouveau@example.com" n'existe pas - -**Quand** je m'inscris avec les données suivantes: - - | champ | valeur | - |---|---| - | email | nouveau@example.com | - | mot_de_passe | Password123 | - | pseudo | nouveau_user | - | date_naissance | 1995-06-15 | - - -**Alors** mon compte est créé avec succès -**Et** je reçois un email de vérification -**Et** le lien de vérification expire dans 7 jours -**Et** je suis redirigé vers l'application - ---- - -## 2. Inscription avec email déjà existant - -**Étant donné** qu'un utilisateur existe avec l'email "existant@example.com" - -**Quand** je m'inscris avec l'email "existant@example.com" - -**Alors** l'inscription échoue -**Et** je vois le message "Cet email est déjà utilisé" - ---- - -## 3. Inscription avec mot de passe invalide - trop court - -**Quand** je m'inscris avec un mot de passe de moins de 8 caractères "Pass1" - -**Alors** l'inscription échoue -**Et** je vois le message "Le mot de passe doit contenir au moins 8 caractères" - ---- - -## 4. Inscription avec mot de passe invalide - sans majuscule - -**Quand** je m'inscris avec un mot de passe sans majuscule "password123" - -**Alors** l'inscription échoue -**Et** je vois le message "Le mot de passe doit contenir au moins une majuscule" - ---- - -## 5. Inscription avec mot de passe invalide - sans chiffre - -**Quand** je m'inscris avec un mot de passe sans chiffre "Password" - -**Alors** l'inscription échoue -**Et** je vois le message "Le mot de passe doit contenir au moins un chiffre" - ---- - -## 6. Inscription avec pseudo invalide - trop court - -**Quand** je m'inscris avec un pseudo de 2 caractères "ab" - -**Alors** l'inscription échoue -**Et** je vois le message "Le pseudo doit contenir entre 3 et 30 caractères" - ---- - -## 7. Inscription avec pseudo invalide - caractères spéciaux - -**Quand** je m'inscris avec un pseudo contenant des caractères spéciaux "user@123" - -**Alors** l'inscription échoue -**Et** je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores" - ---- - -## 8. Inscription avec email invalide - -**Quand** je m'inscris avec un email invalide "email.invalide" - -**Alors** l'inscription échoue -**Et** je vois le message "Format d'email invalide" - ---- - -## 9. 📋 Plan: Inscription avec âge minimum non respecté - -**Étant donné** la date du jour est "2026-01-21" - -**Quand** je m'inscris avec une date de naissance "" - -**Alors** l'inscription échoue -**Et** je vois le message "Vous devez avoir au moins 13 ans pour créer un compte" - -**📊 Exemples de données:** - -| date_naissance | age | -|---|---| -| 2013-01-22 | 12 | -| 2015-06-15 | 10 | -| 2020-01-01 | 6 | - ---- - -## 10. Inscription avec âge limite acceptable (13 ans) - -**Étant donné** la date du jour est "2026-01-21" - -**Quand** je m'inscris avec une date de naissance "2013-01-21" - -**Alors** mon compte est créé avec succès -**Et** le mode Kids est activé automatiquement - ---- - -## 11. Inscription avec âge supérieur à 18 ans - -**Étant donné** la date du jour est "2026-01-21" - -**Quand** je m'inscris avec une date de naissance "1990-06-15" - -**Alors** mon compte est créé avec succès -**Et** j'ai accès à tous les contenus sans restriction d'âge - ---- - -## 12. Données minimales requises à l'inscription - -**Quand** je m'inscris sans fournir de nom complet -**Et** sans fournir de photo de profil -**Et** sans fournir de bio - -**Alors** mon compte est créé avec succès -**Et** un avatar par défaut est généré -**Et** les champs optionnels sont vides - ---- - -## 13. Renvoyer l'email de vérification - -**Étant donné** que je me suis inscrit avec l'email "nouveau@example.com" -**Et** que je n'ai pas vérifié mon email - -**Quand** je demande à renvoyer l'email de vérification - -**Alors** un nouvel email de vérification est envoyé -**Et** le précédent lien est invalidé - ---- - -## 14. Limite de renvoi d'email de vérification - -**Étant donné** que je me suis inscrit avec l'email "nouveau@example.com" -**Et** que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui - -**Quand** je demande à renvoyer l'email de vérification une 4ème fois - -**Alors** la demande échoue -**Et** je vois le message "Vous avez atteint la limite de 3 renvois par jour" - ---- - -## 15. Expiration du lien de vérification - -**Étant donné** que je me suis inscrit il y a 8 jours -**Et** que je n'ai pas vérifié mon email - -**Quand** j'essaie d'utiliser le lien de vérification - -**Alors** la vérification échoue -**Et** je vois le message "Ce lien a expiré" -**Et** je peux demander un nouveau lien - ---- - - - - - -
- - -# Récupération de compte -> *En tant qu'utilisateur ayant oublié son mot de passe* -> *Je veux pouvoir réinitialiser mon mot de passe via email* -> *Afin de récupérer l'accès à mon compte* - -**14 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur existe avec l'email "user@test.fr" -## 1. Demander la réinitialisation du mot de passe - -**Quand** je clique sur "Mot de passe oublié" -**Et** que je saisis mon email "user@test.fr" - -**Alors** je reçois un email avec un lien de réinitialisation -**Et** le lien expire dans 1 heure -**Et** je vois le message "Email de réinitialisation envoyé" - ---- - -## 2. Email inexistant lors de la demande de réinitialisation - -**Quand** je demande une réinitialisation pour l'email "inexistant@test.fr" - -**Alors** je vois le même message "Email de réinitialisation envoyé" -**Mais** aucun email n'est envoyé (sécurité - pas d'énumération d'emails) - ---- - -## 3. Réinitialiser le mot de passe avec un lien valide - -**Étant donné** que j'ai demandé une réinitialisation de mot de passe -**Et** que j'ai reçu le lien de réinitialisation - -**Quand** je clique sur le lien -**Et** que je saisis un nouveau mot de passe "NouveauPass123" -**Et** que je confirme le nouveau mot de passe "NouveauPass123" - -**Alors** mon mot de passe est modifié avec succès -**Et** je suis déconnecté de tous mes appareils sauf celui en cours -**Et** je reçois un email de confirmation de changement - ---- - -## 4. Lien de réinitialisation expiré - -**Étant donné** que j'ai demandé une réinitialisation il y a 2 heures - -**Quand** j'essaie d'utiliser le lien - -**Alors** je vois le message "Ce lien a expiré" -**Et** je peux demander un nouveau lien - ---- - -## 5. Nouveau mot de passe ne respecte pas les règles - -**Étant donné** que j'ai un lien de réinitialisation valide - -**Quand** je saisis un nouveau mot de passe "faible" - -**Alors** la réinitialisation échoue -**Et** je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre" - ---- - -## 6. Confirmation du mot de passe ne correspond pas - -**Étant donné** que j'ai un lien de réinitialisation valide - -**Quand** je saisis un nouveau mot de passe "NouveauPass123" -**Et** que je confirme avec un mot de passe différent "AutrePass123" - -**Alors** la réinitialisation échoue -**Et** je vois le message "Les mots de passe ne correspondent pas" - ---- - -## 7. Limite de demandes de réinitialisation - -**Étant donné** que j'ai déjà demandé 3 réinitialisations dans la dernière heure - -**Quand** je demande une 4ème réinitialisation - -**Alors** la demande échoue -**Et** je vois le message "Maximum 3 demandes par heure. Réessayez plus tard." - ---- - -## 8. Compteur de demandes se réinitialise après 1 heure - -**Étant donné** que j'ai demandé 3 réinitialisations -**Et** que 1 heure s'est écoulée - -**Quand** je demande une nouvelle réinitialisation - -**Alors** la demande réussit -**Et** je reçois un email avec un nouveau lien - ---- - -## 9. Email de notification de changement de mot de passe - -**Étant donné** que je viens de réinitialiser mon mot de passe - -**Alors** je reçois un email de confirmation avec: - - | sujet | Votre mot de passe a été modifié | - |---|---| - | contenu_contient | Votre mot de passe a été modifié | - | date_heure | présente | - | appareil | présent | - | localisation | présente | - | action_urgence | Lien si ce n'était pas vous | - - ---- - -## 10. Notification push si changement depuis appareil non reconnu - -**Étant donné** que je me suis toujours connecté depuis mon iPhone -**Et** que je réinitialise mon mot de passe depuis un PC Windows - -**Alors** je reçois une notification push sur mon iPhone avec: - - | titre | Mot de passe modifié | - |---|---| - | message | Depuis Windows - Paris, France | - | action | Sécuriser le compte si ce n'est pas vous | - - ---- - -## 11. Déconnexion de tous les appareils après réinitialisation - -**Étant donné** que je suis connecté sur 4 appareils différents -**Et** que je réinitialise mon mot de passe depuis un navigateur web - -**Alors** les 3 autres appareils sont déconnectés immédiatement -**Et** seule la session du navigateur web reste active -**Et** je vois le message "Vous avez été déconnecté des autres appareils par sécurité" - ---- - -## 12. Lien de réinitialisation invalide si déjà utilisé - -**Étant donné** que j'ai réinitialisé mon mot de passe avec un lien - -**Quand** j'essaie de réutiliser le même lien - -**Alors** je vois le message "Ce lien a déjà été utilisé" -**Et** je peux demander un nouveau lien si nécessaire - ---- - -## 13. Nouveau lien invalide l'ancien - -**Étant donné** que j'ai demandé une réinitialisation et reçu un lien - -**Quand** je demande une nouvelle réinitialisation - -**Alors** l'ancien lien est invalidé -**Et** seul le nouveau lien fonctionne - ---- - -## 14. Réinitialisation débloque un compte bloqué - -**Étant donné** que mon compte est bloqué après 5 tentatives de connexion - -**Quand** je réinitialise mon mot de passe via email - -**Alors** le blocage est levé immédiatement -**Et** je peux me connecter avec le nouveau mot de passe -**Et** le compteur de tentatives est remis à 0 - ---- - - - - - -
- - -# Gestion des sessions et tokens -> *En tant qu'utilisateur connecté* -> *Je veux que mes sessions soient sécurisées et gérées automatiquement* -> *Afin de maintenir l'accès à l'application sans friction* - -**13 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté avec succès -## 1. Access token expire après 15 minutes - -**Étant donné** que j'ai reçu un access token -**Et** que 15 minutes se sont écoulées - -**Quand** je fais une requête API avec cet access token - -**Alors** la requête échoue avec le code 401 -**Et** je vois le message "Token expiré" - ---- - -## 2. Refresh automatique du token avec refresh token - -**Étant donné** que mon access token a expiré -**Et** que mon refresh token est valide - -**Quand** l'application demande un nouveau access token - -**Alors** je reçois un nouvel access token valide pour 15 minutes -**Et** je reçois un nouveau refresh token (rotation) -**Et** l'ancien refresh token est invalidé - ---- - -## 3. Refresh token expire après 30 jours d'inactivité - -**Étant donné** que je me suis connecté il y a 30 jours -**Et** que je n'ai pas utilisé l'application depuis - -**Quand** j'essaie d'utiliser mon refresh token - -**Alors** la requête échoue -**Et** je dois me reconnecter avec email/password - ---- - -## 4. Prolongation automatique de la session si l'app est utilisée - -**Étant donné** que je me suis connecté il y a 25 jours -**Et** que j'utilise l'application régulièrement - -**Quand** je fais une requête API - -**Alors** ma session est automatiquement prolongée -**Et** mon refresh token reste valide - ---- - -## 5. Détection de token replay attack - -**Étant donné** que j'ai rafraîchi mon token -**Et** que j'ai reçu un nouveau refresh token - -**Quand** j'essaie de réutiliser l'ancien refresh token - -**Alors** la requête échoue -**Et** je vois le message "Token invalide ou révoqué" -**Et** toutes mes sessions sont révoquées par sécurité - ---- - -## 6. Voir la liste des appareils connectés - -**Étant donné** que je suis connecté sur 3 appareils différents - -**Quand** je consulte la liste de mes appareils connectés - -**Alors** je vois 3 appareils avec les informations suivantes: - - | information | exemple | - |---|---| - | OS | iOS 17.1 | - | Navigateur | Safari | - | Dernière connexion | Il y a 2 heures | - | Localisation | Paris, France (IP visible) | - - ---- - -## 7. Révoquer un appareil spécifique - -**Étant donné** que je suis connecté sur mon iPhone et mon iPad - -**Quand** je révoque la session de mon iPad depuis les paramètres - -**Alors** la session iPad est immédiatement déconnectée -**Et** ma session iPhone reste active - ---- - -## 8. Déconnecter tous les appareils sauf celui en cours - -**Étant donné** que je suis connecté sur 4 appareils - -**Quand** je clique sur "Déconnecter tous les appareils" - -**Alors** les 3 autres appareils sont déconnectés -**Et** seul l'appareil actuel reste connecté - ---- - -## 9. Alerte de connexion depuis nouveau device - -**Étant donné** que je me suis toujours connecté depuis Paris - -**Quand** je me connecte depuis un nouvel appareil à Lyon - -**Alors** je reçois une notification push sur mes autres appareils -**Et** je reçois un email avec: - - | sujet | Nouvelle connexion détectée | - |---|---| - | localisation | Lyon, France | - | appareil | Android 14 - Chrome | - | action | Lien pour révoquer la session | - - ---- - -## 10. Alerte de connexion suspecte depuis pays différent - -**Étant donné** que je me suis toujours connecté depuis la France - -**Quand** je me connecte depuis un appareil aux États-Unis - -**Alors** je reçois une notification push immédiate -**Et** je reçois un email d'alerte de sécurité -**Et** la nouvelle session nécessite une validation 2FA même si désactivée - ---- - -## 11. Déconnexion après 30 jours d'inactivité totale - -**Étant donné** que je ne me suis pas connecté depuis 30 jours - -**Quand** j'ouvre l'application - -**Alors** je suis automatiquement déconnecté -**Et** je dois me reconnecter avec email/password -**Et** je vois le message "Session expirée après 30 jours d'inactivité" - ---- - -## 12. Sessions multiples simultanées autorisées - -**Étant donné** que je suis connecté sur: - - | appareil | - |---| - | iPhone | - | iPad | - | PC Windows (Web) | - - -**Quand** je fais des actions sur les 3 appareils simultanément - -**Alors** toutes les sessions fonctionnent sans conflit -**Et** chaque appareil maintient sa propre session - ---- - -## 13. Validation de JWT via Zitadel - -**Étant donné** que j'ai reçu un access token JWT - -**Quand** l'API RoadWave valide le token - -**Alors** la validation est faite localement avec la clé publique Zitadel -**Et** aucune requête externe n'est effectuée (performance) -**Et** le token contient les claims suivants: - - | claim | valeur_exemple | - |---|---| - | sub | user-id-123 | - | email | user@test.fr | - | exp | timestamp + 15 minutes | - | iss | zitadel.roadwave.com | - - ---- - - - - - -
- - -# Authentification à deux facteurs (2FA) -> *En tant qu'utilisateur soucieux de sécurité* -> *Je veux activer la 2FA sur mon compte* -> *Afin de protéger mon accès même si mon mot de passe est compromis* - -**16 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté à mon compte -## 1. Activer la 2FA TOTP (Time-based One-Time Password) - -**Étant donné** que la 2FA n'est pas activée sur mon compte - -**Quand** je choisis d'activer la 2FA TOTP - -**Alors** je vois un QR code à scanner -**Et** je vois le secret partagé en texte clair (backup) -**Et** je dois entrer un code de vérification depuis mon app authenticator - -**Quand** je saisis un code TOTP valide - -**Alors** la 2FA TOTP est activée avec succès -**Et** je reçois des codes de backup (10 codes) - ---- - -## 2. Connexion avec 2FA TOTP activée - -**Étant donné** que la 2FA TOTP est activée sur mon compte - -**Quand** je me connecte avec email/password - -**Alors** je suis redirigé vers la page de saisie du code 2FA - -**Quand** je saisis un code TOTP valide de mon authenticator - -**Alors** je suis connecté avec succès - ---- - -## 3. Connexion échouée avec code TOTP invalide - -**Étant donné** que la 2FA TOTP est activée - -**Quand** je me connecte avec email/password -**Et** que je saisis un code TOTP invalide "000000" - -**Alors** la connexion échoue -**Et** je vois le message "Code d'authentification invalide" -**Et** je peux réessayer - ---- - -## 4. Utiliser un code de backup pour 2FA - -**Étant donné** que la 2FA TOTP est activée -**Et** que j'ai perdu l'accès à mon authenticator - -**Quand** je me connecte avec email/password -**Et** que je clique sur "Utiliser un code de backup" -**Et** que je saisis un code de backup valide - -**Alors** je suis connecté avec succès -**Et** le code de backup utilisé est invalidé -**Et** il me reste 9 codes de backup - ---- - -## 5. Activer la 2FA par email - -**Étant donné** que la 2FA n'est pas activée - -**Quand** je choisis d'activer la 2FA par email - -**Alors** la 2FA email est activée immédiatement -**Et** je vois le message "2FA email activée. Vous recevrez un code à chaque connexion" - ---- - -## 6. Connexion avec 2FA email - -**Étant donné** que la 2FA email est activée - -**Quand** je me connecte avec email/password - -**Alors** je reçois un email avec un code à 6 chiffres -**Et** le code expire dans 10 minutes -**Et** je dois saisir ce code pour terminer la connexion - ---- - -## 7. Code 2FA email expiré - -**Étant donné** que la 2FA email est activée -**Et** que je me suis connecté avec email/password -**Et** que j'ai reçu un code 2FA par email il y a 11 minutes - -**Quand** je saisis ce code - -**Alors** la connexion échoue -**Et** je vois le message "Code expiré. Demandez un nouveau code." - ---- - -## 8. Renvoyer le code 2FA email - -**Étant donné** que la 2FA email est activée -**Et** que je suis sur la page de saisie du code 2FA - -**Quand** je clique sur "Renvoyer le code" - -**Alors** je reçois un nouveau code par email -**Et** l'ancien code est invalidé - ---- - -## 9. Ajouter un appareil de confiance (skip 2FA pendant 30 jours) - -**Étant donné** que la 2FA TOTP est activée - -**Quand** je me connecte avec email/password et code TOTP -**Et** que je coche "Ne plus demander sur cet appareil" - -**Alors** je suis connecté avec succès -**Et** cet appareil est enregistré comme "appareil de confiance" - -**Quand** je me reconnecte dans les 30 jours suivants sur ce même appareil - -**Alors** je ne dois pas saisir de code 2FA - ---- - -## 10. Appareil de confiance expire après 30 jours - -**Étant donné** que j'ai enregistré un appareil de confiance il y a 31 jours - -**Quand** je me connecte depuis cet appareil - -**Alors** je dois saisir un code 2FA -**Et** je vois le message "Appareil de confiance expiré. Veuillez vous authentifier" - ---- - -## 11. Voir la liste des appareils de confiance - -**Étant donné** que j'ai enregistré 3 appareils de confiance - -**Quand** je consulte mes paramètres de sécurité - -**Alors** je vois la liste de mes 3 appareils de confiance avec: - - | information | exemple | - |---|---| - | Nom | iPhone 13 - Safari | - | Date ajout | 15 janvier 2026 | - | Dernière vue | Il y a 2 heures | - | Expire le | 14 février 2026 | - - ---- - -## 12. Révoquer un appareil de confiance - -**Étant donné** que j'ai un iPhone enregistré comme appareil de confiance - -**Quand** je révoque cet appareil depuis les paramètres - -**Alors** l'appareil est supprimé de la liste - -**Quand** je me reconnecte depuis cet iPhone - -**Alors** je dois saisir un code 2FA - ---- - -## 13. Révoquer tous les appareils de confiance - -**Étant donné** que j'ai 5 appareils de confiance enregistrés - -**Quand** je clique sur "Révoquer tous les appareils de confiance" - -**Alors** tous les appareils sont révoqués -**Et** je vois le message "Tous les appareils de confiance ont été révoqués" - ---- - -## 14. 2FA forcée pour connexion suspecte malgré appareil de confiance - -**Étant donné** que j'ai un appareil de confiance enregistré en France -**Et** que je me connecte depuis ce même appareil mais avec une IP américaine - -**Quand** je tente de me connecter - -**Alors** la 2FA est requise malgré l'appareil de confiance -**Et** je vois le message "Connexion suspecte détectée. Authentification requise." - ---- - -## 15. Désactiver la 2FA - -**Étant donné** que la 2FA TOTP est activée - -**Quand** je désactive la 2FA depuis mes paramètres -**Et** que je confirme avec mon mot de passe - -**Alors** la 2FA est désactivée -**Et** tous les codes de backup sont invalidés -**Et** tous les appareils de confiance sont révoqués - ---- - -## 16. Régénérer les codes de backup - -**Étant donné** que la 2FA est activée -**Et** que j'ai utilisé 8 codes de backup sur 10 - -**Quand** je demande à régénérer les codes de backup - -**Alors** je reçois 10 nouveaux codes -**Et** tous les anciens codes (utilisés ou non) sont invalidés - ---- - - - - - -
- - -# Vérification d'email -> *En tant qu'utilisateur inscrit* -> *Je veux vérifier mon adresse email* -> *Afin d'accéder à toutes les fonctionnalités selon mon rôle* - -**10 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -## 1. Auditeur avec email non vérifié - lecture illimitée - -**Étant donné** que je suis un auditeur avec email non vérifié - -**Quand** j'essaie d'écouter du contenu - -**Alors** je peux écouter tous les contenus sans limite - ---- - -## 2. Auditeur avec email non vérifié - création limitée à 5 contenus - -**Étant donné** que je suis un auditeur avec email non vérifié -**Et** que j'ai créé 4 contenus - -**Quand** je crée un 5ème contenu - -**Alors** le contenu est créé avec succès -**Mais** quand j'essaie de créer un 6ème contenu -**Alors** la création échoue -**Et** je vois le message "Vérifiez votre email pour créer plus de contenus" - ---- - -## 3. Rappel de vérification après le 3ème contenu créé - -**Étant donné** que je suis un auditeur avec email non vérifié -**Et** que j'ai créé 2 contenus - -**Quand** je crée mon 3ème contenu - -**Alors** le contenu est créé avec succès -**Et** je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée" - ---- - -## 4. Auditeur vérifie son email - -**Étant donné** que je suis un auditeur avec email non vérifié -**Et** que j'ai reçu un lien de vérification - -**Quand** je clique sur le lien de vérification dans l'email - -**Alors** mon email est marqué comme vérifié -**Et** je vois le message "Email vérifié avec succès" -**Et** toutes les fonctionnalités sont débloquées - ---- - -## 5. Créateur doit vérifier son email sous 7 jours pour monétisation - -**Étant donné** que je suis inscrit comme créateur -**Et** que mon email n'est pas vérifié -**Et** que je remplis les conditions de monétisation - -**Quand** j'essaie d'accéder au programme de monétisation - -**Alors** l'accès est refusé -**Et** je vois le message "Vérifiez votre email pour accéder à la monétisation" - ---- - -## 6. Créateur ne peut pas publier de contenus illimités sans vérification - -**Étant donné** que je suis un créateur avec email non vérifié -**Et** que j'ai créé 5 contenus - -**Quand** j'essaie de créer un 6ème contenu - -**Alors** la création échoue -**Et** je vois le message "Vérifiez votre email pour publier des contenus illimités" - ---- - -## 7. Créateur vérifie son email et déboque tout - -**Étant donné** que je suis un créateur avec email non vérifié -**Et** que j'ai reçu un lien de vérification - -**Quand** je clique sur le lien de vérification - -**Alors** mon email est marqué comme vérifié -**Et** je peux publier des contenus illimités -**Et** je peux accéder au programme de monétisation si j'en remplis les conditions - ---- - -## 8. KYC impossible sans email vérifié - -**Étant donné** que je suis un créateur avec email non vérifié - -**Quand** j'essaie de compléter le KYC via Mangopay - -**Alors** l'accès au KYC est refusé -**Et** je vois le message "Vérifiez votre email avant de procéder au KYC" - ---- - -## 9. Tentative de vérification avec un lien déjà utilisé - -**Étant donné** que j'ai déjà vérifié mon email avec un lien - -**Quand** j'essaie de réutiliser le même lien de vérification - -**Alors** la vérification échoue -**Et** je vois le message "Ce lien a déjà été utilisé" - ---- - -## 10. Auditeur vérifié peut créer plus de 5 contenus - -**Étant donné** que je suis un auditeur avec email vérifié -**Et** que j'ai créé 10 contenus - -**Quand** je crée un 11ème contenu - -**Alors** le contenu est créé avec succès -**Et** il n'y a pas de limite de création - ---- - - - - - -
- - -# Métadonnées et publication de contenu -> *En tant que créateur* -> *Je veux remplir les métadonnées de mon contenu* -> *Afin de le publier sur RoadWave* - -**34 scénarios** (32 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis un créateur connecté -> **Et** que mon fichier audio est encodé et prêt -## 1. Publication avec toutes les métadonnées obligatoires - -**Quand** je remplis les métadonnées suivantes: - - | champ | valeur | - |---|---| - | Titre | Histoire de la Tour Eiffel | - | Type géo | Ancré | - | Zone | Point GPS (48.8584, 2.2945, 500m) | - | Tags | Voyage, Culture générale | - | Classification âge | Tout public | - - -**Alors** la publication réussit -**Et** mon contenu est soumis pour validation - ---- - -## 2. Titre valide entre 5 et 100 caractères - -**Quand** je saisis un titre de 50 caractères - -**Alors** le titre est accepté -**Et** la validation passe - ---- - -## 3. Titre trop court (<5 caractères) - -**Quand** je saisis un titre de 4 caractères "Test" - -**Alors** la publication échoue -**Et** je vois le message "Le titre doit contenir entre 5 et 100 caractères" - ---- - -## 4. Titre trop long (>100 caractères) - -**Quand** je saisis un titre de 101 caractères - -**Alors** la publication échoue -**Et** je vois le message "Le titre doit contenir entre 5 et 100 caractères" - ---- - -## 5. Titre à exactement 5 caractères accepté - -**Quand** je saisis un titre de exactement 5 caractères "Titre" - -**Alors** le titre est accepté - ---- - -## 6. Titre à exactement 100 caractères accepté - -**Quand** je saisis un titre de exactement 100 caractères - -**Alors** le titre est accepté - ---- - -## 7. Sélectionner type géo "Ancré" - -**Quand** je sélectionne le type géo "Ancré" - -**Alors** le système applique une pondération géo de 0.7 -**Et** je dois définir une zone de diffusion précise - ---- - -## 8. Sélectionner type géo "Contextuel" - -**Quand** je sélectionne le type géo "Contextuel" - -**Alors** le système applique une pondération géo de 0.5 -**Et** je peux définir une zone ville/département/région - ---- - -## 9. Sélectionner type géo "Neutre" - -**Quand** je sélectionne le type géo "Neutre" - -**Alors** le système applique une pondération géo de 0.2 -**Et** je peux définir une zone nationale - ---- - -## 10. Zone diffusion - Point GPS avec rayon - -**Quand** je choisis "Point GPS" -**Et** que je définis les coordonnées (48.8584, 2.2945) -**Et** que je définis un rayon de 500 mètres - -**Alors** la zone est validée -**Et** le contenu sera diffusé dans un rayon de 500m autour du point - ---- - -## 11. Zone diffusion - Rayon minimum 100m - -**Quand** je définis un rayon de 50 mètres (< 100m) - -**Alors** la validation échoue -**Et** je vois le message "Le rayon doit être entre 100m et 10km" - ---- - -## 12. Zone diffusion - Rayon maximum 10km - -**Quand** je définis un rayon de 15 km (> 10km) - -**Alors** la validation échoue -**Et** je vois le message "Le rayon doit être entre 100m et 10km" - ---- - -## 13. Zone diffusion - Ville depuis référentiel INSEE - -**Quand** je choisis "Ville" - -**Alors** je vois une liste de villes du référentiel INSEE - -**Quand** je sélectionne "Paris (75000)" - -**Alors** la zone est définie sur toute la ville de Paris - ---- - -## 14. Zone diffusion - Département - -**Quand** je choisis "Département" -**Et** que je sélectionne "Ille-et-Vilaine (35)" - -**Alors** la zone couvre tout le département 35 - ---- - -## 15. Zone diffusion - Région - -**Quand** je choisis "Région" -**Et** que je sélectionne "Bretagne" - -**Alors** la zone couvre toute la région Bretagne - ---- - -## 16. Zone diffusion - National - -**Quand** je choisis "National" - -**Alors** la zone couvre toute la France -**Et** aucune restriction géographique n'est appliquée - ---- - -## 17. Zones mutuellement exclusives - -**Étant donné** que j'ai sélectionné "Point GPS" - -**Quand** j'essaie de sélectionner également "Ville" - -**Alors** la première sélection est remplacée -**Et** seule "Ville" reste active - ---- - -## 18. Sélectionner 1 tag minimum - -**Quand** je sélectionne 1 tag "Voyage" - -**Alors** la validation passe -**Et** le contenu est tagué "Voyage" - ---- - -## 19. Sélectionner 3 tags maximum - -**Quand** je sélectionne 3 tags "Automobile", "Technologie", "Sport" - -**Alors** la validation passe -**Et** le contenu est tagué avec les 3 tags - ---- - -## 20. Impossible de sélectionner 0 tag - -**Quand** j'essaie de publier sans sélectionner de tag - -**Alors** la publication échoue -**Et** je vois le message "Vous devez sélectionner entre 1 et 3 tags" - ---- - -## 21. Impossible de sélectionner 4 tags - -**Quand** j'essaie de sélectionner 4 tags - -**Alors** le 4ème tag ne peut pas être ajouté -**Et** je vois le message "Maximum 3 tags" - ---- - -## 22. Tags disponibles dans la liste - -**Quand** je consulte la liste des tags - -**Alors** je vois les tags suivants: - - | tag | - |---| - | Automobile | - | Voyage | - | Famille | - | Amour | - | Musique | - | Économie | - | Cryptomonnaie | - | Politique | - | Culture générale | - | Sport | - | Technologie | - | Santé | - - ---- - -## 23. Classification âge obligatoire - -**Quand** j'essaie de publier sans classification âge - -**Alors** la publication échoue -**Et** je vois le message "Vous devez sélectionner une classification d'âge" - ---- - -## 24. 📋 Plan: Sélectionner classification âge - -**Quand** je sélectionne la classification "" - -**Alors** le contenu sera visible pour "" - -**📊 Exemples de données:** - -| classification | public_cible | -|---|---| -| Tout public | Tous les utilisateurs | -| 13+ | Utilisateurs 13 ans et plus | -| 16+ | Utilisateurs 16 ans et plus | -| 18+ | Utilisateurs 18 ans et plus | - ---- - -## 25. Image de couverture auto-générée selon type géo - -**Étant donné** que je choisis le type géo "Ancré" -**Et** que mon tag principal est "Voyage" - -**Quand** la publication est soumise - -**Alors** une image de couverture est générée automatiquement: - - | paramètre | valeur | - |---|---| - | Icône | 📍 (Ancré) | - | Couleur | Bleu-vert (Voyage) | - | Format | 800×800px PNG | - - ---- - -## 26. Image de couverture type Contextuel - -**Étant donné** que je choisis "Contextuel" - -**Quand** l'image est générée - -**Alors** l'icône est 🌍 (Contextuel) - ---- - -## 27. Image de couverture type Neutre - -**Étant donné** que je choisis "Neutre" - -**Quand** l'image est générée - -**Alors** l'icône est 🎧 (Neutre) - ---- - -## 28. 📋 Plan: Couleur selon tag principal - -**Étant donné** que mon tag principal est "" - -**Quand** l'image est générée - -**Alors** la couleur de fond est "" - -**📊 Exemples de données:** - -| tag | couleur | -|---|---| -| Automobile | Bleu | -| Voyage | Vert | -| Musique | Rouge | -| Économie | Gris | -| Sport | Orange | - ---- - -## 29. Champs optionnels non obligatoires - -**Quand** je publie sans description -**Et** sans image de couverture personnalisée - -**Alors** la publication réussit -**Et** les champs optionnels restent vides -**Et** une image par défaut est générée - ---- - -## 30. Temps de publication estimé 2 minutes - -**Étant donné** que mon fichier audio est prêt - -**Quand** je commence à remplir les métadonnées - -**Alors** je peux publier en environ 2 minutes - ---- - -## 31. Publication rapide sans friction - -**Quand** je publie mon premier contenu - -**Alors** aucun champ complexe n'est demandé -**Et** je ne suis pas bloqué sur description ou image -**Et** la publication est fluide - ---- - -## 32. Prévisualisation avant publication - -**Étant donné** que j'ai rempli toutes les métadonnées - -**Quand** je clique sur "Prévisualiser" - -**Alors** je vois un aperçu de mon contenu: - - | élément | affiché | - |---|---| - | Titre | ✅ | - | Image couverture | ✅ | - | Tags | ✅ | - | Zone diffusion | ✅ | - | Durée audio | ✅ | - | Classification | ✅ | - - ---- - -## 33. Enregistrer brouillon - -**Étant donné** que j'ai commencé à remplir les métadonnées - -**Quand** je clique sur "Enregistrer brouillon" - -**Alors** mes métadonnées sont sauvegardées -**Et** je peux reprendre la publication plus tard - ---- - -## 34. Reprendre brouillon - -**Étant donné** que j'ai un brouillon sauvegardé - -**Quand** j'accède à mes contenus - -**Alors** je vois le brouillon avec statut "📝 Brouillon" -**Et** je peux reprendre la publication - ---- - - - - - -
- - -# Modification et suppression de contenu -> *En tant que créateur* -> *Je veux pouvoir modifier ou supprimer mes contenus* -> *Afin de garder le contrôle sur mes publications* - -**30 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis un créateur connecté -> **Et** que j'ai publié un contenu -## 1. Modifier le titre d'un contenu - -**Étant donné** que mon contenu a le titre "Histoire de Paris" - -**Quand** je modifie le titre en "Histoire fascinante de Paris" - -**Alors** la modification est enregistrée immédiatement -**Et** je vois le message "Titre modifié avec succès" - ---- - -## 2. Correction de coquilles dans le titre - -**Étant donné** que mon titre contient une faute "Histoore de Paris" - -**Quand** je corrige en "Histoire de Paris" - -**Alors** la modification est acceptée -**Et** le titre corrigé est affiché - ---- - -## 3. Ajouter une description ultérieurement - -**Étant donné** que j'ai publié sans description - -**Quand** j'ajoute une description "Découvrez l'histoire de la capitale" - -**Alors** la description est enregistrée -**Et** elle est visible sur la page du contenu - ---- - -## 4. Modifier la description existante - -**Étant donné** que mon contenu a déjà une description - -**Quand** je modifie la description - -**Alors** la nouvelle description remplace l'ancienne -**Et** la modification est immédiate - ---- - -## 5. Modifier les tags pour ajuster pertinence - -**Étant donné** que mon contenu est tagué "Sport", "Musique" - -**Quand** je change les tags en "Sport", "Santé" - -**Alors** les nouveaux tags sont appliqués -**Et** l'algorithme utilise les nouveaux tags pour recommandations - ---- - -## 6. Personnaliser l'image de couverture - -**Étant donné** que mon contenu a une image auto-générée - -**Quand** j'uploade une image personnalisée 800×800px - -**Alors** l'image personnalisée remplace l'image par défaut -**Et** elle est visible sur le contenu - ---- - -## 7. Impossible de modifier l'audio - -**Étant donné** que mon contenu audio est publié - -**Quand** j'essaie de remplacer le fichier audio - -**Alors** la modification est refusée -**Et** je vois le message "L'audio ne peut pas être modifié après publication" - ---- - -## 8. Raison - Éviter fraude audio - -**Étant donné** que je veux changer l'audio après validation - -**Quand** j'essaie de modifier - -**Alors** le système refuse pour éviter: - - | risque | - |---| - | Uploader contenu validé puis remplacer spam | - | Fraude sur l'intégrité du contenu | - - ---- - -## 9. Impossible de modifier la zone de diffusion - -**Étant donné** que mon contenu est diffusé à Paris - -**Quand** j'essaie de changer la zone en "National" - -**Alors** la modification est refusée -**Et** je vois le message "La zone de diffusion ne peut pas être modifiée" - ---- - -## 10. Raison - Éviter manipulation algorithme - -**Étant donné** que je veux changer ma zone - -**Quand** j'essaie de modifier - -**Alors** le système refuse pour éviter: - - | manipulation | - |---| - | Créer "Local Paris" puis changer en "National" | - | Boost artificiel de visibilité | - - ---- - -## 11. Impossible de modifier le type géo - -**Étant donné** que mon contenu est type "Neutre" (pondération 0.2) - -**Quand** j'essaie de changer en "Ancré" (pondération 0.7) - -**Alors** la modification est refusée -**Et** je vois le message "Le type géographique ne peut pas être modifié" - ---- - -## 12. Raison - Éviter abus de pondération - -**Étant donné** que je veux changer le type géo - -**Quand** j'essaie de modifier - -**Alors** le système refuse pour éviter: - - | abus | - |---| - | Créer "Neutre" puis passer en "Ancré" | - | Manipulation de la pondération algorithme | - - ---- - -## 13. Impossible de modifier la classification âge - -**Étant donné** que mon contenu est classé "Tout public" - -**Quand** j'essaie de changer en "18+" - -**Alors** la modification est refusée -**Et** je vois le message "La classification d'âge ne peut pas être modifiée" - ---- - -## 14. Raison - Sécurité mineurs - -**Étant donné** que je veux changer la classification - -**Quand** j'essaie de modifier - -**Alors** le système refuse pour garantir: - - | protection | - |---| - | Classification vérifiée en modération | - | Pas de contournement validation | - | Sécurité des mineurs | - - ---- - -## 15. Solution si besoin de changer audio/zone/classification - -**Étant donné** que je veux absolument changer l'audio - -**Quand** je consulte les options - -**Alors** je vois "Supprimer et republier le contenu" -**Et** c'est la seule solution disponible - ---- - -## 16. Republication après suppression - créateur <3 validations - -**Étant donné** que je suis un nouveau créateur (2 contenus validés) -**Et** que je supprime puis republie un contenu - -**Quand** je republie avec les modifications - -**Alors** le contenu repasse en file de validation -**Et** une nouvelle validation est effectuée - ---- - -## 17. Republication après suppression - créateur vérifié - -**Étant donné** que je suis créateur vérifié (≥3 contenus validés) -**Et** que je supprime puis republie un contenu - -**Quand** je republie avec les modifications - -**Alors** le contenu est publié immédiatement -**Et** aucune validation préalable n'est requise - ---- - -## 18. Suppression de contenu immédiate - -**Quand** je clique sur "Supprimer le contenu" -**Et** que je confirme la suppression - -**Alors** le contenu est supprimé immédiatement -**Et** disparaît de la liste publique - ---- - -## 19. Confirmation avant suppression - -**Quand** je clique sur "Supprimer" - -**Alors** je vois un message de confirmation: - - | titre | Êtes-vous sûr ? | - |---|---| - | message | Cette action est définitive | - | warning | Le contenu sera supprimé définitivement | - | actions | Confirmer / Annuler | - - ---- - -## 20. Suppression définitive et non réversible - -**Étant donné** que j'ai supprimé un contenu - -**Quand** j'essaie de le récupérer - -**Alors** la récupération est impossible -**Et** le contenu est définitivement perdu - ---- - -## 21. Suppression BDD + CDN sous 5 minutes - -**Quand** je supprime un contenu - -**Alors** l'entrée en base de données est marquée "deleted" -**Et** les fichiers CDN sont marqués pour suppression -**Et** la suppression effective a lieu sous 5 minutes - ---- - -## 22. Historique auditeurs conservé anonymisé - -**Étant donné** que 1000 personnes ont écouté mon contenu - -**Quand** je supprime le contenu - -**Alors** leur historique est conservé -**Mais** marqué "Contenu supprimé par créateur" -**Et** la durée d'écoute est conservée pour leurs stats - ---- - -## 23. Analytics plateforme anonymisées conservées - -**Étant donné** que mon contenu a généré 10K écoutes - -**Quand** je supprime le contenu - -**Alors** les métriques globales sont conservées anonymement: - - | métrique | conservée | - |---|---| - | Total écoutes | ✅ (anonyme) | - | Durée totale | ✅ (anonyme) | - | Catégorie | ✅ (anonyme) | - | Auteur | ❌ (anonymisé) | - -**Et** c'est conforme RGPD - ---- - -## 24. Fichiers CDN supprimés sous 24h - -**Étant donné** que mon contenu est supprimé - -**Quand** 24 heures s'écoulent - -**Alors** tous les fichiers audio sont purgés du CDN Bunny -**Et** l'espace de stockage est libéré - ---- - -## 25. Pas de notification aux auditeurs - -**Étant donné** que 500 utilisateurs ont écouté mon contenu - -**Quand** je supprime le contenu - -**Alors** aucune notification n'est envoyée aux auditeurs -**Et** il n'y a pas d'effet Streisand - ---- - -## 26. Auditeur tente de réécouter contenu supprimé - -**Étant donné** qu'un auditeur a écouté mon contenu -**Et** que j'ai supprimé ce contenu - -**Quand** l'auditeur tente de le réécouter depuis son historique - -**Alors** il voit le message "Ce contenu n'est plus disponible" -**Et** la lecture est impossible - ---- - -## 27. Historique auditeur conserve trace - -**Étant donné** qu'un auditeur a écouté mon contenu le 15 janvier -**Et** que je supprime le contenu le 20 janvier - -**Quand** l'auditeur consulte son historique - -**Alors** il voit "Vous avez écouté ce contenu le 15 janvier 2026" -**Et** le titre est remplacé par "Contenu supprimé" -**Et** la date d'écoute est conservée - ---- - -## 28. Statistiques créateur après suppression - -**Étant donné** que j'ai publié 10 contenus -**Et** que je supprime 2 contenus - -**Quand** je consulte mes statistiques globales - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Contenus publiés | 8 (actifs) | - | Total historique | 10 | - | Suppressions | 2 | - -**Et** l'historique des suppressions est visible - ---- - -## 29. Limite de modifications par contenu - -**Étant donné** que j'ai modifié un titre 10 fois - -**Quand** j'essaie de modifier une 11ème fois - -**Alors** la modification est acceptée - ---- - -## 30. Historique des modifications visible - -**Étant donné** que j'ai modifié un contenu plusieurs fois - -**Quand** je consulte l'historique - -**Alors** je vois: - - | date | modification | - |---|---| - | 21/01/2026 | Titre changé | - | 20/01/2026 | Tags modifiés | - | 19/01/2026 | Description ajoutée | - -**Et** je peux tracer toutes les modifications - ---- - - - - - -
- - -# Upload et encodage de contenu audio -> *En tant que créateur* -> *Je veux uploader mon contenu audio* -> *Afin qu'il soit encodé et disponible pour les auditeurs* - -**29 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis un créateur connecté -## 1. Upload fichier MP3 valide - -**Quand** j'uploade un fichier MP3 de 50 MB et 30 minutes - -**Alors** l'upload réussit -**Et** le fichier est envoyé vers Bunny Storage temporaire -**Et** un job d'encodage asynchrone est lancé - ---- - -## 2. Upload fichier AAC valide (.aac) - -**Quand** j'uploade un fichier AAC de 80 MB et 1 heure - -**Alors** l'upload réussit -**Et** le fichier est accepté -**Et** l'encodage démarre - ---- - -## 3. Upload fichier M4A valide - -**Quand** j'uploade un fichier M4A de 100 MB et 2 heures - -**Alors** l'upload réussit -**Et** le fichier est traité comme AAC -**Et** l'encodage démarre - ---- - -## 4. Rejet fichier WAV (non supporté) - -**Quand** j'essaie d'uploader un fichier WAV - -**Alors** l'upload échoue -**Et** je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)" - ---- - -## 5. Rejet fichier FLAC (non supporté) - -**Quand** j'essaie d'uploader un fichier FLAC - -**Alors** l'upload échoue -**Et** je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)" - ---- - -## 6. Validation taille maximale 200 MB - -**Quand** j'essaie d'uploader un fichier MP3 de 201 MB - -**Alors** l'upload échoue -**Et** je vois le message "Fichier trop volumineux (max 200 MB)" - ---- - -## 7. Upload à la limite de 200 MB accepté - -**Quand** j'uploade un fichier MP3 de exactement 200 MB - -**Alors** l'upload réussit -**Et** le fichier est accepté - ---- - -## 8. Validation durée maximale 4 heures - -**Quand** j'essaie d'uploader un fichier de 4h 10min - -**Alors** l'upload échoue -**Et** je vois le message "Durée trop longue (max 4 heures)" - ---- - -## 9. Upload à la limite de 4h accepté - -**Quand** j'uploade un fichier de exactement 4 heures - -**Alors** l'upload réussit -**Et** le fichier est accepté - ---- - -## 10. Validation format côté client - -**Quand** je sélectionne un fichier dans l'interface - -**Alors** la validation du format est faite immédiatement côté client -**Et** je suis informé avant même de lancer l'upload si le format est invalide - ---- - -## 11. Double validation côté backend - -**Étant donné** qu'un fichier a passé la validation client - -**Quand** le backend reçoit le fichier - -**Alors** une validation supplémentaire est effectuée -**Et** le format et l'intégrité sont vérifiés - ---- - -## 12. Pipeline d'encodage - étape 1 upload - -**Quand** j'uploade un fichier MP3 valide - -**Alors** le fichier est stocké temporairement dans Bunny Storage -**Et** un job d'encodage est mis en file d'attente - ---- - -## 13. Pipeline d'encodage - validation format - -**Étant donné** qu'un job d'encodage est lancé - -**Quand** le worker Go traite le fichier - -**Alors** le format est validé avec FFmpeg -**Et** l'intégrité du fichier est vérifiée - ---- - -## 14. Pipeline d'encodage - génération 3 profils Opus - -**Étant donné** qu'un fichier audio est validé - -**Quand** l'encodage démarre - -**Alors** 3 profils Opus sont générés: - - | qualité | bitrate | usage | - |---|---|---| - | Basse | 24 kbps | 2G/Edge | - | Standard | 48 kbps | 3G | - | Haute | 64 kbps | 4G/5G | - - ---- - -## 15. Pipeline d'encodage - génération segments HLS - -**Étant donné** que les profils Opus sont générés - -**Quand** l'encodage continue - -**Alors** un fichier manifest .m3u8 est créé -**Et** des segments .ts sont générés -**Et** le contenu est prêt pour streaming HLS - ---- - -## 16. Pipeline d'encodage - génération image par défaut - -**Étant donné** que l'encodage est en cours - -**Quand** les métadonnées sont traitées - -**Alors** une image de couverture par défaut est générée -**Et** l'image fait 800×800px au format PNG - ---- - -## 17. Pipeline d'encodage - suppression fichier original - -**Étant donné** que l'encodage est terminé avec succès - -**Quand** tous les fichiers de sortie sont générés - -**Alors** le fichier original MP3/AAC est supprimé -**Et** seuls les profils Opus et HLS sont conservés -**Et** l'espace de stockage est économisé - ---- - -## 18. Temps d'encodage contenu 5 minutes - -**Étant donné** qu'un fichier de 5 minutes est uploadé - -**Quand** l'encodage démarre - -**Alors** l'encodage prend environ 30 secondes -**Et** je reçois une notification "Contenu prêt à publier" - ---- - -## 19. Temps d'encodage podcast 1 heure - -**Étant donné** qu'un fichier de 1 heure est uploadé - -**Quand** l'encodage démarre - -**Alors** l'encodage prend environ 5 minutes -**Et** une barre de progression est affichée - ---- - -## 20. Temps d'encodage podcast 4 heures - -**Étant donné** qu'un fichier de 4 heures est uploadé - -**Quand** l'encodage démarre - -**Alors** l'encodage prend environ 20 minutes -**Et** je peux fermer l'app (traitement asynchrone) - ---- - -## 21. Notification "Contenu prêt à publier" - -**Étant donné** que mon contenu est en cours d'encodage - -**Quand** l'encodage se termine avec succès - -**Alors** je reçois une notification push "✅ Votre contenu est prêt à publier" -**Et** je peux accéder à l'interface de publication - ---- - -## 22. Échec d'encodage - fichier corrompu - -**Étant donné** qu'un fichier MP3 corrompu est uploadé - -**Quand** l'encodage démarre - -**Alors** l'encodage échoue -**Et** je reçois une notification "❌ Erreur d'encodage: fichier corrompu" -**Et** le fichier temporaire est supprimé - ---- - -## 23. Écoute accélérée - vitesses disponibles - -**Étant donné** qu'un contenu est publié - -**Quand** un auditeur écoute le contenu - -**Alors** il peut choisir parmi les vitesses: - - | vitesse | usage | - |---|---| - | 0.75x | Compréhension difficile | - | 1.0x | Normal (défaut) | - | 1.25x | Gain léger | - | 1.5x | Podcasts longs | - | 2.0x | Survol rapide | - - ---- - -## 24. Écoute accélérée pour modérateurs - -**Étant donné** que je suis un modérateur -**Et** qu'un contenu de 30 secondes est à valider - -**Quand** je l'écoute à 2.0x - -**Alors** je termine l'écoute en 15 secondes -**Et** ma productivité est doublée - ---- - -## 25. Écoute accélérée pour auditeurs - -**Étant donné** que je suis un auditeur -**Et** qu'un podcast de 1 heure est disponible - -**Quand** je configure la vitesse à 1.5x - -**Alors** j'écoute le podcast en 40 minutes -**Et** je gagne 20 minutes - ---- - -## 26. Sauvegarde préférence vitesse d'écoute - -**Étant donné** que je configure la vitesse à 1.5x - -**Quand** j'écoute plusieurs contenus - -**Alors** tous les contenus sont lus à 1.5x par défaut -**Et** ma préférence est sauvegardée - ---- - -## 27. Scalabilité horizontale des workers - -**Étant donné** que 100 contenus sont uploadés simultanément - -**Quand** les jobs d'encodage sont distribués - -**Alors** plusieurs workers Go traitent les jobs en parallèle -**Et** Kubernetes scale automatiquement les pods -**Et** tous les contenus sont encodés sans délai excessif - ---- - -## 28. Statut d'encodage visible - -**Étant donné** que mon contenu est en cours d'encodage - -**Quand** je consulte mes contenus - -**Alors** je vois le statut: - - | état | affichage | - |---|---| - | En attente | ⏳ File d'attente | - | En cours | ⚙️ Encodage en cours (45%) | - | Terminé | ✅ Prêt à publier | - | Échec | ❌ Erreur - Réessayer | - - ---- - -## 29. Réessayer après échec d'encodage - -**Étant donné** que l'encodage de mon contenu a échoué - -**Quand** je clique sur "Réessayer" - -**Alors** un nouveau job d'encodage est lancé -**Et** je peux tenter à nouveau - ---- - - - - - -
- - -# Validation des 3 premiers contenus -> *En tant que nouveau créateur* -> *Je veux que mes 3 premiers contenus soient validés* -> *Afin de devenir créateur vérifié* - -**30 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis un nouveau créateur -## 1. Premier contenu passe en file de validation - -**Quand** je publie mon premier contenu - -**Alors** le contenu passe en file d'attente modération -**Et** je vois le message "Votre contenu est en cours de validation (24-48h)" -**Et** le contenu n'est pas encore visible publiquement - ---- - -## 2. Deuxième contenu passe également en validation - -**Étant donné** que mon premier contenu a été validé - -**Quand** je publie mon deuxième contenu - -**Alors** le contenu passe en file d'attente modération -**Et** le délai estimé est 24-48h - ---- - -## 3. Troisième contenu - dernière validation - -**Étant donné** que mes 2 premiers contenus ont été validés - -**Quand** je publie mon troisième contenu - -**Alors** le contenu passe en file d'attente modération -**Et** je vois "Dernière validation avant statut vérifié ✓" - ---- - -## 4. Modérateur écoute 30 secondes du contenu - -**Étant donné** qu'un contenu est en file de validation - -**Quand** le modérateur junior l'examine - -**Alors** il écoute les 30 premières secondes -**Et** il vérifie les métadonnées - ---- - -## 5. Validation - Qualité audio acceptable - -**Étant donné** qu'un contenu a une qualité audio claire - -**Quand** le modérateur l'écoute - -**Alors** il vérifie que l'audio est compréhensible -**Et** qu'il n'y a pas de grésillement excessif - ---- - -## 6. Rejet - Qualité audio insuffisante - -**Étant donné** qu'un contenu a un audio très grésillant - -**Quand** le modérateur l'écoute - -**Alors** le contenu est rejeté -**Et** la raison est "Qualité audio insuffisante" - ---- - -## 7. Validation - Respect des règles - -**Étant donné** qu'un contenu respecte les règles - -**Quand** le modérateur l'examine - -**Alors** il vérifie qu'il n'y a pas de contenu prohibé: - - | type prohibé | - |---| - | Haine | - | Violence | - | Spam | - | Illégalité | - - ---- - -## 8. Rejet - Contenu haineux détecté - -**Étant donné** qu'un contenu contient des propos haineux - -**Quand** le modérateur l'écoute - -**Alors** le contenu est rejeté immédiatement -**Et** la raison est "Contenu haineux (violation des règles)" -**Et** le créateur peut recevoir un strike - ---- - -## 9. Validation - Classification âge cohérente - -**Étant donné** qu'un contenu familial est classé "Tout public" - -**Quand** le modérateur l'écoute - -**Alors** il vérifie que la classification correspond au contenu -**Et** le contenu est accepté - ---- - -## 10. Rejet - Classification incorrecte - -**Étant donné** qu'un contenu adulte est classé "Tout public" - -**Quand** le modérateur détecte l'incohérence - -**Alors** le contenu est rejeté -**Et** la raison est "Classification d'âge incorrecte" - ---- - -## 11. Validation - Tags pertinents - -**Étant donné** qu'un contenu sur l'automobile est tagué "Automobile", "Technologie" - -**Quand** le modérateur vérifie les tags - -**Alors** il confirme que les tags correspondent au contenu -**Et** le contenu est accepté - ---- - -## 12. Rejet - Tags non pertinents - -**Étant donné** qu'un contenu musical est tagué "Automobile", "Sport" - -**Quand** le modérateur détecte l'incohérence - -**Alors** le contenu est rejeté -**Et** la raison est "Tags non pertinents avec le contenu" - ---- - -## 13. Validation - Zone diffusion cohérente - -**Étant donné** qu'un audio-guide de la Tour Eiffel est en "Point GPS" Paris - -**Quand** le modérateur vérifie la cohérence - -**Alors** la zone est appropriée -**Et** le contenu est accepté - ---- - -## 14. Rejet - Zone incohérente - -**Étant donné** qu'un audio-guide de la Tour Eiffel est en zone "National" - -**Quand** le modérateur détecte l'incohérence - -**Alors** le contenu est rejeté -**Et** la raison est "Zone de diffusion incohérente (devrait être Point GPS)" - ---- - -## 15. Délai de validation 24-48h jours ouvrés - -**Étant donné** que je publie un contenu un lundi - -**Quand** le contenu entre en file de validation - -**Alors** le délai estimé est 24-48h (mercredi maximum) - ---- - -## 16. Délai étendu le weekend - -**Étant donné** que je publie un contenu un vendredi soir - -**Quand** le contenu entre en file de validation - -**Alors** le délai peut atteindre 72h (lundi) -**Et** je vois "Validation en cours, délai 24-72h (weekend)" - ---- - -## 17. Priorité FIFO (First In First Out) - -**Étant donné** que 10 contenus sont en file de validation - -**Quand** les modérateurs traitent la file - -**Alors** les contenus sont traités dans l'ordre d'arrivée -**Et** pas de traitement prioritaire - ---- - -## 18. Notification acceptation - -**Étant donné** que mon contenu est validé et accepté - -**Alors** je reçois un email "✅ Votre contenu '[Titre]' est en ligne !" -**Et** je reçois une notification push -**Et** je vois un lien direct vers le contenu - ---- - -## 19. Compteur de validation - -**Étant donné** que mon premier contenu est accepté - -**Alors** je vois "1/3 contenus validés pour devenir créateur vérifié" - -**Quand** mon deuxième contenu est accepté - -**Alors** je vois "2/3 contenus validés pour devenir créateur vérifié" - ---- - -## 20. Notification refus avec raison détaillée - -**Étant donné** que mon contenu est rejeté - -**Alors** je reçois un email "❌ Contenu '[Titre]' refusé" -**Et** je reçois une notification push -**Et** je vois la raison exacte: "Qualité audio insuffisante" -**Et** je vois un lien vers les règles de publication - ---- - -## 21. Possibilité de correction et resoumission - -**Étant donné** que mon contenu a été rejeté pour "Tags non pertinents" - -**Quand** je corrige les tags -**Et** que je resoumets le contenu - -**Alors** le contenu repasse en file de validation -**Et** une nouvelle validation est effectuée - ---- - -## 22. Après 3 validations - Statut vérifié obtenu - -**Étant donné** que mes 3 premiers contenus ont été validés - -**Alors** j'obtiens le statut "Créateur Vérifié" -**Et** je reçois une notification "🎉 Vous êtes maintenant créateur vérifié !" -**Et** un badge ✓ apparaît sur mon profil - ---- - -## 23. Badge vérifié visible publiquement - -**Étant donné** que j'ai le statut vérifié - -**Quand** un utilisateur consulte mon profil - -**Alors** il voit le badge ✓ à côté de mon pseudo -**Et** une mention "Créateur vérifié" - ---- - -## 24. Contenus futurs publiés immédiatement - -**Étant donné** que je suis créateur vérifié - -**Quand** je publie un 4ème contenu - -**Alors** le contenu est publié immédiatement -**Et** il n'y a pas de validation préalable -**Et** je vois "✅ Contenu publié" - ---- - -## 25. Modération a posteriori uniquement - -**Étant donné** que je suis créateur vérifié -**Et** que je publie un contenu - -**Quand** le contenu est en ligne - -**Alors** il peut être signalé par les utilisateurs -**Et** sera modéré uniquement si signalé - ---- - -## 26. Interface modérateur - Queue de contenus - -**Étant donné** que je suis un modérateur junior - -**Quand** j'accède à l'interface de modération - -**Alors** je vois la file des contenus à valider -**Et** je vois le nombre total en attente -**Et** les contenus sont triés par ordre FIFO - ---- - -## 27. Interface modérateur - Écoute accélérée - -**Étant donné** que je suis un modérateur - -**Quand** j'écoute un contenu de 30 secondes - -**Alors** je peux choisir la vitesse 1.5x ou 2.0x -**Et** je termine l'écoute en 15 secondes à 2x -**Et** ma productivité est doublée - ---- - -## 28. Interface modérateur - Raccourcis clavier - -**Étant donné** que je modère un contenu - -**Quand** j'utilise les raccourcis clavier - -**Alors** je peux: - - | touche | action | - |---|---| - | A | Accepter | - | R | Rejeter | - | Espace | Play/Pause | - -**Et** la modération est accélérée - ---- - -## 29. Historique créateur visible - -**Étant donné** qu'un créateur soumet son 2ème contenu - -**Quand** le modérateur examine le contenu - -**Alors** il voit l'historique: - - | contenu | statut | - |---|---| - | Contenu 1 | Validé | - | Contenu 2 | En cours | - -**Et** il peut juger la cohérence du créateur - ---- - -## 30. Temps de modération estimé 1.5 min/créateur - -**Étant donné** qu'un créateur soumet 3 contenus - -**Quand** les modérateurs traitent ces contenus - -**Alors** le temps total est environ: - - | action | temps | - |---|---| - | Écoute 30s × 3 | 90s | - | Vérification metadata | 15s | - | Décision | 5s | - | Total | 110s | - - ---- - - - - - -
- - -# Élargissement automatique de zone quand aucun contenu n'est disponible -**9 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur connecté -> **Et** que la géolocalisation est activée -> **Et** que je suis en mode écoute -## 1. Aucun contenu dans rayon 50km - élargissement à 100km - -**Étant donné** que je suis situé à la position GPS 48.8566, 2.3522 -**Et** qu'aucun contenu n'existe dans un rayon de 50 km autour de ma position -**Mais** qu'au moins 1 contenu existe dans un rayon de 100 km - -**Quand** le système recherche du contenu à me proposer - -**Alors** le système élargit automatiquement la zone de recherche à 100 km -**Et** je reçois un message "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)" -**Et** un contenu dans le rayon de 100 km m'est proposé - ---- - -## 2. Aucun contenu dans rayon 100km - élargissement au département - -**Étant donné** que je suis situé dans le département "75" (Paris) -**Et** qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -**Mais** qu'au moins 1 contenu existe avec la zone "département" pour "75" - -**Quand** le système recherche du contenu à me proposer - -**Alors** le système élargit automatiquement la zone de recherche au département -**Et** je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre département" -**Et** un contenu départemental m'est proposé - ---- - -## 3. Aucun contenu départemental - élargissement à la région - -**Étant donné** que je suis situé dans la région "Île-de-France" -**Et** qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -**Et** qu'aucun contenu départemental n'existe pour mon département -**Mais** qu'au moins 1 contenu existe avec la zone "région" pour "Île-de-France" - -**Quand** le système recherche du contenu à me proposer - -**Alors** le système élargit automatiquement la zone de recherche à la région -**Et** je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre région" -**Et** un contenu régional m'est proposé - ---- - -## 4. Aucun contenu régional - basculement sur contenu national - -**Étant donné** que je suis situé en France -**Et** qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position -**Et** qu'aucun contenu départemental n'existe pour mon département -**Et** qu'aucun contenu régional n'existe pour ma région - -**Quand** le système recherche du contenu à me proposer - -**Alors** le système bascule automatiquement sur du contenu national -**Et** je reçois un message "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser" -**Et** un contenu national m'est proposé -**Et** je ne reste jamais sans contenu disponible - ---- - -## 5. Élargissement progressif avec plusieurs étapes - -**Étant donné** que je suis situé dans une zone rurale isolée -**Et** qu'aucun contenu n'existe dans un rayon de 50 km -**Et** qu'aucun contenu n'existe dans un rayon de 100 km -**Et** qu'aucun contenu départemental n'existe -**Et** qu'aucun contenu régional n'existe - -**Quand** le système recherche du contenu à me proposer - -**Alors** le système essaie d'abord 50 km -**Et** tout ce processus se fait de manière transparente et automatique -**Et** je reçois le message correspondant au dernier niveau trouvé - ---- - -## 6. Message personnalisé selon la distance trouvée - -**Étant donné** que je suis situé à la position GPS 43.6047, 1.4442 -**Et** que contenu(s) est/sont trouvé(s) - -**Quand** le système me propose du contenu - -**Alors** je reçois le message "" - ---- - -## 7. Le contenu national sert de filet de sécurité - -**Étant donné** que le système a épuisé toutes les zones géographiques locales - -**Quand** le système bascule sur du contenu national - -**Alors** je dois toujours avoir au moins 1 contenu disponible -**Et** ce contenu peut être: - - | type_contenu | - |---| - | Actualités Le Monde | - | Podcasts génériques | - | Contenu éducatif national | - | Contenu culturel national | - - ---- - -## 8. Pas d'écran d'erreur "Aucun contenu" - -**Étant donné** que je lance l'application -**Et** qu'aucun contenu local n'est disponible dans ma zone - -**Quand** le système recherche du contenu - -**Alors** je ne dois jamais voir un message d'erreur "Aucun contenu disponible" -**Et** je ne dois jamais voir un écran vide -**Et** un contenu doit toujours m'être proposé, même si c'est du contenu national - ---- - -## 9. Élargissement avec prise en compte des centres d'intérêt - -**Étant donné** que je suis situé dans une zone rurale -**Et** qu'aucun contenu n'existe dans un rayon de 50 km -**Et** que mes centres d'intérêt incluent "Automobile" à 80% et "Voyage" à 70% -**Et** qu'un contenu national existe avec le tag "Automobile" -**Et** qu'un contenu national existe avec le tag "Politique" - -**Quand** le système bascule sur du contenu national - -**Alors** le contenu national proposé prend en compte mes centres d'intérêt -**Et** le contenu "Automobile" a un score supérieur au contenu "Politique" - ---- - - - - - -
- - -# Gestion d'un contenu supprimé pendant l'écoute -**11 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur connecté -> **Et** que je suis en mode écoute -> **Et** qu'un contenu "C123" est en cours de lecture -## 1. Contenu supprimé pendant lecture - fin de lecture sans interruption - -**Étant donné** que j'écoute le contenu "C123" depuis 30 secondes -**Et** que la durée totale du contenu est de 120 secondes - -**Quand** le contenu est supprimé par la modération côté backend - -**Alors** la lecture du contenu continue sans interruption -**Et** je peux écouter le contenu jusqu'à la fin -**Et** aucune interruption brutale ne se produit - ---- - -## 2. Passage automatique après fin du contenu supprimé - -**Étant donné** que le contenu "C123" a été supprimé pendant ma lecture -**Et** que j'ai écouté le contenu jusqu'à la fin - -**Quand** le contenu se termine - -**Alors** le système attend 2 secondes -**Et** passe automatiquement au contenu suivant -**Et** je reçois une notification toast discrète "Contenu précédent retiré (violation règles)" - ---- - -## 3. Bouton Précédent désactivé après suppression - -**Étant donné** que le contenu "C123" a été supprimé pendant ma lecture -**Et** que je suis passé au contenu suivant "C456" - -**Quand** j'essaie d'appuyer sur le bouton "Précédent" - -**Alors** le bouton "Précédent" ne me ramène pas au contenu supprimé -**Et** je reçois un message "Ce contenu n'est plus disponible" -**Et** la lecture du contenu actuel "C456" continue - ---- - -## 4. Tentative de retour manuel au contenu supprimé - -**Étant donné** que je suis sur le contenu "C456" -**Et** que le contenu précédent "C123" a été supprimé - -**Quand** j'appuie sur le bouton "Précédent" pour revenir au contenu supprimé - -**Alors** je reçois un message "Ce contenu n'est plus disponible" -**Et** la lecture reste sur le contenu actuel "C456" -**Et** aucune action n'est effectuée - ---- - -## 5. Notification discrète pendant la conduite - -**Étant donné** que je conduis à une vitesse de 60 km/h -**Et** que le contenu "C123" est supprimé pendant ma lecture - -**Quand** le contenu se termine - -**Alors** la notification "Contenu précédent retiré (violation règles)" s'affiche en toast discret -**Et** la notification disparaît automatiquement après 5 secondes -**Et** aucune popup modale n'interrompt ma conduite -**Et** le contenu suivant démarre automatiquement après 2 secondes - ---- - -## 6. Message informatif mais non alarmiste - -**Étant donné** que le contenu "C123" a été supprimé -**Et** que je passe au contenu suivant - -**Quand** la notification s'affiche - -**Alors** le message doit être informatif: "Contenu précédent retiré (violation règles)" -**Et** le ton ne doit pas être alarmiste -**Et** le message doit être bref et compréhensible -**Et** aucun détail technique n'est affiché pendant la conduite - ---- - -## 7. Contenu supprimé retiré de l'historique - -**Étant donné** que le contenu "C123" a été supprimé - -**Quand** je consulte mon historique d'écoute - -**Alors** le contenu "C123" n'apparaît plus dans mon historique -**Et** je ne peux pas relancer la lecture de ce contenu -**Et** l'historique affiche "[Contenu retiré]" à la place du titre - ---- - -## 8. Contenu supprimé non accessible via lien direct - -**Étant donné** que le contenu "C123" a été supprimé -**Et** que j'ai un lien de partage "roadwave.fr/share/c/C123" - -**Quand** je clique sur le lien de partage - -**Alors** je reçois un message "Ce contenu a été retiré pour violation des règles de la communauté" -**Et** je suis redirigé vers l'accueil de l'application -**Et** aucune lecture n'est possible - ---- - -## 9. Plusieurs contenus supprimés dans l'historique récent - -**Étant donné** que j'ai écouté les contenus suivants: - - | id | statut | - |---|---| - | C123 | supprimé | - | C456 | actif | - | C789 | supprimé | - -**Et** que je suis actuellement sur le contenu "C456" - -**Quand** j'appuie plusieurs fois sur "Précédent" - -**Alors** je ne peux pas revenir aux contenus "C123" ou "C789" -**Et** le système saute automatiquement les contenus supprimés -**Et** je reviens au dernier contenu actif disponible avant "C456" - ---- - -## 10. Consultation détaillée du contenu supprimé à l'arrêt - -**Étant donné** que je suis à l'arrêt -**Et** que le contenu "C123" a été supprimé pendant ma session - -**Quand** j'ouvre les détails de la notification de suppression - -**Alors** je peux voir les informations suivantes: - - | information | - |---| - | Titre du contenu | - | Créateur | - | Raison de suppression | - | Date de suppression | - -**Et** je peux signaler une erreur de modération si je pense qu'elle est injustifiée - ---- - -## 11. Pas d'impact sur les jauges d'intérêt lors de la suppression - -**Étant donné** que j'ai écouté le contenu "C123" pendant 80 secondes (66%) -**Et** que mes jauges d'intérêt ont été mises à jour pendant l'écoute - -**Quand** le contenu est supprimé après mon écoute - -**Alors** les modifications de mes jauges d'intérêt sont conservées -**Et** l'écoute déjà effectuée reste comptabilisée -**Et** seules les futures écoutes de ce contenu sont bloquées - ---- - - - - - -
- - -# Mode dégradé sans géolocalisation -**19 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur connecté -> **Et** que j'ai refusé ou désactivé l'accès à la géolocalisation -## 1. Types de contenu disponibles sans géolocalisation - -**Étant donné** que la géolocalisation est désactivée - -**Quand** j'ouvre l'application - -**Alors** les types de contenu suivants sont disponibles: - - | type_contenu | disponible | - |---|---| - | Contenu national | oui | - | Contenu téléchargé (offline) | oui | - | Contenus "Neutre" géographiquement | oui | - | Contenu géolocalisé Ancré | non | - | Contenu géolocalisé Contextuel | non | - | Audio-guides | non | - | Notifications push géo-déclenchées | non | - - ---- - -## 2. Popup d'information au premier lancement sans GPS - -**Étant donné** que c'est mon premier lancement de l'application -**Et** que j'ai refusé l'accès à la géolocalisation - -**Quand** l'application détecte que le GPS est désactivé - -**Alors** une popup s'affiche avec le message: -**Et** la popup contient les boutons suivants: - - | bouton | action | - |---|---| - | Activer | Redirection vers paramètres OS | - | Continuer sans | Ferme popup et lance en mode dégradé | - -**Et** une checkbox "Ne plus me demander" est disponible - ---- - -## 3. Popup non affichée si case "Ne plus me demander" cochée - -**Étant donné** que j'ai déjà vu la popup de géolocalisation -**Et** que j'ai coché "Ne plus me demander" - -**Quand** je lance l'application avec le GPS désactivé - -**Alors** la popup de géolocalisation ne s'affiche pas -**Et** l'application démarre directement en mode dégradé -**Et** le banner permanent de rappel s'affiche - ---- - -## 4. Redirection vers paramètres OS lors du clic sur "Activer" - -**Étant donné** que la popup de géolocalisation est affichée - -**Quand** je clique sur "Activer" - -**Alors** je suis redirigé vers les paramètres de géolocalisation de mon OS -**Et** sur iOS, j'arrive dans "Réglages > RoadWave > Localisation" -**Et** sur Android, j'arrive dans "Paramètres > Applications > RoadWave > Autorisations > Position" - ---- - -## 5. Banner de rappel permanent sans GPS - -**Étant donné** que j'ai cliqué sur "Continuer sans" géolocalisation - -**Quand** l'application s'affiche - -**Alors** un bandeau s'affiche en haut de l'écran -**Et** le bandeau contient le texte: "Mode limité : géolocalisation désactivée. [Activer]" -**Et** le bandeau a un fond de couleur avertissement (jaune/orange) -**Et** le bandeau n'est pas intrusif mais reste visible -**Et** le bandeau reste affiché sur toutes les pages de l'application - ---- - -## 6. Clic sur le bouton "Activer" du banner - -**Étant donné** que le banner "Mode limité" est affiché - -**Quand** je clique sur le lien "[Activer]" dans le banner - -**Alors** je suis redirigé vers les paramètres de géolocalisation de mon OS - ---- - -## 7. Disparition du banner après activation GPS - -**Étant donné** que le banner "Mode limité" est affiché -**Et** que je reviens dans l'application après avoir activé le GPS dans les paramètres - -**Quand** l'application détecte que la géolocalisation est maintenant active - -**Alors** le banner disparaît automatiquement -**Et** l'application bascule en mode normal avec contenu géolocalisé -**Et** un toast de confirmation s'affiche: "Géolocalisation activée" - ---- - -## 8. Lecture de contenu national sans GPS - -**Étant donné** que la géolocalisation est désactivée -**Et** que du contenu national existe (actualités Le Monde, podcasts génériques) - -**Quand** je lance la lecture - -**Alors** je peux écouter le contenu national sans restriction -**Et** l'algorithme de recommandation se base uniquement sur: - - | critère | - |---| - | Mes centres d'intérêt | - | Mon historique d'écoute | - | Popularité générale | - -**Et** la proximité géographique n'est pas prise en compte - ---- - -## 9. Lecture de contenu téléchargé sans GPS - -**Étant donné** que la géolocalisation est désactivée -**Et** que j'ai téléchargé 30 contenus quand j'avais le GPS activé - -**Quand** j'accède à mes contenus téléchargés - -**Alors** je peux lire tous mes contenus téléchargés normalement -**Et** les contenus géolocalisés téléchargés restent accessibles -**Et** le filtre géographique n'est pas appliqué pour les contenus offline - ---- - -## 10. Contenu "Neutre" géographiquement disponible - -**Étant donné** que la géolocalisation est désactivée -**Et** qu'un créateur a publié du contenu avec la classification géographique "Neutre" - -**Quand** je recherche du contenu - -**Alors** les contenus "Neutre" sont inclus dans les résultats -**Et** ils sont mélangés avec le contenu national -**Et** l'algorithme les priorise selon mes centres d'intérêt - ---- - -## 11. Audio-guides inaccessibles sans GPS - -**Étant donné** que la géolocalisation est désactivée - -**Quand** je recherche un audio-guide spécifique - -**Alors** les audio-guides apparaissent dans les résultats de recherche -**Mais** un badge "GPS requis" est affiché sur chaque audio-guide -**Et** quand je clique sur un audio-guide, un message s'affiche: -**Et** je peux choisir "Activer" ou "Annuler" - ---- - -## 12. Notifications push géo-déclenchées désactivées - -**Étant donné** que la géolocalisation est désactivée -**Et** que je suis abonné à un créateur qui diffuse du contenu géolocalisé - -**Quand** le créateur publie un nouveau contenu géolocalisé - -**Alors** je ne reçois pas de notification push géo-déclenchée -**Mais** je reçois une notification push standard (non géo-déclenchée) si le créateur publie du contenu national -**Et** la notification précise: "Nouveau contenu national de [Créateur]" - ---- - -## 13. Contenu géolocalisé non proposé dans le feed - -**Étant donné** que la géolocalisation est désactivée - -**Quand** le système génère mon feed de contenu - -**Alors** aucun contenu "Ancré" ou "Contextuel" n'est inclus -**Et** seuls les contenus "Neutre" et "National" sont proposés -**Et** mon feed contient au minimum 20 contenus disponibles - ---- - -## 14. Application fonctionnelle sans GPS (pas de blocage) - -**Étant donné** que la géolocalisation est désactivée - -**Quand** j'utilise l'application - -**Alors** je ne suis jamais bloqué par un écran "GPS requis" -**Et** toutes les fonctionnalités non-géolocalisées restent accessibles: - - | fonctionnalité | - |---| - | Écoute contenu national | - | Gestion profil | - | Abonnements créateurs | - | Recherche textuelle | - | Historique d'écoute | - | Paramètres | - | Mode offline | - -**Et** je peux créer et publier du contenu national - ---- - -## 15. Respect du choix utilisateur de ne pas activer GPS - -**Étant donné** que j'ai coché "Ne plus me demander" pour la géolocalisation - -**Quand** j'utilise l'application pendant plusieurs semaines - -**Alors** la popup de demande GPS ne s'affiche plus jamais automatiquement -**Et** seul le banner permanent reste affiché -**Et** l'application ne force jamais l'activation du GPS - ---- - -## 16. Bascule automatique en mode normal après activation GPS - -**Étant donné** que j'utilise l'application en mode dégradé depuis 1 semaine -**Et** que je décide d'activer la géolocalisation - -**Quand** l'application détecte que le GPS est maintenant actif - -**Alors** le mode dégradé est désactivé automatiquement -**Et** le banner "Mode limité" disparaît -**Et** le contenu géolocalisé devient disponible immédiatement -**Et** mon feed se rafraîchit avec du contenu local pertinent -**Et** un toast de confirmation s'affiche: "Géolocalisation activée - Contenu local disponible" - ---- - -## 17. Demande de permission GPS lors de l'utilisation d'une fonctionnalité géo - -**Étant donné** que la géolocalisation est désactivée - -**Quand** j'essaie d'accéder à une fonctionnalité nécessitant le GPS (ex: audio-guide) - -**Alors** une popup contextuelle s'affiche: -**Et** je peux accepter ou refuser -**Et** si j'accepte, je suis redirigé vers les paramètres OS -**Et** si je refuse, je reste en mode dégradé sans message d'erreur répétitif - ---- - -## 18. Statistiques de contenu local disponible non affiché - -**Étant donné** que la géolocalisation est désactivée - -**Quand** je navigue dans l'application - -**Alors** le banner peut afficher occasionnellement: -**Et** ce message incitatif change tous les 3 jours -**Et** il reste non intrusif (pas de popup, juste le banner) - ---- - -## 19. Onboarding différent pour utilisateurs sans GPS - -**Étant donné** que c'est ma première utilisation de RoadWave -**Et** que j'ai refusé la géolocalisation - -**Quand** l'onboarding se termine - -**Alors** un écran explicatif s'affiche: -**Et** je peux continuer avec un bouton "Compris" - ---- - - - - - -
- - -# Gestion de la perte de réseau et buffering adaptatif -**17 scénarios** (16 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur connecté -> **Et** que je suis en mode écoute -> **Et** qu'un contenu est en cours de lecture -## 1. 📋 Plan: Paramètres de buffer selon le type de réseau - -**Étant donné** que je suis connecté en "" - -**Quand** le système initialise le buffer audio - -**Alors** le buffer minimum est de secondes -**Et** le buffer cible est de secondes -**Et** le buffer maximum est de secondes - -**📊 Exemples de données:** - -| type_reseau | buffer_min | buffer_cible | buffer_max | -|---|---|---|---| -| WiFi | 5 | 30 | 120 | -| 4G | 10 | 45 | 120 | -| 5G | 10 | 45 | 120 | -| 3G | 30 | 90 | 300 | - ---- - -## 2. Connexion instable avec latence élevée - aucun message immédiat - -**Étant donné** que je suis connecté en 4G -**Et** que le buffer contient 45 secondes de contenu - -**Quand** la latence réseau dépasse 500ms - -**Alors** aucun message n'est affiché immédiatement -**Et** la lecture continue normalement sur le buffer -**Et** le système tente de continuer le téléchargement en arrière-plan - ---- - -## 3. Connexion instable pendant plus de 10 secondes - toast discret - -**Étant donné** que je suis connecté en 4G -**Et** que la latence réseau dépasse 500ms depuis 10 secondes - -**Quand** le système détecte la latence prolongée - -**Alors** un toast discret s'affiche: "Connexion instable" -**Et** le toast disparaît automatiquement après 3 secondes -**Et** la lecture continue normalement - ---- - -## 4. Perte totale de réseau - lecture sur buffer - -**Étant donné** que je suis connecté en WiFi -**Et** que le buffer contient 30 secondes de contenu - -**Quand** je perds totalement la connexion réseau - -**Alors** la lecture continue sur le buffer disponible -**Et** un toast s'affiche: "Hors ligne, lecture sur buffer (30s restantes)" -**Et** un compte à rebours du temps de buffer restant est visible - ---- - -## 5. Buffer qui s'épuise pendant la perte réseau - -**Étant donné** que je suis hors ligne -**Et** que le buffer contient 30 secondes de contenu - -**Quand** le contenu continue de jouer - -**Alors** le compte à rebours diminue en temps réel -**Et** le toast affiche "Hors ligne, lecture sur buffer (15s restantes)" après 15 secondes -**Et** le toast affiche "Hors ligne, lecture sur buffer (5s restantes)" après 25 secondes - ---- - -## 6. Pause automatique après épuisement du buffer - -**Étant donné** que je suis hors ligne depuis 30 secondes -**Et** que le buffer est complètement épuisé - -**Quand** il n'y a plus de contenu audio à lire - -**Alors** la lecture se met en pause automatiquement -**Et** un overlay s'affiche: "Connexion perdue. Reconnexion en cours..." -**Et** le système tente de se reconnecter automatiquement - ---- - -## 7. Tentatives de reconnexion automatique - -**Étant donné** que la lecture est en pause suite à l'épuisement du buffer - -**Quand** le système tente de se reconnecter - -**Alors** une tentative de reconnexion est effectuée toutes les 5 secondes -**Et** un maximum de 6 tentatives sont effectuées (30 secondes au total) -**Et** l'overlay affiche "Tentative de reconnexion... (X/6)" - ---- - -## 8. Proposition du mode offline après 30 secondes d'échec - -**Étant donné** que 6 tentatives de reconnexion ont échoué -**Et** que cela fait 30 secondes que je suis déconnecté - -**Quand** la 6ème tentative échoue - -**Alors** une popup s'affiche: "Voulez-vous continuer avec vos contenus téléchargés ?" -**Et** la popup contient deux boutons: - - | bouton | action | - |---|---| - | Réessayer | Nouvelle série de 6 tentatives | - | Mode offline | Bascule sur contenus téléchargés | - - ---- - -## 9. Basculement réussi vers le mode offline - -**Étant donné** que la popup de mode offline est affichée -**Et** que j'ai téléchargé 20 contenus dans ma zone géographique - -**Quand** je clique sur "Mode offline" - -**Alors** le système bascule sur les contenus téléchargés -**Et** un nouveau contenu téléchargé démarre automatiquement -**Et** un bandeau permanent indique "Mode hors ligne - Contenus téléchargés" - ---- - -## 10. Aucun contenu téléchargé disponible - -**Étant donné** que la popup de mode offline est affichée -**Et** que je n'ai aucun contenu téléchargé - -**Quand** je clique sur "Mode offline" - -**Alors** un message s'affiche: "Aucun contenu téléchargé disponible" -**Et** je suis invité à me connecter en WiFi pour télécharger du contenu -**Et** le bouton "Réessayer" reste la seule option - ---- - -## 11. Reprise automatique après reconnexion - -**Étant donné** que la lecture est en pause depuis 15 secondes -**Et** que j'étais à 02:35 du contenu en cours - -**Quand** la connexion réseau est rétablie - -**Alors** la lecture reprend automatiquement au point d'arrêt exact (02:35) -**Et** un toast s'affiche: "Connexion rétablie" -**Et** le toast disparaît après 3 secondes -**Et** le buffer se remplit progressivement selon le type de réseau - ---- - -## 12. Reconnexion avec changement de type de réseau - -**Étant donné** que j'étais connecté en WiFi -**Et** que j'ai perdu la connexion - -**Quand** je me reconnecte en 4G - -**Alors** le système ajuste automatiquement les paramètres de buffer -**Et** le buffer minimum passe de 5s à 10s -**Et** le buffer cible passe de 30s à 45s -**Et** la lecture reprend normalement - ---- - -## 13. Passage dans un tunnel avec perte de signal - -**Étant donné** que je conduis à 90 km/h sur autoroute -**Et** que je suis connecté en 4G avec un buffer de 45 secondes - -**Quand** j'entre dans un tunnel et perds le signal - -**Alors** la lecture continue sur le buffer pendant 45 secondes maximum -**Et** aucune notification n'est affichée pendant les 10 premières secondes -**Et** un toast discret s'affiche après 10 secondes: "Connexion instable" - ---- - -## 14. Sortie du tunnel avant épuisement du buffer - -**Étant donné** que je suis dans un tunnel depuis 30 secondes -**Et** qu'il reste 15 secondes de buffer - -**Quand** je sors du tunnel et récupère le signal 4G - -**Alors** la lecture continue sans interruption -**Et** le buffer se remplit à nouveau -**Et** un toast s'affiche: "Connexion rétablie" - ---- - -## 15. Changement de cellule 4G pendant la lecture - -**Étant donné** que je conduis et change de cellule mobile toutes les 5-10 minutes -**Et** que le buffer contient 45 secondes de contenu - -**Quand** un handoff de cellule se produit - -**Alors** la lecture continue sans interruption grâce au buffer -**Et** la connexion à la nouvelle cellule se fait de manière transparente -**Et** aucune notification n'est affichée si le handoff réussit en moins de 5 secondes - ---- - -## 16. Téléchargement préventif en WiFi avant trajet - -**Étant donné** que je suis connecté en WiFi -**Et** que j'ai activé le téléchargement automatique - -**Quand** le système détecte que je suis à l'arrêt en WiFi - -**Alors** le système me propose de télécharger du contenu pour mon trajet -**Et** je peux sélectionner une zone géographique à télécharger -**Et** le téléchargement se fait en arrière-plan - ---- - -## 17. Tracking des événements de perte réseau pour amélioration - -**Étant donné** que je perds la connexion réseau - -**Quand** l'événement de perte est détecté - -**Alors** le système enregistre les métriques suivantes: - - | métrique | - |---| - | Type de réseau avant perte | - | Durée de la coupure | - | Buffer disponible | - | Position GPS approximative | - | Heure de la journée | - -**Et** ces métriques sont anonymisées et envoyées en batch lors de la prochaine connexion WiFi -**Et** les données servent à améliorer les paramètres de buffer - ---- - - - - - -
- - -# Tests BDD - Documentation des fonctionnalités - -Cette documentation est générée automatiquement à partir des fichiers Gherkin (`.feature`). - -## Vue d'ensemble - -| Métrique | Valeur | -|----------|--------| -| Fonctionnalités | **83** | -| Scénarios | **2112** | -| Domaines métier | **18** | - ---- - -## 🔔 Abonnements - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Audio-guides multi-séquences pour piétons](#abonnements/audio-guides-pieton) | 29 | -| [Impact des abonnements sur l'algorithme](#abonnements/impact-algorithme) | 16 | -| [Limites d'abonnements et désabonnement](#abonnements/limites-desabonnement) | 27 | -| [Notifications contextuelles selon le mode de déplacement](#abonnements/notifications-contextuelles) | 28 | - -*4 fonctionnalités • 100 scénarios* - -## 🎧 Audio Guides - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Audio-guide mode piéton (navigation manuelle)](#audio-guides/mode-pieton) | 29 | -| [Audio-guide mode voiture (GPS automatique)](#audio-guides/mode-voiture) | 45 | -| [Audio-guides Premium et monétisation](#audio-guides/premium-monetisation) | 31 | -| [Audio-guides modes vélo et transport](#audio-guides/modes-velo-transport) | 27 | -| [Création d'audio-guide multi-séquences](#audio-guides/creation-audio-guide) | 35 | -| [Intégration audio-guides avec autres fonctionnalités](#audio-guides/integration-fonctionnalites) | 39 | -| [Sauvegarde et reprise de progression audio-guide](#audio-guides/progression-sauvegarde) | 32 | - -*7 fonctionnalités • 238 scénarios* - -## 🔐 Authentication - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Authentification à deux facteurs (2FA)](#authentication/two-factor-authentication) | 16 | -| [Classification des contenus par âge](#authentication/classification-age) | 13 | -| [Connexion utilisateur](#authentication/connexion) | 11 | -| [Gestion des sessions et tokens](#authentication/sessions-tokens) | 13 | -| [Inscription utilisateur](#authentication/inscription) | 15 | -| [Récupération de compte](#authentication/recuperation-compte) | 14 | -| [Vérification d'email](#authentication/verification-email) | 10 | - -*7 fonctionnalités • 92 scénarios* - -## 🎨 Content Creation - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Modification et suppression de contenu](#content-creation/modification-suppression) | 30 | -| [Métadonnées et publication de contenu](#content-creation/metadonnees-publication) | 34 | -| [Upload et encodage de contenu audio](#content-creation/upload-encodage) | 29 | -| [Validation des 3 premiers contenus](#content-creation/validation-premiers-contenus) | 30 | - -*4 fonctionnalités • 123 scénarios* - -## ⚠️ Error Handling - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Gestion d'un contenu supprimé pendant l'écoute](#error-handling/contenu-supprime-pendant-ecoute) | 11 | -| [Gestion de la perte de réseau et buffering adaptatif](#error-handling/perte-reseau) | 17 | -| [Mode dégradé sans géolocalisation](#error-handling/geolocalisation-desactivee) | 19 | -| [Élargissement automatique de zone quand aucun contenu n'est disponible](#error-handling/aucun-contenu-disponible) | 9 | - -*4 fonctionnalités • 56 scénarios* - -## 📊 Interest Gauges - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Jauge initiale et cold start](#interest-gauges/jauge-initiale) | 15 | -| [Pas de dégradation temporelle des jauges](#interest-gauges/degradation-temporelle) | 16 | -| [Évolution des jauges d'intérêt](#interest-gauges/evolution-jauges) | 21 | - -*3 fonctionnalités • 52 scénarios* - -## 📴 Mode Offline - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Synchronisation actions offline](#mode-offline/synchronisation-actions) | 45 | -| [Téléchargement de contenus offline](#mode-offline/telechargement) | 49 | -| [Validité et renouvellement contenus offline](#mode-offline/validite-renouvellement) | 38 | - -*3 fonctionnalités • 132 scénarios* - -## 🛡️ Moderation - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Modération préventive](#moderation/moderation-preventive) | 22 | -| [Sanctions et notifications de modération](#moderation/sanctions-notifications) | 27 | -| [Signalement de contenu inapproprié](#moderation/signalement) | 23 | -| [Traitement des signalements par l'IA et les modérateurs](#moderation/traitement-signalements) | 25 | - -*4 fonctionnalités • 97 scénarios* - -## 💰 Monetisation - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Conditions d'activation de la monétisation](#monetisation/conditions-activation) | 28 | -| [Contenus Premium exclusifs](#monetisation/contenus-premium-exclusifs) | 34 | -| [Désactivation et suspension monétisation](#monetisation/desactivation-suspension) | 35 | -| [KYC et inscription à la monétisation](#monetisation/kyc-inscription) | 37 | -| [Obligations fiscales](#monetisation/obligations-fiscales) | 30 | -| [Paiement des créateurs](#monetisation/paiement-createurs) | 35 | -| [Sources de revenus créateurs](#monetisation/sources-revenus) | 34 | - -*7 fonctionnalités • 233 scénarios* - -## 🧭 Navigation - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Actions complémentaires à l'arrêt](#navigation/actions-complementaires) | 23 | -| [Commande "Précédent"](#navigation/commande-precedent) | 19 | -| [Commandes au volant et interactions simplifiées](#navigation/commandes-volant) | 21 | -| [Commandes vocales CarPlay et Android Auto](#navigation/commandes-vocales) | 25 | -| [File d'attente et commande "Suivant"](#navigation/file-attente-suivant) | 20 | -| [Lecture en boucle et enchaînement automatique](#navigation/lecture-enchainement) | 27 | - -*6 fonctionnalités • 135 scénarios* - -## 🔗 Partage - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Partage de contenu](#partage/partage-contenu) | 22 | - -*1 fonctionnalités • 22 scénarios* - -## ⭐ Premium - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Avantages Premium](#premium/avantages-premium) | 37 | -| [Gestion abonnement Premium](#premium/gestion-abonnement) | 41 | -| [Multi-devices et détection simultanée](#premium/multi-devices-detection) | 30 | -| [Offre et tarification Premium](#premium/offre-tarification) | 31 | - -*4 fonctionnalités • 139 scénarios* - -## 👤 Profil - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Profil créateur](#profil/profil-createur) | 31 | - -*1 fonctionnalités • 31 scénarios* - -## 📢 Publicites - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Caractéristiques et facturation des publicités](#publicites/caracteristiques-pub) | 32 | -| [Création de campagnes publicitaires](#publicites/campagnes-publicitaires) | 30 | -| [Gestion du budget et alertes publicitaires](#publicites/gestion-budget-pub) | 30 | -| [Insertion et fréquence des publicités](#publicites/insertion-frequence-pub) | 31 | -| [Métriques d'engagement et dashboard publicitaire](#publicites/metriques-engagement-pub) | 27 | -| [Validation et modération des publicités](#publicites/validation-moderation-pub) | 29 | - -*6 fonctionnalités • 179 scénarios* - -## 📻 Radio Live - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Architecture technique radio live](#radio-live/architecture-technique-live) | 24 | -| [Arrêt du live](#radio-live/arret-live) | 19 | -| [Comportement auditeur pendant un live](#radio-live/comportement-auditeur) | 27 | -| [Démarrage d'un live](#radio-live/demarrage-live) | 20 | - -*4 fonctionnalités • 90 scénarios* - -## 🔍 Recherche - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Recherche de contenu](#recherche/recherche) | 55 | - -*1 fonctionnalités • 55 scénarios* - -## 🎯 Recommendation - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Classification de géo-pertinence des contenus](#recommendation/classification-geo) | 10 | -| [Contenus géolocalisés en mode voiture](#recommendation/declenchement-geo) | 36 | -| [Formule de scoring et recommandation](#recommendation/scoring-recommandation) | 21 | -| [Gestion de l'historique et reproposition](#recommendation/historique-reproposition) | 19 | -| [Gestion du contenu politique (MVP simplifié)](#recommendation/contenu-politique) | 13 | -| [Mode Kids pour utilisateurs 13-15 ans](#recommendation/mode-kids) | 15 | -| [Médias traditionnels sur RoadWave](#recommendation/medias-traditionnels) | 21 | -| [Paramétrabilité admin et A/B testing](#recommendation/parametrabilite-admin) | 20 | -| [Paramétrabilité utilisateur et profils](#recommendation/parametrabilite-utilisateur) | 25 | - -*9 fonctionnalités • 180 scénarios* - -## 🔒 Rgpd Compliance - -| Fonctionnalité | Scénarios | -|----------------|:---------:| -| [Anonymisation des données GPS après 24h](#rgpd-compliance/anonymisation-gps) | 18 | -| [Conformité administrative RGPD (Registre, Breach, DPO)](#rgpd-compliance/compliance-administrative) | 22 | -| [Cookies et analytics avec Matomo self-hosted](#rgpd-compliance/cookies-analytics) | 20 | -| [Durée de conservation des données et purge automatique](#rgpd-compliance/conservation-donnees) | 19 | -| [Gestion du consentement RGPD](#rgpd-compliance/consentement) | 16 | -| [Mode dégradé avec GeoIP (sans GPS précis)](#rgpd-compliance/mode-degrade-geoip) | 20 | -| [Portabilité des données (Article 20 RGPD)](#rgpd-compliance/portabilite-donnees) | 22 | -| [Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)](#rgpd-compliance/suppression-compte) | 21 | - -*8 fonctionnalités • 158 scénarios* - - - - - -
- - -# Pas de dégradation temporelle des jauges -> *En tant que système de recommandation* -> *Je veux que les jauges n'évoluent que par les actions utilisateur* -> *Afin d'avoir un comportement prévisible et fiable* - -**16 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -## 1. Aucune dégradation automatique avec le temps - -**Étant donné** que ma jauge "Économie" est à 80% -**Et** que je n'écoute aucun contenu pendant 30 jours - -**Quand** je me reconnecte après 30 jours - -**Alors** ma jauge "Économie" est toujours à 80% -**Et** aucune dégradation temporelle n'a été appliquée - ---- - -## 2. Jauges conservées après 6 mois d'inactivité - -**Étant donné** que mes jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 75% | - | Voyage | 60% | - | Musique | 45% | - -**Et** que je pars en vacances pendant 6 mois sans utiliser l'app - -**Quand** je me reconnecte après 6 mois - -**Alors** mes jauges sont exactement les mêmes: - - | catégorie | niveau | - |---|---| - | Automobile | 75% | - | Voyage | 60% | - | Musique | 45% | - - ---- - -## 3. Évolution naturelle par les actions - -**Étant donné** que j'aimais "Économie" il y a 1 an (jauge 80%) -**Et** que depuis, je skip tous les contenus "Économie" -**Et** que j'ai skippé 50 contenus "Économie" en 1 an - -**Alors** ma jauge "Économie" descend naturellement via les skips -**Et** atteint environ 55% (80% - 50 × 0.5% = 55%) -**Et** la dégradation vient des actions, pas du temps - ---- - -## 4. Pas de cron job de dégradation - -**Étant donné** que le système vérifie les jauges quotidiennement - -**Quand** un utilisateur n'a pas d'activité depuis 90 jours - -**Alors** aucun job de dégradation n'est exécuté -**Et** les jauges restent inchangées -**Et** aucune ressource CPU n'est consommée pour la dégradation - ---- - -## 5. Comportement prévisible après absence - -**Étant donné** que ma jauge "Sport" était à 70% -**Et** que je n'utilise pas l'app pendant 1 an - -**Quand** je reviens et demande des recommandations - -**Alors** mes recommandations reflètent toujours mes goûts d'avant -**Et** je reçois du contenu "Sport" prioritaire -**Et** le comportement est cohérent et prévisible - ---- - -## 6. Réinitialiser manuellement mes centres d'intérêt - -**Étant donné** que je veux repartir de zéro - -**Quand** je vais dans les paramètres -**Et** que je clique sur "Réinitialiser mes centres d'intérêt" -**Et** que je confirme l'action - -**Alors** toutes mes jauges reviennent à 50% -**Et** je vois le message "Vos centres d'intérêt ont été réinitialisés" - ---- - -## 7. Confirmation avant réinitialisation - -**Étant donné** que je suis dans les paramètres - -**Quand** je clique sur "Réinitialiser mes centres d'intérêt" - -**Alors** je vois un message de confirmation: - - | titre | Êtes-vous sûr ? | - |---|---| - | message | Cette action remettra toutes vos jauges à 50% | - | actions | Confirmer / Annuler | - - ---- - -## 8. Annuler la réinitialisation - -**Étant donné** que j'ai cliqué sur "Réinitialiser mes centres d'intérêt" -**Et** que la confirmation est affichée - -**Quand** je clique sur "Annuler" - -**Alors** mes jauges ne sont pas modifiées -**Et** je reviens aux paramètres - ---- - -## 9. Raison de réinitialisation - changement de vie - -**Étant donné** que j'utilisais RoadWave pour mes trajets professionnels -**Et** que mes jauges reflétaient "Économie" (85%) et "Technologie" (75%) -**Et** que je change de vie et deviens musicien - -**Quand** je réinitialise mes centres d'intérêt - -**Alors** je peux repartir avec toutes les jauges à 50% -**Et** découvrir du contenu "Musique" et "Culture" sans biais - ---- - -## 10. Pas de suggestion automatique de réinitialisation - -**Étant donné** que je n'ai pas utilisé l'app depuis 1 an - -**Quand** je me reconnecte - -**Alors** aucune suggestion de réinitialisation n'est affichée -**Et** mes jauges sont conservées telles quelles -**Et** je garde le contrôle total - ---- - -## 11. Historique conservé après réinitialisation - -**Étant donné** que j'ai écouté 500 contenus - -**Quand** je réinitialise mes centres d'intérêt - -**Alors** mes jauges reviennent à 50% -**Mais** mon historique d'écoute est conservé -**Et** je peux toujours consulter mes anciens contenus écoutés - ---- - -## 12. Évolution future basée sur nouvelles actions - -**Étant donné** que j'ai réinitialisé mes jauges à 50% - -**Quand** j'écoute 5 contenus "Voyage" à >80% - -**Alors** ma jauge "Voyage" monte à 60% (50% + 5 × 2%) -**Et** l'algorithme recommence à apprendre mes nouvelles préférences - ---- - -## 13. Respect de l'historique utilisateur - -**Étant donné** qu'un utilisateur aime "Cryptomonnaie" depuis 2 ans -**Et** que sa jauge est à 90% - -**Quand** 2 ans s'écoulent sans dégradation temporelle - -**Alors** sa jauge reste à 90% -**Et** le système ne fait pas d'"oubli" artificiel - ---- - -## 14. Coût infrastructure zéro - -**Étant donné** qu'aucune dégradation temporelle n'existe - -**Quand** le système calcule les jauges - -**Alors** aucun calcul de date n'est nécessaire -**Et** aucun batch nocturne ne tourne -**Et** aucun bug de fuseau horaire ne peut survenir -**Et** le coût CPU est minimal - ---- - -## 15. UX prévisible - jauge = actions - -**Étant donné** qu'un utilisateur consulte sa jauge "Sport" à 65% - -**Quand** il se demande pourquoi elle est à 65% - -**Alors** il peut retracer ses actions: - - | action | impact | - |---|---| - | 10 likes automatiques | +10% | - | 3 abonnements Sport | +15% | - | 5 skips de contenu non-Sport | 0% | - -**Et** il comprend que c'est le reflet exact de ses actions -**Et** il n'y a pas de mystère ou automatisme caché - ---- - -## 16. Statistiques affichées sans date - -**Étant donné** que je consulte mes centres d'intérêt - -**Quand** je vois mes jauges - -**Alors** je vois: - - | information | affiché | - |---|---| - | Niveau actuel | ✅ 75% | - | Évolution depuis début | ✅ +25% | - | Dernière mise à jour | ❌ | - -**Et** aucune date n'est affichée car non pertinente -**Et** seules les actions comptent - ---- - - - - - -
- - -# Évolution des jauges d'intérêt -> *En tant que système de recommandation* -> *Je veux faire évoluer les jauges d'intérêt selon les actions utilisateur* -> *Afin d'affiner les recommandations personnalisées* - -**21 scénarios** (20 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -## 1. Like automatique renforcé après écoute ≥80% - -**Étant donné** qu'un contenu de 5 minutes est tagué "Automobile" -**Et** que ma jauge "Automobile" est à 45% - -**Quand** j'écoute le contenu pendant 4 minutes 30 secondes (90%) - -**Alors** je reçois un like automatique renforcé -**Et** ma jauge "Automobile" augmente de 2% -**Et** ma jauge "Automobile" est maintenant à 47% - ---- - -## 2. Like automatique renforcé exactement à 80% - -**Étant donné** qu'un contenu de 10 minutes est tagué "Voyage" -**Et** que ma jauge "Voyage" est à 60% - -**Quand** j'écoute le contenu pendant exactement 8 minutes (80%) - -**Alors** je reçois un like automatique renforcé -**Et** ma jauge "Voyage" augmente de 2% -**Et** ma jauge "Voyage" est maintenant à 62% - ---- - -## 3. Like automatique standard après écoute 30-79% - -**Étant donné** qu'un contenu de 5 minutes est tagué "Automobile" -**Et** que ma jauge "Automobile" est à 45% - -**Quand** j'écoute le contenu pendant 2 minutes 30 secondes (50%) - -**Alors** je reçois un like automatique standard -**Et** ma jauge "Automobile" augmente de 1% -**Et** ma jauge "Automobile" est maintenant à 46% - ---- - -## 4. Like automatique standard à 30% exactement - -**Étant donné** qu'un contenu de 10 minutes est tagué "Musique" -**Et** que ma jauge "Musique" est à 40% - -**Quand** j'écoute le contenu pendant exactement 3 minutes (30%) - -**Alors** je reçois un like automatique standard -**Et** ma jauge "Musique" augmente de 1% - ---- - -## 5. Like automatique standard à 79% - -**Étant donné** qu'un contenu de 10 minutes est tagué "Sport" -**Et** que ma jauge "Sport" est à 55% - -**Quand** j'écoute le contenu pendant 7 minutes 54 secondes (79%) - -**Alors** je reçois un like automatique standard -**Et** ma jauge "Sport" augmente de 1% -**Et** ma jauge "Sport" est maintenant à 56% - ---- - -## 6. Like explicite (manuel) +2% - -**Étant donné** qu'un contenu est tagué "Économie" -**Et** que ma jauge "Économie" est à 70% - -**Quand** j'écoute le contenu partiellement -**Et** que je clique manuellement sur le bouton "Like" - -**Alors** ma jauge "Économie" augmente de 2% -**Et** ma jauge "Économie" est maintenant à 72% - ---- - -## 7. Like manuel cumulable avec like automatique - -**Étant donné** qu'un contenu de 5 minutes est tagué "Automobile" -**Et** que ma jauge "Automobile" est à 45% - -**Quand** j'écoute le contenu pendant 2 minutes 30 secondes (50%) - -**Alors** je reçois un like automatique standard (+1%) - -**Quand** je clique ensuite sur le bouton "Like" - -**Alors** ma jauge augmente encore de 2% (like manuel) -**Et** ma jauge "Automobile" a augmenté de 3% au total -**Et** ma jauge "Automobile" est maintenant à 48% - ---- - -## 8. Abonnement créateur impacte tous ses tags - -**Étant donné** qu'un créateur publie des contenus tagués "Automobile" et "Technologie" -**Et** que mes jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 50% | - | Technologie | 45% | - - -**Quand** je m'abonne à ce créateur - -**Alors** ma jauge "Automobile" augmente de 5% -**Et** ma jauge "Technologie" augmente de 5% -**Et** mes nouvelles jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 55% | - | Technologie | 50% | - - ---- - -## 9. Skip rapide (<10s) diminue la jauge - -**Étant donné** qu'un contenu est tagué "Économie" -**Et** que ma jauge "Économie" est à 45% - -**Quand** je skip le contenu après 5 secondes - -**Alors** ma jauge "Économie" diminue de 0.5% -**Et** ma jauge "Économie" est maintenant à 44.5% - ---- - -## 10. Skip à exactement 10s ne diminue pas la jauge - -**Étant donné** qu'un contenu est tagué "Politique" -**Et** que ma jauge "Politique" est à 50% - -**Quand** je skip le contenu après exactement 10 secondes - -**Alors** ma jauge "Politique" ne change pas -**Et** reste à 50% - ---- - -## 11. Skip tardif (≥30%) est neutre - -**Étant donné** qu'un contenu de 10 minutes est tagué "Musique" -**Et** que ma jauge "Musique" est à 60% - -**Quand** j'écoute pendant 3 minutes (30%) -**Et** que je skip ensuite - -**Alors** ma jauge "Musique" ne diminue pas (signal neutre) -**Et** ma jauge reste à 60% (plus le +1% de like auto si applicable) - ---- - -## 12. Contenu avec plusieurs tags impacte toutes les jauges - -**Étant donné** qu'un contenu est tagué "Automobile" et "Voyage" -**Et** que mes jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 45% | - | Voyage | 60% | - - -**Quand** j'écoute le contenu à 90% - -**Alors** les deux jauges augmentent de 2% -**Et** mes nouvelles jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 47% | - | Voyage | 62% | - - ---- - -## 13. Contenu avec 3 tags impacte les 3 jauges - -**Étant donné** qu'un contenu est tagué "Sport", "Santé" et "Technologie" -**Et** que mes jauges sont à 50% pour chaque catégorie - -**Quand** je skip rapidement après 5 secondes - -**Alors** les 3 jauges diminuent de 0.5% -**Et** toutes passent à 49.5% - ---- - -## 14. Jauges bornées - ne peut pas dépasser 100% - -**Étant donné** que ma jauge "Cryptomonnaie" est à 99% -**Et** qu'un contenu tagué "Cryptomonnaie" est disponible - -**Quand** j'écoute le contenu à 95% (like auto renforcé +2%) - -**Alors** ma jauge "Cryptomonnaie" passe à 100% (maximum) -**Et** ne dépasse pas 100% - ---- - -## 15. Jauges bornées - ne peut pas descendre sous 0% - -**Étant donné** que ma jauge "Politique" est à 0.3% -**Et** qu'un contenu tagué "Politique" est disponible - -**Quand** je skip rapidement après 3 secondes (-0.5%) - -**Alors** ma jauge "Politique" passe à 0% (minimum) -**Et** ne devient pas négative - ---- - -## 16. Calcul immédiat à chaque action - -**Étant donné** que ma jauge "Voyage" est à 50% - -**Quand** j'écoute un contenu "Voyage" à 85% - -**Alors** la jauge est mise à jour immédiatement (pas de batch) -**Et** passe à 52% - -**Quand** je demande mes recommandations dans la seconde suivante - -**Alors** l'algorithme utilise déjà la valeur 52% - ---- - -## 17. Like manuel après écoute <30% (pas de like auto) - -**Étant donné** qu'un contenu de 10 minutes est tagué "Culture" -**Et** que ma jauge "Culture" est à 60% - -**Quand** j'écoute pendant 2 minutes (20%) - -**Alors** je ne reçois pas de like automatique - -**Quand** je clique sur le bouton "Like" - -**Alors** ma jauge "Culture" augmente de 2% uniquement -**Et** ma jauge "Culture" est maintenant à 62% - ---- - -## 18. Unlike retire le like manuel - -**Étant donné** que j'ai liké manuellement un contenu "Sport" -**Et** que ma jauge "Sport" est passée de 55% à 57% (+2%) - -**Quand** je clique sur "Unlike" - -**Alors** ma jauge "Sport" diminue de 2% -**Et** ma jauge "Sport" revient à 55% - ---- - -## 19. Unlike ne peut pas retirer un like automatique - -**Étant donné** que j'ai écouté un contenu "Musique" à 90% -**Et** que j'ai reçu un like automatique renforcé (+2%) -**Et** que ma jauge "Musique" est à 52% - -**Quand** j'essaie de faire "Unlike" - -**Alors** l'action n'est pas disponible -**Et** ma jauge reste à 52% - ---- - -## 20. Tags définis par créateur à la publication - -**Étant donné** que je suis un créateur - -**Quand** je publie un contenu - -**Alors** je dois sélectionner 1 à 3 tags -**Et** ces tags sont fixés après publication -**Et** impacteront les jauges de tous les auditeurs - ---- - -## 21. 📋 Plan: Calculs avec différentes durées d'écoute - -**Étant donné** qu'un contenu de 10 minutes est tagué "Voyage" -**Et** que ma jauge "Voyage" est à 50% - -**Quand** j'écoute pendant () - -**Alors** ma jauge évolue de -**Et** ma nouvelle jauge est à - -**📊 Exemples de données:** - -| duree | pourcentage | impact | nouveau_niveau | -|---|---|---|---| -| 1 min | 10% | 0% | 50% | -| 3 min | 30% | +1% | 51% | -| 5 min | 50% | +1% | 51% | -| 7.9 min | 79% | +1% | 51% | -| 8 min | 80% | +2% | 52% | -| 9.5 min | 95% | +2% | 52% | -| 5 sec | <1% | -0.5% | 49.5% | - ---- - - - - - -
- - -# Jauge initiale et cold start -> *En tant que nouvel utilisateur* -> *Je veux que mes jauges d'intérêt démarrent de manière neutre* -> *Afin de découvrir du contenu sans biais initial* - -**15 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -## 1. Inscription - toutes les jauges à 50% - -**Quand** je m'inscris sur RoadWave - -**Alors** toutes mes jauges d'intérêt sont initialisées à 50% -**Et** je ne dois pas remplir de questionnaire -**Et** l'inscription est ultra-rapide - ---- - -## 2. Liste des catégories disponibles - -**Étant donné** que je suis un nouvel utilisateur - -**Quand** je consulte mes centres d'intérêt - -**Alors** je vois les catégories suivantes à 50%: - - | catégorie | - |---| - | Automobile | - | Voyage | - | Famille | - | Amour | - | Musique | - | Économie | - | Cryptomonnaie | - | Politique | - | Culture générale | - | Sport | - | Technologie | - | Santé | - - ---- - -## 3. Cold start - premier contenu écouté - -**Étant donné** que je viens de m'inscrire -**Et** que toutes mes jauges sont à 50% - -**Quand** j'écoute mon premier podcast "Automobile" à 90% - -**Alors** ma jauge "Automobile" monte à 52% (+2%) -**Et** toutes les autres jauges restent à 50% - ---- - -## 4. Cold start - premier skip - -**Étant donné** que je viens de m'inscrire -**Et** que toutes mes jauges sont à 50% - -**Quand** je skip rapidement un contenu "Économie" - -**Alors** ma jauge "Économie" descend à 49.5% (-0.5%) -**Et** toutes les autres jauges restent à 50% - ---- - -## 5. Après 10 écoutes, profil commence à se dessiner - -**Étant donné** que je suis un nouvel utilisateur -**Et** que j'ai écouté: - - | contenu | tags | completion | - |---|---|---| - | Contenu 1 | Automobile | 90% | - | Contenu 2 | Automobile, Sport | 85% | - | Contenu 3 | Voyage | 75% | - | Contenu 4 | Économie | skip 5s | - | Contenu 5 | Automobile | 95% | - | Contenu 6 | Sport | 80% | - | Contenu 7 | Politique | skip 8s | - | Contenu 8 | Voyage | 88% | - | Contenu 9 | Automobile | 92% | - | Contenu 10 | Technologie | 40% | - - -**Alors** mes jauges reflètent mes préférences: - - | catégorie | tendance | - |---|---| - | Automobile | Forte hausse (>55%) | - | Voyage | Hausse modérée (~53%) | - | Sport | Hausse modérée (~53%) | - | Économie | Baisse légère (~49.5%) | - | Politique | Baisse légère (~49.5%) | - | Technologie | Neutre (~51%) | - - ---- - -## 6. Pas de questionnaire onboarding par défaut - -**Quand** je termine l'inscription - -**Alors** aucun questionnaire de centres d'intérêt n'est affiché -**Et** je peux commencer à écouter immédiatement -**Et** l'algorithme apprend naturellement - ---- - -## 7. Algorithme avec jauges à 50% - chances égales - -**Étant donné** que toutes mes jauges sont à 50% - -**Quand** l'algorithme calcule les recommandations - -**Alors** tous les types de contenus ont une chance égale -**Et** aucun biais initial n'est appliqué -**Et** la géolocalisation prime sur les intérêts - ---- - -## 8. Questionnaire optionnel après 3 écoutes (post-MVP) - -**Étant donné** que j'ai écouté 3 contenus - -**Quand** je termine ma 3ème écoute - -**Alors** je vois une notification in-app optionnelle: - - | titre | Améliorez vos recommandations | - |---|---| - | message | Sélectionnez vos centres d'intérêt | - | actions | Configurer maintenant / Plus tard | - - ---- - -## 9. Remplir le questionnaire optionnel (post-MVP) - -**Étant donné** que le questionnaire optionnel est affiché - -**Quand** je sélectionne les centres d'intérêt suivants: - - | catégorie | - |---| - | Automobile | - | Voyage | - | Sport | - - -**Alors** les jauges sélectionnées passent à 70% -**Et** les jauges non sélectionnées passent à 30% -**Et** je vois le message "Vos préférences ont été enregistrées" - ---- - -## 10. Skipper le questionnaire optionnel (post-MVP) - -**Étant donné** que le questionnaire optionnel est affiché - -**Quand** je clique sur "Plus tard" - -**Alors** toutes mes jauges conservent 50% -**Et** l'algorithme continue d'apprendre naturellement -**Et** je ne suis plus sollicité - ---- - -## 11. Comportement déterministe et testable - -**Étant donné** deux nouveaux utilisateurs A et B - -**Quand** les deux s'inscrivent au même moment - -**Alors** leurs jauges sont identiques (toutes à 50%) -**Et** leurs recommandations initiales sont identiques (basées sur géo uniquement) - ---- - -## 12. Équité entre créateurs au cold start - -**Étant donné** qu'un nouvel utilisateur s'inscrit -**Et** qu'il existe 1000 contenus de catégories variées dans sa zone - -**Quand** l'algorithme génère les premières recommandations - -**Alors** tous les contenus ont une pondération intérêts identique (50%) -**Et** seuls la géolocalisation et l'engagement différencient les contenus -**Et** aucun créateur n'a d'avantage initial - ---- - -## 13. Catégories extensibles - -**Étant donné** que RoadWave ajoute une nouvelle catégorie "Gastronomie" - -**Quand** je consulte mes centres d'intérêt - -**Alors** je vois la nouvelle catégorie "Gastronomie" à 50% -**Et** je peux commencer à l'explorer normalement - ---- - -## 14. Voir l'évolution de mes jauges - -**Étant donné** que je suis un utilisateur avec historique - -**Quand** je consulte mes centres d'intérêt dans les paramètres - -**Alors** je vois mes jauges actuelles: - - | catégorie | niveau | evolution | - |---|---|---| - | Automobile | 67% | +17% | - | Voyage | 82% | +32% | - | Économie | 34% | -16% | - | Sport | 50% | 0% | - -**Et** je comprends mes préférences actuelles - ---- - -## 15. Friction zéro à l'inscription - -**Étant donné** que je veux m'inscrire rapidement - -**Quand** je remplis les 4 champs obligatoires -**Et** que je clique sur "S'inscrire" - -**Alors** mon compte est créé immédiatement -**Et** je peux commencer à écouter dans les 30 secondes -**Et** aucune configuration supplémentaire n'est requise - ---- - - - - - -
- - -# Synchronisation actions offline -> *En tant qu'utilisateur* -> *Je veux que mes actions offline soient synchronisées quand je me reconnecte* -> *Afin de ne perdre aucune interaction même sans connexion* - -**45 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que j'utilise l'application RoadWave -## 1. Like d'un contenu en mode offline - -**Étant donné** que je n'ai aucune connexion Internet - -**Quand** je like un contenu téléchargé - -**Alors** l'action est enregistrée localement dans SQLite: -**Et** l'UI affiche immédiatement le like (optimistic update) - ---- - -## 2. Unlike d'un contenu en mode offline - -**Étant donné** que je n'ai aucune connexion Internet -**Et** que j'avais liké un contenu - -**Quand** je retire mon like - -**Alors** l'action est enregistrée localement: -**Et** l'UI retire immédiatement le like - ---- - -## 3. Abonnement à un créateur en mode offline - -**Étant donné** que je n'ai aucune connexion Internet - -**Quand** je m'abonne à un créateur - -**Alors** l'action est enregistrée localement: -**Et** l'UI affiche immédiatement "Abonné ✓" - ---- - -## 4. Désabonnement d'un créateur en mode offline - -**Étant donné** que je n'ai aucune connexion Internet -**Et** que j'étais abonné à un créateur - -**Quand** je me désabonne - -**Alors** l'action est enregistrée localement: -**Et** l'UI affiche "S'abonner" - ---- - -## 5. Signalement d'un contenu en mode offline - -**Étant donné** que je n'ai aucune connexion Internet - -**Quand** je signale un contenu pour "Contenu inapproprié" - -**Alors** l'action est enregistrée localement: -**Et** je vois "Signalement enregistré. Sera envoyé à la reconnexion." - ---- - -## 6. Progression audio-guide en mode offline - -**Étant donné** que je n'ai aucune connexion Internet -**Et** que j'écoute un audio-guide multi-séquences - -**Quand** je termine la séquence 3/10 - -**Alors** la progression est enregistrée localement: -**Et** ma progression est sauvegardée - ---- - -## 7. Multiple actions offline stockées en queue - -**Étant donné** que je n'ai aucune connexion Internet pendant 2 jours - -**Quand** j'effectue plusieurs actions: - - | action | cible | - |---|---| - | like | contenu A | - | like | contenu B | - | subscribe | créateur X | - | unlike | contenu C | - | report | contenu D | - - -**Alors** les 5 actions sont stockées dans pending_actions -**Et** elles seront synchronisées dans l'ordre à la reconnexion - ---- - -## 8. Détection reconnexion Internet - -**Étant donné** que j'étais en mode offline - -**Quand** l'app détecte une reconnexion Internet - -**Alors** le processus de synchronisation démarre automatiquement -**Et** je vois une notification "Synchronisation en cours..." - ---- - -## 9. Récupération queue locale pendant sync - -**Étant donné** que la synchronisation démarre - -**Quand** l'app récupère les actions en attente - -**Alors** une requête SQL est exécutée: -**Et** toutes les actions sont récupérées dans l'ordre chronologique - ---- - -## 10. Envoi batch API des actions - -**Étant donné** que 15 actions sont en attente - -**Quand** le batch est envoyé au backend - -**Alors** une requête POST /sync/actions est faite: -**Et** toutes les actions sont groupées en une seule requête - ---- - -## 11. Backend traite chaque action - -**Étant donné** que le backend reçoit le batch d'actions - -**Quand** il traite chaque action - -**Alors** pour chaque action: - - | étape | détail | - |---|---| - | Validation | Vérifier user_id, content_id valides | - | Vérification existence | Contenu/créateur existe toujours ? | - | Application action | INSERT/UPDATE/DELETE en base | - | Mise à jour compteurs | Likes, abonnés, etc. | - | Impact sur algorithme | Mise à jour jauges si nécessaire | - - ---- - -## 12. Confirmation réception et suppression queue locale - -**Étant donné** que le backend a traité toutes les actions avec succès - -**Quand** la confirmation est reçue par l'app - -**Alors** les actions sont supprimées de la queue locale: -**Et** la table pending_actions est vidée - ---- - -## 13. Toast confirmation synchronisation - -**Étant donné** que 15 actions ont été synchronisées - -**Quand** la synchronisation se termine - -**Alors** je vois un toast: - ---- - -## 14. Synchronisation silencieuse si peu d'actions - -**Étant donné** que j'ai seulement 2 actions en attente - -**Quand** la synchronisation se termine - -**Alors** aucun toast n'est affiché (sync silencieuse) -**Et** l'expérience reste fluide -**Mais** je peux voir le détail dans l'historique des syncs - ---- - -## 15. Échec synchronisation - Retry automatique - -**Étant donné** que la synchronisation échoue (erreur réseau) - -**Quand** l'échec est détecté - -**Alors** un retry automatique est programmé dans 30 secondes -**Et** les actions restent dans pending_actions - ---- - -## 16. 3 tentatives échouées - Notification utilisateur - -**Étant donné** que 3 tentatives de synchronisation ont échoué - -**Quand** la 3ème tentative échoue - -**Alors** je reçois une notification: - ---- - -## 17. Actions conservées jusqu'à sync réussie - -**Étant donné** que la synchronisation échoue plusieurs fois - -**Quand** les tentatives continuent d'échouer - -**Alors** les actions restent dans pending_actions -**Et** aucune action n'est perdue -**Et** elles seront envoyées dès que la connexion sera stable - ---- - -## 18. Rétention max 7 jours - Purge automatique - -**Étant donné** qu'une action est en attente depuis 7 jours - -**Quand** le système détecte cette ancienneté - -**Alors** l'action est automatiquement supprimée de la queue -**Et** je vois "1 action trop ancienne supprimée (>7 jours)" -**Et** cela évite une queue infinie - ---- - -## 19. Justification rétention 7 jours - -**Étant donné** qu'un utilisateur ne se connecte jamais pendant 2 semaines - -**Quand** ses actions ont >7 jours - -**Alors** elles sont purgées automatiquement -**Et** évite une queue qui grandit indéfiniment - ---- - -## 20. Retry manuel après échec - -**Étant donné** que la synchronisation a échoué - -**Quand** je clique sur "Réessayer maintenant" - -**Alors** une nouvelle tentative de synchronisation est lancée immédiatement -**Et** si elle réussit, les actions sont synchronisées - ---- - -## 21. Backend retourne contenus supprimés - -**Étant donné** que j'ai liké un contenu offline -**Mais** que le contenu a été supprimé entre temps - -**Quand** le backend traite la synchronisation - -**Alors** il retourne: - ---- - -## 22. App supprime fichiers locaux contenus supprimés - -**Étant donné** que le backend retourne deleted_content_ids: [123, 456] - -**Quand** l'app traite la réponse - -**Alors** elle supprime les fichiers locaux des contenus 123 et 456 -**Et** libère l'espace disque -**Et** les actions associées sont retirées de la queue - ---- - -## 23. Contenu supprimé en cours d'écoute - -**Étant donné** que j'écoute le contenu 123 en offline -**Et** que la sync détecte que le contenu a été supprimé - -**Quand** la lecture actuelle se termine - -**Alors** l'app attend 2 secondes -**Et** passe automatiquement au contenu suivant -**Et** le fichier du contenu 123 est supprimé en arrière-plan - ---- - -## 24. Toast notification contenu retiré - -**Étant donné** que 2 contenus téléchargés ont été supprimés - -**Quand** la synchronisation se termine - -**Alors** je vois un toast: - ---- - -## 25. Contenu modéré après téléchargement - -**Étant donné** que j'ai téléchargé un contenu qui est ensuite modéré - -**Quand** la synchronisation détecte la modération - -**Alors** le contenu est immédiatement supprimé du device -**Et** je ne peux plus l'écouter -**Et** cela garantit la conformité même offline - ---- - -## 26. Justification pas de conflit possible - -**Étant donné** que les actions offline sont unilatérales (likes, abonnements) - -**Quand** elles sont synchronisées - -**Alors** il n'y a pas de conflit de version possible -**Et** pas de merge complexe nécessaire - ---- - -## 27. Justification UX fluide offline - -**Étant donné** que toutes les actions fonctionnent offline - -**Quand** l'utilisateur interagit sans connexion - -**Alors** l'expérience est identique au mode online -**Et** l'utilisateur n'est pas bloqué -**Et** peut utiliser l'app normalement - ---- - -## 28. Justification batch = Économie requêtes - -**Étant donné** que 15 actions sont en attente - -**Quand** elles sont synchronisées en batch - -**Alors** 1 seule requête HTTP est envoyée (vs 15 si individuelles) -**Et** cela économise la bande passante et la batterie -**Et** réduit la charge serveur - ---- - -## 29. Justification conformité modération offline - -**Étant donné** qu'un contenu illégal est modéré pendant qu'un user est offline - -**Quand** le user se reconnecte - -**Alors** le contenu est immédiatement supprimé de son device -**Et** cela garantit que les contenus illégaux disparaissent même offline - ---- - -## 30. Historique synchronisations - -**Étant donné** que j'accède à "Paramètres > Synchronisation" - -**Quand** je consulte l'historique - -**Alors** je vois: - - | date | actions sync | statut | - |---|---|---| - | 15/06/2025 14:30:00 | 15 | Réussi ✅ | - | 14/06/2025 09:15:00 | 7 | Réussi ✅ | - | 13/06/2025 18:45:00 | 3 | Échec ❌ | - - ---- - -## 31. Détail d'une synchronisation - -**Étant donné** que je clique sur une ligne de l'historique - -**Quand** le détail s'affiche - -**Alors** je vois: - ---- - -## 32. Compteur actions en attente visible - -**Étant donné** que j'ai 12 actions en attente de synchronisation - -**Quand** j'accède à l'onglet Profil - -**Alors** je vois un badge "12" sur l'icône de synchronisation -**Et** je sais qu'il y a des actions en attente - ---- - -## 33. Synchronisation manuelle forcée - -**Étant donné** que je veux forcer une synchronisation immédiate - -**Quand** je vais dans "Paramètres > Synchronisation" -**Et** que je clique sur "Synchroniser maintenant" - -**Alors** la synchronisation démarre immédiatement -**Et** toutes les actions en attente sont envoyées - ---- - -## 34. Statistiques utilisateur - Syncs effectuées - -**Étant donné** que j'accède à mes statistiques - -**Quand** je consulte la section Synchronisation - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Synchronisations depuis début | 87 | - | Actions synchronisées total | 1,234 | - | Taux de succès | 94% | - | Dernière sync | Il y a 2h | - - ---- - -## 35. Statistiques admin - Volume synchronisations - -**Étant donné** qu'un admin consulte les métriques de synchronisation - -**Quand** il accède au dashboard - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Synchronisations/jour | 45,678 | - | Actions synchronisées/jour | 234,567 | - | Taux succès sync | 96.5% | - | Temps moyen traitement batch | 0.8s | - | Actions en attente (global) | 12,345 | - - ---- - -## 36. Alerte admin si taux échec sync >10% - -**Étant donné** que le taux d'échec sync dépasse 10% - -**Quand** le système détecte cette anomalie - -**Alors** une alerte est envoyée: - ---- - -## 37. Synchronisation rapide <2s - -**Étant donné** que j'ai 20 actions en attente - -**Quand** la synchronisation démarre - -**Alors** le traitement prend <2 secondes -**Et** je ne remarque aucun ralentissement de l'app - ---- - -## 38. Synchronisation de gros batch (100 actions) - -**Étant donné** que je n'ai pas synchronisé pendant 1 semaine -**Et** que j'ai 100 actions en attente - -**Quand** la synchronisation démarre - -**Alors** le batch de 100 actions est traité en <5 secondes -**Et** toutes les actions sont synchronisées avec succès - ---- - -## 39. Gestion charge serveur - 10 000 syncs simultanées - -**Étant donné** que 10 000 utilisateurs se reconnectent simultanément - -**Quand** chacun envoie un batch de 20 actions - -**Alors** le serveur traite 200 000 actions -**Et** grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s -**Et** aucun timeout n'est constaté - ---- - -## 40. Stockage SQLite optimisé - -**Étant donné** que la table pending_actions stocke des centaines d'actions - -**Quand** des requêtes sont exécutées - -**Alors** la table est indexée sur created_at -**Et** les requêtes SELECT et DELETE sont instantanées (<10ms) -**Et** l'expérience utilisateur reste fluide - ---- - -## 41. Nettoyage automatique table pending_actions - -**Étant donné** que la table pending_actions grossit avec le temps - -**Quand** les actions sont synchronisées et supprimées - -**Alors** la table est automatiquement optimisée (VACUUM sur SQLite) -**Et** l'espace disque est libéré -**Et** les performances restent optimales - ---- - -## 42. Action dupliquée - Idempotence - -**Étant donné** que j'ai liké un contenu offline -**Et** que la sync échoue et retry - -**Quand** le backend reçoit 2 fois le même like - -**Alors** il applique l'idempotence (1 seul like enregistré) -**Et** le compteur de likes n'est pas faussé - ---- - -## 43. Séquence like/unlike offline - -**Étant donné** que j'ai liké puis unliké un contenu offline - -**Quand** les 2 actions sont synchronisées - -**Alors** le backend applique les 2 actions dans l'ordre -**Et** le résultat final est "pas de like" (état correct) - ---- - -## 44. Abonnement puis désabonnement offline - -**Étant donné** que je me suis abonné puis désabonné d'un créateur offline - -**Quand** les 2 actions sont synchronisées - -**Alors** le backend applique les 2 actions dans l'ordre -**Et** le résultat final est "pas abonné" -**Et** les jauges évoluent correctement (+5% puis -5% = 0% net) - ---- - -## 45. Créateur supprimé pendant offline - -**Étant donné** que je me suis abonné à un créateur offline -**Mais** que le créateur a supprimé son compte entre temps - -**Quand** la sync traite l'abonnement - -**Alors** le backend retourne "creator_deleted" -**Et** l'action est ignorée silencieusement -**Et** aucune erreur n'est affichée à l'utilisateur - ---- - - - - - -
- - -# Téléchargement de contenus offline -> *En tant qu'utilisateur* -> *Je veux télécharger des contenus pour les écouter sans connexion* -> *Afin de profiter de RoadWave même dans les zones sans réseau* - -**49 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis connecté à l'application RoadWave -## 1. Option "Autour de moi" - Rayon 50 km - -**Étant donné** que je suis à Paris (position GPS détectée) - -**Quand** je sélectionne "Télécharger > Autour de moi" - -**Alors** l'app recherche tous les contenus géolocalisés dans un rayon de 50 km -**Et** je vois une liste de contenus de Paris et banlieue proche -**Et** l'estimation affiche "~150 contenus disponibles" - ---- - -## 2. Option "Ma ville" - Limite administrative détectée - -**Étant donné** que je suis à Lyon (position GPS détectée) - -**Quand** je sélectionne "Télécharger > Ma ville" - -**Alors** l'app détecte automatiquement "Lyon" comme ville -**Et** recherche tous les contenus géolocalisés "Lyon" -**Et** je vois uniquement les contenus de la ville de Lyon (pas banlieue) - ---- - -## 3. Option "Mon département" - Sélection dans liste - -**Étant donné** que je veux télécharger des contenus pour un département - -**Quand** je sélectionne "Télécharger > Mon département" - -**Alors** je vois une liste de tous les départements français: - - | département | - |---| - | 01 - Ain | - | 02 - Aisne | - | 75 - Paris | - | 69 - Rhône | - | ... | - -**Et** je peux choisir un département - ---- - -## 4. Sélection département et téléchargement contenus - -**Étant donné** que je sélectionne "75 - Paris" dans la liste des départements - -**Quand** la sélection est confirmée - -**Alors** l'app recherche tous les contenus géolocalisés "Paris" -**Et** je vois "~234 contenus disponibles pour Paris" - ---- - -## 5. Option "Ma région" - Sélection dans liste - -**Étant donné** que je veux télécharger des contenus pour une région - -**Quand** je sélectionne "Télécharger > Ma région" - -**Alors** je vois une liste de toutes les régions françaises: - - | région | - |---| - | Auvergne-Rhône-Alpes | - | Bretagne | - | Île-de-France | - | Nouvelle-Aquitaine | - | Occitanie | - | ... | - -**Et** je peux choisir une région - ---- - -## 6. Sélection région et téléchargement contenus - -**Étant donné** que je sélectionne "Bretagne" dans la liste des régions - -**Quand** la sélection est confirmée - -**Alors** l'app recherche tous les contenus géolocalisés des départements bretons: - - | département | - |---| - | Côtes-d'Armor (22) | - | Finistère (29) | - | Ille-et-Vilaine (35) | - | Morbihan (56) | - -**Et** je vois "~487 contenus disponibles pour Bretagne" - ---- - -## 7. Recherche manuelle ville - -**Étant donné** que je veux télécharger des contenus pour une ville spécifique - -**Quand** je tape "Marseille" dans la barre de recherche - -**Alors** l'app propose des suggestions: - - | suggestion | - |---| - | Marseille (13) | - | Marseille-en-Beauvaisis | - -**Et** je peux sélectionner "Marseille (13)" - ---- - -## 8. Recherche manuelle avec autocomplétion - -**Étant donné** que je tape "Ly" dans la barre de recherche - -**Quand** l'autocomplétion s'active - -**Alors** je vois des suggestions: - - | suggestion | - |---| - | Lyon (69) | - | Lys-lez-Lannoy | - -**Et** je peux affiner ma recherche - ---- - -## 9. Utilisateur gratuit - Limite 50 contenus max - -**Étant donné** que je suis un utilisateur gratuit -**Et** que j'ai déjà téléchargé 45 contenus - -**Quand** j'accède à la page Téléchargements - -**Alors** je vois "45 / 50 contenus téléchargés" -**Et** je peux télécharger 5 contenus supplémentaires maximum - ---- - -## 10. Utilisateur gratuit - Tentative dépasser limite 50 - -**Étant donné** que je suis gratuit et j'ai déjà 50 contenus téléchargés - -**Quand** j'essaie de télécharger un 51ème contenu - -**Alors** le téléchargement est refusé -**Et** je vois le message: - ---- - -## 11. Utilisateur Premium - Téléchargements illimités - -**Étant donné** que je suis un utilisateur Premium -**Et** que j'ai déjà téléchargé 245 contenus - -**Quand** j'accède à la page Téléchargements - -**Alors** je vois "245 contenus (3.2 GB)" -**Et** aucune limite n'est affichée -**Et** je peux télécharger autant de contenus que je veux - ---- - -## 12. Limite Premium = Espace disque disponible - -**Étant donné** que je suis Premium -**Et** que mon device a 500 MB d'espace disque disponible - -**Quand** j'essaie de télécharger 100 contenus (2 GB) - -**Alors** le téléchargement échoue après ~50 contenus (500 MB) -**Et** je vois "Espace disque insuffisant. Libérez de l'espace pour continuer." - ---- - -## 13. Calcul temps écoute disponible gratuit - -**Étant donné** que je suis gratuit avec 50 contenus téléchargés -**Et** que la durée moyenne d'un contenu est 5 minutes - -**Quand** je calcule le temps d'écoute disponible - -**Alors** 50 contenus × 5 min = 250 minutes = 4h10 d'écoute -**Et** cela suffit pour un trajet quotidien ou road trip court - ---- - -## 14. Calcul temps écoute disponible Premium illimité - -**Étant donné** que je suis Premium avec 300 contenus téléchargés -**Et** que la durée moyenne est 5 minutes - -**Quand** je calcule le temps d'écoute disponible - -**Alors** 300 contenus × 5 min = 1500 minutes = 25h d'écoute -**Et** cela suffit pour un road trip de plusieurs jours - ---- - -## 15. Téléchargement par défaut en WiFi uniquement - -**Étant donné** que je suis connecté en WiFi - -**Quand** je clique sur "Télécharger 20 contenus" - -**Alors** le téléchargement démarre immédiatement -**Et** aucune popup de confirmation n'apparaît - ---- - -## 16. Tentative téléchargement en données mobiles - Popup confirmation - -**Étant donné** que je suis connecté en 4G (pas de WiFi) - -**Quand** je clique sur "Télécharger 20 contenus" - -**Alors** une popup apparaît: - ---- - -## 17. Calcul estimation consommation data mobile - -**Étant donné** que je veux télécharger 20 contenus -**Et** que la durée moyenne est 5 minutes -**Et** que la qualité Standard est 48 kbps Opus - -**Quand** l'estimation est calculée - -**Alors** consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB -**Et** ce montant est affiché dans la popup - ---- - -## 18. Confirmation téléchargement en données mobiles - -**Étant donné** que je vois la popup de confirmation données mobiles - -**Quand** je clique sur "Continuer quand même" - -**Alors** le téléchargement démarre immédiatement via 4G -**Et** la consommation data est comptabilisée sur mon forfait mobile - ---- - -## 19. Refus téléchargement données mobiles - Attendre WiFi - -**Étant donné** que je vois la popup de confirmation données mobiles - -**Quand** je clique sur "Attendre WiFi" - -**Alors** les téléchargements sont mis en file d'attente -**Et** ils démarreront automatiquement quand le WiFi sera détecté - ---- - -## 20. Détection automatique WiFi et reprise téléchargements - -**Étant donné** que j'ai mis 20 contenus en file d'attente (attente WiFi) - -**Quand** l'app détecte une connexion WiFi - -**Alors** les téléchargements démarrent automatiquement -**Et** je reçois une notification "Téléchargements en cours via WiFi" - ---- - -## 21. Qualité Standard (48 kbps) par défaut - -**Étant donné** que je configure mes téléchargements - -**Quand** j'accède aux paramètres de qualité - -**Alors** la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut -**Et** elle est disponible pour tous (gratuit + Premium) - ---- - -## 22. Qualité Basse (24 kbps) disponible pour tous - -**Étant donné** que j'ai peu d'espace disque disponible - -**Quand** je sélectionne qualité "Basse (24 kbps - ~10 MB/h)" - -**Alors** mes prochains téléchargements seront en 24 kbps -**Et** l'espace utilisé sera divisé par 2 par rapport à Standard -**Et** cette option est disponible pour gratuit + Premium - ---- - -## 23. Qualité Haute (64 kbps) réservée Premium - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** je consulte les options de qualité - -**Alors** l'option "Haute (64 kbps - ~30 MB/h)" est grisée -**Et** je vois "👑 Premium uniquement" -**Et** je ne peux pas la sélectionner - ---- - -## 24. Utilisateur Premium peut choisir qualité Haute - -**Étant donné** que je suis un utilisateur Premium - -**Quand** je consulte les options de qualité - -**Alors** l'option "Haute (64 kbps - ~30 MB/h)" est disponible -**Et** je peux la sélectionner pour mes téléchargements -**Et** la qualité audio sera excellente (meilleure restitution voix et ambiances) - ---- - -## 25. Comparaison taille fichiers selon qualité - -**Étant donné** que je veux télécharger 50 contenus de 5 min chacun - -**Quand** je compare les qualités - -**Alors** les tailles totales sont: - - | qualité | bitrate | taille totale | - |---|---|---| - | Basse | 24 kbps | ~250 MB | - | Standard | 48 kbps | ~500 MB | - | Haute | 64 kbps | ~650 MB | - - ---- - -## 26. Justification Standard = Bon compromis - -**Étant donné** que le contenu RoadWave est principalement de la voix - -**Quand** la qualité Standard (48 kbps Opus) est utilisée - -**Alors** la qualité est très correcte pour la voix -**Et** équivalente à la radio FM -**Et** le compromis qualité/taille est optimal - ---- - -## 27. Justification Haute réservée Premium = Incitation upgrade - -**Étant donné** qu'un utilisateur gratuit veut la meilleure qualité - -**Quand** il voit que Haute est réservée Premium - -**Alors** cela l'incite à passer Premium pour 4.99€/mois -**Et** c'est un avantage tangible supplémentaire de Premium - ---- - -## 28. Changement qualité après téléchargements existants - -**Étant donné** que j'ai déjà téléchargé 30 contenus en qualité Standard - -**Quand** je change la qualité vers Haute (si Premium) - -**Alors** les 30 contenus existants restent en Standard -**Et** seuls les nouveaux téléchargements seront en Haute -**Et** je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute - ---- - -## 29. Téléchargement individuel d'un contenu - -**Étant donné** que je consulte la page d'un contenu - -**Quand** je clique sur l'icône de téléchargement 📥 - -**Alors** le téléchargement démarre -**Et** une barre de progression apparaît -**Et** l'icône devient ✅ quand terminé - ---- - -## 30. Téléchargement batch de contenus sélectionnés - -**Étant donné** que je consulte une liste de contenus pour "Paris" - -**Quand** je sélectionne 15 contenus manuellement -**Et** que je clique sur "Télécharger la sélection" - -**Alors** les 15 contenus sont téléchargés en parallèle (max 3 simultanés) -**Et** une notification affiche "15 contenus téléchargés" - ---- - -## 31. Téléchargement automatique recommandations zone - -**Étant donné** que je sélectionne "Autour de moi" (Paris) - -**Quand** je clique sur "Télécharger les 50 meilleurs contenus" - -**Alors** l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents -**Et** les télécharge tous -**Et** je n'ai pas besoin de choisir manuellement - ---- - -## 32. Barre de progression téléchargement global - -**Étant donné** que je télécharge 20 contenus - -**Quand** les téléchargements sont en cours - -**Alors** je vois une barre de progression globale: - ---- - -## 33. Téléchargements en tâche de fond - -**Étant donné** que je lance le téléchargement de 30 contenus - -**Quand** je ferme l'app ou passe à une autre activité - -**Alors** les téléchargements continuent en arrière-plan -**Et** je reçois une notification quand tous sont terminés - ---- - -## 34. Pause et reprise téléchargements - -**Étant donné** que je télécharge 20 contenus - -**Quand** je clique sur "Pause" - -**Alors** les téléchargements en cours se terminent -**Et** les téléchargements en attente sont mis en pause -**Et** je peux cliquer sur "Reprendre" plus tard - ---- - -## 35. Annulation téléchargements - -**Étant donné** que je télécharge 20 contenus - -**Quand** je clique sur "Annuler" - -**Alors** tous les téléchargements sont arrêtés -**Et** les fichiers partiels sont supprimés -**Et** l'espace disque est libéré - ---- - -## 36. Gestion erreurs téléchargement - -**Étant donné** que je télécharge un contenu -**Mais** que la connexion Internet coupe au milieu - -**Quand** la connexion revient - -**Alors** le téléchargement reprend automatiquement où il s'était arrêté -**Et** aucune perte de progression n'a lieu - ---- - -## 37. Retry automatique après échec - -**Étant donné** qu'un téléchargement échoue 3 fois consécutives - -**Quand** l'échec est détecté - -**Alors** le contenu est marqué "Échec" -**Et** je vois une notification "3 contenus n'ont pas pu être téléchargés" -**Et** je peux retry manuellement en cliquant sur "Réessayer" - ---- - -## 38. Liste contenus téléchargés - -**Étant donné** que j'ai téléchargé 45 contenus - -**Quand** j'accède à "Téléchargements" - -**Alors** je vois la liste complète de mes 45 contenus -**Et** pour chaque contenu: titre, créateur, durée, taille, date téléchargement - ---- - -## 39. Tri contenus téléchargés - -**Étant donné** que je consulte ma liste de téléchargements - -**Quand** je clique sur "Trier par" - -**Alors** je peux trier par: - - | critère | ordre | - |---|---| - | Date téléchargement | Plus récent / Plus ancien | - | Titre | A-Z / Z-A | - | Créateur | A-Z / Z-A | - | Durée | Plus long / Plus court | - | Taille | Plus gros / Plus petit | - - ---- - -## 40. Recherche dans contenus téléchargés - -**Étant donné** que j'ai 200 contenus téléchargés - -**Quand** je tape "Tesla" dans la barre de recherche - -**Alors** seuls les contenus contenant "Tesla" s'affichent -**Et** je peux rapidement trouver un contenu spécifique - ---- - -## 41. Suppression individuelle contenu téléchargé - -**Étant donné** que je veux supprimer un contenu téléchargé - -**Quand** je swipe left (iOS) ou long press (Android) sur le contenu -**Et** que je clique sur "Supprimer" - -**Alors** le fichier est supprimé du device -**Et** l'espace disque est libéré -**Et** le compteur est décrémenté (ex: 45/50 → 44/50) - ---- - -## 42. Suppression batch contenus téléchargés - -**Étant donné** que je veux supprimer plusieurs contenus - -**Quand** je sélectionne 10 contenus -**Et** que je clique sur "Supprimer la sélection" - -**Alors** les 10 fichiers sont supprimés -**Et** ~100 MB d'espace disque sont libérés -**Et** une notification confirme "10 contenus supprimés" - ---- - -## 43. Suppression tous les contenus téléchargés - -**Étant donné** que j'ai 45 contenus téléchargés - -**Quand** je clique sur "Supprimer tout" -**Et** que je confirme l'action - -**Alors** tous les 45 contenus sont supprimés -**Et** l'espace disque total est libéré (~450 MB) -**Et** le compteur repasse à 0/50 - ---- - -## 44. Espace disque utilisé visible - -**Étant donné** que j'ai téléchargé 45 contenus - -**Quand** j'accède à la page Téléchargements - -**Alors** je vois l'espace disque utilisé: - ---- - -## 45. Statistiques téléchargements - -**Étant donné** que j'accède à mes statistiques - -**Quand** je consulte la section Téléchargements - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Contenus actuellement téléchargés | 45 | - | Espace disque utilisé | 478 MB | - | Contenus téléchargés depuis début | 287 | - | Total data téléchargée | 3.2 GB | - | Téléchargements via WiFi | 92% | - | Téléchargements via mobile | 8% | - - ---- - -## 46. Lecture contenu téléchargé sans connexion - -**Étant donné** que je n'ai aucune connexion Internet (mode avion) -**Et** que j'ai des contenus téléchargés - -**Quand** je lance un contenu téléchargé - -**Alors** la lecture démarre normalement depuis le fichier local -**Et** aucune erreur de connexion n'apparaît - ---- - -## 47. Badge "Téléchargé" sur contenus offline - -**Étant donné** que j'ai téléchargé certains contenus - -**Quand** je consulte une liste de contenus - -**Alors** les contenus téléchargés ont un badge ✅ "Offline" -**Et** je sais immédiatement lesquels sont disponibles sans connexion - ---- - -## 48. Filtre "Téléchargés uniquement" - -**Étant donné** que je veux voir uniquement mes contenus offline - -**Quand** j'active le filtre "Téléchargés uniquement" - -**Alors** seuls les contenus téléchargés s'affichent -**Et** je peux facilement naviguer dans mon catalogue offline - ---- - -## 49. Playlist offline automatique - -**Étant donné** que j'ai téléchargé 45 contenus - -**Quand** j'accède à "Téléchargements" - -**Alors** je peux lancer une playlist aléatoire de mes 45 contenus -**Et** profiter d'une écoute continue offline - ---- - - - - - -
- - -# Validité et renouvellement contenus offline -> *En tant qu'utilisateur* -> *Je veux que mes contenus téléchargés restent valides un certain temps* -> *Afin de garantir la légalité et la fraîcheur du contenu* - -**38 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis connecté à l'application RoadWave -> **Et** que j'ai des contenus téléchargés -## 1. Validité de 30 jours après téléchargement - -**Étant donné** que je télécharge un contenu le 1er juin 2025 - -**Quand** le téléchargement est terminé - -**Alors** le contenu est valide jusqu'au 1er juillet 2025 (30 jours) -**Et** la date d'expiration est stockée en local - ---- - -## 2. Affichage date expiration sur contenu téléchargé - -**Étant donné** que j'ai téléchargé un contenu il y a 20 jours - -**Quand** je consulte les détails du contenu - -**Alors** je vois "Expire dans 10 jours" -**Et** je sais combien de temps il reste avant expiration - ---- - -## 3. Standard industrie aligné (Spotify, YouTube, Deezer) - -**Étant donné** que Spotify, YouTube Music et Deezer utilisent 30 jours - -**Quand** RoadWave fixe également 30 jours - -**Alors** c'est le standard accepté par les utilisateurs -**Et** il n'y a pas de confusion avec les autres plateformes - ---- - -## 4. Justification 30 jours - Force reconnexion régulière - -**Étant donné** qu'un utilisateur ne se connecte jamais - -**Quand** ses contenus expirent après 30 jours - -**Alors** il est obligé de se reconnecter pour les renouveler -**Et** le système peut vérifier: - - | vérification | - |---| - | Abonnement Premium toujours actif | - | Contenus non modérés/supprimés | - | Métadonnées à jour | - - ---- - -## 5. Justification 30 jours - Évite stockage obsolète - -**Étant donné** qu'un contenu a été modéré après téléchargement - -**Quand** le contenu expire après 30 jours maximum - -**Alors** le contenu illégal est automatiquement supprimé -**Et** ne reste pas indéfiniment sur le device - ---- - -## 6. Détection WiFi et contenus >25 jours - -**Étant donné** que j'ai des contenus téléchargés il y a 26 jours - -**Quand** l'app détecte une connexion WiFi - -**Alors** une requête GET /offline/contents/refresh est envoyée -**Et** le backend vérifie chaque contenu - ---- - -## 7. Vérification abonnement Premium toujours actif - -**Étant donné** qu'un contenu téléchargé en Premium est à renouveler - -**Quand** le backend vérifie le statut -**Et** que l'abonnement Premium est toujours actif - -**Alors** la validité est renouvelée à 30 jours supplémentaires - ---- - -## 8. Abonnement Premium expiré - Contenu non renouvelé - -**Étant donné** qu'un contenu Premium téléchargé est à renouveler - -**Quand** le backend vérifie le statut -**Et** que l'abonnement Premium a expiré - -**Alors** le contenu n'est pas renouvelé -**Et** il sera supprimé à l'expiration (J-0) -**Et** l'utilisateur voit "Contenu Premium expiré (abonnement inactif)" - ---- - -## 9. Vérification contenu pas modéré/supprimé - -**Étant donné** qu'un contenu téléchargé est à renouveler - -**Quand** le backend vérifie le statut -**Et** que le contenu a été modéré ou supprimé entre temps - -**Alors** le contenu n'est pas renouvelé -**Et** sera supprimé immédiatement du device -**Et** l'utilisateur voit "1 contenu retiré (violation règles)" - ---- - -## 10. Mise à jour métadonnées lors du renouvellement - -**Étant donné** qu'un contenu téléchargé est renouvelé - -**Quand** le backend traite le renouvellement - -**Alors** les métadonnées sont mises à jour: - - | métadonnée | mise à jour si changée | - |---|---| - | Titre | ✅ | - | Nom créateur | ✅ | - | Description | ✅ | - | Tags | ✅ | - | Statut Premium | ✅ | - -**Et** l'utilisateur voit les infos à jour - ---- - -## 11. Pas de re-téléchargement audio si fichier OK - -**Étant donné** qu'un contenu est renouvelé - -**Quand** le fichier audio local est intact - -**Alors** seules les métadonnées sont mises à jour -**Et** le fichier audio n'est pas re-téléchargé -**Et** cela économise la bande passante - ---- - -## 12. Re-téléchargement audio si fichier corrompu - -**Étant donné** qu'un contenu est renouvelé - -**Quand** le fichier audio local est corrompu (checksum invalide) - -**Alors** le fichier audio est re-téléchargé entièrement -**Et** le nouveau fichier remplace le corrompu - ---- - -## 13. Renouvellement silencieux si WiFi régulier - -**Étant donné** que je me connecte en WiFi tous les jours - -**Quand** mes contenus atteignent 25-30 jours - -**Alors** ils sont automatiquement renouvelés en arrière-plan -**Et** je ne vois aucune notification (processus transparent) -**Et** mes contenus restent valides indéfiniment - ---- - -## 14. Renouvellement batch de plusieurs contenus - -**Étant donné** que j'ai 30 contenus à renouveler - -**Quand** le renouvellement automatique se déclenche - -**Alors** une requête batch est envoyée: -**Et** le backend traite les 30 contenus en une seule requête -**Et** cela économise les requêtes HTTP - ---- - -## 15. Temps de traitement renouvellement - -**Étant donné** que 30 contenus sont à renouveler - -**Quand** la requête batch est traitée - -**Alors** le backend répond en <2 secondes -**Et** les métadonnées sont mises à jour localement -**Et** l'utilisateur ne remarque aucun ralentissement - ---- - -## 16. Notification J-3 avant expiration - -**Étant donné** que j'ai 15 contenus qui expirent dans 3 jours - -**Quand** le système vérifie les expirations - -**Alors** je reçois une notification: -**Et** je peux agir avant l'expiration - ---- - -## 17. Pas de notification si connexion WiFi régulière - -**Étant donné** que je me connecte en WiFi tous les jours -**Et** que mes contenus sont automatiquement renouvelés - -**Quand** le système vérifie les expirations - -**Alors** aucune notification J-3 n'est envoyée - ---- - -## 18. Notification uniquement si contenus non renouvelés - -**Étant donné** que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés - -**Quand** le J-3 arrive pour les 5 non renouvelés - -**Alors** je reçois "5 contenus expirent dans 3 jours" -**Et** seuls les contenus à risque sont mentionnés - ---- - -## 19. Action utilisateur après notification J-3 - -**Étant donné** que je reçois la notification J-3 - -**Quand** je clique sur la notification - -**Alors** l'app s'ouvre sur la page Téléchargements -**Et** je vois les contenus qui vont expirer en rouge -**Et** je peux me connecter en WiFi pour les renouveler - ---- - -## 20. Suppression automatique J-0 (expiration) - -**Étant donné** qu'un contenu n'a pas été renouvelé - -**Quand** le jour d'expiration arrive (J-0) - -**Alors** le fichier est automatiquement supprimé du device -**Et** l'espace disque est libéré -**Et** le compteur est décrémenté (ex: 45/50 → 44/50) - ---- - -## 21. Toast après suppression automatique J-0 - -**Étant donné** que 15 contenus viennent d'expirer - -**Quand** l'utilisateur ouvre l'app - -**Alors** il voit un toast: - ---- - -## 22. Liste contenus supprimés après expiration - -**Étant donné** que 15 contenus ont expiré - -**Quand** je consulte l'historique des suppressions - -**Alors** je vois la liste des 15 contenus supprimés: - - | titre | créateur | date expiration | - |---|---|---| - | Mon épisode préféré | JeanDupont | 15 juin 2025 | - | Road trip Bretagne | MarieLambert | 15 juin 2025 | - | ... | ... | ... | - -**Et** je peux les re-télécharger si je veux - ---- - -## 23. Re-téléchargement après expiration - -**Étant donné** qu'un contenu a expiré et été supprimé - -**Quand** je retrouve ce contenu dans l'app - -**Alors** le badge ✅ "Offline" n'est plus affiché -**Et** je peux le re-télécharger normalement -**Et** la validité repart à 30 jours - ---- - -## 24. Utilisateur ne se connecte jamais pendant 30 jours - -**Étant donné** que je télécharge 50 contenus le 1er juin -**Mais** que je ne me connecte jamais en WiFi pendant 30 jours - -**Quand** le 1er juillet arrive - -**Alors** tous les 50 contenus expirent -**Et** sont automatiquement supprimés -**Et** je n'ai plus aucun contenu offline - ---- - -## 25. Utilisateur en zone blanche 30+ jours - -**Étant donné** que je télécharge 50 contenus avant de partir en zone sans réseau -**Et** que je reste 45 jours sans connexion - -**Quand** les contenus expirent après 30 jours - -**Alors** ils sont supprimés même si je ne peux pas me connecter -**Et** je perds l'accès à mes contenus offline - ---- - -## 26. Recommandation téléchargement avant zone blanche longue - -**Étant donné** que je prépare un road trip de 60 jours - -**Quand** je consulte la FAQ - -**Alors** je vois la recommandation: - ---- - -## 27. Changement statut Premium en gratuit pendant validité - -**Étant donné** que je suis Premium et j'ai téléchargé 200 contenus - -**Quand** mon abonnement Premium expire -**Et** que je repasse en gratuit - -**Alors** au prochain renouvellement, seulement 50 contenus sont conservés -**Et** les 150 autres sont supprimés (limite gratuit) -**Et** je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés." - ---- - -## 28. Sélection automatique 50 meilleurs contenus si passage gratuit - -**Étant donné** que je repasse en gratuit avec 200 contenus téléchargés - -**Quand** le système applique la limite de 50 - -**Alors** les 50 contenus les plus récemment écoutés sont conservés -**Et** les 150 autres sont supprimés -**Et** cela maximise les chances de garder les contenus que j'aime - ---- - -## 29. Contenus Premium exclusifs supprimés si abonnement expire - -**Étant donné** que j'ai téléchargé 20 contenus Premium exclusifs - -**Quand** mon abonnement Premium expire - -**Alors** les 20 contenus Premium sont immédiatement supprimés -**Et** je vois "20 contenus Premium supprimés (abonnement expiré)" - ---- - -## 30. Affichage temps restant avant expiration - -**Étant donné** que j'ai 45 contenus téléchargés - -**Quand** je consulte la page Téléchargements - -**Alors** je vois pour chaque contenu: - - | contenu | temps restant | - |---|---| - | Mon épisode (récent) | Expire dans 28 jours | - | Road trip (ancien) | Expire dans 3 jours | - -**Et** je sais lesquels sont prioritaires pour renouvellement - ---- - -## 31. Tri par date expiration - -**Étant donné** que j'ai 45 contenus avec différentes dates d'expiration - -**Quand** je trie par "Expiration" - -**Alors** les contenus qui expirent le plus tôt apparaissent en premier -**Et** je peux voir rapidement lesquels nécessitent une reconnexion urgente - ---- - -## 32. Badge rouge si expiration <3 jours - -**Étant donné** qu'un contenu expire dans 2 jours - -**Quand** je consulte la liste des téléchargements - -**Alors** le contenu a un badge rouge "⚠️ Expire bientôt" -**Et** il est visuellement mis en avant - ---- - -## 33. Statistiques utilisateur - Taux de renouvellement - -**Étant donné** que j'accède à mes statistiques - -**Quand** je consulte la section Téléchargements - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Contenus actuels | 45 | - | Contenus expirés depuis début | 87 | - | Contenus renouvelés (auto) | 234 | - | Taux renouvellement automatique | 73% | - - ---- - -## 34. Statistiques admin - Taux expiration global - -**Étant donné** qu'un admin consulte les métriques offline - -**Quand** il accède au dashboard - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Contenus téléchargés actifs | 1,234,567 | - | Expirations ce mois | 45,678 | - | Taux expiration | 3.7% | - | Renouvellements automatiques/mois | 234,567 | - - ---- - -## 35. Alerte admin si taux expiration >10% - -**Étant donné** que le taux d'expiration mensuel dépasse 10% - -**Quand** le système détecte cette anomalie - -**Alors** une alerte est envoyée: - ---- - -## 36. Email rappel si pas de connexion WiFi depuis 20 jours - -**Étant donné** que je n'ai pas connecté l'app en WiFi depuis 20 jours -**Et** que j'ai 45 contenus téléchargés - -**Quand** le système détecte cette inactivité WiFi - -**Alors** je reçois un email: - ---- - -## 37. Performance renouvellement avec 10 000 utilisateurs simultanés - -**Étant donné** que 10 000 utilisateurs se connectent en WiFi simultanément - -**Quand** chacun demande le renouvellement de 50 contenus - -**Alors** le serveur traite 500 000 vérifications -**Et** grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s -**Et** les serveurs gèrent la charge sans problème - ---- - -## 38. Logs audit renouvellements - -**Étant donné** qu'un contenu est renouvelé - -**Quand** l'opération se termine - -**Alors** un log est enregistré: - - | timestamp | user_id | content_id | action | résultat | - |---|---|---|---|---| - | 2025-06-15 14:30:00 | abc123 | xyz789 | renew | success (+30d) | - | 2025-06-15 14:30:01 | abc123 | def456 | renew | failed (deleted) | - -**Et** ces logs aident à débugger les problèmes - ---- - - - - - -
- - -# Modération préventive -**22 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que le système de modération préventive est actif -## 1. Créateur nouvellement inscrit - -**Étant donné** que je viens de créer un compte créateur -**Et** que je n'ai jamais publié de contenu - -**Quand** j'examine mon statut de créateur - -**Alors** mon compte est marqué comme "Nouveau créateur" -**Et** mes 3 premiers contenus devront être validés manuellement -**Et** je suis informé de ce processus lors de l'onboarding - ---- - -## 2. Publication du premier contenu par un nouveau créateur - -**Étant donné** que je suis un nouveau créateur -**Et** que je n'ai publié aucun contenu auparavant - -**Quand** je publie mon premier contenu - -**Alors** le contenu entre en file d'attente de validation manuelle -**Et** le statut du contenu est "En attente de validation" -**Et** le contenu n'est pas diffusé sur la plateforme -**Et** je reçois une notification: - ---- - -## 3. Validation manuelle par un modérateur - -**Étant donné** que j'ai publié mon premier contenu -**Et** que le contenu est en attente de validation - -**Quand** un modérateur examine mon contenu - -**Alors** le modérateur utilise la transcription automatique Whisper -**Et** le modérateur vérifie: - - | critère | conforme | - |---|---| - | Respect des règles communauté | oui | - | Pas de contenu inapproprié | oui | - | Qualité audio acceptable | oui | - | Métadonnées cohérentes | oui | - | Tags appropriés | oui | - -**Et** si tout est conforme, le contenu est validé - ---- - -## 4. Délai de validation de 24-48h jours ouvrés - -**Étant donné** que j'ai publié mon premier contenu lundi à 10:00 - -**Quand** le contenu entre en file de validation - -**Alors** le contenu est validé avant mercredi 10:00 (48h jours ouvrés) -**Et** dans la plupart des cas, la validation est effectuée sous 24h -**Et** je reçois une notification dès que le contenu est validé - ---- - -## 5. Notification de validation réussie - -**Étant donné** que mon premier contenu a été validé par un modérateur - -**Quand** la validation est approuvée - -**Alors** je reçois une notification: -**Et** le statut du contenu passe à "Publié" -**Et** le contenu devient visible pour tous les utilisateurs -**Et** il entre dans l'algorithme de recommandation - ---- - -## 6. Refus de validation si contenu non conforme - -**Étant donné** que mon premier contenu viole les règles de la communauté - -**Quand** le modérateur examine le contenu - -**Alors** le contenu est refusé -**Et** je reçois une notification détaillée: -**Et** le contenu reste en statut "Refusé" -**Et** je peux modifier et republier - ---- - -## 7. Les 3 premiers contenus sont validés manuellement - -**Étant donné** que je suis un nouveau créateur - -**Quand** je publie mes contenus - -**Alors** les contenus suivants nécessitent une validation manuelle: - - | contenu | validation manuelle | - |---|---| - | 1er | oui | - | 2ème | oui | - | 3ème | oui | - | 4ème | non (auto) | - -**Et** après 3 contenus validés, mes futurs contenus sont publiés automatiquement - ---- - -## 8. Passage en mode automatique après 3 validations - -**Étant donné** que mes 3 premiers contenus ont été validés avec succès - -**Quand** je publie mon 4ème contenu - -**Alors** le contenu est publié automatiquement -**Et** aucune validation manuelle n'est requise -**Et** le statut passe directement à "Publié" -**Et** je reçois une notification: - ---- - -## 9. Évolution du score de confiance - -**Étant donné** que je suis un créateur établi - -**Quand** le système évalue mon historique - -**Alors** un score de confiance est calculé basé sur: - - | critère | poids | - |---|---| - | Nombre de contenus publiés | 20% | - | Strikes reçus | 40% | - | Signalements infondés | 20% | - | Ancienneté du compte | 10% | - | Taux d'engagement positif | 10% | - -**Et** le score évolue dynamiquement - ---- - -## 10. Créateur fiable - Publication automatique - -**Étant donné** que je suis un créateur -**Et** que j'ai 0 strike depuis 6 mois -**Et** que tous mes contenus précédents ont été conformes - -**Quand** mon score de confiance est calculé - -**Alors** je suis classé comme "Créateur fiable" -**Et** tous mes nouveaux contenus sont publiés automatiquement -**Et** aucune validation manuelle n'est nécessaire -**Et** je bénéficie d'une publication instantanée - ---- - -## 11. Créateur suspect - Validation manuelle systématique - -**Étant donné** que je suis un créateur -**Et** que j'ai reçu 2 strikes récents (< 3 mois) - -**Quand** mon score de confiance est recalculé - -**Alors** je suis classé comme "Créateur suspect" -**Et** tous mes nouveaux contenus nécessitent une validation manuelle -**Et** chaque contenu est examiné avant publication -**Et** je suis notifié de ce changement de statut: - ---- - -## 12. Réhabilitation après période sans incident - -**Étant donné** que j'étais un "Créateur suspect" -**Et** que je publie 10 contenus conformes sur 6 mois -**Et** que je ne reçois aucun nouveau strike - -**Quand** le système réévalue mon score de confiance - -**Alors** je passe en "Créateur fiable" -**Et** la publication automatique est rétablie -**Et** je reçois une notification de réhabilitation: - ---- - -## 13. Toute publicité nécessite validation manuelle - -**Étant donné** qu'un annonceur soumet une publicité audio - -**Quand** la publicité est créée - -**Alors** elle entre automatiquement en file de validation manuelle -**Et** aucune publicité n'est diffusée sans validation préalable -**Et** cela est obligatoire pour des raisons de responsabilité juridique - ---- - -## 14. Validation d'une publicité - Processus complet - -**Étant donné** qu'une publicité est en attente de validation - -**Quand** un modérateur senior examine la publicité - -**Alors** le modérateur vérifie: - - | critère | conforme | - |---|---| - | Transcription automatique Whisper | effectuée | - | Contenu conforme aux règles | oui | - | Pas de fausse publicité / arnaque | oui | - | Respect du ciblage géographique | oui | - | Durée conforme (10-60s) | oui | - | Volume audio acceptable (pas trop fort) | oui | - | Métadonnées correctes | oui | - -**Et** si tout est conforme, la publicité est validée - ---- - -## 15. Délai de validation d'une publicité - 24-48h - -**Étant donné** qu'un annonceur soumet une publicité lundi à 10:00 - -**Quand** la publicité entre en file de validation - -**Alors** la publicité est validée avant mercredi 10:00 (48h jours ouvrés) -**Et** l'annonceur est notifié dès la validation -**Et** la campagne publicitaire peut alors démarrer - ---- - -## 16. Refus de validation d'une publicité - -**Étant donné** qu'une publicité contient des éléments non conformes - -**Quand** le modérateur examine la publicité - -**Alors** la publicité est refusée -**Et** l'annonceur reçoit une notification détaillée: -**Et** l'annonceur peut modifier et resoumettre la publicité -**Et** aucun remboursement n'est effectué pour une publicité refusée - ---- - -## 17. Économie de modération grâce à la prévention - -**Étant donné** que la modération préventive est active - -**Quand** on analyse l'efficacité du système - -**Alors** 80% des contenus inappropriés sont détectés avant publication -**Et** cela réduit le nombre de signalements de 70% -**Et** les ressources de modération sont optimisées -**Et** la qualité de la plateforme est préservée dès le début - ---- - -## 18. Qualité de la plateforme maintenue - -**Étant donné** que tous les nouveaux créateurs sont vérifiés - -**Quand** on analyse la qualité globale des contenus - -**Alors** le taux de contenus inappropriés est <1% -**Et** les utilisateurs font confiance à la plateforme -**Et** la réputation de RoadWave est préservée -**Et** l'expérience utilisateur est optimale - ---- - -## 19. Information claire sur le processus de validation - -**Étant donné** que je suis un nouveau créateur - -**Quand** je consulte la page d'aide "Validation des contenus" - -**Alors** j'apprends que: -**Et** le processus est clair et transparent - ---- - -## 20. Badge "Créateur vérifié" après validation - -**Étant donné** que mes 3 premiers contenus ont été validés avec succès - -**Quand** je consulte mon profil créateur - -**Alors** un badge discret "✓ Créateur vérifié" s'affiche -**Et** ce badge rassure les auditeurs sur la qualité de mes contenus -**Et** il améliore ma crédibilité sur la plateforme - ---- - -## 21. Justification de la modération préventive - -**Étant donné** que la modération préventive est en place - -**Quand** on évalue les bénéfices - -**Alors** les avantages suivants sont constatés: - - | bénéfice | - |---| - | Prévention meilleure que réaction | - | Économie de ressources de modération (×3-5) | - | Qualité de la plateforme préservée dès le début | - | Confiance des utilisateurs renforcée | - | Moins de contenus inappropriés signalés | - | Réputation de la plateforme protégée | - -**Et** l'investissement dans la prévention est rentable - ---- - -## 22. Coût de la modération préventive - -**Étant donné** que 100 nouveaux créateurs publient 3 contenus chacun -**Et** que 50 publicités sont soumises par mois - -**Quand** on calcule le coût de modération préventive - -**Alors** le coût en temps modérateur est: - - | type | nombre | temps/contenu | total | - |---|---|---|---| - | Nouveaux créateurs | 300 | 5 min | 25h | - | Publicités | 50 | 10 min | 8.3h | - -**Et** le coût total est d'environ 33h de modération/mois -**Et** c'est largement compensé par la réduction des signalements réactifs - ---- - - - - - -
- - -# Sanctions et notifications de modération -**27 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur de contenu -> **Et** que j'ai publié un contenu -## 1. Notification multi-canal après sanction - -**Étant donné** que mon contenu a été modéré - -**Quand** la sanction est appliquée - -**Alors** je reçois une notification sur 3 canaux: - - | canal | timing | contenu | - |---|---|---| - | Push | Immédiat | "Votre contenu a été modéré" | - | In-app | Au prochain lancement | Popup détaillée avec bouton "Voir détails" | - | Email | Dans l'heure | Notification complète avec lien d'appel | - -**Et** chaque canal contient un lien vers les détails complets - ---- - -## 2. Notification push immédiate - -**Étant donné** que mon contenu vient d'être modéré - -**Quand** la sanction est appliquée - -**Alors** je reçois une notification push immédiate -**Et** le message est court: "⚠️ Votre contenu a été modéré" -**Et** je peux cliquer pour voir les détails -**Et** la notification utilise Firebase Cloud Messaging (Android) ou APNs (iOS) -**Et** le coût est de 0€ - ---- - -## 3. Popup in-app au prochain lancement - -**Étant donné** que mon contenu a été modéré - -**Quand** j'ouvre l'application - -**Alors** une popup détaillée s'affiche automatiquement -**Et** la popup contient: - - | élément | description | - |---|---| - | Titre du contenu | "Mon podcast #42" | - | Icône d'avertissement | ⚠️ | - | Catégorie violée | 🚫 Haine & violence | - | Sanction | Strike 2/4 - Suspension 7 jours | - | Bouton "Voir détails" | Redirige vers page détaillée | - | Bouton "Compris" | Ferme la popup | - -**Et** je ne peux pas fermer la popup sans l'avoir vue - ---- - -## 4. Email de notification complet dans l'heure - -**Étant donné** que mon contenu a été modéré à 14:00 - -**Quand** la sanction est appliquée - -**Alors** je reçois un email avant 15:00 (dans l'heure) -**Et** l'objet de l'email est "Modération de votre contenu \"[Titre du contenu]\"" -**Et** l'email contient toutes les informations détaillées -**Et** le coût est d'environ 0.001€ par email (Brevo, Resend) - ---- - -## 5. Email de notification complet et structuré - -**Étant donné** que mon contenu "Mon podcast #42" a été modéré - -**Quand** je reçois l'email de notification - -**Alors** l'email contient la structure suivante: - ---- - -## 6. Page détaillée de la sanction in-app - -**Étant donné** que je clique sur "Voir détails" dans la notification - -**Quand** la page détaillée s'affiche - -**Alors** je vois les 6 éléments obligatoires: - - | élément | contenu | - |---|---| - | 1. Catégorie violée | 🚫 Haine & violence (Article 3.2 CGU) | - | 2. Raison détaillée | Explication claire et non juridique | - | 3. Extrait audio | Timestamp exact: 3:42-4:15 | - | 4. Transcription | Texte problématique surligné en rouge | - | 5. Gravité | Strike actuel + conséquences (Strike 2/4, 7j susp) | - | 6. Recours | Lien formulaire d'appel + délai 7j | - - ---- - -## 7. Affichage du passage problématique avec timestamp - -**Étant donné** que la page détaillée de la sanction est affichée - -**Quand** je consulte l'extrait audio concerné - -**Alors** le timestamp exact est affiché: "3:42-4:15" -**Et** je peux écouter uniquement cette portion de l'audio -**Et** un player audio intégré permet l'écoute du passage -**Et** la transcription correspondante est affichée en dessous -**Et** les mots/phrases problématiques sont surlignés en rouge - ---- - -## 8. Référence précise aux CGU - -**Étant donné** que la sanction fait référence à l'Article 3.2 des CGU - -**Quand** je clique sur "Article 3.2" - -**Alors** je suis redirigé vers la section correspondante des CGU -**Et** la section "Haine & violence" est mise en évidence -**Et** je peux lire exactement ce qui est interdit -**Et** cela m'aide à comprendre mon erreur - ---- - -## 9. Gravité de la sanction avec système de strikes - -**Étant donné** que c'est mon 2ème strike - -**Quand** je consulte les détails de la sanction - -**Alors** je vois clairement "Strike 2/4" -**Et** les conséquences sont explicitées: -**Et** je comprends l'escalade des sanctions - ---- - -## 10. Accès au formulaire d'appel depuis la notification - -**Étant donné** que j'ai reçu une notification de modération - -**Quand** je clique sur "Contester cette décision" - -**Alors** je suis redirigé vers le formulaire d'appel -**Et** le formulaire est pré-rempli avec les informations de la sanction -**Et** je peux commencer à rédiger mon appel - ---- - -## 11. Accès au formulaire d'appel depuis "Mes sanctions" - -**Étant donné** que j'ai reçu une sanction il y a 2 jours - -**Quand** j'ouvre "Profil créateur > Mes sanctions" - -**Alors** je vois la liste de mes sanctions -**Et** chaque sanction a un bouton "Faire appel" (si délai <7j) -**Et** je peux accéder au formulaire d'appel - ---- - -## 12. Structure du formulaire d'appel - -**Étant donné** que j'ouvre le formulaire d'appel - -**Quand** le formulaire s'affiche - -**Alors** je vois les champs suivants: - - | champ | type | obligatoire | description | - |---|---|---|---| - | Sanction contestée | Pré-rempli (readonly) | oui | "Strike 2 - Podcast #42" | - | Raison de l'appel | Texte (50-1000 car) | oui | Explication courte de la contestation | - | Arguments détaillés | Zone texte enrichie | oui | Arguments complets | - | Preuves | Upload fichiers | non | Max 5 fichiers, 10 MB total | - -**Et** tous les champs obligatoires sont marqués d'un astérisque - ---- - -## 13. Validation du formulaire d'appel - -**Étant donné** que je remplis le formulaire d'appel - -**Quand** je clique sur "Soumettre l'appel" - -**Alors** le système valide les champs obligatoires -**Et** si un champ obligatoire est vide, une erreur s'affiche -**Et** si la raison fait moins de 50 caractères, une erreur s'affiche -**Et** si tout est valide, l'appel est soumis - ---- - -## 14. Confirmation après soumission de l'appel - -**Étant donné** que j'ai soumis un appel valide - -**Quand** l'appel est enregistré - -**Alors** un numéro de ticket unique est généré: "#MOD-2026-00142" -**Et** un email de confirmation est envoyé: -**Et** le statut de l'appel est "En cours d'examen" -**Et** je peux suivre le statut dans "Mes sanctions" - ---- - -## 15. Délai de soumission de 7 jours maximum - -**Étant donné** que j'ai reçu une sanction le 2026-01-15 - -**Quand** j'essaie de faire appel le 2026-01-25 (10 jours plus tard) - -**Alors** le formulaire d'appel est désactivé -**Et** un message s'affiche: -**Et** je ne peux plus contester la sanction - ---- - -## 16. Bouton "Faire appel" visible si délai respecté - -**Étant donné** que j'ai reçu une sanction il y a 3 jours - -**Quand** je consulte "Mes sanctions" - -**Alors** le bouton "Faire appel" est actif -**Et** un compteur indique "4 jours restants pour faire appel" -**Et** je peux cliquer pour soumettre un appel - ---- - -## 17. SLA de 72h garanti pour appel standard - -**Étant donné** que j'ai soumis un appel standard le lundi à 10:00 - -**Quand** l'appel est en cours de traitement - -**Alors** un modérateur senior est assigné -**Et** l'appel doit être traité avant jeudi 10:00 (72h - 3 jours ouvrés) -**Et** je reçois une réponse dans ce délai - ---- - -## 18. Appel complexe avec notification intermédiaire - -**Étant donné** que j'ai soumis un appel complexe -**Et** que le traitement nécessite plus de 72h - -**Quand** 3 jours se sont écoulés - -**Alors** je reçois un email de notification intermédiaire: -**Et** l'appel est traité sous 5 jours ouvrés au total -**Et** un modérateur senior + admin modération examinent le cas - ---- - -## 19. Appel CRITIQUE traité en 24h - -**Étant donné** que j'ai reçu une suspension longue ou un ban -**Et** que je soumets un appel - -**Quand** l'appel est classé en priorité CRITIQUE - -**Alors** l'admin modération traite l'appel sous 24h -**Et** je reçois une réponse rapide -**Et** le cas est examiné en priorité absolue - ---- - -## 20. Réponse finale détaillée - Appel accepté - -**Étant donné** que mon appel est accepté - -**Quand** je reçois la réponse finale - -**Alors** l'email contient: - - | élément | contenu | - |---|---| - | Décision | Annulation de la sanction | - | Justification | Explication de pourquoi l'appel est accepté | - | Actions | Strike retiré, suspension annulée, contenu rétabli | - | Définitif | "Cette décision est définitive" | - -**Et** le strike est retiré de mon compte -**Et** le contenu est rétabli sur la plateforme -**Et** je peux continuer normalement - ---- - -## 21. Réponse finale détaillée - Appel rejeté - -**Étant donné** que mon appel est rejeté - -**Quand** je reçois la réponse finale - -**Alors** l'email contient: - - | élément | contenu | - |---|---| - | Décision | Maintien de la sanction | - | Justification | Explication de pourquoi l'appel est rejeté | - | Actions | Sanction maintenue, strike conservé | - | Définitif | "Cette décision est définitive" | - -**Et** la sanction reste active -**Et** je ne peux pas faire de second appel -**Et** je dois respecter la suspension - ---- - -## 22. Réponse finale - Réduction de sanction - -**Étant donné** que mon appel est partiellement accepté - -**Quand** je reçois la réponse finale - -**Alors** la décision est "Réduction de sanction" -**Et** l'email explique: -**Et** le strike est réduit -**Et** la suspension est raccourcie -**Et** je suis notifié de la nouvelle date de fin - ---- - -## 23. Suivi du statut de l'appel in-app - -**Étant donné** que j'ai soumis un appel - -**Quand** je consulte "Mes sanctions" - -**Alors** je vois le statut actuel de l'appel: - - | statut | badge | couleur | - |---|---|---| - | En cours d'examen | En cours 🔍 | orange | - | Appel accepté | Accepté ✓ | vert | - | Appel rejeté | Rejeté ✗ | rouge | - | Sanction réduite | Partiellement accepté | bleu | - -**Et** une notification badge m'alerte quand le statut change - ---- - -## 24. Historique complet des sanctions visible - -**Étant donné** que je suis un créateur - -**Quand** j'ouvre "Profil créateur > Mes sanctions" - -**Alors** je vois la liste complète de mes sanctions passées: - - | colonne | description | - |---|---| - | Date | 15/01/2026 | - | Contenu | "Mon podcast #42" | - | Catégorie | 🚫 Haine & violence | - | Sanction | Strike 2 - Suspension 7j | - | Statut | Active / Terminée / Annulée | - | Appel | Aucun / Accepté / Rejeté | - -**Et** les sanctions sont triées par date décroissante - ---- - -## 25. Conformité DSA - Transparence obligatoire - -**Étant donné** que le système de sanction est en place - -**Quand** un audit DSA est effectué - -**Alors** chaque sanction contient: - - | élément DSA | présent | - |---|---| - | Référence précise à la règle violée | oui | - | Explication claire et compréhensible | oui | - | Preuve (extrait + transcription) | oui | - | Possibilité de recours (appel) | oui | - | Délai de recours clairement indiqué | oui | - | Réponse motivée au recours | oui | - -**Et** le système est conforme au Digital Services Act - ---- - -## 26. Décision définitive après premier appel - -**Étant donné** que mon premier appel a été rejeté - -**Quand** j'essaie de faire un second appel - -**Alors** le bouton "Faire appel" est désactivé -**Et** un message s'affiche: "Cette décision est définitive. Aucun second appel n'est possible." -**Et** je ne peux plus contester la sanction -**Et** je dois respecter la décision finale - ---- - -## 27. Coût des notifications multi-canal - -**Étant donné** que 100 sanctions sont appliquées en un mois - -**Quand** on calcule le coût des notifications - -**Alors** le coût total est d'environ 0.10€: - - | canal | coût unitaire | coût pour 100 | - |---|---|---| - | Email | 0.001€ | 0.10€ | - | Push | 0€ | 0€ | - | In-app | 0€ | 0€ | - -**Et** le coût est négligeable même à grande échelle - ---- - - - - - -
- - -# Signalement de contenu inapproprié -**23 scénarios** (22 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur connecté -> **Et** que je suis en train d'écouter un contenu -## 1. Affichage du formulaire de signalement - -**Étant donné** que j'écoute un contenu inapproprié - -**Quand** j'ouvre le menu du contenu -**Et** que je clique sur "Signaler" - -**Alors** un formulaire de signalement s'affiche -**Et** le formulaire contient une liste déroulante "Catégorie du problème" -**Et** le formulaire contient un champ texte "Commentaire (optionnel)" -**Et** le formulaire contient un bouton "Envoyer le signalement" - ---- - -## 2. Liste des 7 catégories prédéfinies - -**Étant donné** que le formulaire de signalement est affiché - -**Quand** je clique sur la liste déroulante "Catégorie du problème" - -**Alors** je vois les 7 catégories suivantes: - - | icône | catégorie | description | - |---|---|---| - | 🚫 | Haine & violence | Incitation à la haine, discrimination, menaces | - | 🔞 | Contenu sexuel | Pornographie, contenu explicite | - | ⚖️ | Illégalité | Terrorisme, apologie de crimes | - | 🎵 | Droits d'auteur | Musique/contenu protégé non autorisé | - | 📧 | Spam | Publicité non sollicitée, répétition | - | ❌ | Fausse information | Désinformation sur santé, sécurité routière | - | 🔧 | Autre | Champ texte obligatoire si sélectionné | - -**Et** chaque catégorie a une description claire - ---- - -## 3. Sélection de la catégorie "Haine & violence" - -**Étant donné** que le formulaire de signalement est affiché - -**Quand** je sélectionne la catégorie "🚫 Haine & violence" - -**Alors** la catégorie est sélectionnée -**Et** la description "Incitation à la haine, discrimination, menaces" s'affiche -**Et** je peux passer au champ commentaire - ---- - -## 4. Catégorie "Autre" nécessite un commentaire obligatoire - -**Étant donné** que le formulaire de signalement est affiché - -**Quand** je sélectionne la catégorie "🔧 Autre" - -**Alors** le champ "Commentaire" devient obligatoire -**Et** un message s'affiche: "Veuillez décrire le problème (obligatoire)" -**Et** le placeholder change en "Décrivez le problème rencontré" -**Et** je ne peux pas envoyer le signalement sans commentaire - ---- - -## 5. Champ commentaire optionnel avec incitation - -**Étant donné** que le formulaire de signalement est affiché -**Et** que j'ai sélectionné une catégorie autre que "Autre" - -**Quand** je consulte le champ "Commentaire" - -**Alors** le champ est optionnel (pas d'astérisque rouge) -**Et** le placeholder indique "Décrivez le problème (optionnel mais recommandé)" -**Et** la limite de caractères est de 500 -**Et** un compteur affiche "0/500" - ---- - -## 6. Envoi de signalement sans commentaire - -**Étant donné** que j'ai sélectionné la catégorie "📧 Spam" -**Et** que je n'ai pas rempli le champ commentaire - -**Quand** je clique sur "Envoyer le signalement" - -**Alors** le signalement est envoyé avec succès -**Et** aucune erreur de validation ne s'affiche -**Et** le commentaire est enregistré comme vide - ---- - -## 7. Envoi de signalement avec commentaire - -**Étant donné** que j'ai sélectionné la catégorie "🚫 Haine & violence" -**Et** que j'ai saisi le commentaire "Le créateur tient des propos discriminatoires à 2:30" - -**Quand** je clique sur "Envoyer le signalement" - -**Alors** le signalement est envoyé avec succès -**Et** le commentaire est enregistré avec le signalement -**Et** il sera visible par les modérateurs - ---- - -## 8. Limite de 500 caractères pour le commentaire - -**Étant donné** que le formulaire de signalement est affiché - -**Quand** je saisis un commentaire de 501 caractères - -**Alors** le champ limite automatiquement à 500 caractères -**Et** le compteur affiche "500/500" -**Et** les caractères supplémentaires ne sont pas acceptés - ---- - -## 9. Toast de confirmation après signalement - -**Étant donné** que j'ai envoyé un signalement - -**Quand** le signalement est enregistré - -**Alors** un toast notification s'affiche -**Et** le toast contient le message "✓ Signalement envoyé. Nous l'examinerons sous 24-48h." -**Et** le toast s'affiche pendant 5 secondes -**Et** le toast contient un bouton "Voir mes signalements" -**Et** je peux fermer le toast manuellement avec un bouton X - ---- - -## 10. Accès à l'historique des signalements via le toast - -**Étant donné** que le toast de confirmation est affiché - -**Quand** je clique sur "Voir mes signalements" - -**Alors** je suis redirigé vers la page "Mes signalements" -**Et** je vois la liste de tous mes signalements -**Et** le signalement que je viens d'envoyer apparaît en premier - ---- - -## 11. Historique personnel des signalements - -**Étant donné** que j'ai envoyé 3 signalements précédemment - -**Quand** j'ouvre "Profil > Mes signalements" - -**Alors** je vois la liste de mes 3 signalements -**Et** chaque signalement affiche: - - | information | description | - |---|---| - | Titre du contenu | "Podcast #42" | - | Créateur | @pseudo_createur | - | Catégorie | 🚫 Haine & violence | - | Date | 15/01/2026 | - | Statut | En cours / Traité / Rejeté | - | Mon commentaire | Texte que j'ai saisi | - -**Et** les signalements sont triés par date décroissante - ---- - -## 12. 📋 Plan: Statuts possibles d'un signalement - -**Étant donné** que j'ai envoyé un signalement - -**Quand** le statut du signalement est "" - -**Alors** le badge affiché est "" -**Et** la couleur du badge est "" - -**📊 Exemples de données:** - -| statut | badge | couleur | -|---|---|---| -| En cours | En cours | orange | -| Traité | Traité ✓ | vert | -| Rejeté | Rejeté ✗ | rouge | - ---- - -## 13. Notification in-app si action prise - -**Étant donné** que j'ai signalé un contenu il y a 24h - -**Quand** le modérateur traite mon signalement -**Et** que le contenu est effectivement retiré - -**Alors** je reçois une notification in-app -**Et** la notification indique "Votre signalement a été traité. Le contenu a été retiré." -**Et** le statut de mon signalement passe à "Traité ✓" -**Et** je peux voir les détails de l'action prise - ---- - -## 14. Notification si signalement rejeté - -**Étant donné** que j'ai signalé un contenu - -**Quand** le modérateur rejette mon signalement - -**Alors** je reçois une notification in-app -**Et** la notification indique "Votre signalement a été examiné. Le contenu ne viole pas les règles de la communauté." -**Et** le statut de mon signalement passe à "Rejeté ✗" -**Et** je peux voir la raison du rejet - ---- - -## 15. Un contenu peut être signalé plusieurs fois - -**Étant donné** qu'un contenu a déjà été signalé par 5 autres utilisateurs - -**Quand** je signale le même contenu - -**Alors** mon signalement est enregistré indépendamment -**Et** le compteur de signalements du contenu passe à 6 -**Et** mon signalement rejoint la file d'attente de modération -**Et** les signalements cumulés augmentent la priorité de traitement - ---- - -## 16. Limite de signalements par utilisateur - -**Étant donné** que j'ai déjà signalé le même contenu il y a 2 jours - -**Quand** j'essaie de signaler à nouveau le même contenu - -**Alors** un message m'informe "Vous avez déjà signalé ce contenu" -**Et** le formulaire de signalement n'est pas affiché -**Et** je peux consulter le statut de mon signalement précédent - ---- - -## 17. Détection de signalements abusifs répétés - -**Étant donné** que j'ai envoyé 10 signalements ce mois-ci -**Et** que 8 d'entre eux ont été rejetés comme infondés - -**Quand** j'essaie d'envoyer un nouveau signalement - -**Alors** mon compte est marqué comme "signaleur suspect" -**Et** un avertissement s'affiche: -**Et** je peux toujours envoyer le signalement -**Mais** mes futurs signalements auront une priorité réduite - ---- - -## 18. Sanction pour signalements abusifs graves - -**Étant donné** que j'ai envoyé 20 signalements abusifs en 1 mois -**Et** que tous ont été rejetés comme volontairement faux - -**Quand** le modérateur détecte le pattern abusif - -**Alors** mon compte reçoit un avertissement formel -**Et** je perds la possibilité de signaler pendant 30 jours -**Et** je reçois un email m'expliquant la sanction - ---- - -## 19. Signalement depuis le player audio - -**Étant donné** que j'écoute un contenu - -**Quand** j'ouvre le menu "⋮" du player - -**Alors** je vois l'option "Signaler" -**Et** je peux ouvrir le formulaire de signalement - ---- - -## 20. Signalement depuis la page de détails du contenu - -**Étant donné** que je consulte la page de détails d'un contenu - -**Quand** je clique sur le bouton "⋮" en haut à droite - -**Alors** je vois l'option "Signaler" -**Et** je peux ouvrir le formulaire de signalement - ---- - -## 21. Signalement depuis l'historique d'écoute - -**Étant donné** que je consulte mon historique d'écoute - -**Quand** je clique sur "⋮" à côté d'un contenu passé - -**Alors** je vois l'option "Signaler" -**Et** je peux signaler ce contenu même si je ne l'écoute plus actuellement - ---- - -## 22. Identité du signaleur anonyme pour le créateur - -**Étant donné** que j'ai signalé un contenu - -**Quand** le créateur est notifié de la modération - -**Alors** mon identité reste anonyme -**Et** le créateur ne peut pas savoir qui a signalé -**Et** seuls les modérateurs ont accès à l'identité du signaleur - ---- - -## 23. Coût du système de signalement - -**Étant donné** que le système de signalement est en place - -**Quand** on calcule le coût - -**Alors** le coût est de 0€ -**Et** le formulaire est développé en interne -**Et** aucun service tiers n'est utilisé -**Et** les notifications in-app sont gratuites - ---- - - - - - -
- - -# Traitement des signalements par l'IA et les modérateurs -**25 scénarios** (21 standards, 4 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que le système de modération est actif -## 1. Signalement ajouté à la file d'attente asynchrone - -**Étant donné** qu'un utilisateur envoie un signalement pour un contenu audio - -**Quand** le signalement est reçu - -**Alors** le signalement est ajouté à la file d'attente asynchrone -**Et** un worker de traitement est déclenché -**Et** le traitement se fait en arrière-plan sans bloquer l'utilisateur - ---- - -## 2. Transcription automatique avec Whisper large-v3 - -**Étant donné** qu'un contenu audio signalé dure 5 minutes - -**Quand** le worker de traitement démarre - -**Alors** le système utilise Whisper large-v3 pour transcrire l'audio -**Et** la transcription est en self-hosted (pas de service cloud) -**Et** le texte transcrit est enregistré en base de données -**Et** le délai de transcription est de 1-3 minutes - ---- - -## 3. 📋 Plan: Délai de transcription selon durée audio - -**Étant donné** qu'un contenu audio signalé dure minutes - -**Quand** le système transcrit l'audio - -**Alors** la transcription prend environ - -**📊 Exemples de données:** - -| duree | delai | -|---|---| -| 2 | 1-3 minutes | -| 10 | 3-10 minutes | -| 45 | 10-20 minutes | - ---- - -## 4. Analyse automatique du contenu transcrit - -**Étant donné** que la transcription audio est terminée - -**Quand** le système analyse le texte transcrit - -**Alors** les analyses suivantes sont effectuées: - - | analyse | technologie | - |---|---| - | Analyse de sentiment | distilbert-base-uncased | - | Détection de haine | facebook/roberta-hate-speech | - | Mots-clés interdits | Liste noire FR/EN + regex | - -**Et** chaque analyse génère un score de confiance (0-100%) - ---- - -## 5. Génération du score de confiance IA - -**Étant donné** que toutes les analyses sont terminées - -**Quand** le système calcule le score final - -**Alors** un score de confiance IA entre 0-100% est généré -**Et** le score indique la probabilité que le contenu viole les règles -**Et** la catégorie la plus probable est identifiée -**Et** les timestamps des passages problématiques sont extraits - ---- - -## 6. Détection automatique de contenu clairement inapproprié - -**Étant donné** qu'un contenu contient des insultes graves et répétées - -**Quand** l'IA analyse la transcription - -**Alors** le score de confiance IA est >95% -**Et** la catégorie détectée est "Haine & violence" -**Et** les passages problématiques sont identifiés avec timestamps: - - | timestamp | texte problématique | - |---|---| - | 02:15 | [insulte discriminatoire] | - | 03:42 | [propos haineux] | - -**Et** le signalement est classé en priorité CRITIQUE - ---- - -## 7. 📋 Plan: SLA selon priorité du signalement - -**Étant donné** qu'un signalement a une priorité "" - -**Quand** le signalement entre en file d'attente - -**Alors** le délai de traitement cible est "" -**Et** le responsable du traitement est "" - -**📊 Exemples de données:** - -| priorite | delai | responsable | -|---|---|---| -| CRITIQUE | <2h (24/7) | Modérateur senior (astreinte) | -| HAUTE | <24h (jours ouvrés) | Modérateur junior/senior | -| MOYENNE | <24h (jours ouvrés) | Modérateur junior | -| BASSE | <72h (jours ouvrés) | Modérateur junior | - ---- - -## 8. Traitement automatique pour score IA >95% - -**Étant donné** qu'un signalement a un score IA de 97% -**Et** que la catégorie détectée est "Spam" (évidente) - -**Quand** le système évalue le signalement - -**Alors** une action automatique immédiate est déclenchée -**Et** le contenu est retiré automatiquement -**Et** le créateur est notifié de la modération -**Et** le créateur peut faire appel de la décision -**Et** un modérateur senior vérifie l'action a posteriori - ---- - -## 9. Signalement CRITIQUE traité en moins de 2h - -**Étant donné** qu'un signalement de priorité CRITIQUE est reçu à 14:00 -**Et** que le contenu concerne une menace de violence - -**Quand** le signalement est assigné à un modérateur senior d'astreinte - -**Alors** le modérateur est alerté immédiatement (push + SMS) -**Et** le signalement est traité avant 16:00 (2h) -**Et** une décision est prise et appliquée -**Et** les autorités peuvent être contactées si nécessaire - ---- - -## 10. Astreinte modérateur 24/7 pour signalements CRITIQUES - -**Étant donné** qu'un signalement CRITIQUE est reçu un dimanche à 03:00 - -**Quand** le signalement est classé en priorité CRITIQUE - -**Alors** le modérateur senior d'astreinte est alerté -**Et** le signalement est traité dans les 2h (avant 05:00) -**Et** le service d'astreinte garantit une disponibilité 24/7 - ---- - -## 11. Signalement HAUTE priorité traité en moins de 24h - -**Étant donné** qu'un signalement de priorité HAUTE est reçu lundi à 10:00 -**Et** que le contenu concerne du harcèlement - -**Quand** le signalement entre en file d'attente - -**Alors** le signalement est assigné à un modérateur (junior ou senior) -**Et** le signalement est traité avant mardi 10:00 (24h jours ouvrés) -**Et** une décision est prise et appliquée - ---- - -## 12. Signalement BASSE priorité traité en moins de 72h - -**Étant donné** qu'un signalement de priorité BASSE est reçu lundi à 10:00 -**Et** que le contenu concerne des tags incorrects - -**Quand** le signalement entre en file d'attente - -**Alors** le signalement est traité avant jeudi 10:00 (72h jours ouvrés) -**Et** un modérateur junior peut traiter ce type de signalement - ---- - -## 13. Calcul du score de priorité - -**Étant donné** qu'un signalement a les caractéristiques suivantes: - - | caractéristique | valeur | - |---|---| - | Score IA | 85% | - | Signalements cumulés | 3 | - | Fiabilité du signaleur | 75% | - - -**Quand** le système calcule la priorité - -**Alors** la formule appliquée est: -**Et** le score de priorité est: (85 × 0.7) + (3 × 0.2) + (75 × 0.1) = 67.5 -**Et** le signalement est classé en priorité MOYENNE - ---- - -## 14. 📋 Plan: Classification selon score de priorité - -**Étant donné** qu'un signalement a un score de priorité de - -**Quand** le système classe le signalement - -**Alors** la priorité assignée est "" -**Et** le signalement entre dans la file "" - -**📊 Exemples de données:** - -| score | priorite | file | -|---|---|---| -| 95 | CRITIQUE | Immédiate | -| 82 | HAUTE | Prioritaire | -| 55 | MOYENNE | Normale | -| 25 | BASSE | Différée | - ---- - -## 15. Boost de priorité avec signalements cumulés - -**Étant donné** qu'un contenu a été signalé par 1 utilisateur avec un score IA de 60% -**Et** que le signalement est classé en priorité MOYENNE (score 42) - -**Quand** 5 autres utilisateurs signalent le même contenu - -**Alors** le nombre de signalements cumulés passe à 6 -**Et** le score de priorité augmente significativement -**Et** le signalement peut passer en priorité HAUTE -**Et** le traitement est accéléré - ---- - -## 16. Impact de la fiabilité du signaleur - -**Étant donné** qu'un utilisateur de confiance (90% fiabilité) envoie un signalement -**Et** qu'un utilisateur suspect (20% fiabilité) envoie un signalement similaire - -**Quand** le système calcule les priorités - -**Alors** le signalement de l'utilisateur de confiance a un score plus élevé -**Et** son signalement est traité en priorité -**Et** le signalement de l'utilisateur suspect est traité plus tard - ---- - -## 17. Évolution du score de fiabilité du signaleur - -**Étant donné** qu'un utilisateur a envoyé 10 signalements -**Et** que 8 d'entre eux ont été acceptés par les modérateurs - -**Quand** le système calcule son score de fiabilité - -**Alors** le score est de 80% (8 acceptés / 10 total) -**Et** ses futurs signalements auront plus de poids -**Et** il peut devenir "utilisateur de confiance" - ---- - -## 18. Files d'attente séparées par priorité - -**Étant donné** que 50 signalements sont en attente - -**Quand** le système organise la file d'attente - -**Alors** les signalements sont répartis dans les files suivantes: - - | file | nombre | priorité | - |---|---|---| - | Immédiate (24/7) | 5 | CRITIQUE | - | Prioritaire | 15 | HAUTE | - | Normale | 20 | MOYENNE | - | Différée | 10 | BASSE | - -**Et** les modérateurs traitent en priorité la file Immédiate - ---- - -## 19. Modérateurs assignés selon compétences - -**Étant donné** qu'un signalement complexe de harcèlement est reçu - -**Quand** le système assigne un modérateur - -**Alors** un modérateur senior est prioritairement assigné -**Et** les modérateurs juniors peuvent traiter les cas simples (spam, tags) -**Et** les modérateurs seniors traitent les cas complexes (haine, violence, appels) - ---- - -## 20. Stack technique 100% opensource - -**Étant donné** que le système de modération IA est déployé - -**Quand** on analyse les technologies utilisées - -**Alors** toutes les technologies sont opensource: - - | composant | technologie | hébergement | - |---|---|---| - | Transcription | Whisper large-v3 | Self-hosted | - | Analyse sentiment | distilbert-base-uncased | Self-hosted | - | Détection haine | facebook/roberta-hate-speech | Self-hosted | - | Mots-clés interdits | Liste noire FR/EN + regex | PostgreSQL | - -**Et** aucune dépendance à Google, AWS, Azure - ---- - -## 21. 📋 Plan: Coût selon phase du projet - -**Étant donné** que RoadWave est en phase "" - -**Quand** on calcule le coût de l'infrastructure IA - -**Alors** le coût mensuel est "" - -**📊 Exemples de données:** - -| phase | cout | -|---|---| -| MVP | 0-50€ (CPU) | -| Scale | 50-200€ (GPU VPS) | - ---- - -## 22. Processing asynchrone en MVP avec CPU - -**Étant donné** que RoadWave est en phase MVP -**Et** que le volume est <1000 signalements/mois - -**Quand** le système traite les signalements - -**Alors** un serveur CPU standard est suffisant -**Et** le coût est de 0€ (serveur existant) -**Et** le processing asynchrone absorbe les pics de charge -**Et** les délais restent acceptables (1-20 minutes) - ---- - -## 23. Scaling avec GPU pour gros volumes - -**Étant donné** que RoadWave reçoit >1000 signalements/jour - -**Quand** le système nécessite un scaling - -**Alors** un VPS avec GPU est requis -**Et** le coût passe à 50-200€/mois -**Et** les délais de transcription sont divisés par 5-10 -**Et** le système peut gérer 10 000+ signalements/mois - ---- - -## 24. Logs d'audit pour chaque traitement - -**Étant donné** qu'un signalement est traité - -**Quand** une action est prise (rejet, acceptation, sanction) - -**Alors** un log d'audit complet est créé: - - | champ | description | - |---|---| - | signalement_id | ID unique du signalement | - | content_id | ID du contenu signalé | - | ia_score | Score de confiance IA | - | ia_category | Catégorie détectée par IA | - | priority | CRITIQUE / HAUTE / MOYENNE / BASSE | - | moderator_id | ID du modérateur assigné | - | action_taken | Retiré / Rejeté / Strike | - | processing_time | Durée du traitement | - | timestamp | Date et heure de la décision | - -**Et** le log est conservé pour conformité DSA -**Et** les logs sont anonymisés après 3 ans (RGPD) - ---- - -## 25. Traçabilité complète pour conformité DSA - -**Étant donné** que le système de modération est actif - -**Quand** un audit DSA est effectué - -**Alors** toutes les actions de modération sont tracées -**Et** les délais de traitement sont mesurés et respectés -**Et** les décisions sont justifiées et documentées -**Et** la transparence vis-à-vis des utilisateurs est garantie -**Et** le système est conforme au Digital Services Act - ---- - - - - - -
- - -# Conditions d'activation de la monétisation -> *En tant que créateur* -> *Je veux pouvoir activer la monétisation quand je remplis les critères* -> *Afin de générer des revenus avec mes contenus* - -**28 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant que créateur -## 1. Critère 1 - Ancienneté de 3 mois validée - -**Étant donné** que mon compte a été créé il y a 91 jours - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "Ancienneté ≥ 3 mois" est validé ✅ - ---- - -## 2. Critère 1 - Ancienneté insuffisante - -**Étant donné** que mon compte a été créé il y a 60 jours - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "Ancienneté ≥ 3 mois" n'est pas validé ❌ -**Et** je vois "Encore 30 jours avant d'être éligible" - ---- - -## 3. Critère 2 - 500 abonnés atteints - -**Étant donné** que j'ai exactement 500 abonnés - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "≥ 500 abonnés" est validé ✅ - ---- - -## 4. Critère 2 - Pas assez d'abonnés - -**Étant donné** que j'ai 347 abonnés - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "≥ 500 abonnés" n'est pas validé ❌ -**Et** je vois "Encore 153 abonnés nécessaires" - ---- - -## 5. Critère 3 - 10 000 écoutes complètes atteintes - -**Étant donné** que mes contenus ont cumulé 10 487 écoutes complètes - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "≥ 10 000 écoutes complètes" est validé ✅ - ---- - -## 6. Critère 3 - Écoutes incomplètes non comptabilisées - -**Étant donné** que mes contenus ont: - - | type écoute | nombre | - |---|---| - | Écoutes complètes | 8 500 | - | Écoutes <80% | 3 000 | - - -**Quand** je consulte les critères de monétisation - -**Alors** seules les 8 500 écoutes complètes comptent -**Et** je vois "Encore 1 500 écoutes complètes nécessaires" - ---- - -## 7. Critère 4 - Aucun strike actif - -**Étant donné** que je n'ai aucun strike actif -**Et** que je n'ai eu aucun contenu modéré dans les 6 derniers mois - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "Fiabilité" est validé ✅ - ---- - -## 8. Critère 4 - Strike actif bloque l'éligibilité - -**Étant donné** que j'ai 1 strike actif pour contenu inapproprié - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "Fiabilité" n'est pas validé ❌ -**Et** je vois "Vous devez résoudre votre strike avant d'être éligible" - ---- - -## 9. Critère 4 - Contenu modéré dans les 6 derniers mois - -**Étant donné** que je n'ai pas de strike actif -**Mais** qu'un de mes contenus a été modéré il y a 4 mois - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "Fiabilité" n'est pas validé ❌ -**Et** je vois "Attendre 2 mois après le dernier contenu modéré" - ---- - -## 10. Critère 5 - 5 contenus publiés dans les 90 derniers jours - -**Étant donné** que j'ai publié: - - | date de publication | titre | - |---|---| - | Il y a 15 jours | Contenu 1 | - | Il y a 30 jours | Contenu 2 | - | Il y a 45 jours | Contenu 3 | - | Il y a 60 jours | Contenu 4 | - | Il y a 75 jours | Contenu 5 | - - -**Quand** je consulte les critères de monétisation - -**Alors** le critère "≥ 5 contenus publiés dans les 90 derniers jours" est validé ✅ - ---- - -## 11. Critère 5 - Contenus trop anciens ne comptent pas - -**Étant donné** que j'ai publié: - - | date de publication | titre | - |---|---| - | Il y a 15 jours | Contenu 1 | - | Il y a 30 jours | Contenu 2 | - | Il y a 95 jours | Contenu 3 | - | Il y a 120 jours | Contenu 4 | - - -**Quand** je consulte les critères de monétisation - -**Alors** seuls 2 contenus comptent (dans les 90 jours) -**Et** je vois "Encore 3 contenus à publier dans les 90 prochains jours" - ---- - -## 12. Tous les critères validés - Bouton disponible - -**Étant donné** que tous mes critères sont validés: - - | critère | statut | - |---|---| - | Ancienneté ≥ 3 mois | ✅ | - | ≥ 500 abonnés | ✅ | - | ≥ 10 000 écoutes | ✅ | - | Fiabilité | ✅ | - | Régularité (5 contenus) | ✅ | - - -**Quand** j'accède à mon profil créateur - -**Alors** le bouton "Demander la monétisation" est actif -**Et** je peux cliquer pour démarrer le KYC - ---- - -## 13. Critères incomplets - Bouton grisé avec progression - -**Étant donné** que mes critères sont: - - | critère | statut | progression | - |---|---|---| - | Ancienneté ≥ 3 mois | ✅ | 100% | - | ≥ 500 abonnés | ❌ | 347/500 (69%) | - | ≥ 10 000 écoutes | ❌ | 8500/10000 (85%) | - | Fiabilité | ✅ | 100% | - | Régularité (5 contenus) | ✅ | 100% | - - -**Quand** j'accède à mon profil créateur - -**Alors** le bouton "Demander la monétisation" est grisé -**Et** je vois la progression détaillée de chaque critère - ---- - -## 14. Vérification automatique SQL lors de la demande - -**Étant donné** que je clique sur "Demander la monétisation" - -**Quand** le système vérifie mes critères - -**Alors** une requête SQL est exécutée: -**Et** si tous les critères sont TRUE, je suis redirigé vers le KYC - ---- - -## 15. Notification par email quand critères atteints - -**Étant donné** que je viens d'atteindre 500 abonnés -**Et** que c'était mon dernier critère manquant - -**Quand** le système détecte l'éligibilité - -**Alors** je reçois un email: - ---- - -## 16. Badge "Éligible monétisation" dans profil - -**Étant donné** que je remplis tous les critères -**Mais** que je n'ai pas encore activé la monétisation - -**Quand** un utilisateur consulte mon profil - -**Alors** il voit un badge "Éligible monétisation 💰" -**Et** cela renforce ma crédibilité de créateur - ---- - -## 17. Justification anti-fraude - Délai 3 mois - -**Étant donné** qu'un compte suspect crée du contenu frauduleux - -**Quand** le compte est détecté dans les 2 premiers mois - -**Alors** le compte est banni avant d'atteindre les 3 mois -**Et** le créateur n'a jamais été éligible à la monétisation -**Et** aucun paiement n'a été effectué - ---- - -## 18. Justification qualité - 10 000 écoutes - -**Étant donné** qu'un créateur produit du contenu de mauvaise qualité - -**Quand** ses contenus ne génèrent que 2 000 écoutes complètes - -**Alors** il ne peut pas activer la monétisation -**Et** seuls les créateurs avec contenu apprécié sont monétisés - ---- - -## 19. Réduction coût administratif plateforme - -**Étant donné** que RoadWave a 10 000 créateurs inscrits -**Et** que seuls 500 remplissent tous les critères - -**Quand** le système calcule le coût administratif - -**Alors** seulement 500 KYC sont à gérer (vs 10 000) -**Et** seulement 500 virements mensuels (vs 10 000) -**Et** la charge comptable est réduite de 95% - ---- - -## 20. Statistiques publiques pour transparence - -**Quand** un utilisateur consulte la page "Devenir créateur" - -**Alors** il voit les statistiques: - - | métrique | valeur exemple | - |---|---| - | Nombre créateurs monétisés | 1 247 | - | Revenus moyens par créateur | 127€/mois | - | Top créateur (anonymisé) | 2 450€/mois | - | Critères d'éligibilité à remplir | 5 critères | - -**Et** cela permet de fixer des attentes réalistes - ---- - -## 21. Cache Redis pour calcul rapide critères - -**Étant donné** que je consulte mes critères de monétisation - -**Quand** le système charge la page - -**Alors** les compteurs sont récupérés depuis Redis: - - | clé Redis | exemple valeur | - |---|---| - | creator:[id]:subscribers_count | 347 | - | creator:[id]:complete_listens_total | 8500 | - | creator:[id]:recent_contents_count | 7 | - -**Et** le temps de réponse est <50ms - ---- - -## 22. Mise à jour temps réel des compteurs - -**Étant donné** que je viens de publier un nouveau contenu - -**Quand** un utilisateur écoute ce contenu en entier - -**Alors** le compteur "complete_listens_total" est incrémenté immédiatement -**Et** si je rafraîchis la page critères, je vois la nouvelle valeur -**Et** cela encourage les créateurs à continuer de produire - ---- - -## 23. Historique des tentatives d'activation - -**Étant donné** que j'ai tenté d'activer la monétisation il y a 2 mois -**Mais** que les critères n'étaient pas remplis - -**Quand** j'accède à mes logs d'activité - -**Alors** je vois: - - | date | action | résultat | raison | - |---|---|---|---| - | 2025-11-15 | Demande monétisation | Refusée | Seulement 300 abonnés | - -**Et** cela m'aide à suivre ma progression - ---- - -## 24. Performance avec 100 000 créateurs - -**Étant donné** que RoadWave a 100 000 créateurs -**Et** que chacun consulte ses critères 1 fois par jour - -**Quand** le système traite ces requêtes - -**Alors** la table users est indexée sur created_at -**Et** la table subscriptions est indexée sur creator_id -**Et** la table contents est indexée sur creator_id et published_at -**Et** chaque requête reste <50ms grâce aux index - ---- - -## 25. Export des critères pour support client - -**Étant donné** que je contacte le support car je pense être éligible - -**Quand** l'agent support consulte mon compte - -**Alors** il voit un export JSON complet: -**Et** l'agent peut expliquer précisément pourquoi je ne suis pas éligible - ---- - -## 26. Notification 30 jours avant éligibilité probable - -**Étant donné** que mes critères sont: - - | critère | statut | progression | - |---|---|---| - | Ancienneté ≥ 3 mois | ❌ | 60/90 jours | - | Tous les autres critères | ✅ | 100% | - - -**Quand** il reste exactement 30 jours avant les 90 jours - -**Alors** je reçois une notification: - ---- - -## 27. Pas de bypass possible pour amis/influenceurs - -**Étant donné** qu'un créateur influent me contacte directement -**Et** qu'il demande un bypass des critères - -**Quand** je consulte la politique RoadWave - -**Alors** la réponse est "Aucune exception possible, critères automatiques uniquement" -**Et** cela garantit l'équité pour tous les créateurs - ---- - -## 28. A/B test futur sur seuils (post-MVP) - -**Étant donné** que RoadWave veut tester des seuils différents - -**Quand** un A/B test est lancé en 2027 - -**Alors** groupe A voit: 500 abonnés, 10 000 écoutes -**Et** groupe B voit: 300 abonnés, 5 000 écoutes -**Et** les métriques (taux activation, fraude, qualité) sont comparées -**Et** le meilleur seuil est déployé définitivement - ---- - - - - - -
- - -# Contenus Premium exclusifs -> *En tant que créateur monétisé* -> *Je veux pouvoir rendre certains contenus exclusifs aux abonnés Premium* -> *Afin d'inciter les utilisateurs à s'abonner* - -**34 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur avec la monétisation activée -## 1. Toggle "Réservé Premium" lors de la création - -**Étant donné** que je crée un nouveau contenu - -**Quand** j'accède aux options de publication - -**Alors** je vois un toggle "Réservé aux abonnés Premium 👑" -**Et** je peux l'activer ou le désactiver - ---- - -## 2. Contenu marqué Premium lors de la création - -**Étant donné** que je crée un nouveau contenu - -**Quand** j'active le toggle "Réservé Premium" -**Et** que je publie le contenu - -**Alors** le champ `is_premium` en base est mis à `true` -**Et** le contenu est visible uniquement pour les utilisateurs Premium - ---- - -## 3. Contenu gratuit par défaut - -**Étant donné** que je crée un nouveau contenu - -**Quand** je ne touche pas au toggle "Réservé Premium" -**Et** que je publie le contenu - -**Alors** le champ `is_premium` en base est mis à `false` (défaut) -**Et** le contenu est accessible à tous les utilisateurs - ---- - -## 4. Modification d'un contenu existant en Premium - -**Étant donné** que j'ai publié un contenu gratuit il y a 2 jours - -**Quand** je modifie le contenu et active le toggle "Réservé Premium" -**Et** que j'enregistre les modifications - -**Alors** le contenu devient immédiatement Premium -**Et** les utilisateurs gratuits ne peuvent plus y accéder - ---- - -## 5. Passage d'un contenu Premium en gratuit - -**Étant donné** que j'ai publié un contenu Premium il y a 1 mois - -**Quand** je modifie le contenu et désactive le toggle "Réservé Premium" -**Et** que j'enregistre les modifications - -**Alors** le contenu devient immédiatement gratuit -**Et** tous les utilisateurs peuvent maintenant y accéder - ---- - -## 6. Aucune limite sur pourcentage de contenus Premium - -**Étant donné** que je publie 10 nouveaux contenus - -**Quand** je décide de rendre les 10 contenus Premium (100%) - -**Alors** le système accepte sans limitation -**Et** je peux avoir 100% de mon catalogue en Premium - ---- - -## 7. Stratégie freemium - Mix gratuit/premium - -**Étant donné** que je publie 10 nouveaux contenus - -**Quand** je décide de rendre 5 contenus Premium et 5 gratuits (50/50) - -**Alors** le système accepte cette stratégie -**Et** je peux tester différents mix pour optimiser mes revenus - ---- - -## 8. Stratégie tout gratuit possible - -**Étant donné** que je suis monétisé via publicités - -**Quand** je décide de ne mettre aucun contenu en Premium (0%) - -**Alors** le système accepte cette stratégie -**Et** je génère des revenus uniquement via les publicités - ---- - -## 9. Badge 👑 visible sur l'interface utilisateur - -**Étant donné** qu'un utilisateur consulte ma liste de contenus - -**Quand** il voit un contenu Premium - -**Alors** un badge 👑 "Premium" est affiché -**Et** le contenu est clairement identifiable comme réservé - ---- - -## 10. Utilisateur gratuit voit les contenus Premium dans la liste - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** je consulte les contenus d'un créateur - -**Alors** je vois aussi les contenus Premium dans la liste -**Et** ils sont affichés avec un badge 👑 -**Mais** je ne peux pas les lire - ---- - -## 11. Tentative de lecture Premium par utilisateur gratuit - Overlay bloquant - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** je clique sur un contenu Premium pour le lire - -**Alors** un overlay bloquant apparaît -**Et** je vois le message: -**Et** un bouton "Passer Premium" est affiché - ---- - -## 12. CTA "Passer Premium" redirige vers abonnement - -**Étant donné** que je vois l'overlay de contenu Premium bloqué - -**Quand** je clique sur "Passer Premium" - -**Alors** je suis redirigé vers la page d'abonnement Premium -**Et** je peux m'abonner pour 4.99€/mois - ---- - -## 13. Utilisateur Premium peut lire tous les contenus Premium - -**Étant donné** que je suis un utilisateur Premium actif - -**Quand** je clique sur un contenu Premium - -**Alors** le contenu se lance immédiatement -**Et** je n'ai aucun overlay bloquant -**Et** je peux profiter pleinement du contenu exclusif - ---- - -## 14. Contenus Premium inclus dans les recommandations - -**Étant donné** que l'algorithme génère ma file de 5 contenus - -**Quand** je suis un utilisateur gratuit - -**Alors** les contenus Premium peuvent apparaître dans les recommandations -**Et** cela me fait découvrir qu'il existe du contenu exclusif - ---- - -## 15. Contenu Premium skippé automatiquement pour utilisateur gratuit - -**Étant donné** que je suis un utilisateur gratuit -**Et** qu'un contenu Premium apparaît dans ma file de recommandation - -**Quand** j'écoute le contenu précédent jusqu'à la fin - -**Alors** le contenu Premium est automatiquement skippé -**Et** le contenu suivant (gratuit) est lancé -**Et** le slot Premium ne compte pas dans ma file de 5 contenus - ---- - -## 16. Contenu Premium diffusé normalement pour utilisateur Premium - -**Étant donné** que je suis un utilisateur Premium -**Et** qu'un contenu Premium apparaît dans ma file de recommandation - -**Quand** j'écoute le contenu précédent jusqu'à la fin - -**Alors** le contenu Premium est lancé normalement -**Et** je profite du contenu exclusif sans interruption - ---- - -## 17. Champ `is_premium` boolean en base PostgreSQL - -**Étant donné** qu'un contenu est créé - -**Quand** il est stocké en base de données - -**Alors** la table `contents` contient un champ `is_premium BOOLEAN DEFAULT FALSE` -**Et** ce champ est indexé pour requêtes rapides - ---- - -## 18. Index PostgreSQL sur `is_premium` - -**Étant donné** que l'algorithme doit filtrer les contenus selon le statut Premium - -**Quand** une requête SQL est exécutée: - -**Alors** l'index sur `is_premium` accélère la requête -**Et** le temps de réponse reste <20ms - ---- - -## 19. Cache Redis pour statut Premium - -**Étant donné** qu'un contenu Premium est consulté fréquemment - -**Quand** l'API vérifie le statut Premium - -**Alors** la valeur est récupérée depuis Redis: -**Et** le cache a un TTL de 1 heure -**Et** cela évite des requêtes SQL inutiles - ---- - -## 20. Invalidation cache lors de modification statut Premium - -**Étant donné** qu'un contenu est passé de gratuit à Premium - -**Quand** le créateur enregistre la modification - -**Alors** le cache Redis `content:[id]:premium` est invalidé immédiatement -**Et** la nouvelle valeur est mise à jour -**Et** les utilisateurs voient le changement en temps réel - ---- - -## 21. Justification liberté créateur - Stratégie personnalisée - -**Étant donné** que chaque créateur a une audience différente - -**Quand** un créateur décide de sa stratégie Premium - -**Alors** il peut tester différentes approches: - - | stratégie | % Premium | objectif | - |---|---|---| - | Tout gratuit | 0% | Maximiser audience + revenus pub | - | Mix 50/50 | 50% | Équilibrer audience et exclusivité | - | Premium majoritaire | 80% | Cibler abonnés fidèles | - | 100% Premium | 100% | Contenu ultra-exclusif | - - ---- - -## 22. Justification incitation Premium - Argument fort pour s'abonner - -**Étant donné** qu'un utilisateur gratuit voit beaucoup de contenus Premium - -**Quand** il consulte les profils de ses créateurs préférés - -**Alors** il voit que 60% de leur contenu est réservé Premium -**Et** cela l'incite à s'abonner pour 4.99€/mois -**Et** RoadWave augmente son taux de conversion vers Premium - ---- - -## 23. Justification équité - Petit créateur peut tout mettre en Premium - -**Étant donné** que je suis un petit créateur avec 600 abonnés -**Et** que 50 sont abonnés Premium - -**Quand** je mets 100% de mon contenu en Premium - -**Alors** je génère des revenus uniquement via mes 50 abonnés Premium -**Et** cela me permet de vivre de mon contenu malgré une petite audience - ---- - -## 24. Justification équité - Gros créateur peut tout offrir gratuitement - -**Étant donné** que je suis un gros créateur avec 50 000 abonnés -**Et** que je génère déjà beaucoup de revenus publicitaires - -**Quand** je laisse 100% de mon contenu gratuit - -**Alors** je maximise mon audience et mes revenus pub -**Et** je n'ai pas besoin de mettre du contenu en Premium - ---- - -## 25. Statistiques créateur - Ratio Premium/Gratuit - -**Étant donné** que j'accède à mon tableau de bord créateur - -**Quand** je consulte mes statistiques de contenus - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Contenus totaux | 47 | - | Contenus gratuits | 32 (68%) | - | Contenus Premium | 15 (32%) | - | Écoutes Premium ce mois | 12,345 | - | Écoutes gratuites ce mois | 28,901 | - - ---- - -## 26. Statistiques créateur - Revenus par type - -**Étant donné** que j'ai des contenus gratuits et Premium - -**Quand** je consulte mes revenus détaillés - -**Alors** je vois: - - | source | montant | - |---|---| - | Revenus pub (gratuit) | 86.70€ | - | Revenus Premium (exclusifs) | 34.20€ | - | Revenus Premium (tout contenu) | 78.90€ | - -**Et** je peux comparer l'efficacité de chaque stratégie - ---- - -## 27. Notification créateur - Contenu Premium très écouté - -**Étant donné** que j'ai publié un contenu Premium il y a 3 jours -**Et** qu'il a généré 5 000 écoutes Premium (très élevé) - -**Quand** le système détecte cette performance - -**Alors** je reçois une notification: - ---- - -## 28. A/B test utilisateur - Impact badge Premium sur conversion - -**Étant donné** que RoadWave veut optimiser le taux de conversion Premium - -**Quand** un A/B test est lancé - -**Alors** groupe A voit le badge 👑 "Premium" -**Et** groupe B voit le badge 💎 "Exclusif" -**Et** les taux de clic et conversion sont mesurés -**Et** le badge le plus performant est déployé définitivement - ---- - -## 29. Analytics plateforme - Adoption fonctionnalité Premium - -**Étant donné** que RoadWave suit l'adoption de la fonctionnalité - -**Quand** un admin consulte les métriques - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Créateurs utilisant Premium | 847 (68%) | - | % moyen contenus Premium | 23% | - | Taux conversion vers Premium (users) | 8.5% | - | Revenus Premium/mois | 47,890€ | - - ---- - -## 30. Impact sur churn - Contenus Premium réduisent le churn Premium - -**Étant donné** qu'un utilisateur Premium envisage de résilier -**Mais** qu'il a accès à 150 contenus Premium de ses créateurs préférés - -**Quand** il voit la valeur exclusive qu'il perdrait - -**Alors** il est moins susceptible de résilier (churn réduit de ~30%) -**Et** les contenus Premium augmentent la rétention - ---- - -## 31. Transparence - Créateur voit combien de contenus Premium il a - -**Étant donné** que j'accède à mon profil créateur - -**Quand** je consulte mes contenus - -**Alors** je peux filtrer par statut: - - | filtre | résultats | - |---|---| - | Tous | 47 | - | Gratuits | 32 | - | Premium 👑 | 15 | - -**Et** je peux facilement gérer mon catalogue - ---- - -## 32. Export liste contenus avec statut Premium (RGPD) - -**Étant donné** que je demande l'export de mes données - -**Quand** l'export est généré - -**Alors** la liste de mes contenus inclut le statut Premium: - ---- - -## 33. Suppression compte créateur et contenus Premium - -**Étant donné** que je supprime définitivement mon compte créateur - -**Quand** la suppression est confirmée - -**Alors** tous mes contenus (gratuits et Premium) sont supprimés -**Et** les utilisateurs Premium ne peuvent plus y accéder -**Et** les fichiers audio sont supprimés du CDN sous 7 jours - ---- - -## 34. Performance avec 1 million de contenus Premium - -**Étant donné** que RoadWave a 1 million de contenus dont 300 000 Premium - -**Quand** l'algorithme génère une recommandation - -**Alors** la requête SQL filtre efficacement avec l'index `is_premium` -**Et** le temps de réponse reste <50ms -**Et** la scalabilité est garantie - ---- - - - - - -
- - -# Désactivation et suspension monétisation -> *En tant que créateur ou plateforme* -> *Je veux pouvoir désactiver ou suspendre la monétisation selon certaines conditions* -> *Afin de gérer les pauses, problèmes techniques ou violations des règles* - -**35 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur avec la monétisation activée -## 1. Désactivation temporaire par le créateur - -**Étant donné** que je veux faire une pause dans ma création de contenu - -**Quand** j'accède à "Paramètres > Monétisation" -**Et** que je clique sur "Désactiver temporairement la monétisation" - -**Alors** ma monétisation est désactivée immédiatement -**Et** je ne génère plus de revenus à partir de maintenant - ---- - -## 2. Confirmation avant désactivation - -**Étant donné** que je clique sur "Désactiver temporairement" - -**Quand** une popup de confirmation apparaît - -**Alors** je vois le message: -**Et** je dois confirmer pour continuer - ---- - -## 3. Solde conservé pendant désactivation - -**Étant donné** que mon solde actuel est 87.45€ - -**Quand** je désactive ma monétisation le 15 du mois - -**Alors** mon solde de 87.45€ est conservé -**Et** il sera reporté au mois suivant -**Et** si le total dépasse 50€, il sera versé normalement le 15 du mois prochain - ---- - -## 4. Contenus restent accessibles pendant désactivation - -**Étant donné** que j'ai désactivé ma monétisation - -**Quand** des utilisateurs écoutent mes contenus - -**Alors** mes contenus restent accessibles normalement -**Mais** je ne génère aucun revenu (ni pub ni Premium) - ---- - -## 5. Réactivation sans refaire le KYC si <2 ans - -**Étant donné** que j'ai désactivé ma monétisation il y a 8 mois -**Et** que mes documents KYC sont toujours valides - -**Quand** je clique sur "Réactiver la monétisation" - -**Alors** la réactivation est immédiate -**Et** je n'ai pas besoin de refaire le KYC -**Et** je recommence à générer des revenus dès maintenant - ---- - -## 6. Nouveau KYC requis si inactivité >2 ans - -**Étant donné** que j'ai désactivé ma monétisation il y a 25 mois - -**Quand** j'essaie de réactiver - -**Alors** le système demande un nouveau KYC -**Et** je vois: -**Et** je dois soumettre à nouveau mes documents - ---- - -## 7. Historique des désactivations/réactivations - -**Étant donné** que j'ai désactivé et réactivé ma monétisation plusieurs fois - -**Quand** j'accède à "Paramètres > Monétisation > Historique" - -**Alors** je vois la liste complète: - - | date | action | raison | - |---|---|---| - | 15/06/2025 | Réactivation | Reprise création contenu | - | 01/03/2025 | Désactivation | Pause vacances | - | 20/01/2025 | Activation | KYC validé | - - ---- - -## 8. Suspension si 3+ strikes actifs - -**Étant donné** que je reçois un 3ème strike pour violation des règles - -**Quand** le strike devient actif - -**Alors** ma monétisation est suspendue automatiquement -**Et** je vois: - ---- - -## 9. Réactivation après résolution des strikes - -**Étant donné** que ma monétisation est suspendue pour 3 strikes - -**Quand** je résous tous mes strikes (après expiration ou contestation) -**Et** que mon compteur de strikes passe à 0 - -**Alors** ma monétisation est réactivée automatiquement -**Et** je reçois un email de confirmation - ---- - -## 10. Suspension si RIB invalide après 3 échecs de virement - -**Étant donné** que 3 tentatives de virement ont échoué (15, 18, 22 du mois) - -**Quand** le 3ème échec est confirmé - -**Alors** ma monétisation est suspendue automatiquement -**Et** je vois: - ---- - -## 11. Réactivation après mise à jour RIB valide - -**Étant donné** que ma monétisation est suspendue pour RIB invalide - -**Quand** je mets à jour mon RIB avec un compte bancaire valide -**Et** que Mangopay valide le nouveau RIB - -**Alors** ma monétisation est réactivée automatiquement -**Et** un virement est tenté immédiatement pour le solde en attente - ---- - -## 12. Suspension si documents KYC expirés - -**Étant donné** que ma carte d'identité expire dans 30 jours - -**Quand** je reçois un email de rappel de mise à jour -**Mais** que je ne mets pas à jour mes documents -**Et** que ma CNI expire - -**Alors** ma monétisation est suspendue automatiquement après 30 jours de grâce - ---- - -## 13. Préavis 30 jours avant suspension pour docs expirés - -**Étant donné** que ma CNI expire le 15 juin 2025 - -**Quand** le 15 mai 2025 arrive (30 jours avant) - -**Alors** je reçois un email d'alerte: - ---- - -## 14. Réactivation après renouvellement documents KYC - -**Étant donné** que ma monétisation est suspendue pour CNI expirée - -**Quand** je soumets une nouvelle CNI valide -**Et** que Mangopay valide le document sous 24-72h - -**Alors** ma monétisation est réactivée automatiquement -**Et** je recommence à générer des revenus - ---- - -## 15. Suspension si fraude détectée - -**Étant donné** que le système détecte une activité frauduleuse (bots, écoutes artificielles) - -**Quand** l'équipe modération confirme la fraude - -**Alors** ma monétisation est suspendue immédiatement -**Et** mon compte est mis sous enquête -**Et** je reçois un email m'informant de la suspension - ---- - -## 16. Enquête fraude - Vérification manuelle - -**Étant donné** que ma monétisation est suspendue pour suspicion de fraude - -**Quand** l'équipe modération enquête - -**Alors** elle analyse: - - | élément à vérifier | outil | - |---|---| - | Patterns d'écoute suspects | Analytics + logs | - | Origine géographique | Logs IP | - | Vitesse de croissance anormale | Graphiques statistiques | - | Plaintes utilisateurs | Système de signalement | - - ---- - -## 17. Levée suspension si fraude non confirmée - -**Étant donné** que mon compte était suspendu pour suspicion de fraude - -**Quand** l'enquête conclut qu'il n'y a pas eu de fraude - -**Alors** ma monétisation est réactivée -**Et** les revenus suspendus pendant l'enquête sont versés normalement -**Et** je reçois un email d'excuses avec explication - ---- - -## 18. Suspension définitive si fraude confirmée - -**Étant donné** que l'enquête confirme une fraude avérée - -**Quand** l'équipe modération prend la décision - -**Alors** ma monétisation est définitivement désactivée -**Et** mon solde en attente est gelé (non versé) -**Et** je peux recevoir un strike 4 (ban définitif du compte) - ---- - -## 19. Suppression définitive sur demande créateur - -**Étant donné** que je veux arrêter définitivement la monétisation - -**Quand** j'accède à "Paramètres > Monétisation > Supprimer définitivement" - -**Alors** une confirmation stricte est demandée -**Et** je dois taper "SUPPRIMER" pour confirmer - ---- - -## 20. Solde versé sous 30 jours après suppression - -**Étant donné** que je supprime définitivement ma monétisation -**Et** que mon solde en attente est 127.45€ - -**Quand** la suppression est confirmée - -**Alors** mon solde sera versé sous 30 jours -**Et** je reçois un dernier virement de clôture -**Et** mon e-wallet Mangopay est clôturé - ---- - -## 21. Suppression auto si inactivité 24 mois + solde <50€ - -**Étant donné** que je n'ai plus publié de contenu depuis 24 mois -**Et** que mon solde en attente est 12.30€ (<50€) - -**Quand** le processus de purge RGPD s'exécute - -**Alors** ma monétisation est automatiquement supprimée -**Et** mon solde de 12.30€ est perdu (trop faible pour virement) -**Et** mes données KYC sont archivées puis supprimées selon la législation - ---- - -## 22. Email de préavis 60 jours avant purge RGPD - -**Étant donné** que je suis inactif depuis 22 mois - -**Quand** le système détecte l'inactivité - -**Alors** je reçois un email: - ---- - -## 23. Ban définitif compte - Strike 4 - -**Étant donné** que je reçois un 4ème strike (violation grave ou répétée) - -**Quand** l'équipe modération applique le strike 4 - -**Alors** mon compte est banni définitivement -**Et** ma monétisation est supprimée définitivement -**Et** mon solde en attente est gelé (non versé) -**Et** je ne peux plus créer de nouveau compte (blacklist email/SIRET) - ---- - -## 24. Email pour toute suspension - -**Étant donné** que ma monétisation est suspendue (quelle qu'en soit la raison) - -**Quand** la suspension devient effective - -**Alors** je reçois immédiatement un email: - ---- - -## 25. Notification in-app avec raison explicite - -**Étant donné** que ma monétisation est suspendue - -**Quand** je me connecte à l'application - -**Alors** je vois une bannière en haut de mon dashboard: - ---- - -## 26. Email de confirmation lors de réactivation - -**Étant donné** que ma monétisation était suspendue - -**Quand** elle est réactivée (automatiquement ou manuellement) - -**Alors** je reçois un email: - ---- - -## 27. Dashboard admin - Suspensions actives - -**Étant donné** qu'un admin RoadWave consulte les suspensions - -**Quand** il accède au dashboard admin "Monétisation > Suspensions" - -**Alors** il voit: - - | raison suspension | nombre actif | taux | - |---|---|---| - | Strikes (3+) | 23 | 1.8% | - | RIB invalide | 12 | 0.9% | - | Documents KYC expirés | 8 | 0.6% | - | Fraude sous enquête | 3 | 0.2% | - | TOTAL | 46 | 3.7% | - - ---- - -## 28. Alertes si taux de suspension >5% - -**Étant donné** que le taux de suspension dépasse 5% - -**Quand** le système détecte cette anomalie - -**Alors** une alerte est envoyée à l'équipe: - ---- - -## 29. Statistiques personnelles - Temps actif monétisation - -**Étant donné** que j'accède à mon dashboard créateur - -**Quand** je consulte "Statistiques > Monétisation" - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Date activation monétisation | 20 janvier 2025 | - | Temps actif total | 8 mois | - | Périodes de désactivation | 2 (3 mois total) | - | Suspensions subies | 0 | - | Statut actuel | ✅ Actif | - - ---- - -## 30. Export données suspension (RGPD) - -**Étant donné** que je demande l'export de mes données - -**Quand** l'export est généré - -**Alors** l'historique des suspensions est inclus: - ---- - -## 31. Suppression compte et données monétisation - -**Étant donné** que je supprime définitivement mon compte RoadWave - -**Quand** la suppression est confirmée - -**Alors** toutes mes données de monétisation sont supprimées: - - | donnée | action | - |---|---| - | Solde en attente | Versé sous 30 jours puis supprimé | - | Historique revenus | Archivé 10 ans (obligation légale) | - | Documents KYC | Archivés 10 ans chez Mangopay puis supprimés | - | E-wallet Mangopay | Clôturé après versement final | - - ---- - -## 32. Conservation archives 10 ans obligation légale - -**Étant donné** que je supprime mon compte - -**Quand** mes données sont archivées - -**Alors** RoadWave conserve 10 ans: - - | donnée archivée | raison | - |---|---| - | Relevés mensuels PDF | Obligation comptable France | - | Déclarations DAS2 | Obligation fiscale France | - | Justificatifs virements | Preuve paiement en cas d'audit | - -**Et** après 10 ans, tout est supprimé définitivement - ---- - -## 33. Suspension temporaire pour maintenance technique - -**Étant donné** que Mangopay effectue une maintenance planifiée - -**Quand** la maintenance est programmée - -**Alors** tous les créateurs reçoivent un email préventif 7 jours avant: - ---- - -## 34. Réactivation progressive après incident majeur - -**Étant donné** qu'un incident technique majeur suspend toutes les monétisations - -**Quand** l'incident est résolu - -**Alors** les réactivations se font progressivement: - - | vague | critère | % créateurs | - |---|---|---| - | 1 | Top 10% créateurs (revenus) | 10% | - | 2 | Créateurs vérifiés | 30% | - | 3 | Tous les autres créateurs | 60% | - -**Et** cela évite une surcharge système lors de la reprise - ---- - -## 35. Support prioritaire pour créateurs suspendus injustement - -**Étant donné** que ma monétisation est suspendue -**Et** que je pense que c'est une erreur - -**Quand** je contacte le support avec tag "Suspension monétisation" - -**Alors** mon ticket est traité en priorité (SLA 24h) -**Et** un agent expert examine mon cas -**Et** si suspension injustifiée, je suis réactivé immédiatement avec excuses - ---- - - - - - -
- - -# KYC et inscription à la monétisation -> *En tant que créateur éligible* -> *Je veux compléter le KYC pour activer la monétisation* -> *Afin de recevoir des paiements légalement* - -**37 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je remplis tous les critères de monétisation -> **Et** que j'ai cliqué sur "Demander la monétisation" -## 1. Redirection vers formulaire KYC Mangopay - -**Quand** je démarre le processus d'activation - -**Alors** je suis redirigé vers un formulaire KYC -**Et** le formulaire est fourni par Mangopay (iframe sécurisée) -**Et** toutes les données sont chiffrées et hébergées en EU - ---- - -## 2. Statut auto-entrepreneur accepté - -**Étant donné** que je suis auto-entrepreneur - -**Quand** je renseigne mon statut juridique - -**Alors** l'option "Auto-entrepreneur (micro-BNC)" est disponible -**Et** je peux continuer le processus - ---- - -## 3. Statut société SARL/SAS/SASU accepté - -**Étant donné** que j'ai créé une société - -**Quand** je renseigne mon statut juridique - -**Alors** les options suivantes sont disponibles: - - | statut juridique | - |---| - | SARL | - | SAS | - | SASU | - -**Et** je peux continuer le processus - ---- - -## 4. Statut particulier refusé - -**Étant donné** que je n'ai pas de statut professionnel - -**Quand** j'essaie de m'inscrire en tant que "Particulier" - -**Alors** le formulaire affiche: -**Et** je ne peux pas continuer sans statut professionnel - ---- - -## 5. Document SIRET obligatoire et validé - -**Étant donné** que je renseigne mon SIRET - -**Quand** je saisis "12345678901234" (14 chiffres) - -**Alors** le format est validé -**Et** Mangopay vérifie l'existence du SIRET auprès du répertoire SIRENE -**Et** si valide, le document est accepté - ---- - -## 6. SIRET invalide ou inexistant - -**Étant donné** que je renseigne un SIRET inexistant - -**Quand** je saisis "99999999999999" - -**Alors** Mangopay rejette le SIRET -**Et** je vois "SIRET non trouvé dans le répertoire SIRENE. Vérifiez le numéro." -**Et** je dois corriger avant de continuer - ---- - -## 7. RIB professionnel obligatoire - -**Étant donné** que j'upload mon RIB - -**Quand** le RIB est scanné par Mangopay - -**Alors** le système vérifie que le titulaire correspond à mon SIRET -**Et** que l'IBAN commence par "FR" (compte français) -**Et** si valide, le document est accepté - ---- - -## 8. RIB particulier refusé - -**Étant donné** que j'upload un RIB de compte particulier - -**Quand** Mangopay détecte que le compte n'est pas professionnel - -**Alors** le RIB est rejeté -**Et** je vois: - ---- - -## 9. Pièce d'identité CNI en cours de validité - -**Étant donné** que j'upload ma carte nationale d'identité - -**Quand** Mangopay analyse le document - -**Alors** la date d'expiration est vérifiée -**Et** si la CNI est valide, le document est accepté -**Et** mon identité est vérifiée par OCR + vérification manuelle - ---- - -## 10. Pièce d'identité expirée refusée - -**Étant donné** que j'upload une CNI expirée depuis 2 ans - -**Quand** Mangopay analyse le document - -**Alors** le document est rejeté -**Et** je vois "Pièce d'identité expirée. Veuillez fournir un document en cours de validité." - ---- - -## 11. Passeport accepté comme alternative - -**Étant donné** que je n'ai pas de CNI - -**Quand** j'upload mon passeport en cours de validité - -**Alors** Mangopay accepte le passeport -**Et** mon identité est vérifiée de la même manière - ---- - -## 12. Numéro TVA intracommunautaire si applicable - -**Étant donné** que mon CA dépasse 37 000€/an -**Et** que je suis sorti de la franchise en base - -**Quand** je renseigne mon numéro TVA intracommunautaire - -**Alors** le format "FR + 11 chiffres" est validé -**Et** Mangopay vérifie l'existence auprès de la Commission Européenne (VIES) - ---- - -## 13. TVA non applicable pour micro-BNC sous franchise - -**Étant donné** que je suis auto-entrepreneur sous franchise en base -**Et** que mon CA est <37 000€/an - -**Quand** je remplis le formulaire KYC - -**Alors** le champ "Numéro TVA" est optionnel -**Et** je peux continuer sans TVA - ---- - -## 14. Kbis <3 mois pour sociétés - -**Étant donné** que je suis gérant d'une SARL - -**Quand** j'upload mon extrait Kbis - -**Alors** Mangopay vérifie que le Kbis date de moins de 3 mois -**Et** que le SIRET correspond -**Et** si valide, le document est accepté - ---- - -## 15. Kbis trop ancien refusé - -**Étant donné** que j'upload un Kbis de 5 mois - -**Quand** Mangopay analyse le document - -**Alors** le Kbis est rejeté -**Et** je vois "Le Kbis doit dater de moins de 3 mois. Téléchargez un extrait récent sur infogreffe.fr" - ---- - -## 16. Vérification identité ne correspond pas au compte - -**Étant donné** que mon compte RoadWave est au nom de "Jean Dupont" -**Mais** que ma CNI est au nom de "Pierre Martin" - -**Quand** Mangopay compare les identités - -**Alors** le KYC est rejeté -**Et** je vois: - ---- - -## 17. Liste noire anti-blanchiment détectée - -**Étant donné** que mon identité apparaît sur une liste anti-blanchiment - -**Quand** Mangopay effectue la vérification AML (Anti-Money Laundering) - -**Alors** le KYC est automatiquement rejeté -**Et** je vois "Votre demande ne peut être acceptée pour des raisons de conformité légale" -**Et** mon compte créateur peut être suspendu - ---- - -## 18. Délai de vérification 24-72h si documents conformes - -**Étant donné** que j'ai soumis tous les documents valides - -**Quand** Mangopay traite ma demande - -**Alors** je reçois un email "KYC en cours de vérification (24-72h)" -**Et** mon statut est "En attente de validation" -**Et** je peux continuer à publier des contenus en attendant - ---- - -## 19. Validation KYC réussie - -**Étant donné** que mes documents sont conformes - -**Quand** Mangopay valide mon KYC après 48h - -**Alors** je reçois un email "Monétisation activée !" -**Et** mon statut passe à "Monétisé" -**Et** je commence à générer des revenus dès maintenant - ---- - -## 20. Rejet KYC pour documents invalides - -**Étant donné** que j'ai soumis une CNI floue et illisible - -**Quand** Mangopay analyse les documents - -**Alors** le KYC est rejeté après 24h -**Et** je reçois un email détaillant les documents à refournir: - ---- - -## 21. E-wallet Mangopay créé automatiquement - -**Étant donné** que mon KYC est validé - -**Quand** Mangopay finalise mon inscription - -**Alors** un e-wallet Mangopay est créé automatiquement à mon nom -**Et** tous mes futurs revenus seront transférés vers ce wallet -**Et** les virements SEPA vers mon RIB seront effectués depuis ce wallet - ---- - -## 22. Conformité RGPD - Données hébergées EU - -**Étant donné** que je fournis mes documents KYC - -**Quand** Mangopay stocke mes données - -**Alors** toutes les données sont hébergées en Union Européenne -**Et** Mangopay est régulé par l'ACPR (Autorité de Contrôle Prudentiel) -**Et** mes données sont protégées selon le RGPD - ---- - -## 23. KYC gratuit inclus dans Mangopay - -**Étant donné** que je complète le KYC - -**Quand** le processus se termine - -**Alors** aucun frais ne m'est facturé (0€) -**Et** aucun frais n'est facturé à RoadWave (inclus dans l'offre Mangopay) - ---- - -## 24. Base légale - Conformité fiscale française - -**Étant donné** que RoadWave est une plateforme française - -**Quand** je génère des revenus >1200€/an - -**Alors** RoadWave doit déclarer ces revenus aux impôts (DAS2) -**Et** le KYC permet de garantir l'identité réelle du bénéficiaire -**Et** cela respecte la réglementation fiscale française - ---- - -## 25. Base légale - Directive anti-blanchiment EU 2018/843 - -**Étant donné** que RoadWave verse de l'argent aux créateurs - -**Quand** le KYC est effectué - -**Alors** RoadWave respecte la 5ème directive anti-blanchiment EU -**Et** Mangopay effectue les vérifications requises (identité, liste noire, origine fonds) - ---- - -## 26. Notification de mise à jour documents expirés - -**Étant donné** que ma CNI va expirer dans 30 jours - -**Quand** le système détecte l'expiration proche - -**Alors** je reçois un email: - ---- - -## 27. Suspension monétisation si documents expirés - -**Étant donné** que ma CNI est expirée depuis 10 jours -**Et** que je n'ai pas mis à jour mes documents - -**Quand** le système vérifie mon statut KYC - -**Alors** ma monétisation est suspendue automatiquement -**Et** je ne génère plus de revenus jusqu'à mise à jour - ---- - -## 28. Réactivation sans nouveau KYC si données à jour - -**Étant donné** que j'ai désactivé temporairement ma monétisation il y a 6 mois -**Et** que mes documents KYC sont toujours valides - -**Quand** je réactive la monétisation - -**Alors** je n'ai pas besoin de refaire le KYC -**Et** la réactivation est immédiate - ---- - -## 29. Nouveau KYC requis après 2 ans d'inactivité - -**Étant donné** que j'ai désactivé ma monétisation il y a 25 mois - -**Quand** j'essaie de réactiver - -**Alors** le système demande un nouveau KYC -**Et** je dois soumettre des documents à jour (CNI peut avoir changé) - ---- - -## 30. Support créateur pour problèmes KYC - -**Étant donné** que mon KYC est rejeté et je ne comprends pas pourquoi - -**Quand** je contacte le support RoadWave - -**Alors** un agent peut consulter les raisons du rejet Mangopay -**Et** m'aider à fournir les bons documents - ---- - -## 31. Export données KYC pour RGPD - -**Étant donné** que je demande l'export de mes données personnelles - -**Quand** l'export est généré - -**Alors** les informations KYC sont incluses: -**Et** les documents scannés (CNI, RIB) sont exclus pour sécurité - ---- - -## 32. Suppression compte et données KYC - -**Étant donné** que je supprime définitivement mon compte RoadWave - -**Quand** la suppression est confirmée - -**Alors** mes données KYC chez Mangopay sont archivées 10 ans (obligation légale) -**Mais** supprimées de la base RoadWave immédiatement -**Et** mon e-wallet est clôturé après versement du solde final - ---- - -## 33. Statistiques KYC pour monitoring plateforme - -**Étant donné** que RoadWave suit la qualité du processus KYC - -**Quand** un admin consulte les métriques - -**Alors** il voit: - - | métrique | valeur exemple | - |---|---| - | Demandes KYC ce mois | 247 | - | Taux de validation | 87% | - | Délai moyen validation | 36h | - | Taux de rejet (documents invalides) | 13% | - -**Et** cela permet d'optimiser le processus - ---- - -## 34. Vérification SIRET via API INSEE - -**Étant donné** que je saisis mon SIRET - -**Quand** le système le valide - -**Alors** une requête est faite à l'API SIRENE de l'INSEE -**Et** le système vérifie que le SIRET existe et est actif -**Et** récupère le nom de l'entreprise pour pré-remplir le formulaire - ---- - -## 35. Détection fraude - Même SIRET utilisé par plusieurs comptes - -**Étant donné** qu'un SIRET "12345678901234" est déjà utilisé par un autre créateur - -**Quand** j'essaie d'utiliser le même SIRET - -**Alors** le système détecte la duplication -**Et** affiche "Ce SIRET est déjà utilisé par un autre compte RoadWave" -**Et** je dois contacter le support si c'est une erreur - ---- - -## 36. Protection données sensibles - Logs chiffrés - -**Étant donné** que des données KYC sensibles transitent dans le système - -**Quand** les logs sont enregistrés - -**Alors** les numéros SIRET, IBAN et données CNI sont masqués: -**Et** seule l'équipe sécurité peut accéder aux données complètes - ---- - -## 37. Backup Mangopay des documents KYC - -**Étant donné** que mes documents KYC sont stockés chez Mangopay - -**Quand** un audit est demandé par les autorités - -**Alors** Mangopay peut fournir les documents originaux -**Et** RoadWave n'a pas besoin de stocker ces documents (réduction risque RGPD) - ---- - - - - - -
- - -# Obligations fiscales -> *En tant que créateur monétisé* -> *Je veux que RoadWave génère automatiquement les documents fiscaux requis* -> *Afin de faciliter ma comptabilité et respecter la loi* - -**30 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur avec la monétisation activée -> **Et** que je génère des revenus sur RoadWave -## 1. Génération automatique relevé mensuel PDF - -**Étant donné** que le mois de janvier se termine - -**Quand** le système calcule mes revenus du mois - -**Alors** un relevé mensuel PDF est généré automatiquement -**Et** le PDF est disponible dans mon tableau de bord - ---- - -## 2. Contenu du relevé mensuel PDF - -**Étant donné** que mon relevé de janvier est généré - -**Quand** je télécharge le PDF - -**Alors** le document contient: - ---- - -## 3. Téléchargement relevé depuis tableau de bord - -**Étant donné** que je suis sur mon tableau de bord créateur - -**Quand** j'accède à l'onglet "Revenus > Historique" - -**Alors** je vois la liste de mes relevés mensuels: - - | mois | montant | actions | - |---|---|---| - | Janvier 2025 | 150.00€ | 📄 Télécharger PDF | - | Décembre 2024 | 123.50€ | 📄 Télécharger PDF | - | Novembre 2024 | 98.75€ | 📄 Télécharger PDF | - - ---- - -## 4. Conservation relevés accessibles 10 ans - -**Étant donné** que j'ai commencé la monétisation en janvier 2025 - -**Quand** je consulte mes relevés en janvier 2035 (10 ans plus tard) - -**Alors** tous les relevés depuis 2025 sont toujours accessibles -**Et** je peux télécharger n'importe quel relevé historique -**Et** cela respecte l'obligation de conservation comptable de 10 ans - ---- - -## 5. Export CSV à la demande - -**Étant donné** que je clique sur "Exporter pour comptable" - -**Quand** je choisis la période "Année 2025" - -**Alors** un fichier CSV est généré et téléchargé - ---- - -## 6. Contenu export CSV détaillé - -**Étant donné** que j'exporte mes données comptables 2025 - -**Quand** je télécharge le fichier CSV - -**Alors** le fichier contient: - ---- - -## 7. Transmission à l'expert-comptable - -**Étant donné** que j'ai téléchargé mon export CSV 2025 - -**Quand** je l'envoie à mon expert-comptable - -**Alors** il peut importer le fichier dans son logiciel comptable -**Et** il saisit rapidement mes revenus RoadWave -**Et** cela facilite ma déclaration fiscale annuelle - ---- - -## 8. DAS2 généré automatiquement si revenus >1200€/an - -**Étant donné** que mes revenus 2025 totalisent 2,450€ - -**Quand** l'année 2025 se termine - -**Alors** RoadWave génère automatiquement une DAS2 pour les impôts -**Et** la DAS2 est transmise à la DGFIP en janvier 2026 - ---- - -## 9. Contenu de la DAS2 - -**Étant donné** que RoadWave génère ma DAS2 pour 2025 - -**Quand** la DGFIP reçoit la déclaration - -**Alors** le document contient: - ---- - -## 10. Créateur reçoit une copie de la DAS2 - -**Étant donné** que RoadWave transmet ma DAS2 aux impôts - -**Quand** la transmission est confirmée - -**Alors** je reçois un email avec une copie de la DAS2 en pièce jointe -**Et** je peux consulter le document dans mon tableau de bord - ---- - -## 11. Pas de DAS2 si revenus <1200€/an - -**Étant donné** que mes revenus 2025 totalisent seulement 890€ - -**Quand** l'année 2025 se termine - -**Alors** aucune DAS2 n'est générée car le seuil de 1200€ n'est pas atteint -**Mais** je dois quand même déclarer mes revenus dans ma déclaration personnelle - ---- - -## 12. Base légale DAS2 - Obligation France - -**Étant donné** que RoadWave verse des honoraires à des prestataires - -**Quand** les revenus dépassent 1200€/an - -**Alors** la déclaration DAS2 est obligatoire selon l'article 87 du Code Général des Impôts -**Et** le non-respect entraîne une amende de 15€ par bénéficiaire non déclaré - ---- - -## 13. Transmission DAS2 via EDI-TDFC - -**Étant donné** que RoadWave génère 1,247 DAS2 pour l'année 2025 - -**Quand** la transmission aux impôts est effectuée - -**Alors** la transmission se fait via le portail EDI-TDFC de la DGFIP -**Et** la transmission est automatisée (pas de saisie manuelle) -**Et** un accusé de réception est reçu sous 48h - ---- - -## 14. Créateur responsable de déclarer aux impôts - -**Étant donné** que j'ai reçu 2,450€ de revenus RoadWave en 2025 - -**Quand** je fais ma déclaration fiscale en mai 2026 - -**Alors** je dois déclarer ces 2,450€ dans ma déclaration annuelle -**Et** si je suis auto-entrepreneur, je déclare en BNC (Bénéfices Non Commerciaux) - ---- - -## 15. Créateur responsable des cotisations URSSAF - -**Étant donné** que je suis auto-entrepreneur -**Et** que j'ai reçu 2,450€ de revenus RoadWave en 2025 - -**Quand** je fais ma déclaration URSSAF trimestrielle - -**Alors** je dois déclarer ces revenus à l'URSSAF -**Et** je paie ~22% de cotisations sociales (soit ~539€) - ---- - -## 16. TVA non applicable en franchise en base - -**Étant donné** que je suis auto-entrepreneur en micro-BNC -**Et** que mon chiffre d'affaires est <37,800€/an - -**Quand** je génère des revenus sur RoadWave - -**Alors** je bénéficie de la franchise en base de TVA -**Et** je ne facture pas de TVA à RoadWave -**Et** je ne récupère pas la TVA sur mes achats - ---- - -## 17. TVA applicable si CA >37,800€/an - -**Étant donné** que mon chiffre d'affaires total 2025 est 45,000€ - -**Quand** je dépasse le seuil de franchise en base (37,800€) - -**Alors** je dois facturer de la TVA (20%) à RoadWave -**Et** je dois obtenir un numéro TVA intracommunautaire -**Et** je dois déclarer ma TVA mensuellement ou trimestriellement - ---- - -## 18. Conservation justificatifs 10 ans - Obligation légale - -**Étant donné** que je génère des revenus sur RoadWave - -**Quand** je télécharge mes relevés mensuels et exports CSV - -**Alors** je dois les conserver 10 ans (obligation comptable France) -**Et** en cas de contrôle fiscal, je dois pouvoir les fournir - ---- - -## 19. Mangopay transmet automatiquement via DAC7 - -**Étant donné** que je suis créateur monétisé sur RoadWave - -**Quand** l'année se termine - -**Alors** Mangopay transmet automatiquement mes revenus aux autorités fiscales EU -**Et** cela respecte la directive DAC7 (2021/514) sur la transparence fiscale des plateformes - ---- - -## 20. Directive DAC7 - Obligations plateforme - -**Étant donné** que RoadWave est une plateforme facilitant des transactions - -**Quand** Mangopay gère les paiements - -**Alors** Mangopay transmet automatiquement: - - | information | destinataire | - |---|---| - | Identité créateur (SIRET) | Autorités fiscales pays EU | - | Revenus annuels | Autorités fiscales pays EU | - | Nombre transactions | Autorités fiscales pays EU | - -**Et** RoadWave n'a pas besoin de faire cette transmission manuellement - ---- - -## 21. Justificatif virement = Preuve bancaire comptable - -**Étant donné** que je reçois un virement de 150.00€ de Mangopay - -**Quand** je consulte mon relevé bancaire - -**Alors** je vois le virement avec la référence MANGOPAY-ABC123 -**Et** ce relevé bancaire sert de justificatif comptable -**Et** je peux le fournir à mon expert-comptable ou aux impôts - ---- - -## 22. Notification annuelle rappel déclaration fiscale - -**Étant donné** que je suis créateur monétisé - -**Quand** le mois d'avril 2026 arrive (période déclaration impôts France) - -**Alors** je reçois un email de rappel: - ---- - -## 23. Page ressources fiscales pour créateurs - -**Étant donné** que je suis créateur monétisé - -**Quand** j'accède à "Aide > Fiscalité" - -**Alors** je vois une page avec: - - | ressource | description | - |---|---| - | Guide auto-entrepreneur RoadWave | PDF expliquant démarches et déclarations | - | FAQ fiscalité | Questions fréquentes sur TVA, cotisations, etc. | - | Liens URSSAF et impots.gouv.fr | Portails officiels | - | Contact expert-comptable partenaire | Recommandations d'experts connaissant RoadWave | - - ---- - -## 24. Dashboard créateur - Récapitulatif annuel - -**Étant donné** que je consulte mon dashboard en décembre 2025 - -**Quand** j'accède à "Revenus > Récapitulatif annuel" - -**Alors** je vois: - ---- - -## 25. Génération automatique minimise erreurs - -**Étant donné** que tous les documents fiscaux sont générés automatiquement - -**Quand** un créateur télécharge ses documents - -**Alors** les montants sont garantis corrects (issus de la base de données) -**Et** il n'y a pas d'erreur de saisie manuelle -**Et** cela réduit les risques de contrôle fiscal - ---- - -## 26. Conformité RGPD - Données fiscales chiffrées - -**Étant donné** que les documents fiscaux contiennent des données sensibles (SIRET, revenus) - -**Quand** les documents sont stockés - -**Alors** ils sont chiffrés au repos (encryption AES-256) -**Et** seul le créateur et les admins autorisés peuvent y accéder -**Et** les logs d'accès sont conservés pour audit - ---- - -## 27. Backup documents fiscaux 10 ans - -**Étant donné** qu'un document fiscal est généré - -**Quand** il est stocké dans la base de données - -**Alors** une copie est sauvegardée sur S3 (stockage durable) -**Et** les backups sont répliqués sur 3 zones de disponibilité -**Et** la conservation est garantie 10 ans minimum - ---- - -## 28. Audit trail génération DAS2 - -**Étant donné** que 1,247 DAS2 sont générées en janvier 2026 - -**Quand** un audit est demandé - -**Alors** tous les événements sont loggés: - - | événement | timestamp | détails | - |---|---|---| - | Calcul revenus annuels | 2025-12-31 23:59:00 | 1,247 créateurs éligibles | - | Génération fichier EDI | 2026-01-10 08:00:00 | Format EDI-TDFC | - | Transmission DGFIP | 2026-01-10 10:30:00 | Via portail EDI-TDFC | - | Accusé réception DGFIP | 2026-01-11 14:20:00 | Transmission confirmée | - | Email créateurs | 2026-01-11 16:00:00 | 1,247 emails envoyés | - - ---- - -## 29. Statistiques admin - Conformité fiscale - -**Étant donné** qu'un admin RoadWave consulte les métriques fiscales - -**Quand** il accède au dashboard admin - -**Alors** il voit: - - | métrique | valeur 2025 | - |---|---| - | Créateurs monétisés | 1,247 | - | Créateurs éligibles DAS2 (>1200€) | 847 (68%) | - | Revenus totaux versés | 1,890,345€ | - | DAS2 transmises à la DGFIP | 847 | - | Taux conformité | 100% | - - ---- - -## 30. Support créateur pour questions fiscales - -**Étant donné** que j'ai une question sur ma déclaration fiscale - -**Quand** je contacte le support RoadWave - -**Alors** l'agent peut consulter mes documents fiscaux -**Et** m'aider à comprendre ce que je dois déclarer -**Mais** il ne peut pas me conseiller fiscalement (pas expert-comptable) -**Et** il me recommande de consulter un expert-comptable si nécessaire - ---- - - - - - -
- - -# Paiement des créateurs -> *En tant que créateur monétisé* -> *Je veux recevoir mes paiements mensuels de manière fiable* -> *Afin d'être rémunéré pour mon travail* - -**35 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur avec la monétisation activée -> **Et** que mon KYC est validé -## 1. Seuil minimum de 50€ atteint - Paiement effectué - -**Étant donné** que mes revenus du mois sont 73.45€ - -**Quand** le dernier jour du mois arrive - -**Alors** mon solde de 73.45€ est transféré vers "en attente de paiement" -**Et** le paiement sera effectué le 15 du mois prochain - ---- - -## 2. Seuil minimum de 50€ non atteint - Report mois suivant - -**Étant donné** que mes revenus du mois sont 32.17€ - -**Quand** le dernier jour du mois arrive - -**Alors** mon solde de 32.17€ est reporté au mois suivant -**Et** je vois "Solde insuffisant pour paiement (<50€). Report mois prochain." - ---- - -## 3. Cumul sur plusieurs mois jusqu'à atteindre 50€ - -**Étant donné** que mes revenus sont: - - | mois | revenus | solde cumulé | - |---|---|---| - | Janvier | 18.50€ | 18.50€ | - | Février | 22.30€ | 40.80€ | - | Mars | 15.70€ | 56.50€ | - - -**Quand** la fin du mois de mars arrive - -**Alors** le solde cumulé de 56.50€ dépasse les 50€ -**Et** un paiement de 56.50€ est effectué le 15 avril - ---- - -## 4. Calcul des revenus le dernier jour du mois - -**Étant donné** que nous sommes le 31 janvier à 23h59 - -**Quand** le système calcule les revenus du mois - -**Alors** une requête SQL agrège tous les revenus pub et premium -**Et** le solde final du mois est figé dans monthly_revenues -**Et** le compteur du mois en cours repart à 0€ le 1er février - ---- - -## 5. Période de traitement contestations 1-14 du mois - -**Étant donné** que mes revenus de janvier sont calculés à 150.00€ - -**Quand** la période du 1-14 février arrive - -**Alors** RoadWave analyse les éventuelles fraudes ou contestations -**Et** si une fraude est détectée, les revenus concernés sont retirés du solde -**Et** le solde final est validé le 14 février - ---- - -## 6. Virement SEPA le 15 du mois suivant - -**Étant donné** que mes revenus de janvier validés sont 150.00€ - -**Quand** le 15 février arrive - -**Alors** Mangopay initie un virement SEPA depuis mon e-wallet vers mon RIB -**Et** le statut du paiement passe à "En cours" - ---- - -## 7. Réception virement 16-18 du mois (1-3 jours SEPA) - -**Étant donné** qu'un virement SEPA a été initié le 15 février - -**Quand** 1-3 jours ouvrés s'écoulent - -**Alors** je reçois le virement sur mon compte bancaire entre le 16 et 18 février -**Et** je peux consulter l'historique des paiements dans mon dashboard - ---- - -## 8. Virement SEPA gratuit pour comptes EU - -**Étant donné** que mon RIB est français (IBAN FR) - -**Quand** Mangopay effectue le virement - -**Alors** aucun frais n'est prélevé (virement SEPA gratuit) -**Et** je reçois 100% du montant annoncé - ---- - -## 9. Virement international hors EU avec frais variables - -**Étant donné** que je suis créateur expatrié avec RIB hors Union Européenne - -**Quand** Mangopay effectue le virement international - -**Alors** des frais variables s'appliquent selon le pays -**Et** les frais sont déduits du montant final -**Et** je vois le détail des frais dans mon historique - ---- - -## 10. E-wallet Mangopay automatique - -**Étant donné** que mon KYC est validé - -**Quand** mes revenus sont calculés - -**Alors** les revenus sont automatiquement transférés vers mon e-wallet Mangopay -**Et** l'e-wallet est débité lors du virement SEPA vers mon RIB -**Et** je n'ai aucune action manuelle à faire - ---- - -## 11. Tableau de bord - Revenus pub temps réel - -**Étant donné** que j'accède à mon tableau de bord créateur - -**Quand** je consulte l'onglet "Revenus" - -**Alors** je vois: - - | métrique | valeur exemple | - |---|---| - | Revenus pub ce mois | 123.45€ | - | Revenus premium ce mois | 67.89€ | - | Solde disponible ce mois | 191.34€ | - | Prochain paiement | 15 mars 2025 | - -**Et** ces valeurs sont mises à jour en temps réel (cache Redis, refresh 10 min) - ---- - -## 12. Tableau de bord - Solde en attente de paiement - -**Étant donné** que mes revenus de janvier sont calculés et validés -**Et** que nous sommes le 10 février - -**Quand** je consulte mon tableau de bord - -**Alors** je vois: - - | métrique | valeur exemple | - |---|---| - | Solde en attente | 150.00€ | - | Date de paiement | 15 février 2025 | - | Statut | En attente | - - ---- - -## 13. Historique des virements permanents - -**Étant donné** que je suis monétisé depuis 6 mois - -**Quand** je consulte l'historique des paiements - -**Alors** je vois la liste complète: - - | date paiement | montant | statut | référence virement | - |---|---|---|---| - | 15/02/2025 | 150.00€ | Payé | MANGOPAY-ABC123 | - | 15/01/2025 | 123.50€ | Payé | MANGOPAY-XYZ789 | - | 15/12/2024 | 98.75€ | Payé | MANGOPAY-DEF456 | - | ... | ... | ... | ... | - - ---- - -## 14. Export comptable CSV téléchargeable - -**Étant donné** que je clique sur "Télécharger export comptable" - -**Quand** le fichier CSV est généré - -**Alors** je télécharge un fichier contenant: -**Et** je peux transmettre ce fichier à mon expert-comptable - ---- - -## 15. Échec virement - Tentative 1 échouée - -**Étant donné** qu'un virement est initié le 15 février -**Mais** que mon RIB est invalide ou le compte est fermé - -**Quand** Mangopay détecte l'échec - -**Alors** le statut passe à "Échec - Retry programmé le 18 février" -**Et** je reçois un email m'alertant du problème - ---- - -## 16. Échec virement - Retry automatique J+3 - -**Étant donné** que le virement du 15 février a échoué - -**Quand** le 18 février arrive (J+3) - -**Alors** Mangopay tente automatiquement un nouveau virement -**Et** si le RIB est toujours invalide, le virement échoue à nouveau - ---- - -## 17. Échec virement - Retry automatique J+7 - -**Étant donné** que les 2 premières tentatives ont échoué - -**Quand** le 22 février arrive (J+7) - -**Alors** Mangopay tente une 3ème et dernière fois -**Et** si le virement échoue encore, la monétisation est suspendue - ---- - -## 18. Échec virement - Suspension monétisation après 3 échecs - -**Étant donné** que les 3 tentatives de virement ont échoué - -**Quand** le système détecte le 3ème échec - -**Alors** ma monétisation est suspendue automatiquement -**Et** je reçois un email: - ---- - -## 19. Mise à jour RIB et réactivation paiement - -**Étant donné** que ma monétisation est suspendue pour RIB invalide -**Et** que mon solde en attente est 150.00€ - -**Quand** je mets à jour mon RIB avec un compte valide - -**Alors** Mangopay tente immédiatement un nouveau virement -**Et** si le virement réussit, ma monétisation est réactivée automatiquement - ---- - -## 20. Notification email lors de chaque paiement - -**Étant donné** qu'un virement de 150.00€ est effectué le 15 février - -**Quand** le virement est confirmé par Mangopay - -**Alors** je reçois un email: - ---- - -## 21. Justification seuil 50€ - Éviter frais bancaires micro-sommes - -**Étant donné** que Mangopay facture des frais fixes par virement -**Et** que les banques peuvent facturer des frais de réception - -**Quand** un créateur génère seulement 5€/mois - -**Alors** un virement mensuel coûterait proportionnellement trop cher -**Et** le seuil de 50€ garantit des frais proportionnels raisonnables - ---- - -## 22. Comparaison avec YouTube (seuil 100$) - -**Étant donné** que YouTube fixe le seuil à 100$ (~90€) - -**Quand** RoadWave fixe le seuil à 50€ - -**Alors** RoadWave est plus accessible pour petits créateurs -**Et** les paiements arrivent plus rapidement - ---- - -## 23. Comparaison avec Twitch (seuil 50$) - -**Étant donné** que Twitch fixe le seuil à 50$ (~45€) - -**Quand** RoadWave fixe le seuil à 50€ - -**Alors** le seuil est aligné sur Twitch -**Et** les créateurs comprennent facilement le système - ---- - -## 24. Comparaison avec Spotify (seuil 10€ mais délais longs) - -**Étant donné** que Spotify a un seuil bas de 10€ mais verse tous les 3 mois - -**Quand** RoadWave a un seuil de 50€ mais verse chaque mois - -**Alors** les créateurs reçoivent leurs paiements plus régulièrement -**Et** la trésorerie est plus prévisible - ---- - -## 25. Relevé mensuel PDF automatique - -**Étant donné** que mes revenus de janvier sont calculés - -**Quand** le 1er février arrive - -**Alors** un relevé mensuel PDF est généré automatiquement: -**Et** le PDF est téléchargeable depuis mon tableau de bord - ---- - -## 26. Conservation relevés 10 ans (obligation comptable) - -**Étant donné** que je génère des revenus sur RoadWave - -**Quand** je télécharge mes relevés mensuels - -**Alors** je dois les conserver 10 ans (obligation légale France) -**Et** RoadWave conserve également une copie pendant 10 ans pour audit - ---- - -## 27. Dashboard admin - Monitoring paiements - -**Étant donné** qu'un admin RoadWave consulte les paiements du mois - -**Quand** il accède au dashboard admin - -**Alors** il voit: - - | métrique | valeur exemple | - |---|---| - | Créateurs payés ce mois | 1,247 | - | Montant total versé | 127,345€ | - | Paiements en attente | 34 | - | Échecs virements | 3 | - | Délai moyen réception (jours) | 1.8 | - - ---- - -## 28. Alerte admin si taux échec >5% - -**Étant donné** que 8% des virements du mois ont échoué - -**Quand** le système détecte le taux d'échec élevé - -**Alors** une alerte est envoyée à l'équipe technique: - ---- - -## 29. Statistiques personnelles - Moyenne revenus sur 6 mois - -**Étant donné** que je suis monétisé depuis 6 mois - -**Quand** je consulte mes statistiques - -**Alors** je vois: - - | métrique | valeur | - |---|---| - | Revenus moyens/mois | 134.50€ | - | Meilleur mois | 189.00€ | - | Mois le plus bas | 87.30€ | - | Tendance | +12% ↗ | - -**Et** cela m'aide à suivre ma progression - ---- - -## 30. Projection revenus annuels - -**Étant donné** que mes revenus moyens sont 134.50€/mois - -**Quand** je consulte les projections - -**Alors** le système estime mes revenus annuels à ~1,614€ -**Et** je peux anticiper mes déclarations fiscales - ---- - -## 31. Notification seuil symbolique 1000€ cumulés - -**Étant donné** que mes revenus cumulés depuis inscription atteignent 1000€ - -**Quand** le paiement qui franchit ce seuil est effectué - -**Alors** je reçois une notification: - ---- - -## 32. Performance calcul avec 100 000 créateurs monétisés - -**Étant donné** que RoadWave a 100 000 créateurs monétisés - -**Quand** le calcul des paiements du 15 du mois est lancé - -**Alors** un job asynchrone traite les paiements par batch de 1000 -**Et** tous les virements sont initiés en 2-4 heures -**Et** les serveurs Mangopay gèrent la charge sans problème - ---- - -## 33. Backup des données de paiement - -**Étant donné** que les paiements sont critiques pour les créateurs - -**Quand** un paiement est effectué - -**Alors** les données sont sauvegardées dans PostgreSQL (principal) -**Et** répliquées vers une base de backup (replica) -**Et** une copie d'archive est stockée sur S3 (conservation 10 ans) - ---- - -## 34. Audit trail complet des paiements - -**Étant donné** qu'un paiement est initié, traité et complété - -**Quand** un audit est demandé - -**Alors** tous les événements sont loggés: - - | événement | timestamp | détails | - |---|---|---| - | Calcul revenus mois | 2025-01-31 23:59:00 | Montant: 150.00€ | - | Validation période fraude | 2025-02-14 23:59:00 | Aucune fraude détectée | - | Initiation virement | 2025-02-15 09:00:00 | Mangopay ref: ABC123 | - | Confirmation virement | 2025-02-16 14:30:00 | Reçu par banque créateur | - -**Et** ces logs sont conservés 10 ans pour conformité - ---- - -## 35. Protection fraude - Détection pattern suspect - -**Étant donné** qu'un créateur génère subitement 10 000€ de revenus en 1 mois - -**Alors** que sa moyenne est de 50€/mois - -**Quand** le système détecte cette anomalie - -**Alors** le paiement est mis en attente pour vérification manuelle -**Et** l'équipe modération analyse le compte avant validation - ---- - - - - - -
- - -# Sources de revenus créateurs -> *En tant que créateur monétisé* -> *Je veux générer des revenus via publicités et abonnés Premium* -> *Afin d'être rémunéré pour mon travail* - -**34 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un créateur avec la monétisation activée -> **Et** que mon KYC est validé -## 1. CPM créateur de 3€ / 1000 écoutes complètes - -**Étant donné** que mes contenus ont généré 1000 écoutes complètes par des utilisateurs gratuits - -**Quand** le calcul des revenus du mois est effectué - -**Alors** je touche 3.00€ pour ces 1000 écoutes -**Et** ce montant est ajouté à mon solde disponible - ---- - -## 2. 10 000 écoutes gratuits → 30€ de revenus pub - -**Étant donné** que mes contenus ont généré 10 000 écoutes complètes (utilisateurs gratuits) - -**Quand** le mois se termine - -**Alors** je touche 30.00€ de revenus publicitaires -**Et** ces revenus sont visibles en temps réel dans mon tableau de bord - ---- - -## 3. 50 000 écoutes gratuits → 150€ de revenus pub - -**Étant donné** que mes contenus ont généré 50 000 écoutes complètes (utilisateurs gratuits) - -**Quand** le mois se termine - -**Alors** je touche 150.00€ de revenus publicitaires - ---- - -## 4. 100 000 écoutes gratuits → 300€ de revenus pub - -**Étant donné** que mes contenus ont généré 100 000 écoutes complètes (utilisateurs gratuits) - -**Quand** le mois se termine - -**Alors** je touche 300.00€ de revenus publicitaires - ---- - -## 5. Répartition économique - Plateforme garde 94% - -**Étant donné** qu'une publicité facturée 0.05€/écoute génère 50€ CPM - -**Quand** la plateforme calcule la répartition - -**Alors** le créateur touche 3€ (6% du CA pub) -**Et** la plateforme garde 47€ (94%) pour: - - | poste budgétaire | coût estimé | - |---|---| - | CDN + infrastructure | 10-15€ | - | Modération + support | 5-10€ | - | Développement + R&D | 10-15€ | - | Marge opérationnelle | 10-15€ | - - ---- - -## 6. Écoute complète = ≥80% du contenu écouté - -**Étant donné** qu'un utilisateur gratuit écoute mon contenu de 10 minutes - -**Quand** il écoute 8 minutes (80%) - -**Alors** l'écoute compte comme "complète" -**Et** je génère 0.003€ de revenus pub (3€/1000) - ---- - -## 7. Écoute incomplète <80% ne compte pas - -**Étant donné** qu'un utilisateur gratuit écoute mon contenu de 10 minutes -**Mais** il skip après 5 minutes (50%) - -**Quand** le calcul des revenus est effectué - -**Alors** cette écoute ne compte pas comme "complète" -**Et** je ne génère aucun revenu publicitaire pour cette écoute - ---- - -## 8. Écoutes Premium ne comptent pas pour les revenus pub - -**Étant donné** qu'un utilisateur Premium écoute 100% de mon contenu - -**Quand** le calcul des revenus publicitaires est effectué - -**Alors** cette écoute ne compte pas dans les revenus pub -**Mais** elle compte dans les revenus Premium (système séparé) - ---- - -## 9. Détection bots - Écoutes exclues - -**Étant donné** qu'un bot génère 10 000 écoutes artificielles sur mes contenus - -**Quand** le système détecte le pattern suspect (rate limiting, IP unique, etc.) - -**Alors** ces écoutes sont marquées comme frauduleuses -**Et** elles sont exclues du calcul des revenus publicitaires - ---- - -## 10. Comparaison avec YouTube (3-5€/1000 vues) - -**Étant donné** que YouTube paie 3-5€/1000 vues - -**Quand** RoadWave fixe le CPM créateur à 3€/1000 écoutes - -**Alors** le tarif est aligné sur le bas de la fourchette YouTube -**Et** cela est compétitif pour un MVP sans marché publicitaire mature - ---- - -## 11. Comparaison avec Spotify (3-4€/1000 écoutes) - -**Étant donné** que Spotify paie ~3-4€/1000 écoutes - -**Quand** RoadWave fixe le CPM créateur à 3€/1000 écoutes - -**Alors** le tarif est aligné sur l'industrie musicale -**Et** les créateurs audio peuvent anticiper des revenus similaires - ---- - -## 12. Tableau de bord - Revenus pub temps réel - -**Étant donné** que j'accède à mon tableau de bord créateur - -**Quand** je consulte mes revenus publicitaires - -**Alors** je vois: - - | métrique | valeur exemple | - |---|---| - | Écoutes complètes ce mois (gratuit) | 23 456 | - | Revenus pub ce mois | 70.37€ | - | CPM effectif | 3.00€ | - -**Et** ces valeurs sont mises à jour toutes les 10 minutes - ---- - -## 13. Répartition 70/30 - Créateur touche 70% - -**Étant donné** qu'un utilisateur Premium paie 4.99€/mois - -**Quand** la répartition est calculée - -**Alors** 3.49€ sont reversés aux créateurs écoutés (70%) -**Et** 1.50€ sont gardés par la plateforme (30%) - ---- - -## 14. Utilisateur écoute 3 créateurs - Répartition proportionnelle - -**Étant donné** qu'un utilisateur Premium paie 4.99€/mois -**Et** qu'il écoute 3 créateurs ce mois: - - | créateur | temps écoute | ratio | - |---|---|---| - | Créateur A | 10h | 50% | - | Créateur B | 6h | 30% | - | Créateur C | 4h | 20% | - - -**Quand** le calcul des revenus Premium est effectué - -**Alors** la répartition est: - - | créateur | revenus | - |---|---| - | Créateur A | 1.75€ | - | Créateur B | 1.05€ | - | Créateur C | 0.70€ | - -**Et** la somme totale versée aux créateurs est 3.50€ (70% de 4.99€) - ---- - -## 15. Calcul SQL proportionnel au temps d'écoute - -**Étant donné** qu'un utilisateur Premium a écouté plusieurs créateurs - -**Quand** le système calcule les revenus du mois - -**Alors** la requête SQL suivante est exécutée: - ---- - -## 16. Utilisateur écoute un seul créateur - 100% à ce créateur - -**Étant donné** qu'un utilisateur Premium paie 4.99€/mois -**Et** qu'il n'écoute qu'un seul créateur (moi) - -**Quand** le mois se termine - -**Alors** je touche 3.49€ (70% de 4.99€) -**Et** je reçois 100% de la part créateurs - ---- - -## 17. Utilisateur Premium inactif - Aucun revenu généré - -**Étant donné** qu'un utilisateur Premium paie 4.99€/mois -**Mais** qu'il n'écoute aucun contenu ce mois - -**Quand** le calcul des revenus Premium est effectué - -**Alors** aucun créateur ne reçoit de revenus de cet utilisateur -**Et** les 3.49€ de la part créateurs restent à la plateforme -**Et** cela couvre les coûts d'infrastructure - ---- - -## 18. Comparaison avec YouTube Premium (70/30) - -**Étant donné** que YouTube Premium reverse 70% aux créateurs - -**Quand** RoadWave fixe également 70/30 - -**Alors** le modèle est aligné sur le standard industrie -**Et** les créateurs ont confiance dans l'équité du système - ---- - -## 19. Comparaison avec Spotify (70/30) - -**Étant donné** que Spotify reverse 70% aux artistes - -**Quand** RoadWave fixe également 70/30 - -**Alors** le modèle est identique à Spotify -**Et** les créateurs audio comprennent facilement le système - ---- - -## 20. Apple Music moins avantageux (52/48) - -**Étant donné** qu'Apple Music ne reverse que 52% aux artistes - -**Quand** RoadWave offre 70% aux créateurs - -**Alors** RoadWave est plus avantageux de 18 points -**Et** cela devient un argument marketing fort - ---- - -## 21. Justification équité - Créateurs les plus écoutés gagnent plus - -**Étant donné** que 2 créateurs ont le même nombre d'abonnés Premium -**Mais** que le Créateur A est écouté 20h/mois et le Créateur B seulement 2h/mois - -**Quand** les revenus Premium sont calculés - -**Alors** le Créateur A gagne 10× plus que le Créateur B -**Et** cela récompense la qualité et l'engagement (pas juste l'abonnement) - ---- - -## 22. Pas de "winner takes all" - Équité totale - -**Étant donné** qu'un utilisateur Premium écoute 10 créateurs différents - -**Quand** les revenus sont calculés - -**Alors** chacun des 10 créateurs reçoit sa part proportionnelle -**Et** il n'y a pas de système où un seul créateur prend tout - ---- - -## 23. Marge plateforme 30% couvre absence revenus pub Premium - -**Étant donné** qu'un utilisateur Premium ne voit aucune publicité - -**Quand** la plateforme calcule ses revenus - -**Alors** elle ne touche que les 30% de l'abonnement Premium (1.50€) -**Et** cette marge compense la perte des revenus publicitaires (qui auraient été ~47€/1000 écoutes) - ---- - -## 24. Tableau de bord - Revenus Premium temps réel - -**Étant donné** que j'accède à mon tableau de bord créateur - -**Quand** je consulte mes revenus Premium - -**Alors** je vois: - - | métrique | valeur exemple | - |---|---| - | Abonnés Premium actifs ayant écouté | 47 | - | Heures d'écoute Premium ce mois | 234h | - | Revenus Premium ce mois | 89.23€ | - -**Et** ces valeurs sont mises à jour toutes les 10 minutes - ---- - -## 25. Revenus cumulés pub + premium - -**Étant donné** que j'ai généré ce mois: - - | source | montant | - |---|---| - | Revenus pub | 150.00€ | - | Revenus Premium | 89.23€ | - - -**Quand** je consulte mon solde disponible - -**Alors** le total est 239.23€ -**Et** ce solde sera versé le 15 du mois prochain (si ≥50€) - ---- - -## 26. Dashboard créateur - Vue d'ensemble - -**Étant donné** que j'accède à mon tableau de bord créateur - -**Quand** je consulte la page revenus - -**Alors** je vois: - ---- - -## 27. Export comptable CSV pour expert-comptable - -**Étant donné** que je clique sur "Exporter pour comptable" - -**Quand** l'export est généré - -**Alors** je télécharge un fichier CSV: -**Et** je peux transmettre ce fichier à mon expert-comptable - ---- - -## 28. Notification hebdomadaire progression revenus - -**Étant donné** que je suis créateur monétisé - -**Quand** chaque lundi matin arrive - -**Alors** je reçois un email récapitulatif: - ---- - -## 29. Graphique évolution revenus sur 12 mois - -**Étant donné** que je suis monétisé depuis 12 mois - -**Quand** j'accède à mes statistiques - -**Alors** je vois un graphique en courbes montrant: - - | mois | revenus pub | revenus premium | total | - |---|---|---|---| - | Jan 25 | 150€ | 89€ | 239€ | - | Déc 24 | 123€ | 55€ | 178€ | - | Nov 24 | 100€ | 56€ | 156€ | - | ... | ... | ... | ... | - -**Et** cela m'aide à suivre ma progression - ---- - -## 30. Top 3 contenus les plus rentables du mois - -**Étant donné** que j'ai publié 20 contenus ce mois - -**Quand** je consulte mes statistiques détaillées - -**Alors** je vois mon top 3 contenus: - - | titre | écoutes | revenus pub | revenus premium | total | - |---|---|---|---|---| - | Mon meilleur épisode | 12,345 | 37.04€ | 23.45€ | 60.49€ | - | Discussion tech | 8,901 | 26.70€ | 15.67€ | 42.37€ | - | Road trip Bretagne | 7,234 | 21.70€ | 12.34€ | 34.04€ | - -**Et** cela m'aide à comprendre quel type de contenu plaît le plus - ---- - -## 31. Alertes seuils de revenus - -**Étant donné** que j'ai activé les notifications de seuils - -**Quand** mes revenus du mois dépassent 100€ pour la première fois - -**Alors** je reçois une notification: - ---- - -## 32. Performance calcul avec 100 000 créateurs - -**Étant donné** que RoadWave a 100 000 créateurs monétisés - -**Quand** le calcul des revenus mensuels est lancé le dernier jour du mois - -**Alors** un job asynchrone traite tous les créateurs -**Et** le calcul prend environ 2-4 heures pour tous les créateurs -**Et** les résultats sont stockés dans la table monthly_revenues - ---- - -## 33. Cache Redis pour métriques temps réel - -**Étant donné** que je consulte mon dashboard plusieurs fois par jour - -**Quand** la page se charge - -**Alors** les compteurs sont récupérés depuis Redis: - - | clé Redis | valeur exemple | - |---|---| - | creator:[id]:complete_listens:202501 | 50234 | - | creator:[id]:premium_hours:202501 | 234 | - | creator:[id]:revenue_ads:202501 | 150.70 | - | creator:[id]:revenue_premium:202501 | 89.23 | - -**Et** le temps de réponse est <30ms - ---- - -## 34. Prévision revenus fin de mois - -**Étant donné** que nous sommes le 20 du mois -**Et** que mes revenus actuels sont 160€ - -**Quand** le système calcule la projection - -**Alors** il estime les revenus fin de mois à ~240€ (extrapolation linéaire) -**Et** affiche "Projection fin de mois: ~240€" -**Et** cela m'aide à anticiper mes revenus - ---- - - - - - -
- - -# Actions complémentaires à l'arrêt -> *En tant qu'auditeur avec véhicule arrêté* -> *Je veux accéder à des actions avancées depuis l'application mobile* -> *Afin de liker explicitement, m'abonner ou signaler du contenu* - -**23 scénarios** (21 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -> **Et** que le véhicule est à l'arrêt (vitesse GPS = 0 km/h) -## 1. Like explicite avec bouton cœur - -**Étant donné** que j'écoute un contenu tagué "Automobile" -**Et** que ma jauge "Automobile" est à 60% - -**Quand** je clique sur le bouton cœur "Like" - -**Alors** ma jauge "Automobile" augmente de 2% -**Et** une animation de cœur rouge s'affiche -**Et** une vibration courte est déclenchée -**Et** ma jauge "Automobile" est maintenant à 62% - ---- - -## 2. Like explicite cumulable avec like automatique - -**Étant donné** que j'ai écouté un contenu "Voyage" à 85% -**Et** que j'ai reçu un like automatique renforcé (+2%) -**Et** que ma jauge "Voyage" est à 52% - -**Quand** je clique sur le bouton cœur "Like" - -**Alors** ma jauge "Voyage" augmente encore de 2% -**Et** ma jauge "Voyage" passe à 54% -**Et** les deux likes sont cumulés - ---- - -## 3. Unlike retire le like manuel uniquement - -**Étant donné** que j'ai liké manuellement un contenu "Sport" -**Et** que ma jauge "Sport" est à 57% - -**Quand** je clique à nouveau sur le bouton cœur (toggle) - -**Alors** le cœur redevient vide (unlike) -**Et** ma jauge "Sport" diminue de 2% -**Et** ma jauge "Sport" revient à 55% - ---- - -## 4. Unlike ne retire pas le like automatique - -**Étant donné** que j'ai écouté un contenu "Musique" à 90% -**Et** que j'ai reçu un like automatique renforcé (+2%) -**Et** que ma jauge "Musique" est à 52% -**Et** que je n'ai PAS liké manuellement - -**Quand** je consulte l'interface - -**Alors** le bouton "Unlike" n'est pas disponible -**Et** le cœur reste grisé (aucun like manuel) -**Et** ma jauge reste à 52% - ---- - -## 5. Abonnement à un créateur - -**Étant donné** qu'un créateur publie des contenus tagués "Automobile" et "Technologie" -**Et** que mes jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 50% | - | Technologie | 45% | - - -**Quand** je clique sur "S'abonner" sur le profil du créateur - -**Alors** ma jauge "Automobile" augmente de 5% -**Et** ma jauge "Technologie" augmente de 5% -**Et** une animation d'étoile dorée s'affiche -**Et** un badge "Abonné ✓" apparaît sur le profil -**Et** mes nouvelles jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 55% | - | Technologie | 50% | - - ---- - -## 6. Désabonnement d'un créateur - -**Étant donné** que je suis abonné à un créateur -**Et** que mes jauges "Automobile" et "Technologie" sont à 55% et 50% - -**Quand** je clique sur "Se désabonner" - -**Alors** ma jauge "Automobile" diminue de 5% -**Et** ma jauge "Technologie" diminue de 5% -**Et** le badge "Abonné ✓" disparaît -**Et** mes nouvelles jauges sont: - - | catégorie | niveau | - |---|---| - | Automobile | 50% | - | Technologie | 45% | - - ---- - -## 7. Signalement d'un contenu inapproprié - -**Étant donné** que j'écoute un contenu - -**Quand** je clique sur le menu contextuel "⋮" -**Et** que je sélectionne "Signaler" - -**Alors** un formulaire de signalement s'ouvre -**Et** je dois sélectionner une catégorie: - - | Catégorie | - |---| - | Haine et violence | - | Contenu sexuel | - | Illégalité | - | Droits d'auteur | - | Spam | - | Désinformation (fake news) | - | Autre | - -**Et** je peux ajouter un commentaire optionnel -**Et** le signalement est envoyé au flux de modération - ---- - -## 8. Feedback visuel pour like explicite - -**Étant donné** que je clique sur le bouton cœur - -**Quand** le like est enregistré - -**Alors** une animation de cœur rouge se lance (0.5s) -**Et** le cœur reste rouge plein -**Et** une vibration haptique courte est déclenchée (iOS: .light, Android: 50ms) -**Et** un badge "♥ Ajouté à vos favoris" s'affiche 2 secondes - ---- - -## 9. Feedback visuel pour abonnement - -**Étant donné** que je clique sur "S'abonner" - -**Quand** l'abonnement est enregistré - -**Alors** une animation d'étoile dorée se lance (0.8s) -**Et** le bouton devient "Abonné ✓" avec badge doré -**Et** une notification "Abonné à [Créateur]" s'affiche -**Et** les contenus du créateur seront boostés +30% dans l'algo - ---- - -## 10. Bouton like désactivé si vitesse >10 km/h - -**Étant donné** que je conduis à 50 km/h - -**Quand** j'essaie d'accéder au bouton cœur dans l'app mobile - -**Alors** le bouton est grisé et non cliquable -**Et** un message "Arrêtez-vous pour liker" s'affiche si clic tenté -**Et** seules les commandes au volant physiques fonctionnent - ---- - -## 11. Bouton abonnement désactivé en conduite - -**Étant donné** que je conduis à 40 km/h - -**Quand** j'essaie d'accéder au profil créateur dans l'app - -**Alors** le bouton "S'abonner" est désactivé -**Et** un message "Arrêtez-vous pour vous abonner" s'affiche -**Et** la navigation dans l'app est limitée aux fonctions lecture - ---- - -## 12. Signalement possible en conduite via vocal - -**Étant donné** que je conduis à 60 km/h -**Et** que j'utilise CarPlay avec Siri - -**Quand** je dis "Hey Siri, signale ce contenu" - -**Alors** Siri demande "Quelle catégorie ?" -**Et** je peux répondre vocalement "Spam" ou autre catégorie -**Et** le signalement est enregistré sans toucher l'écran - ---- - -## 13. Actions vocales disponibles avec CarPlay/Android Auto - -**Étant donné** que je conduis avec CarPlay activé - -**Quand** je dis "Hey Siri, like ce podcast" - -**Alors** un like explicite (+2%) est enregistré -**Et** Siri confirme "J'ai ajouté ce contenu à vos favoris" - -**Quand** je dis "OK Google, abonne-moi à ce créateur" - -**Alors** l'abonnement est enregistré (+5% toutes jauges) -**Et** Google Assistant confirme "Vous êtes maintenant abonné" - ---- - -## 14. Menu contextuel accessible à l'arrêt uniquement - -**Étant donné** que le véhicule est à l'arrêt - -**Quand** je clique sur le menu "⋮" (3 points verticaux) - -**Alors** les options disponibles sont: - - | Option | - |---| - | Like (cœur) | - | S'abonner au créateur | - | Signaler | - | Partager | - | Voir le profil du créateur | - | Télécharger (mode offline) | - -**Et** toutes les options sont cliquables - ---- - -## 15. Menu contextuel limité en conduite - -**Étant donné** que je conduis à 30 km/h - -**Quand** j'essaie d'ouvrir le menu "⋮" - -**Alors** seules 2 options sont disponibles: - - | Option | - |---| - | Signaler (vocal possible) | - | Suivant | - -**Et** les actions complexes sont désactivées - ---- - -## 16. Persistance des likes manuels en base de données - -**Étant donné** que je like manuellement 5 contenus - -**Quand** je ferme l'application -**Et** que je me reconnecte plus tard - -**Alors** tous mes likes manuels sont toujours présents -**Et** les cœurs rouges sont affichés sur les contenus likés -**Et** mes jauges reflètent toujours l'impact (+2% × 5 likes) - ---- - -## 17. Liste "Mes contenus likés" accessible dans profil - -**Étant donné** que j'ai liké manuellement 10 contenus - -**Quand** j'accède à mon profil utilisateur - -**Alors** je vois une section "❤️ Mes favoris" -**Et** la liste affiche les 10 contenus likés -**Et** je peux cliquer pour réécouter -**Et** je peux retirer un like (unlike) depuis cette liste - ---- - -## 18. Liste "Mes abonnements" accessible dans profil - -**Étant donné** que je suis abonné à 5 créateurs - -**Quand** j'accède à mon profil utilisateur - -**Alors** je vois une section "⭐ Mes abonnements" -**Et** la liste affiche les 5 créateurs avec leurs avatars -**Et** je peux accéder au profil de chaque créateur -**Et** je peux me désabonner depuis cette liste - ---- - -## 19. Impact abonnement sur tous les tags du créateur - -**Étant donné** qu'un créateur a publié des contenus avec ces tags: - - | Contenu | Tags | - |---|---| - | C1 | Automobile, Voyage | - | C2 | Automobile, Technologie | - | C3 | Voyage, Famille | - -**Et** que mes jauges sont toutes à 50% - -**Quand** je m'abonne à ce créateur - -**Alors** les jauges impactées sont: - - | Tag | Impact | - |---|---| - | Automobile | +5% | - | Voyage | +5% | - | Technologie | +5% | - | Famille | +5% | - -**Et** toutes les autres jauges restent à 50% - ---- - -## 20. Limite d'abonnements (200 maximum) - -**Étant donné** que je suis abonné à 200 créateurs - -**Quand** j'essaie de m'abonner à un 201ème créateur - -**Alors** un message "Limite de 200 abonnements atteinte" s'affiche -**Et** je dois me désabonner d'un créateur existant pour en ajouter un nouveau - ---- - -## 21. Confirmation avant désabonnement - -**Étant donné** que je suis abonné à un créateur - -**Quand** je clique sur "Se désabonner" - -**Alors** une popup de confirmation s'affiche: -**Et** je dois confirmer pour valider -**Et** je peux annuler pour conserver l'abonnement - ---- - -## 22. 📋 Plan: Cumul like automatique + like manuel - -**Étant donné** qu'un contenu est tagué "Sport" -**Et** que ma jauge "Sport" est à 50% - -**Quand** j'écoute à % (like auto ) -**Et** que je like manuellement (+2%) - -**Alors** l'impact total est -**Et** ma nouvelle jauge est - -**📊 Exemples de données:** - -| pourcentage | auto | total | nouveau_niveau | -|---|---|---|---| -| 10 | 0 | +2% | 52% | -| 30 | +1% | +3% | 53% | -| 50 | +1% | +3% | 53% | -| 80 | +2% | +4% | 54% | -| 95 | +2% | +4% | 54% | - ---- - -## 23. 📋 Plan: Actions disponibles selon vitesse GPS - -**Étant donné** que je roule à km/h - -**Quand** j'essaie d'accéder à - -**Alors** l'action est - -**📊 Exemples de données:** - -| vitesse | action | disponibilite | -|---|---|---| -| 0 | Like manuel | disponible | -| 0 | Abonnement | disponible | -| 0 | Signalement | disponible | -| 5 | Like manuel | disponible | -| 5 | Abonnement | disponible | -| 10 | Like manuel | désactivée | -| 10 | Abonnement | désactivée | -| 50 | Like manuel | désactivée | -| 50 | Abonnement | désactivée | -| 50 | Signalement vocal | disponible | - ---- - - - - - -
- - -# Commande "Précédent" -> *En tant qu'auditeur* -> *Je veux que le bouton "Précédent" ait un comportement intelligent* -> *Afin de rejouer le contenu actuel ou revenir au précédent selon la progression* - -**19 scénarios** (17 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -## 1. Précédent après <10s revient au contenu précédent - -**Étant donné** que j'ai écouté le contenu "A" pendant 2 minutes -**Et** que j'écoute maintenant le contenu "B" depuis 5 secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** la lecture revient au contenu "A" -**Et** la position de lecture est à 2 minutes (position exacte sauvegardée) -**Et** le contenu "B" reste en historique - ---- - -## 2. Précédent après ≥10s rejoue le contenu actuel - -**Étant donné** que j'écoute le contenu "C" depuis 15 secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** le contenu "C" rejoue depuis le début (position 0:00) -**Et** la lecture ne revient pas au contenu précédent -**Et** la progress bar revient à 0% - ---- - -## 3. Précédent exactement à 10s rejoue le contenu actuel - -**Étant donné** que j'écoute le contenu "D" depuis exactement 10 secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** le contenu "D" rejoue depuis le début -**Et** la lecture ne revient pas au contenu précédent - ---- - -## 4. Précédent sur le premier contenu de session - -**Étant donné** que je viens de démarrer l'application -**Et** que j'écoute le contenu "Premier" depuis 3 secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** le contenu "Premier" rejoue depuis le début -**Et** aucun contenu précédent n'existe - ---- - -## 5. Historique de navigation limité à 10 contenus - -**Étant donné** que j'ai écouté 10 contenus [C1, C2, ..., C10] -**Et** que l'historique Redis contient 10 entrées - -**Quand** je passe au contenu C11 - -**Alors** le contenu C1 est supprimé de l'historique (FIFO) -**Et** l'historique contient [C2, C3, ..., C10, C11] -**Et** la taille reste à 10 contenus maximum - ---- - -## 6. Position exacte sauvegardée dans l'historique - -**Étant donné** que j'écoute le contenu "A" (durée 5 minutes) - -**Quand** j'atteins 2 minutes 30 secondes -**Et** que j'appuie sur "Suivant" - -**Alors** l'historique enregistre: - - | content_id | position_seconds | listened_at | - |---|---|---| - | A | 150 | 2026-01-21T10:30:00 | - - -**Quand** je reviens au contenu "A" via "Précédent" - -**Alors** la lecture reprend exactement à 2 minutes 30 secondes - ---- - -## 7. Navigation arrière sur plusieurs contenus - -**Étant donné** que j'ai écouté dans l'ordre: A (2min), B (30s), C (3min) -**Et** que j'écoute maintenant D depuis 1 seconde - -**Quand** j'appuie sur "Précédent" (1ère fois) - -**Alors** je reviens au contenu C à la position 3 minutes - -**Quand** j'appuie sur "Précédent" (<10s sur C) - -**Alors** je reviens au contenu B à la position 30 secondes - -**Quand** j'appuie sur "Précédent" (<10s sur B) - -**Alors** je reviens au contenu A à la position 2 minutes - ---- - -## 8. Précédent après milieu du contenu rejoue depuis début - -**Étant donné** que j'écoute un contenu de 5 minutes - -**Quand** j'atteins 2 minutes 30 secondes (milieu) -**Et** que j'appuie sur "Précédent" - -**Alors** le contenu actuel rejoue depuis 0:00 -**Et** je ne reviens pas au contenu précédent - ---- - -## 9. Enchaînement Suivant puis Précédent rapide - -**Étant donné** que j'écoute le contenu "A" depuis 1 minute - -**Quand** j'appuie sur "Suivant" - -**Alors** le contenu "B" démarre - -**Quand** j'appuie immédiatement sur "Précédent" (2s après) - -**Alors** je reviens au contenu "A" à la position 1 minute -**Et** le contenu "B" reste dans l'historique - ---- - -## 10. Transition fluide avec animation 0.3s - -**Étant donné** que j'appuie sur "Précédent" - -**Quand** le changement de contenu se produit - -**Alors** la transition audio utilise un fade out/in de 0.3 secondes -**Et** la progress bar revient avec une animation fluide -**Et** l'interface ne montre aucun message de confirmation - ---- - -## 11. Historique survit au changement de réseau - -**Étant donné** que j'ai un historique de 5 contenus en cache Redis - -**Quand** je perds la connexion réseau temporairement -**Et** que je reviens en ligne - -**Alors** l'historique de navigation est toujours disponible -**Et** je peux toujours utiliser "Précédent" - ---- - -## 12. Historique stocké en Redis avec structure complète - -**Étant donné** que j'ai écouté 3 contenus - -**Quand** je consulte le cache Redis - -**Alors** la structure est: -**Et** l'ordre est du plus récent au plus ancien - ---- - -## 13. Précédent sur contenu en cours au début (<10s) du premier - -**Étant donné** que je démarre une session avec le contenu "Initial" -**Et** que j'écoute depuis 3 secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** le contenu "Initial" rejoue depuis le début -**Et** aucune erreur n'est générée -**Et** l'historique reste vide - ---- - -## 14. Compteur de temps respecte les seuils exacts - -**Étant donné** que j'écoute un contenu - -**Quand** le temps écoulé est de 9.9 secondes -**Et** que j'appuie sur "Précédent" - -**Alors** je reviens au contenu précédent - -**Quand** le temps écoulé est de 10.0 secondes -**Et** que j'appuie sur "Précédent" - -**Alors** le contenu actuel rejoue depuis le début - ---- - -## 15. Progress bar visuelle reflète le retour exact - -**Étant donné** que j'ai écouté le contenu "A" jusqu'à 75% (3min45 sur 5min) -**Et** que je suis passé au contenu "B" - -**Quand** je reviens au contenu "A" via "Précédent" - -**Alors** la progress bar affiche 75% -**Et** l'indicateur de temps affiche "3:45 / 5:00" -**Et** la lecture reprend exactement à cet endroit - ---- - -## 16. Métadonnées d'historique incluent timestamp précis - -**Étant donné** que j'écoute un contenu "X" pendant 45 secondes à 10:30:15 - -**Quand** je passe au contenu suivant - -**Alors** l'historique enregistre: - - | content_id | position_seconds | listened_at | - |---|---|---| - | X | 45 | 2026-01-21T10:30:15Z | - -**Et** le timestamp précis permet l'analyse d'usage - ---- - -## 17. Suppression FIFO respecte l'ordre chronologique - -**Étant donné** un historique de [C1@10:00, C2@10:02, ..., C10@10:20] - -**Quand** j'ajoute C11 à 10:22 - -**Alors** C1 (le plus ancien) est supprimé -**Et** l'historique contient [C2@10:02, ..., C11@10:22] -**Et** la taille reste exactement 10 entrées - ---- - -## 18. 📋 Plan: Comportement selon temps écouté - -**Étant donné** que j'écoute un contenu depuis secondes - -**Quand** j'appuie sur "Précédent" - -**Alors** l'action est - -**📊 Exemples de données:** - -| temps | comportement | -|---|---| -| 1 | revenir au contenu précédent | -| 5 | revenir au contenu précédent | -| 9 | revenir au contenu précédent | -| 10 | rejouer le contenu actuel depuis 0:00 | -| 11 | rejouer le contenu actuel depuis 0:00 | -| 30 | rejouer le contenu actuel depuis 0:00 | -| 180 | rejouer le contenu actuel depuis 0:00 | - ---- - -## 19. 📋 Plan: Positions de reprise exactes - -**Étant donné** que j'écoute un contenu de 10 minutes - -**Quand** j'atteins et passe au suivant -**Et** que je reviens via "Précédent" - -**Alors** la lecture reprend exactement à - -**📊 Exemples de données:** - -| position | -|---| -| 0:15 | -| 1:30 | -| 3:45 | -| 5:00 | -| 7:23 | -| 9:50 | - ---- - - - - - -
- - -# Commandes vocales CarPlay et Android Auto -> *En tant que conducteur avec CarPlay ou Android Auto* -> *Je veux utiliser des commandes vocales pour interagir avec l'application* -> *Afin de garder les mains sur le volant et les yeux sur la route* - -**25 scénarios** (23 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -> **Et** que CarPlay ou Android Auto est activé -## 1. Disponibilité des commandes vocales uniquement avec CarPlay/Android Auto - -**Étant donné** que je conduis avec CarPlay activé - -**Quand** je dis "Hey Siri" - -**Alors** Siri est disponible pour les commandes RoadWave - -**Étant donné** que je conduis avec Android Auto activé - -**Quand** je dis "OK Google" - -**Alors** Google Assistant est disponible pour les commandes RoadWave - ---- - -## 2. Parc automobile compatible avec vocal (30-40% en 2026) - -**Étant donné** que nous sommes en 2026 - -**Quand** je consulte les statistiques du parc automobile EU - -**Alors** environ 30-40% des véhicules ont CarPlay ou Android Auto -**Et** ces utilisateurs peuvent utiliser les commandes vocales -**Et** les 60-70% restants utilisent les commandes au volant uniquement - ---- - -## 3. Commande vocale "Like ce podcast" avec Siri - -**Étant donné** que j'écoute un contenu tagué "Automobile" -**Et** que ma jauge "Automobile" est à 60% - -**Quand** je dis "Hey Siri, like ce podcast" - -**Alors** un like explicite (+2%) est enregistré -**Et** ma jauge "Automobile" passe à 62% -**Et** Siri confirme vocalement "J'ai ajouté ce contenu à vos favoris" -**Et** aucune interaction écran n'est requise - ---- - -## 4. Commande vocale "Like ce contenu" avec Google Assistant - -**Étant donné** que j'écoute un contenu tagué "Voyage" - -**Quand** je dis "OK Google, like ce contenu" - -**Alors** un like explicite est enregistré (+2%) -**Et** Google Assistant confirme "J'ai liké ce contenu pour vous" -**Et** la commande fonctionne sans toucher l'écran - ---- - -## 5. Commande vocale "Abonne-moi à ce créateur" - -**Étant donné** que j'écoute un contenu d'un créateur tagué "Automobile" et "Technologie" -**Et** que mes jauges sont à 50% et 45% - -**Quand** je dis "Hey Siri, abonne-moi à ce créateur" - -**Alors** l'abonnement est enregistré -**Et** mes jauges augmentent de 5% chacune (55% et 50%) -**Et** Siri confirme "Vous êtes maintenant abonné à [Nom du créateur]" - ---- - -## 6. Commande vocale "Passe au contenu suivant" - -**Étant donné** que j'écoute un contenu "A" - -**Quand** je dis "Hey Siri, passe au contenu suivant" - -**Alors** le contenu "B" démarre immédiatement -**Et** la commande a le même effet que le bouton physique "Suivant" - ---- - -## 7. Commande vocale "Signale ce contenu" - -**Étant donné** que j'écoute un contenu inapproprié - -**Quand** je dis "OK Google, signale ce contenu" - -**Alors** Google Assistant demande "Quelle catégorie ?" -**Et** je réponds vocalement "Spam" -**Alors** le signalement est enregistré avec la catégorie "Spam" -**Et** Google Assistant confirme "J'ai signalé ce contenu" - ---- - -## 8. Commande vocale avec catégorie de signalement - -**Étant donné** que j'écoute un contenu - -**Quand** je dis "Hey Siri, signale ce contenu pour haine" - -**Alors** le signalement est enregistré avec la catégorie "Haine et violence" -**Et** Siri confirme "J'ai signalé ce contenu pour haine et violence" -**Et** le flux de modération reçoit le signalement - ---- - -## 9. Liste des catégories de signalement vocales supportées - -**Étant donné** que je dis "signale ce contenu pour [catégorie]" - -**Quand** la catégorie est: - - | Mot-clé vocal | Catégorie mappée | - |---|---| - | "haine" | Haine et violence | - | "sexuel" | Contenu sexuel | - | "illégalité" | Illégalité | - | "droits d'auteur" | Droits d'auteur | - | "spam" | Spam | - | "fake news" | Désinformation | - | "autre" | Autre | - - -**Alors** le signalement est enregistré avec la bonne catégorie - ---- - -## 10. Commande vocale non reconnue - fallback - -**Étant donné** que je dis "Hey Siri, super ce podcast" - -**Quand** Siri ne reconnaît pas l'intent RoadWave - -**Alors** Siri répond "Je ne comprends pas cette commande RoadWave" -**Et** elle suggère "Dites 'like ce podcast' ou 'passe au suivant'" - ---- - -## 11. Commandes vocales disponibles en conduite uniquement - -**Étant donné** que je roule à 50 km/h - -**Quand** j'utilise les commandes vocales - -**Alors** toutes les commandes sont disponibles: - - | Commande | Action | - |---|---| - | "Like ce podcast" | Like explicite +2% | - | "Abonne-moi à ce créateur" | Abonnement +5% | - | "Passe au suivant" | Contenu suivant | - | "Reviens au précédent" | Contenu précédent (règle 10s) | - | "Pause" | Pause lecture | - | "Reprends la lecture" | Play | - | "Signale ce contenu" | Signalement | - - ---- - -## 12. Intent iOS personnalisé pour RoadWave - -**Étant donné** que l'app iOS implémente les Intents - -**Quand** je configure les Shortcuts iOS - -**Alors** les intents suivants sont disponibles: - - | Intent Name | Action | - |---|---| - | LikeCurrentContentIntent | Like explicite | - | SubscribeToCreatorIntent | Abonnement | - | ReportContentIntent | Signalement | - | SkipToNextContentIntent | Suivant | - -**Et** Siri les reconnaît automatiquement - ---- - -## 13. Intent Android personnalisé pour RoadWave - -**Étant donné** que l'app Android implémente les Voice Actions - -**Quand** je configure les actions Google Assistant - -**Alors** les actions suivantes sont disponibles: - - | Action Name | Action | - |---|---| - | com.roadwave.LIKE_CONTENT | Like explicite | - | com.roadwave.SUBSCRIBE_CREATOR | Abonnement | - | com.roadwave.REPORT_CONTENT | Signalement | - | com.roadwave.SKIP_NEXT | Suivant | - -**Et** Google Assistant les reconnaît - ---- - -## 14. Confirmation vocale après action réussie - -**Étant donné** que je dis "Hey Siri, like ce podcast" - -**Quand** l'action est enregistrée avec succès - -**Alors** Siri répond immédiatement avec confirmation: -**Et** la réponse est naturelle et concise -**Et** elle ne distrait pas de la conduite - ---- - -## 15. Gestion d'erreur vocale si action échoue - -**Étant donné** que je dis "Hey Siri, abonne-moi à ce créateur" -**Et** que j'ai atteint la limite de 200 abonnements - -**Quand** Siri essaie d'enregistrer l'abonnement - -**Alors** l'action échoue -**Et** Siri répond "Impossible de s'abonner, limite de 200 abonnements atteinte" -**Et** elle suggère "Désabonnez-vous d'un créateur pour continuer" - ---- - -## 16. Commandes vocales multilingues (français) - -**Étant donné** que mon Siri est configuré en français - -**Quand** je dis "Hey Siri, j'aime ce podcast" - -**Alors** la commande est reconnue (variante de "like ce podcast") - -**Quand** je dis "Hey Siri, mets une étoile" - -**Alors** la commande est reconnue (variante de "like") - ---- - -## 17. Implémentation post-MVP (Sprint 5) - -**Étant donné** que les commandes vocales sont une feature Sprint 5 - -**Quand** le MVP est lancé - -**Alors** seules les commandes au volant physiques sont disponibles - -**Quand** le Sprint 5 est déployé - -**Alors** les intents iOS/Android sont activés -**Et** les commandes vocales deviennent disponibles - ---- - -## 18. Priorisation commandes vocales vs boutons physiques - -**Étant donné** que je conduis avec CarPlay -**Et** que j'ai accès aux boutons physiques ET aux commandes vocales - -**Quand** je veux liker un contenu - -**Alors** je peux soit: -**Et** les 3 méthodes sont valides - ---- - -## 19. Statistiques d'usage des commandes vocales - -**Étant donné** que 100 utilisateurs avec CarPlay utilisent RoadWave - -**Quand** je consulte les analytics - -**Alors** je peux voir: - - | Métrique | Exemple valeur | - |---|---| - | Taux d'utilisation commandes vocal | 15% | - | Commande la plus utilisée | "Like" | - | Taux de reconnaissance réussie | 92% | - | Taux d'échec / incompréhension | 8% | - - ---- - -## 20. Feedback haptique désactivé pour commandes vocales - -**Étant donné** que je like un contenu via commande vocale - -**Quand** l'action est enregistrée - -**Alors** aucune vibration haptique n'est déclenchée -**Et** seule la confirmation vocale est donnée - ---- - -## 21. Badge visuel mis à jour après commande vocale - -**Étant donné** que je dis "Hey Siri, like ce podcast" - -**Quand** l'action est enregistrée - -**Alors** le badge "♥ Ajouté à vos favoris" s'affiche sur l'écran CarPlay -**Et** le cœur devient rouge plein dans l'interface -**Et** la mise à jour est visible même sans toucher l'écran - ---- - -## 22. Commandes vocales avec contenu sans créateur - -**Étant donné** que j'écoute un contenu anonyme (créateur supprimé) - -**Quand** je dis "Hey Siri, abonne-moi à ce créateur" - -**Alors** Siri répond "Ce créateur n'est plus disponible" -**Et** aucun abonnement n'est enregistré - ---- - -## 23. Limitation temporelle des commandes vocales - -**Étant donné** que je dis "Hey Siri, like ce podcast" -**Et** que le contenu change 1 seconde après - -**Quand** Siri traite la commande 2 secondes plus tard - -**Alors** la commande s'applique au contenu qui était en lecture au moment de la commande -**Et** non au contenu actuel (système de timestamp) - ---- - -## 24. 📋 Plan: Commandes vocales avec différents assistants - -**Étant donné** que j'utilise - -**Quand** je dis - -**Alors** l'action est exécutée -**Et** la confirmation est - -**📊 Exemples de données:** - -| assistant | commande | action | confirmation | -|---|---|---|---| -| Siri | "Like ce podcast" | Like +2% | "Ajouté à vos favoris" | -| Google Assistant | "Like ce contenu" | Like +2% | "J'ai liké ce contenu" | -| Siri | "Abonne-moi à ce créateur" | Abonnement +5% | "Vous êtes abonné" | -| Google Assistant | "Abonne-moi à ce créateur" | Abonnement +5% | "Abonnement enregistré" | -| Siri | "Signale ce contenu" | Signalement | "J'ai signalé ce contenu" | -| Google Assistant | "Signale ce contenu" | Signalement | "Contenu signalé" | - ---- - -## 25. 📋 Plan: Mapping catégories signalement vocal - -**Étant donné** que je dis "signale ce contenu pour " - -**Quand** est reconnu - -**Alors** la catégorie mappée est - -**📊 Exemples de données:** - -| mot_cle | categorie | -|---|---| -| haine | Haine et violence | -| violence | Haine et violence | -| sexuel | Contenu sexuel | -| porno | Contenu sexuel | -| illégal | Illégalité | -| terrorisme | Illégalité | -| copyright | Droits d'auteur | -| droits auteur | Droits d'auteur | -| spam | Spam | -| fake news | Désinformation | -| fausse info | Désinformation | - ---- - - - - - -
- - -# Commandes au volant et interactions simplifiées -> *En tant que conducteur en sécurité* -> *Je veux utiliser uniquement les commandes simplifiées au volant* -> *Afin de naviguer sans distraction et en toute sécurité* - -**21 scénarios** (19 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -> **Et** que l'application est connectée via CarPlay ou Android Auto -## 1. Trois commandes disponibles au volant uniquement - -**Étant donné** que je conduis à 50 km/h - -**Quand** je consulte les commandes physiques disponibles - -**Alors** seules 3 actions sont disponibles: - - | Commande | Action | - |---|---| - | Suivant | Passer au contenu suivant | - | Précédent | Revenir au précédent (règle 10s) | - | Play/Pause | Pause/reprise avec fade 0.3s | - -**Et** aucune commande complexe n'est proposée - ---- - -## 2. Commande "Suivant" au volant - -**Étant donné** que j'écoute un contenu "A" - -**Quand** j'appuie sur le bouton physique "Suivant" au volant - -**Alors** le contenu "B" démarre immédiatement -**Et** aucune action supplémentaire n'est requise -**Et** l'interface ne demande aucune confirmation - ---- - -## 3. Commande "Précédent" au volant respecte règle 10s - -**Étant donné** que j'écoute un contenu depuis 5 secondes - -**Quand** j'appuie sur "Précédent" au volant - -**Alors** je reviens au contenu précédent (règle <10s) - -**Étant donné** que j'écoute un contenu depuis 15 secondes - -**Quand** j'appuie sur "Précédent" au volant - -**Alors** le contenu actuel rejoue depuis le début (règle ≥10s) - ---- - -## 4. Commande "Play/Pause" avec fade audio - -**Étant donné** qu'un contenu est en lecture - -**Quand** j'appuie sur "Pause" au volant - -**Alors** la lecture se met en pause avec un fade out de 0.3 secondes -**Et** la position de lecture est sauvegardée - -**Quand** j'appuie sur "Play" au volant - -**Alors** la lecture reprend avec un fade in de 0.3 secondes -**Et** la reprise se fait à la position exacte - ---- - -## 5. Aucune commande complexe supportée - -**Étant donné** que je conduis - -**Quand** j'essaie un appui long sur "Suivant" - -**Alors** l'action n'est pas détectée (non supporté iOS/Android) - -**Quand** j'essaie un double-appui sur "Pause" - -**Alors** l'action n'est pas détectée -**Et** seules les actions simples (clic simple) fonctionnent - ---- - -## 6. Compatibilité 100% tous véhicules - -**Étant donné** que je conduis une voiture avec commandes basiques -**Et** que mon véhicule a seulement Suivant/Précédent/Pause - -**Quand** j'utilise RoadWave - -**Alors** toutes les fonctions essentielles sont accessibles -**Et** je n'ai pas besoin de boutons supplémentaires - ---- - -## 7. Feedback visuel discret après action - -**Étant donné** que j'appuie sur "Suivant" - -**Quand** le contenu change - -**Alors** l'interface CarPlay/Android Auto affiche le nouveau titre -**Et** aucune popup ne bloque la vue -**Et** le changement est fluide et immédiat - ---- - -## 8. Like automatique renforcé après écoute ≥80% - -**Étant donné** que j'écoute un contenu de 5 minutes tagué "Automobile" - -**Quand** j'écoute pendant 4 minutes 30 secondes (90%) - -**Alors** un like automatique renforcé (+2 points) est enregistré -**Et** un badge discret "♥ Ajouté à vos favoris" s'affiche 2 secondes -**Et** aucune action manuelle n'est requise - ---- - -## 9. Like automatique standard après écoute 30-79% - -**Étant donné** que j'écoute un contenu de 5 minutes tagué "Voyage" - -**Quand** j'écoute pendant 2 minutes (40%) -**Et** que j'appuie sur "Suivant" - -**Alors** un like automatique standard (+1 point) est enregistré -**Et** un badge discret s'affiche brièvement -**Et** je peux continuer à conduire sans interruption - ---- - -## 10. Signal négatif après skip rapide <10s - -**Étant donné** que j'écoute un contenu tagué "Politique" - -**Quand** j'appuie sur "Suivant" après seulement 5 secondes - -**Alors** un signal négatif (-0.5 point) est enregistré -**Et** la jauge "Politique" diminue légèrement -**Et** aucun message n'est affiché (transparence) - ---- - -## 11. Pas de like si écoute <30% - -**Étant donné** que j'écoute un contenu de 10 minutes - -**Quand** j'écoute pendant 2 minutes (20%) -**Et** que j'appuie sur "Suivant" - -**Alors** aucun like n'est enregistré -**Et** les jauges ne changent pas -**Et** le système considère l'écoute comme neutre - ---- - -## 12. Badge de feedback visuel disparaît après 2 secondes - -**Étant donné** que je reçois un like automatique - -**Quand** le badge "♥ Ajouté à vos favoris" apparaît - -**Alors** il reste visible 2 secondes en bas de l'écran -**Et** il disparaît automatiquement sans action -**Et** il ne bloque pas la vue de la route - ---- - -## 13. Tracking du temps d'écoute précis côté client - -**Étant donné** que je démarre la lecture d'un contenu - -**Quand** le player audio iOS/Android enregistre le temps - -**Alors** le startTime est enregistré à la milliseconde - -**Quand** j'arrête la lecture (Suivant, Pause, ou fin) - -**Alors** la durée exacte écoutée est calculée -**Et** le pourcentage (durée / durée_totale * 100) est envoyé à l'API - ---- - -## 14. API reçoit les événements d'écoute pour calcul - -**Étant donné** que j'écoute un contenu de 5 minutes à 80% - -**Quand** l'événement est envoyé à l'API - -**Alors** le backend reçoit: -**Et** le backend calcule le like automatique (+2 points) -**Et** les jauges sont mises à jour immédiatement (Redis + PostgreSQL) - ---- - -## 15. Actions différentes selon arrêt du contenu - -**Étant donné** que j'écoute un contenu - -**Quand** j'appuie sur "Suivant" - -**Alors** l'action envoyée est "skipped" - -**Quand** le contenu se termine naturellement - -**Alors** l'action envoyée est "completed" - -**Quand** j'appuie sur "Pause" - -**Alors** l'action envoyée est "paused" -**Et** le backend traite chaque action différemment - ---- - -## 16. Calcul immédiat côté backend sans délai - -**Étant donné** que l'API reçoit un événement d'écoute - -**Quand** le backend traite l'événement - -**Alors** les jauges sont mises à jour immédiatement (< 100ms) -**Et** les nouvelles recommandations utilisent les valeurs actualisées -**Et** il n'y a aucun batch différé - ---- - -## 17. Compatibilité iOS avec AVPlayer - -**Étant donné** que l'app iOS utilise AVPlayer - -**Quand** les commandes physiques sont interceptées - -**Alors** les événements MPRemoteCommandCenter sont capturés: - - | Commande | Événement iOS | - |---|---| - | Suivant | nextTrackCommand | - | Précédent | previousTrackCommand | - | Play/Pause | playCommand / pauseCommand | - -**Et** le tracking du temps utilise CMTime - ---- - -## 18. Compatibilité Android avec MediaSession - -**Étant donné** que l'app Android utilise MediaPlayer - -**Quand** les commandes physiques sont interceptées - -**Alors** les événements MediaSession sont capturés: - - | Commande | Action Android | - |---|---| - | Suivant | ACTION_SKIP_TO_NEXT | - | Précédent | ACTION_SKIP_TO_PREVIOUS | - | Play/Pause | ACTION_PLAY / ACTION_PAUSE | - -**Et** le tracking du temps utilise SystemClock.elapsedRealtime() - ---- - -## 19. Sécurité maximale - pas de distraction - -**Étant donné** que je conduis à 80 km/h - -**Quand** j'utilise RoadWave avec les commandes au volant - -**Alors** je n'ai jamais besoin de regarder mon téléphone -**Et** je n'ai jamais besoin de toucher l'écran CarPlay/Android Auto -**Et** toutes les actions sont accessibles via boutons physiques -**Et** les likes sont enregistrés automatiquement - ---- - -## 20. 📋 Plan: Calcul du like automatique selon pourcentage - -**Étant donné** que j'écoute un contenu tagué "Sport" - -**Quand** j'écoute pendant % - -**Alors** le like automatique est -**Et** l'impact sur la jauge est - -**📊 Exemples de données:** - -| pourcentage | type | points | -|---|---|---| -| 10 | aucun | 0 | -| 25 | aucun | 0 | -| 29 | aucun | 0 | -| 30 | standard | +1 | -| 50 | standard | +1 | -| 79 | standard | +1 | -| 80 | renforcé | +2 | -| 90 | renforcé | +2 | -| 100 | renforcé | +2 | - ---- - -## 21. 📋 Plan: Signal négatif uniquement si skip très rapide - -**Étant donné** que j'écoute un contenu - -**Quand** je skip après secondes - -**Alors** le signal est -**Et** l'impact est - -**📊 Exemples de données:** - -| secondes | type | points | -|---|---|---| -| 3 | négatif | -0.5 | -| 5 | négatif | -0.5 | -| 9 | négatif | -0.5 | -| 10 | neutre | 0 | -| 15 | neutre | 0 | -| 30 | neutre | 0 | - ---- - - - - - -
- - -# File d'attente et commande "Suivant" -> *En tant qu'auditeur en déplacement* -> *Je veux que l'application pré-calcule intelligemment les prochains contenus* -> *Afin d'avoir une navigation fluide sans latence* - -**20 scénarios** (19 standards, 1 plan) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -> **Et** que la géolocalisation est activée -## 1. Pré-calcul initial de 5 contenus en cache - -**Étant donné** que je viens de démarrer l'application -**Et** que je suis situé à Paris (48.8566, 2.3522) -**Et** que je suis en mode voiture (vitesse ≥ 5 km/h) - -**Quand** l'application initialise la lecture - -**Alors** une file d'attente de 5 contenus est pré-calculée -**Et** la file est stockée en cache Redis avec la clé "user:{user_id}:queue" -**Et** les métadonnées incluent ma position, le timestamp de calcul et le mode -**Et** le cache a un TTL de 15 minutes - ---- - -## 2. Commande "Suivant" sans latence - -**Étant donné** qu'une file d'attente de 5 contenus est en cache -**Et** que j'écoute actuellement le contenu "A" - -**Quand** j'appuie sur le bouton "Suivant" - -**Alors** le contenu suivant démarre immédiatement (< 100ms) -**Et** le contenu est retiré de la file d'attente -**Et** il reste 4 contenus dans la file - ---- - -## 3. Recalcul automatique après déplacement >10km - -**Étant donné** que la file a été calculée à Paris (48.8566, 2.3522) -**Et** que j'ai 5 contenus en cache - -**Quand** je me déplace à Versailles (48.8049, 2.1204) soit 12km - -**Alors** la file d'attente est invalidée automatiquement -**Et** une nouvelle file de 5 contenus est recalculée -**Et** elle est basée sur ma nouvelle position - ---- - -## 4. Recalcul automatique toutes les 10 minutes - -**Étant donné** qu'une file a été calculée il y a 10 minutes -**Et** que ma position n'a pas changé - -**Quand** le timer de rafraîchissement expire - -**Alors** une nouvelle file de 5 contenus est recalculée -**Et** les anciens contenus non écoutés sont remplacés -**Et** les nouveaux contenus publiés depuis sont inclus - ---- - -## 5. Recalcul quand il reste moins de 3 contenus - -**Étant donné** qu'il reste 3 contenus dans ma file d'attente - -**Quand** j'appuie sur "Suivant" - -**Alors** il reste 2 contenus -**Et** un recalcul asynchrone est déclenché en arrière-plan -**Et** 3 nouveaux contenus sont ajoutés à la file -**Et** la file contient maintenant 5 contenus - ---- - -## 6. Insertion prioritaire d'un contenu géolocalisé en mode voiture - -**Étant donné** que j'ai une file de 5 contenus pré-calculée -**Et** que je suis en mode voiture -**Et** que je me déplace à 50 km/h vers un point avec contenu géolocalisé - -**Quand** je suis à 98m du point (ETA = 7 secondes) - -**Alors** une notification est envoyée (icône + compteur 7→1 + son) -**Et** je dois appuyer sur "Suivant" dans les 7 secondes pour valider - -**Quand** j'appuie sur "Suivant" - -**Alors** un décompte de 5 secondes démarre -**Et** après 5 secondes, le contenu géolocalisé s'insère et démarre -**Et** il remplace le contenu actuel dans la lecture - ---- - -## 7. Contenu géolocalisé ignoré est perdu (cooldown activé) - -**Étant donné** qu'une notification géolocalisée est affichée (compteur 7→1) - -**Quand** je ne clique pas sur "Suivant" pendant les 7 secondes - -**Alors** la notification disparaît -**Et** le contenu géolocalisé est perdu (pas d'insertion dans la file) -**Et** un cooldown de 10 minutes est activé -**Et** aucune nouvelle notification géolocalisée ne sera envoyée pendant 10 minutes - ---- - -## 8. Validation d'une notification géolocalisée - -**Étant donné** qu'une notification géolocalisée est affichée (compteur à 5) -**Et** que j'écoute un podcast - -**Quand** j'appuie sur "Suivant" - -**Alors** le compteur bascule à "5" (décompte final) -**Et** le podcast actuel continue de jouer -**Et** après 5 secondes, le contenu géolocalisé démarre -**Et** le podcast est mis en pause et sauvegardé dans l'historique - ---- - -## 9. Invalidation immédiate après modification des préférences - -**Étant donné** que j'ai une file de 5 contenus en cache -**Et** que ma vitesse GPS est de 5 km/h (piéton) - -**Quand** je modifie mes curseurs de préférences (géo/découverte/politique) - -**Alors** la file d'attente est invalidée immédiatement -**Et** une nouvelle file est recalculée avec les nouvelles préférences -**Et** les anciens contenus en cache sont supprimés - ---- - -## 10. Blocage modification préférences en conduite (>10 km/h) - -**Étant donné** que ma vitesse GPS est de 50 km/h (en voiture) - -**Quand** j'essaie d'accéder aux réglages de préférences - -**Alors** l'interface affiche "Paramètres verrouillés en conduite" -**Et** je ne peux pas modifier les curseurs géo/découverte/politique -**Et** un message "Arrêtez-vous pour modifier vos préférences" s'affiche - ---- - -## 11. Invalidation lors du démarrage d'un live suivi - -**Étant donné** que je suis abonné au créateur "RadioVoyage" -**Et** que j'ai une file de 5 contenus en cache -**Et** que je suis dans la zone géographique du créateur - -**Quand** le créateur "RadioVoyage" démarre une radio live - -**Alors** je reçois une notification push -**Et** le contenu live s'insère en tête de la file d'attente -**Et** la file d'attente est recalculée - ---- - -## 12. Métadonnées de cache Redis - -**Étant donné** qu'une file d'attente est calculée - -**Quand** elle est stockée dans Redis - -**Alors** la clé est "user:{user_id}:queue" -**Et** les métadonnées incluent: - - | champ | valeur | - |---|---| - | last_lat | 48.8566 | - | last_lon | 2.3522 | - | computed_at | 2026-01-21T10:30:00Z | - | mode | voiture | - -**Et** le TTL est de 15 minutes (900 secondes) - ---- - -## 13. Contenu géolocalisé remplace le contenu actuel (pas d'insertion en file) - -**Étant donné** que j'écoute le contenu C2 de ma file [C1, C2, C3, C4, C5] -**Et** qu'une notification géolocalisée "Tour Eiffel" est déclenchée - -**Quand** je valide la notification -**Et** que le décompte de 5s se termine - -**Alors** le contenu "Tour Eiffel" remplace C2 et démarre -**Et** C2 est sauvegardé dans l'historique de navigation -**Et** la file reste [C3, C4, C5] (pas de contenu retiré) -**Et** quand "Tour Eiffel" se termine, C3 démarre - ---- - -## 14. Invalidation après déplacement exactement 10km - -**Étant donné** que la file a été calculée à une position donnée - -**Quand** je me déplace d'exactement 10.0 km - -**Alors** la file d'attente n'est PAS invalidée (seuil strict >10km) -**Et** les contenus en cache restent valides - -**Quand** je me déplace de 10.1 km supplémentaires (total 10.1km) - -**Alors** la file d'attente est invalidée -**Et** une nouvelle file est calculée - ---- - -## 15. Rafraîchissement exactement après 10 minutes - -**Étant donné** qu'une file a été calculée à 10:00:00 - -**Quand** l'heure actuelle est 10:10:00 - -**Alors** le timer de rafraîchissement expire -**Et** une nouvelle file de 5 contenus est recalculée -**Et** le timestamp "computed_at" est mis à jour - ---- - -## 16. Recalcul asynchrone non-bloquant - -**Étant donné** qu'il reste 2 contenus dans la file -**Et** que j'appuie sur "Suivant" - -**Quand** le recalcul asynchrone démarre - -**Alors** la lecture du contenu actuel n'est pas interrompue -**Et** le recalcul se fait en arrière-plan -**Et** les nouveaux contenus sont ajoutés dès disponibles (< 500ms) -**Et** l'utilisateur ne perçoit aucune latence - ---- - -## 17. Notification basée sur ETA (pas distance fixe) - -**Étant donné** qu'un contenu géolocalisé existe à un point GPS -**Et** que je roule à 130 km/h - -**Quand** je suis à 252m du point (ETA = 7 secondes) - -**Alors** une notification est envoyée - -**Quand** je suis à 300m du point (ETA = 8 secondes) - -**Alors** aucune notification n'est envoyée (ETA >7s) - ---- - -## 18. 📋 Plan: Différentes distances de déplacement et invalidation - -**Étant donné** qu'une file a été calculée à une position donnée - -**Quand** je me déplace de km - -**Alors** la file est - -**📊 Exemples de données:** - -| distance | action | -|---|---| -| 5 | conservée | -| 9.9 | conservée | -| 10.0 | conservée | -| 10.1 | invalidée et recalculée | -| 15 | invalidée et recalculée | -| 50 | invalidée et recalculée | - ---- - -## 19. Quota de 6 contenus géolocalisés par heure - -**Étant donné** que j'ai validé 6 notifications géolocalisées dans la dernière heure - -**Quand** un 7ème contenu géolocalisé est détecté (ETA 7s) - -**Alors** aucune notification n'est envoyée -**Et** le quota horaire est respecté - ---- - -## 20. Mode piéton - pas de notification avec compteur 7s - -**Étant donné** que je suis en mode piéton (vitesse <5 km/h) -**Et** qu'un audio-guide géolocalisé existe à 150m - -**Quand** je passe dans le rayon de 200m - -**Alors** une notification push système est envoyée -**Et** aucun compteur 7s n'est affiché -**Et** je peux ouvrir l'app en tapant sur la notification - ---- - - - - - -
- - -# Lecture en boucle et enchaînement automatique -> *En tant qu'auditeur* -> *Je veux que les contenus s'enchaînent automatiquement avec un délai paramétrable* -> *Afin d'avoir une expérience fluide sans interruption* - -**27 scénarios** (24 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur est connecté -## 1. Passage automatique après 2 secondes (mode standard) - -**Étant donné** que j'écoute un contenu "A" en mode standard - -**Quand** la lecture se termine naturellement - -**Alors** un timer de 2 secondes démarre -**Et** un overlay s'affiche: "Contenu suivant dans 2s..." -**Et** une barre de décompte visuelle s'affiche - -**Quand** le timer atteint 0 - -**Alors** le contenu "B" démarre automatiquement -**Et** l'overlay disparaît - ---- - -## 2. Passage automatique après 1 seconde (mode Kids) - -**Étant donné** que je suis en mode Kids -**Et** que j'écoute un contenu pour enfants - -**Quand** la lecture se termine - -**Alors** un timer de 1 seconde démarre -**Et** le message "Contenu suivant dans 1s..." s'affiche - -**Quand** le timer expire - -**Alors** le contenu suivant démarre automatiquement - ---- - -## 3. Passage immédiat après une radio live (0 seconde) - -**Étant donné** que j'écoute une radio live - -**Quand** le créateur arrête la diffusion - -**Alors** le passage au contenu suivant est immédiat (0s de délai) -**Et** aucun overlay de décompte n'est affiché -**Et** la transition est fluide - ---- - -## 4. Annulation du passage automatique - -**Étant donné** qu'un contenu se termine -**Et** que le timer de 2 secondes démarre - -**Quand** je clique sur "Rester sur ce contenu" pendant le décompte - -**Alors** le timer est annulé -**Et** le contenu actuel reste en pause à la fin -**Et** le contenu suivant n'est pas lancé - ---- - -## 5. Insertion de publicité pendant le délai de transition - -**Étant donné** que j'ai écouté 4 contenus sans publicité -**Et** que le 5ème contenu se termine - -**Quand** le délai de 2 secondes démarre - -**Alors** une publicité s'insère dans la file d'attente -**Et** le message devient "Publicité (15s)" -**Et** la publicité démarre après les 2 secondes -**Et** elle ne coupe jamais un contenu en cours - ---- - -## 6. Fréquence de publicité paramétrable admin - -**Étant donné** que la fréquence pub est configurée à "1/5 contenus" - -**Quand** j'écoute 10 contenus - -**Alors** 2 publicités sont insérées (après les contenus 5 et 10) - -**Étant donné** que l'admin change la fréquence à "1/3 contenus" - -**Quand** j'écoute 9 contenus - -**Alors** 3 publicités sont insérées (après les contenus 3, 6 et 9) - ---- - -## 7. Publicité skippable après 5 secondes par défaut - -**Étant donné** qu'une publicité de 30 secondes démarre -**Et** que le délai minimal de visionnage est configuré à 5 secondes - -**Quand** j'écoute pendant 3 secondes - -**Alors** le bouton "Passer" n'est pas encore visible - -**Quand** j'atteins 5 secondes d'écoute - -**Alors** le bouton "Passer" apparaît -**Et** je peux cliquer pour passer au contenu suivant - ---- - -## 8. Délai minimal de publicité paramétrable admin - -**Étant donné** qu'une publicité démarre -**Et** que l'admin a configuré le délai à 10 secondes - -**Quand** j'écoute pendant 9 secondes - -**Alors** le bouton "Passer" n'est pas visible - -**Quand** j'atteins 10 secondes - -**Alors** le bouton "Passer" apparaît -**Et** je peux skipper la publicité - ---- - -## 9. Like et abonnement autorisés sur une publicité - -**Étant donné** qu'une publicité est en lecture - -**Quand** je clique sur le bouton cœur (véhicule arrêté) - -**Alors** la publicité reçoit un like (+2% jauges tags pub) - -**Quand** je clique sur "S'abonner" au créateur de la pub - -**Alors** je suis abonné (+5% jauges tags créateur) -**Et** le créateur de pub bénéficie de l'engagement - ---- - -## 10. Métriques d'engagement publicité trackées - -**Étant donné** qu'une publicité de 30s est diffusée à 100 auditeurs - -**Quand** 40 auditeurs écoutent entièrement (30s) -**Et** que 50 auditeurs skippent après 10s -**Et** que 10 auditeurs skippent avant 5s - -**Alors** les métriques sont: - - | Métrique | Valeur | - |---|---| - | Taux d'écoute complète | 40% | - | Taux de skip après seuil | 50% | - | Taux de skip immédiat | 10% | - | Durée moyenne d'écoute | 18s | - - ---- - -## 11. Message "Aucun contenu disponible" si file vide - -**Étant donné** que la file d'attente est vide -**Et** qu'aucun contenu n'est disponible dans ma zone - -**Quand** le contenu actuel se termine - -**Alors** un message s'affiche: "Aucun contenu disponible dans cette zone" -**Et** une proposition apparaît: "Élargir la zone de recherche ?" -**Et** un bouton "Élargir" est disponible -**Et** la lecture se met en pause automatiquement - ---- - -## 12. Élargissement automatique de la zone de recherche - -**Étant donné** que le message "Aucun contenu disponible" s'affiche - -**Quand** je clique sur "Élargir la zone" - -**Alors** l'algorithme relance une recherche avec rayon +50km -**Et** une notification "Recherche élargie à 50km" s'affiche -**Et** la file d'attente est recalculée -**Et** la lecture reprend automatiquement - ---- - -## 13. Refus d'élargissement laisse en pause - -**Étant donné** que le message "Aucun contenu disponible" s'affiche - -**Quand** je clique sur "Annuler" - -**Alors** la lecture reste en pause -**Et** l'interface affiche "En attente de contenu" -**Et** je peux manuellement naviguer ou chercher du contenu - ---- - -## 14. Retry avec backoff exponentiel en cas d'échec réseau - -**Étant donné** que le contenu suivant échoue au chargement - -**Quand** la première tentative échoue - -**Alors** le système retente après 1 seconde (backoff 1s) - -**Quand** la 2ème tentative échoue - -**Alors** le système retente après 2 secondes (backoff 2s) - -**Quand** la 3ème tentative échoue - -**Alors** le système retente après 4 secondes (backoff 4s) -**Et** après 3 échecs totaux, le système bascule en mode offline - ---- - -## 15. Basculement mode offline après 3 échecs réseau - -**Étant donné** que j'ai eu 3 échecs de chargement consécutifs - -**Quand** le 3ème échec se produit - -**Alors** un message "Connexion instable, basculement mode offline" s'affiche -**Et** la lecture continue avec les contenus téléchargés uniquement -**Et** les contenus en ligne sont temporairement désactivés - -**Quand** la connexion revient - -**Alors** le mode en ligne est automatiquement rétabli - ---- - -## 16. Overlay de décompte avec barre visuelle - -**Étant donné** qu'un contenu se termine - -**Quand** le timer de 2 secondes démarre - -**Alors** un overlay semi-transparent s'affiche en bas de l'écran -**Et** le texte "Contenu suivant dans 2s..." est visible -**Et** une barre de progression décroît de 100% à 0% en 2 secondes -**Et** la couleur de la barre passe de vert à orange -**Et** l'overlay disparaît automatiquement après le décompte - ---- - -## 17. Bouton "Rester sur ce contenu" pendant décompte - -**Étant donné** que le décompte de 2 secondes est actif - -**Quand** l'overlay s'affiche - -**Alors** un bouton "Rester sur ce contenu" est visible -**Et** il est cliquable pendant les 2 secondes - -**Quand** je clique dessus - -**Alors** le timer est annulé immédiatement -**Et** l'overlay disparaît -**Et** le contenu actuel reste affiché en pause - ---- - -## 18. Pas d'interruption d'un contenu en cours - -**Étant donné** que j'écoute un contenu de 10 minutes -**Et** que je suis à 5 minutes de lecture - -**Quand** une publicité devrait s'insérer (fréquence 1/5) - -**Alors** la publicité n'interrompt jamais le contenu en cours -**Et** elle attend la fin du contenu actuel -**Et** elle s'insère pendant le délai de transition (2s) - ---- - -## 19. Publicités uniquement pour utilisateurs gratuits - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** j'écoute 5 contenus - -**Alors** une publicité est insérée après le 5ème contenu - -**Étant donné** que je passe en compte Premium - -**Quand** j'écoute 100 contenus - -**Alors** aucune publicité n'est insérée -**Et** l'enchaînement est direct (2s de transition seulement) - ---- - -## 20. Message clair pour l'utilisateur lors de la publicité - -**Étant donné** qu'une publicité va démarrer - -**Quand** le délai de transition démarre - -**Alors** le message affiché est: "Publicité (15s)" -**Et** la durée totale de la pub est indiquée -**Et** l'utilisateur sait qu'il s'agit d'une pub -**Et** la transparence est maximale - ---- - -## 21. Transition fluide entre contenus sans coupure - -**Étant donné** qu'un contenu se termine -**Et** que le suivant est pré-chargé en cache - -**Quand** le timer de 2s expire - -**Alors** la transition audio utilise un crossfade de 0.3s -**Et** il n'y a aucun blanc ou coupure -**Et** l'expérience est fluide - ---- - -## 22. Gestion des erreurs de chargement avec retry - -**Étant donné** que le contenu suivant échoue au chargement - -**Quand** la 1ère tentative échoue - -**Alors** une notification "Chargement..." s'affiche -**Et** le système retente automatiquement - -**Quand** la 2ème tentative réussit - -**Alors** la lecture démarre normalement -**Et** aucune action utilisateur n'est requise - ---- - -## 23. Mode offline après échecs multiples - -**Étant donné** que j'ai 50 contenus téléchargés en mode offline -**Et** que j'ai eu 3 échecs réseau consécutifs - -**Quand** le mode offline s'active - -**Alors** seuls les contenus téléchargés sont disponibles -**Et** un badge "Mode offline" s'affiche en haut de l'écran -**Et** la lecture continue sans interruption - ---- - -## 24. Compteur de contenus avant prochaine publicité - -**Étant donné** que la fréquence pub est 1/5 contenus -**Et** que j'ai écouté 3 contenus depuis la dernière pub - -**Quand** je consulte l'interface - -**Alors** un indicateur discret affiche "2 contenus avant pub" -**Et** l'utilisateur sait quand attendre la prochaine publicité - ---- - -## 25. 📋 Plan: Délai de transition selon mode - -**Étant donné** que je suis en mode - -**Quand** un contenu se termine - -**Alors** le délai de transition est secondes -**Et** le message affiché est - -**📊 Exemples de données:** - -| mode | delai | message | -|---|---|---| -| Standard | 2 | "Contenu suivant dans 2s..." | -| Kids | 1 | "Contenu suivant dans 1s..." | -| Live | 0 | (aucun message) | - ---- - -## 26. 📋 Plan: Fréquence d'insertion des publicités - -**Étant donné** que la fréquence pub est configurée à - -**Quand** j'écoute contenus - -**Alors** publicités sont insérées - -**📊 Exemples de données:** - -| frequence | contenus | pubs | -|---|---|---| -| 1/3 | 6 | 2 | -| 1/3 | 9 | 3 | -| 1/5 | 10 | 2 | -| 1/5 | 15 | 3 | -| 1/7 | 14 | 2 | -| 1/7 | 21 | 3 | - ---- - -## 27. 📋 Plan: Backoff exponentiel retry - -**Étant donné** que le chargement échoue - -**Quand** je suis à la tentative - -**Alors** le délai de retry est secondes - -**📊 Exemples de données:** - -| tentative | delai | -|---|---| -| 1 | 1 | -| 2 | 2 | -| 3 | 4 | - ---- - - - - - -
- - -# Partage de contenu -> *En tant qu'utilisateur de RoadWave* -> *Je veux pouvoir partager du contenu audio* -> *Afin de faire découvrir l'application à d'autres personnes* - -**22 scénarios** (20 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté -## 1. Bouton partager disponible dans le player en lecture - -**Étant donné** que le contenu "Balade à Paris" est en cours de lecture - -**Quand** l'utilisateur consulte les contrôles du player - -**Alors** le bouton "Partager" ⬆️ est visible - ---- - -## 2. Bouton partager disponible sur la page profil créateur - -**Étant donné** que l'utilisateur consulte le profil de "@paris_stories" - -**Quand** l'utilisateur consulte un contenu dans la liste - -**Alors** le bouton "Partager" est disponible pour chaque contenu - ---- - -## 3. Bouton partager dans la liste de recherche - -**Étant donné** que l'utilisateur effectue une recherche "voyage paris" - -**Quand** l'utilisateur ouvre le menu contextuel d'un résultat - -**Alors** l'option "Partager" est disponible - ---- - -## 4. Bouton partager dans l'historique personnel - -**Étant donné** que l'utilisateur consulte son historique d'écoute - -**Quand** l'utilisateur sélectionne un contenu de l'historique - -**Alors** le bouton "Partager" est accessible - ---- - -## 5. 📋 Plan: Menu de partage avec options multiples - -**Étant donné** que le contenu "" est disponible - -**Quand** l'utilisateur clique sur le bouton "Partager" - -**Alors** le menu natif OS s'ouvre -**Et** les options suivantes sont disponibles: - - | option | - |---| - | Copier le lien | - | WhatsApp | - | Email | - | SMS | - | Plus... | - - -**📊 Exemples de données:** - -| contenu | -|---| -| Balade à Paris | -| Secrets de Montmartre | - ---- - -## 6. Génération du lien de partage - -**Étant donné** un contenu avec l'ID "content_12345" - -**Quand** l'utilisateur copie le lien de partage - -**Alors** le lien généré est "https://roadwave.fr/share/c/content_12345" - ---- - -## 7. Ouverture du lien partagé avec l'application installée (Deep link) - -**Étant donné** que l'application RoadWave est installée sur l'appareil -**Et** qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé - -**Quand** l'utilisateur clique sur le lien - -**Alors** l'application RoadWave s'ouvre automatiquement -**Et** le contenu "content_12345" commence à jouer - ---- - -## 8. Ouverture du lien partagé sans l'application installée (Web player) - -**Étant donné** que l'application RoadWave n'est pas installée -**Et** qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé - -**Quand** l'utilisateur clique sur le lien - -**Alors** une page web responsive s'affiche -**Et** le web player HTML5 est visible -**Et** les boutons de téléchargement App Store et Google Play sont affichés - ---- - -## 9. Contenu de la page web de partage - -**Étant donné** un contenu public avec les métadonnées suivantes: - - | champ | valeur | - |---|---| - | titre | Balade à Paris | - | créateur | @paris_stories | - | durée | 12 min | - | écoutes | 2300 | - | localisation | Paris 5e | - | type_geo | Ancré | - | tags | Voyage, Histoire | - - -**Quand** la page de partage est affichée - -**Alors** la page contient: - - | élément | - |---| - | Cover image 16:9 | - | Titre "Balade à Paris" | - | "@paris_stories" | - | "12 min · 🎧 2.3K" | - | "📍 Paris 5e · Ancré" | - | "🏷️ #Voyage #Histoire" | - | Description | - | Player HTML5 | - | Bouton App Store | - | Bouton Google Play | - - ---- - -## 10. Métadonnées Open Graph pour partage social - -**Étant donné** un contenu "Balade à Paris" par "@paris_stories" - -**Quand** la page de partage est générée - -**Alors** les métadonnées Open Graph incluent: - - | propriété | valeur | - |---|---| - | og:title | Balade à Paris - RoadWave | - | og:description | Écoutez ce contenu par @paris_stories | - | og:type | music.song | - | og:site_name | RoadWave | - | twitter:card | player | - -**Et** l'aperçu s'affiche correctement sur WhatsApp -**Et** l'aperçu s'affiche correctement sur Facebook -**Et** l'aperçu s'affiche correctement sur Twitter - ---- - -## 11. 📋 Plan: Deep linking par plateforme - -**Étant donné** que l'application RoadWave est installée sur -**Et** qu'un lien de partage est ouvert - -**Quand** le système détecte l'application - -**Alors** l'application s'ouvre via - -**📊 Exemples de données:** - -| plateforme | mécanisme | -|---|---| -| iOS | Universal Links | -| Android | App Links | - ---- - -## 12. Fallback URL scheme pour deep linking - -**Étant donné** que les App Links ne fonctionnent pas - -**Quand** le système tente d'ouvrir le contenu - -**Alors** l'URL scheme "roadwave://content/content_12345" est utilisé - ---- - -## 13. Badge Premium visible sur le lien partagé - -**Étant donné** un contenu Premium "Visite VIP Louvre" - -**Quand** l'utilisateur non-premium clique sur le lien partagé - -**Alors** la page web affiche le badge "👑 Contenu Premium" - ---- - -## 14. Preview 30 secondes d'un contenu Premium partagé - -**Étant donné** un contenu Premium "Visite VIP Louvre" de 15 minutes -**Et** qu'un utilisateur non-premium ouvre le lien partagé - -**Quand** le player démarre automatiquement - -**Alors** l'audio joue pendant 30 secondes exactement -**Et** un fade out de 2 secondes est appliqué -**Et** un overlay "Contenu réservé Premium" s'affiche après 32 secondes - ---- - -## 15. Contenu de l'overlay paywall Premium - -**Étant donné** qu'un contenu Premium a atteint la limite de 30 secondes - -**Quand** l'overlay paywall s'affiche - -**Alors** le texte suivant est visible: - ---- - -## 16. Actions disponibles sur l'overlay Premium - -**Étant donné** que l'overlay paywall Premium est affiché - -**Quand** l'utilisateur consulte les options - -**Alors** les actions suivantes sont disponibles: - - | action | comportement | - |---|---| - | Passer Premium | Redirection vers paiement Mangopay web | - | Télécharger l'app | Redirection vers App Store/Google Play | - | Rejouer les 30 premières sec | Relecture illimitée du preview | - - ---- - -## 17. Relecture illimitée du preview Premium - -**Étant donné** un contenu Premium partagé -**Et** que l'utilisateur a écouté les 30 premières secondes - -**Quand** l'utilisateur clique sur "Rejouer" - -**Alors** les 30 premières secondes sont rejouées -**Et** cette action est possible de manière illimitée - ---- - -## 18. Tracking des partages Premium - -**Étant donné** un créateur "@guide_louvre" avec un contenu Premium - -**Quand** son contenu est partagé - -**Alors** les métriques suivantes sont enregistrées: - - | métrique | valeur | - |---|---| - | Partages Premium | +1 | - | Ouvertures lien | compteur | - | Conversions Premium | si souscription | - - ---- - -## 19. Rémunération créateur sur conversion Premium via partage - -**Étant donné** un contenu Premium partagé par "@guide_louvre" - -**Quand** un utilisateur s'abonne via le lien partagé - -**Alors** le créateur reçoit 70% des revenus de cet abonnement -**Et** la conversion est trackée dans son dashboard - ---- - -## 20. Partage d'un contenu supprimé - -**Étant donné** qu'un lien de partage "https://roadwave.fr/share/c/deleted_content" est ouvert -**Et** que le contenu n'existe plus - -**Quand** la page web se charge - -**Alors** un message "Ce contenu n'est plus disponible" s'affiche -**Et** les boutons de téléchargement de l'app sont affichés - ---- - -## 21. Partage d'un contenu en attente de modération - -**Étant donné** un contenu en cours de validation modération - -**Quand** un lien de partage est ouvert - -**Alors** le message "Ce contenu est en cours de validation" s'affiche - ---- - -## 22. Génération du lien hors connexion - -**Étant donné** que l'utilisateur n'a pas de connexion réseau - -**Quand** l'utilisateur tente de partager un contenu - -**Alors** le lien est copié dans le presse-papiers -**Et** un message "Lien copié (nécessite connexion pour ouvrir)" s'affiche - ---- - - - - - -
- - -# Avantages Premium -> *En tant qu'abonné Premium* -> *Je veux bénéficier d'avantages exclusifs* -> *Afin de profiter d'une expérience audio améliorée sans publicité* - -**37 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis connecté à l'application RoadWave -## 1. Utilisateur gratuit voit 1 publicité tous les 5 contenus - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** j'écoute ma file de contenus - -**Alors** je vois une publicité tous les 5 contenus -**Et** la publicité dure 30 secondes en moyenne -**Et** je ne peux pas la skip - ---- - -## 2. Utilisateur Premium ne voit aucune publicité - -**Étant donné** que je suis un utilisateur Premium - -**Quand** j'écoute mes contenus - -**Alors** aucune publicité n'est diffusée -**Et** je passe directement d'un contenu à l'autre -**Et** l'expérience d'écoute est fluide et ininterrompue - ---- - -## 3. Badge "0 publicité" sur page Premium - -**Étant donné** que je consulte la page des avantages Premium - -**Quand** je lis la liste des avantages - -**Alors** je vois en premier: -**Et** c'est l'argument principal mis en avant - ---- - -## 4. Utilisateur gratuit voit contenus Premium bloqués - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** je consulte les contenus d'un créateur - -**Alors** je vois les contenus marqués Premium avec badge 👑 -**Mais** je ne peux pas les lire (overlay bloquant) - ---- - -## 5. Utilisateur Premium accède à tous les contenus exclusifs - -**Étant donné** que je suis un utilisateur Premium - -**Quand** je consulte les contenus d'un créateur - -**Alors** tous les contenus Premium sont accessibles -**Et** je peux les lire sans restriction -**Et** j'ai accès à 100% du catalogue (gratuit + Premium) - ---- - -## 6. Nombre de contenus Premium disponibles - -**Étant donné** que je suis Premium - -**Quand** je consulte les statistiques - -**Alors** je vois combien de contenus Premium sont disponibles sur la plateforme -**Et** par exemple: "8,547 contenus Premium exclusifs disponibles" -**Et** cela justifie la valeur de l'abonnement - ---- - -## 7. Utilisateur gratuit écoute en 48 kbps Opus - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** je lance un contenu - -**Alors** l'audio est streamé en 48 kbps Opus -**Et** cela consomme environ 20 MB/heure -**Et** la qualité est très correcte pour de la voix - ---- - -## 8. Utilisateur Premium écoute en 64 kbps Opus - -**Étant donné** que je suis un utilisateur Premium - -**Quand** je lance un contenu - -**Alors** l'audio est streamé en 64 kbps Opus -**Et** cela consomme environ 30 MB/heure -**Et** la qualité est excellente (détails audio supérieurs) - ---- - -## 9. Comparaison qualité 48 kbps vs 64 kbps - -**Étant donné** que je consulte la page Premium - -**Quand** je lis la section qualité audio - -**Alors** je vois l'explication: - ---- - -## 10. Justification 48 kbps suffisant pour gratuit - -**Étant donné** que le contenu RoadWave est principalement de la voix - -**Quand** la qualité est fixée à 48 kbps pour gratuit - -**Alors** c'est largement suffisant pour comprendre clairement -**Et** équivalent à la qualité radio FM -**Et** les utilisateurs gratuits ne sont pas frustrés - ---- - -## 11. Justification 64 kbps avantage tangible Premium - -**Étant donné** que les audiophiles et créateurs audio sont exigeants - -**Quand** la qualité Premium est à 64 kbps - -**Alors** la différence est perceptible à l'oreille -**Et** les ambiances, musiques de fond, nuances de voix sont mieux rendues -**Et** cela justifie l'abonnement Premium - ---- - -## 12. Switch automatique qualité selon abonnement - -**Étant donné** que je suis gratuit et j'écoute en 48 kbps - -**Quand** je souscris à Premium - -**Alors** dès le contenu suivant, je passe automatiquement en 64 kbps -**Et** je peux entendre la différence de qualité immédiatement - ---- - -## 13. Consommation data Premium vs Gratuit - -**Étant donné** que je roule 1 heure par jour - -**Quand** je calcule la consommation mensuelle - -**Alors** en gratuit: 20 MB/h × 1h × 22 jours = 440 MB/mois -**Et** en Premium: 30 MB/h × 1h × 22 jours = 660 MB/mois -**Et** la différence est de 220 MB/mois (acceptable pour 4G/5G illimitée) - ---- - -## 14. Utilisateur gratuit limité à 50 contenus téléchargés - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** j'accède au mode offline - -**Alors** je peux télécharger jusqu'à 50 contenus maximum -**Et** si j'essaie de télécharger un 51ème, je vois: - ---- - -## 15. Utilisateur Premium téléchargements illimités - -**Étant donné** que je suis un utilisateur Premium - -**Quand** j'accède au mode offline - -**Alors** je peux télécharger autant de contenus que je veux -**Et** la seule limite est l'espace de stockage de mon device -**Et** par exemple 500 contenus × 10 MB = 5 GB - ---- - -## 16. Justification limite 50 contenus gratuit - -**Étant donné** que 50 contenus de 10 minutes = ~8 heures d'écoute - -**Quand** un utilisateur gratuit prépare un road trip - -**Alors** 8 heures couvrent largement une journée de trajet -**Et** cela permet un usage offline raisonnable sans abuser - ---- - -## 17. Justification illimité Premium pour longs road trips - -**Étant donné** qu'un road trip de plusieurs jours nécessite 20-50h de contenu - -**Quand** un utilisateur Premium télécharge 200 contenus - -**Alors** il peut partir serein sans connexion internet pendant 1 semaine -**Et** cela justifie pleinement l'abonnement Premium - ---- - -## 18. Affichage compteur téléchargements gratuit - -**Étant donné** que je suis gratuit et j'ai téléchargé 37 contenus - -**Quand** j'accède à la page Téléchargements - -**Alors** je vois: - ---- - -## 19. Pas de compteur pour Premium - -**Étant donné** que je suis Premium et j'ai téléchargé 187 contenus - -**Quand** j'accède à la page Téléchargements - -**Alors** je vois simplement: -**Et** aucune limite n'est affichée - ---- - -## 20. Utilisateur gratuit historique limité à 100 derniers - -**Étant donné** que je suis un utilisateur gratuit - -**Quand** j'accède à mon historique d'écoute - -**Alors** je vois les 100 derniers contenus écoutés -**Et** les contenus plus anciens ne sont pas affichés -**Et** je vois un message "Historique limité à 100 contenus. Passez Premium pour un historique illimité." - ---- - -## 21. Utilisateur Premium historique illimité - -**Étant donné** que je suis un utilisateur Premium - -**Quand** j'accède à mon historique d'écoute - -**Alors** je vois tous les contenus que j'ai écoutés depuis mon inscription -**Et** je peux scroller jusqu'au premier contenu jamais écouté -**Et** l'historique est complet et permanent - ---- - -## 22. Recherche dans historique Premium - -**Étant donné** que je suis Premium et j'ai 2 000 contenus dans mon historique - -**Quand** je recherche "Tesla" dans mon historique - -**Alors** tous les contenus écoutés contenant "Tesla" sont affichés -**Et** je peux retrouver facilement un contenu écouté il y a 6 mois - ---- - -## 23. Justification limite 100 gratuit suffisante - -**Étant donné** que 100 contenus de 10 min = ~16 heures d'écoute - -**Quand** un utilisateur gratuit écoute 1h/jour - -**Alors** l'historique couvre les 16 derniers jours -**Et** cela suffit pour retrouver un contenu récent - ---- - -## 24. Justification illimité Premium pour power users - -**Étant donné** qu'un power user écoute 3h/jour depuis 2 ans - -**Quand** il veut retrouver un contenu spécifique écouté il y a 1 an - -**Alors** l'historique illimité Premium lui permet de retrouver ce contenu -**Et** cela apporte une vraie valeur ajoutée - ---- - -## 25. Export historique complet (Premium uniquement) - -**Étant donné** que je suis Premium - -**Quand** je demande l'export de mes données - -**Alors** l'historique complet est inclus dans l'export: - ---- - -## 26. Affichage tableau comparatif Gratuit vs Premium - -**Étant donné** que je consulte la page Premium - -**Quand** je vois le tableau comparatif - -**Alors** il affiche: - ---- - -## 27. Justification 0 pub = argument principal - -**Étant donné** qu'une publicité de 30s tous les 5 contenus = 6 min/h de pub - -**Quand** un utilisateur écoute 1h/jour - -**Alors** il subit 180 min de pub/mois (3 heures !) -**Et** payer 4.99€ pour éviter 3h de pub/mois est très rentable -**Et** c'est l'argument de conversion n°1 - ---- - -## 28. Justification qualité audio avantage tangible - -**Étant donné** que la différence 48 kbps → 64 kbps est audible - -**Quand** un audiophile compare les deux - -**Alors** il entend clairement la différence sur un bon système audio voiture -**Et** cela justifie l'abonnement pour les exigeants - ---- - -## 29. Justification offline illimité pour road trips - -**Étant donné** qu'un road trip de 2 semaines nécessite 50-100h de contenu - -**Quand** un utilisateur Premium télécharge 300 contenus avant de partir - -**Alors** il peut partir en zone sans réseau sereinement -**Et** cela apporte une vraie valeur pratique - ---- - -## 30. Justification pas d'over-engineering - -**Étant donné** que RoadWave se concentre sur l'essentiel - -**Quand** les avantages Premium sont définis - -**Alors** il n'y a pas de: - - | fonctionnalité superflue | raison exclusion | - |---|---| - | Badges cosmétiques | Pas de valeur réelle | - | Avatar Premium exclusif | Inutile pour audio | - | Fonctionnalités sociales avancées | Pas prioritaire MVP | - | Early access nouveaux contenus | Complexité > bénéfice | - -**Et** cela réduit la complexité et le coût de développement - ---- - -## 31. CTA Premium après 5ème publicité - -**Étant donné** que je suis gratuit et je viens d'entendre ma 5ème pub - -**Quand** la publicité se termine - -**Alors** je vois un message: - ---- - -## 32. CTA Premium quand limite 50 téléchargements atteinte - -**Étant donné** que je suis gratuit et j'ai atteint 50 téléchargements - -**Quand** j'essaie de télécharger un 51ème contenu - -**Alors** je vois une popup: - ---- - -## 33. CTA Premium quand contenu exclusif bloqué - -**Étant donné** que je suis gratuit et je clique sur un contenu Premium - -**Quand** l'overlay bloquant apparaît - -**Alors** je vois: - ---- - -## 34. Statistiques conversion - Quel avantage convertit le mieux ? - -**Étant donné** qu'un admin consulte les statistiques de conversion - -**Quand** il analyse les sources de conversion - -**Alors** il voit: - - | source de conversion | % conversions | - |---|---| - | CTA après 5ème pub | 42% | - | CTA contenu Premium bloqué | 28% | - | CTA limite 50 téléchargements | 18% | - | Page Premium directe | 12% | - -**Et** cela aide à optimiser le placement des CTA - ---- - -## 35. A/B test message CTA - -**Étant donné** que RoadWave veut optimiser les conversions - -**Quand** un A/B test est lancé sur le CTA après pub - -**Alors** groupe A voit "Marre des pubs ?" (focus négatif) -**Et** groupe B voit "Profitez de 0 publicité" (focus positif) -**Et** le taux de conversion est mesuré -**Et** le message le plus performant est déployé - ---- - -## 36. Notification Premium après 30 jours d'utilisation gratuite - -**Étant donné** que je suis utilisateur gratuit depuis 30 jours -**Et** que j'écoute régulièrement (15h cumulées) - -**Quand** le 30ème jour arrive - -**Alors** je reçois une notification: - ---- - -## 37. Trial gratuit refusé mais onboarding amélioré - -**Étant donné** qu'il n'y a pas de trial gratuit - -**Quand** un nouvel utilisateur s'inscrit - -**Alors** un onboarding explique clairement les avantages Premium -**Et** il peut comparer gratuit vs Premium dès le premier lancement -**Et** cela l'aide à décider rapidement s'il veut payer - ---- - - - - - -
- - -# Gestion abonnement Premium -> *En tant qu'utilisateur* -> *Je veux gérer facilement mon abonnement Premium* -> *Afin de souscrire, renouveler ou annuler en toute transparence* - -**41 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis connecté à l'application RoadWave -## 1. Souscription via Web (desktop/mobile) avec Mangopay - -**Étant donné** que je consulte la page Premium sur le site web - -**Quand** je clique sur "S'abonner - Mensuel 4.99€" - -**Alors** je suis redirigé vers le formulaire de paiement Mangopay -**Et** je saisis mes informations de carte bancaire -**Et** le paiement de 4.99€ est prélevé immédiatement -**Et** la commission Mangopay est de 1.8% + 0.18€ = 0.27€ -**Et** RoadWave reçoit 4.72€ net - ---- - -## 2. Calcul commission Mangopay - -**Étant donné** qu'un utilisateur paie 4.99€ via Mangopay - -**Quand** la commission est calculée - -**Alors** la commission est : 4.99€ × 1.8% + 0.18€ = 0.09€ + 0.18€ = 0.27€ -**Et** RoadWave reçoit : 4.99€ - 0.27€ = 4.72€ -**Et** la commission représente 5.4% du prix - ---- - -## 3. Souscription via iOS App avec Apple IAP - -**Étant donné** que j'utilise l'app iOS - -**Quand** je clique sur "S'abonner - Mensuel 5.99€" - -**Alors** je suis redirigé vers l'interface Apple In-App Purchase -**Et** le prix affiché est 5.99€ (majoré de 20%) -**Et** le paiement est effectué via mon compte Apple -**Et** Apple prend 30% de commission = 1.80€ -**Et** RoadWave reçoit 4.19€ net - ---- - -## 4. Souscription via Android App avec Google Play Billing - -**Étant donné** que j'utilise l'app Android - -**Quand** je clique sur "S'abonner - Mensuel 5.99€" - -**Alors** je suis redirigé vers l'interface Google Play Billing -**Et** le prix affiché est 5.99€ (majoré de 20%) -**Et** le paiement est effectué via mon compte Google -**Et** Google prend 30% de commission = 1.80€ -**Et** RoadWave reçoit 4.19€ net - ---- - -## 5. Majoration 20% sur mobile pour compenser commission 30% - -**Étant donné** que Apple/Google prennent 30% de commission - -**Quand** RoadWave fixe le prix mobile - -**Alors** le prix web est 4.99€ (commission Mangopay 5.4%) -**Et** le prix mobile est 5.99€ (commission Apple/Google 30%) -**Et** la majoration est de 1€ (+20%) -**Et** cela compense partiellement la commission excessive - ---- - -## 6. Email incitation souscription web moins chère - -**Étant donné** que je consulte Premium depuis l'app mobile - -**Quand** je vois le prix 5.99€ - -**Alors** je vois aussi un message: -**Et** un lien vers le site web est fourni - ---- - -## 7. Calcul économie souscription web vs mobile - -**Étant donné** que le prix web est 4.99€/mois -**Et** que le prix mobile est 5.99€/mois - -**Quand** je calcule l'économie annuelle - -**Alors** web : 4.99€ × 12 = 59.88€/an -**Et** mobile : 5.99€ × 12 = 71.88€/an -**Et** économie : 12€/an (soit 20% d'économie) - ---- - -## 8. Activation immédiate après paiement réussi - -**Étant donné** que je viens de payer mon abonnement Premium - -**Quand** le paiement est confirmé - -**Alors** mon statut passe immédiatement à "Premium" -**Et** je peux accéder aux avantages Premium dès maintenant -**Et** je reçois un email de confirmation - ---- - -## 9. Email confirmation souscription - -**Étant donné** que j'ai souscrit à Premium - -**Quand** la souscription est confirmée - -**Alors** je reçois un email: - ---- - -## 10. Email rappel 7 jours avant renouvellement - -**Étant donné** que mon abonnement mensuel se renouvelle le 15 juillet - -**Quand** le 8 juillet arrive (7 jours avant) - -**Alors** je reçois un email de rappel: - ---- - -## 11. Renouvellement automatique réussi - -**Étant donné** que mon abonnement mensuel arrive à échéance le 15 juillet - -**Quand** le 15 juillet arrive - -**Alors** Mangopay/Apple/Google prélève automatiquement 4.99€ (ou 5.99€) -**Et** mon abonnement est renouvelé pour 1 mois supplémentaire -**Et** je reçois un email de confirmation - ---- - -## 12. Email confirmation renouvellement - -**Étant donné** que mon abonnement vient d'être renouvelé - -**Quand** le paiement est confirmé - -**Alors** je reçois un email: - ---- - -## 13. Échec paiement renouvellement - Tentative 1 - -**Étant donné** que mon abonnement doit se renouveler le 15 juillet -**Mais** que ma carte bancaire est expirée ou sans fonds - -**Quand** le prélèvement échoue - -**Alors** je reçois un email: - ---- - -## 14. Retry automatique paiement après 3 jours - -**Étant donné** que le paiement a échoué le 15 juillet - -**Quand** le 18 juillet arrive (J+3) - -**Alors** Mangopay/Apple/Google tente automatiquement un nouveau prélèvement -**Et** si le paiement réussit, l'abonnement est renouvelé normalement -**Et** si le paiement échoue encore, un 2ème retry est programmé - ---- - -## 15. Retry automatique paiement après 7 jours - -**Étant donné** que 2 tentatives ont échoué (15 juillet et 18 juillet) - -**Quand** le 22 juillet arrive (J+7) - -**Alors** une 3ème et dernière tentative est effectuée -**Et** si le paiement réussit, l'abonnement est sauvé -**Et** si le paiement échoue, l'abonnement est annulé automatiquement - ---- - -## 16. Annulation automatique après 3 échecs paiement - -**Étant donné** que les 3 tentatives de renouvellement ont échoué (J+0, J+3, J+7) - -**Quand** la 3ème tentative échoue - -**Alors** mon abonnement Premium est annulé automatiquement -**Et** mon statut repasse à "Gratuit" -**Et** je perds accès aux avantages Premium -**Et** je reçois un email d'annulation - ---- - -## 17. Email annulation automatique pour impayé - -**Étant donné** que mon abonnement a été annulé pour échec paiement - -**Quand** l'annulation devient effective - -**Alors** je reçois un email: - ---- - -## 18. Annulation self-service dans Settings - -**Étant donné** que je veux annuler mon abonnement - -**Quand** j'accède à "Paramètres > Abonnement" - -**Alors** je vois un bouton "Annuler l'abonnement" -**Et** je peux annuler en 2 clics sans contacter le support - ---- - -## 19. Confirmation avant annulation - -**Étant donné** que je clique sur "Annuler l'abonnement" - -**Quand** une popup de confirmation apparaît - -**Alors** je vois: - ---- - -## 20. Accès Premium maintenu jusqu'à fin période payée - -**Étant donné** que j'ai annulé mon abonnement le 1er juillet -**Et** que mon abonnement mensuel était valable jusqu'au 15 juillet - -**Quand** l'annulation est confirmée - -**Alors** je garde l'accès Premium jusqu'au 15 juillet -**Et** à partir du 16 juillet, je repasse en gratuit -**Et** je ne suis pas remboursé pour les 14 jours restants - ---- - -## 21. Justification pas de remboursement prorata - -**Étant donné** que l'industrie (Spotify, Netflix, YouTube) ne rembourse pas prorata - -**Quand** RoadWave applique la même règle - -**Alors** c'est le standard accepté par les utilisateurs -**Et** cela simplifie la gestion comptable -**Et** évite les abus (souscription puis annulation immédiate pour remboursement) - ---- - -## 22. Email confirmation annulation - -**Étant donné** que j'ai annulé mon abonnement - -**Quand** l'annulation est enregistrée - -**Alors** je reçois un email: - ---- - -## 23. Pas de renouvellement après annulation - -**Étant donné** que j'ai annulé mon abonnement le 1er juillet - -**Quand** le 15 juillet arrive (date de renouvellement prévue) - -**Alors** aucun prélèvement n'est effectué -**Et** mon statut passe automatiquement à "Gratuit" -**Et** je ne reçois pas d'email de renouvellement - ---- - -## 24. Réabonnement possible immédiatement - -**Étant donné** que j'ai annulé mon abonnement il y a 5 jours - -**Quand** j'accède à la page Premium - -**Alors** je peux me réabonner immédiatement -**Et** le processus de paiement est le même que la première fois - ---- - -## 25. Pas de nouvelle période d'essai au réabonnement - -**Étant donné** que j'ai annulé mon abonnement il y a 3 mois - -**Quand** je me réabonne - -**Alors** je paie immédiatement 4.99€ (pas d'essai gratuit) - ---- - -## 26. Offre win-back pour utilisateurs ayant annulé - -**Étant donné** que j'ai annulé mon abonnement il y a 1 mois - -**Quand** je reçois un email de win-back - -**Alors** je vois une offre spéciale: - ---- - -## 27. Table subscriptions en base PostgreSQL - -**Étant donné** qu'un utilisateur souscrit à Premium - -**Quand** les données sont enregistrées - -**Alors** la table subscriptions contient: - ---- - -## 28. Statuts possibles dans subscription.status - -**Étant donné** qu'un abonnement peut avoir différents statuts - -**Quand** le statut est stocké en base - -**Alors** les valeurs possibles sont: - - | statut | description | - |---|---| - | active | Abonnement actif et payé | - | cancelled | Annulé par utilisateur (accès jusqu'à fin période) | - | expired | Période terminée, pas renouvelé | - | past_due | Échec paiement, en retry automatique | - - ---- - -## 29. Cache Redis pour vérification Premium temps réel - -**Étant donné** qu'un utilisateur lance un contenu - -**Quand** l'app vérifie s'il est Premium - -**Alors** une clé Redis est consultée: -**Et** si la clé n'existe pas, elle est recalculée depuis PostgreSQL -**Et** cela garantit des performances <10ms - ---- - -## 30. Refresh cache Redis via webhooks - -**Étant donné** qu'un paiement est confirmé par Mangopay/Apple/Google - -**Quand** un webhook est reçu par RoadWave - -**Alors** le cache Redis premium:{user_id} est mis à jour immédiatement -**Et** l'utilisateur voit son statut Premium activé sans délai - ---- - -## 31. Webhooks Mangopay - PAYIN_NORMAL_SUCCEEDED - -**Étant donné** qu'un paiement Mangopay réussit - -**Quand** Mangopay envoie le webhook PAYIN_NORMAL_SUCCEEDED - -**Alors** RoadWave met à jour subscriptions.status = 'active' -**Et** met à jour current_period_end = NOW() + 1 mois -**Et** refresh le cache Redis premium:{user_id} = true - ---- - -## 32. Webhooks Mangopay - PAYIN_NORMAL_FAILED - -**Étant donné** qu'un paiement Mangopay échoue - -**Quand** Mangopay envoie le webhook PAYIN_NORMAL_FAILED - -**Alors** RoadWave met à jour subscriptions.status = 'past_due' -**Et** programme un retry automatique dans 3 jours -**Et** envoie un email à l'utilisateur - ---- - -## 33. Webhooks Apple - App Store Server Notifications - -**Étant donné** qu'un paiement Apple IAP change de statut - -**Quand** Apple envoie une notification serveur - -**Alors** RoadWave parse la notification (JSON) -**Et** met à jour la subscription en conséquence -**Et** refresh le cache Redis - ---- - -## 34. Webhooks Google - Real-time Developer Notifications - -**Étant donné** qu'un paiement Google Play change de statut - -**Quand** Google envoie une notification temps réel - -**Alors** RoadWave parse la notification (JSON) -**Et** met à jour la subscription en conséquence -**Et** refresh le cache Redis - ---- - -## 35. Dashboard admin - Métriques abonnements - -**Étant donné** qu'un admin consulte les métriques Premium - -**Quand** il accède au dashboard - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Abonnés actifs | 12,547 | - | Nouveaux abonnements ce mois | 1,234 | - | Annulations ce mois | 287 (2.3%) | - | Churn rate mensuel | 2.3% | - | MRR (Revenus mensuels récurrents) | 58,890€ | - | Taux conversion gratuit → Premium | 8.5% | - - ---- - -## 36. Calcul churn rate mensuel - -**Étant donné** que 287 utilisateurs ont annulé ce mois -**Et** qu'il y avait 12,547 abonnés au début du mois - -**Quand** le churn rate est calculé - -**Alors** churn = 287 / 12,547 = 2.3% -**Et** un churn <5% est considéré comme excellent -**Et** RoadWave surveille cette métrique de près - ---- - -## 37. Alerte si churn rate >5% - -**Étant donné** que le churn rate mensuel dépasse 5% - -**Quand** le système détecte cette anomalie - -**Alors** une alerte est envoyée à l'équipe: - ---- - -## 38. Enquête satisfaction à l'annulation - -**Étant donné** que je viens d'annuler mon abonnement - -**Quand** l'annulation est confirmée - -**Alors** je vois un questionnaire rapide: -**Et** les réponses aident à améliorer l'offre Premium - ---- - -## 39. Répartition canaux souscription - -**Étant donné** qu'un admin analyse les canaux de souscription - -**Quand** il consulte les statistiques - -**Alors** il voit: - - | canal | abonnés | % total | revenus/mois | - |---|---|---|---| - | Web (Mangopay) | 8,234 | 65.6% | 41,088€ | - | iOS (Apple) | 2,845 | 22.7% | 17,042€ | - | Android (Google) | 1,468 | 11.7% | 8,793€ | - -**Et** cela aide à orienter les efforts marketing (inciter web = moins de commission) - ---- - -## 40. Performance vérification Premium <10ms - -**Étant donné** que 100 000 utilisateurs consultent des contenus simultanément - -**Quand** chaque requête vérifie le statut Premium via Redis - -**Alors** le temps de réponse moyen est <10ms -**Et** Redis gère facilement 100 000 requêtes/seconde -**Et** l'expérience utilisateur est fluide - ---- - -## 41. Backup données abonnements - -**Étant donné** que les données d'abonnements sont critiques - -**Quand** un backup est effectué - -**Alors** PostgreSQL est répliqué en temps réel sur un replica -**Et** un snapshot quotidien est stocké sur S3 -**Et** en cas de crash, les données peuvent être restaurées <5 minutes - ---- - - - - - -
- - -# Multi-devices et détection simultanée -> *En tant qu'abonné Premium* -> *Je veux utiliser mon compte sur plusieurs appareils* -> *Mais limité à 1 seul stream actif à la fois pour éviter le partage abusif* - -**30 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que je suis un utilisateur Premium actif -> **Et** que mon compte est valide -## 1. 1 seul stream actif autorisé par compte - -**Étant donné** que je n'écoute rien actuellement - -**Quand** je lance un contenu sur mon iPhone - -**Alors** le stream démarre normalement -**Et** Redis enregistre: active_streams:{user_id} = {device_id: "iPhone", started_at: timestamp} - ---- - -## 2. Détection connexion simultanée - Arrêt premier device - -**Étant donné** que j'écoute un contenu sur mon iPhone - -**Quand** je lance un contenu sur mon iPad - -**Alors** le système détecte une session active sur iPhone -**Et** la lecture sur iPhone est arrêtée immédiatement (WebSocket close) -**Et** je vois sur iPhone: "Lecture interrompue : votre compte est utilisé sur un autre appareil" -**Et** la lecture démarre sur iPad normalement - ---- - -## 3. Message explicite sur device interrompu - -**Étant donné** que ma lecture sur iPhone vient d'être interrompue - -**Quand** je regarde l'écran de mon iPhone - -**Alors** je vois une overlay avec le message: -**Et** un bouton "Reprendre ici" est disponible - ---- - -## 4. Reprendre lecture sur device interrompu - -**Étant donné** que ma lecture sur iPhone a été interrompue -**Et** que je veux reprendre sur iPhone - -**Quand** je clique sur "Reprendre ici" - -**Alors** la lecture démarre sur iPhone -**Et** l'iPad est à son tour interrompu avec le même message -**Et** le "ping-pong" entre devices est possible (mais pénible) - ---- - -## 5. Enregistrement session active dans Redis - -**Étant donné** que je lance un contenu sur mon iPhone - -**Quand** la lecture démarre - -**Alors** une entrée Redis est créée: - ---- - -## 6. Heartbeat toutes les 30 secondes pour maintenir session - -**Étant donné** que j'écoute un contenu sur mon iPhone - -**Quand** 30 secondes s'écoulent - -**Alors** l'app envoie un heartbeat au serveur -**Et** le serveur refresh le TTL Redis à 300 secondes -**Et** la session reste active - ---- - -## 7. Session considérée morte après 5 minutes sans heartbeat - -**Étant donné** que j'écoute un contenu sur mon iPhone -**Mais** que l'app crash ou que le réseau coupe - -**Quand** 5 minutes s'écoulent sans heartbeat - -**Alors** l'entrée Redis expire automatiquement (TTL atteint) -**Et** je peux relancer sur n'importe quel device sans conflit - ---- - -## 8. Vérification session avant démarrage lecture - -**Étant donné** que je veux lancer un contenu sur mon iPad - -**Quand** j'appuie sur Play - -**Alors** le serveur vérifie Redis: active_streams:{user_id} -**Et** si une session existe sur un autre device, elle est tuée -**Et** la nouvelle session iPad est enregistrée dans Redis - ---- - -## 9. Gestion multi-utilisateurs simultanés - -**Étant donné** que 100 000 utilisateurs Premium écoutent simultanément - -**Quand** Redis stocke 100 000 entrées active_streams - -**Alors** chaque entrée a un TTL de 5 minutes -**Et** Redis gère facilement cette charge (~10 MB de RAM) -**Et** les vérifications sont quasi-instantanées (O(1)) - ---- - -## 10. Contenus téléchargés (offline) ne comptent pas comme stream - -**Étant donné** que j'ai téléchargé 20 contenus en mode offline - -**Quand** j'écoute un contenu téléchargé sur mon iPhone sans réseau - -**Alors** aucune session active n'est enregistrée dans Redis -**Et** je peux écouter offline pendant qu'un autre device stream online - ---- - -## 11. Transition rapide device <10s tolérée - -**Étant donné** que j'écoute dans ma voiture sur mon iPhone -**Et** que j'arrive chez moi - -**Quand** je lance la lecture sur mon iPad dans les 10 secondes - -**Alors** la transition est considérée comme un changement de device légitime -**Et** aucun message d'erreur n'est affiché sur iPhone -**Et** la lecture reprend exactement où j'étais sur iPad - ---- - -## 12. Détection transition rapide via timestamps - -**Étant donné** que la session iPhone a started_at = 14:30:00 - -**Quand** je lance sur iPad à 14:30:05 (5 secondes après) - -**Alors** le serveur détecte: diff = 5s < 10s -**Et** applique une "graceful transition" (pas de message d'erreur iPhone) -**Et** Redis met à jour: active_streams:{user_id} = {device_id: "iPad", ...} - ---- - -## 13. Plusieurs devices disponibles mais 1 seul actif - -**Étant donné** que je possède: - - | device | status | - |---|---| - | iPhone | Installé | - | iPad | Installé | - | MacBook (web) | Connecté | - | Android (conjoint) | Installé | - - -**Quand** je lance un stream sur n'importe quel device - -**Alors** seulement 1 peut être actif à la fois -**Et** les autres devices sont en "standby" - ---- - -## 14. Justification anti-partage compte - -**Étant donné** qu'un utilisateur Premium partage son compte avec un ami - -**Quand** les 2 personnes essaient d'écouter simultanément - -**Alors** la lecture est constamment interrompue sur l'un ou l'autre -**Et** l'expérience devient inutilisable -**Et** cela décourage fortement le partage de compte - ---- - -## 15. Justification protection revenus créateurs - -**Étant donné** que 1 abonnement Premium = 4.99€/mois - -**Quand** 70% sont reversés aux créateurs (3.49€) - -**Alors** les créateurs sont rémunérés pour 1 personne -**Et** si 2 personnes utilisent le même compte simultanément, c'est injuste -**Et** la limite 1 stream protège l'équité du système - ---- - -## 16. Justification UX claire - -**Étant donné** qu'un stream est interrompu sur un device - -**Quand** l'utilisateur voit le message explicite - -**Alors** il comprend immédiatement pourquoi (autre device actif) -**Et** il peut choisir de reprendre sur le device actuel ou l'autre -**Et** il n'y a pas de confusion ou frustration - ---- - -## 17. Comparaison avec Spotify (limite 1 stream) - -**Étant donné** que Spotify Premium limite aussi à 1 stream actif - -**Quand** RoadWave applique la même règle - -**Alors** les utilisateurs connaissent déjà ce comportement -**Et** cela paraît normal et accepté par l'industrie - ---- - -## 18. Comparaison avec Netflix (plusieurs streams selon formule) - -**Étant donné** que Netflix permet 1-4 streams selon la formule - -**Quand** RoadWave limite à 1 stream pour tous - -**Alors** c'est plus strict que Netflix -**Mais** Netflix cible le foyer familial (TV partagée) -**Alors** que RoadWave cible l'individu conducteur (usage personnel) - ---- - -## 19. Détection pattern suspect - Changements devices fréquents - -**Étant donné** qu'un utilisateur change de device 50 fois en 1 heure - -**Quand** le système détecte ce pattern anormal - -**Alors** une alerte est générée pour l'équipe modération -**Et** le compte peut être marqué pour surveillance -**Et** si abus confirmé, suspension possible - ---- - -## 20. Logs des changements de device - -**Étant donné** que je change de device plusieurs fois par jour - -**Quand** les changements sont loggés - -**Alors** chaque événement est enregistré: - - | timestamp | from_device | to_device | content_id | - |---|---|---|---| - | 2025-06-15 08:30:00 | null | iPhone | abc123 | - | 2025-06-15 09:15:00 | iPhone | iPad | def456 | - | 2025-06-15 18:30:00 | iPad | iPhone | ghi789 | - -**Et** ces logs aident à détecter les partages de compte - ---- - -## 21. Métriques admin - Changements devices par utilisateur - -**Étant donné** qu'un admin consulte les métriques de streaming - -**Quand** il accède au dashboard - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Utilisateurs Premium actifs | 12,547 | - | Changements de device/jour (médiane) | 2 | - | Utilisateurs >10 changements/jour | 47 (0.4%) | - | Comptes suspects (>20 changements/j) | 3 | - - ---- - -## 22. Email d'avertissement si changements excessifs - -**Étant donné** que je change de device 30 fois par jour pendant 3 jours - -**Quand** le système détecte ce pattern - -**Alors** je reçois un email d'avertissement: - ---- - -## 23. Suspension compte après avertissement ignoré - -**Étant donné** que j'ai reçu un email d'avertissement il y a 7 jours -**Mais** que je continue à changer de device 30 fois par jour - -**Quand** l'équipe modération examine le compte - -**Alors** mon compte Premium peut être suspendu pour partage abusif -**Et** je reçois un email de suspension avec justification - ---- - -## 24. FAQ - Pourquoi ma lecture s'arrête quand j'utilise un autre device ? - -**Étant donné** que je consulte la FAQ Premium - -**Quand** je cherche "lecture interrompue" - -**Alors** je trouve la réponse: - ---- - -## 25. Support - Utilisateur pense être piraté - -**Étant donné** qu'un utilisateur voit constamment "Lecture interrompue" -**Et** qu'il pense que son compte est piraté - -**Quand** il contacte le support - -**Alors** le support vérifie les logs de changements de device -**Et** peut identifier les devices (iPhone, iPad perso vs iPhone inconnu) -**Et** conseille de changer le mot de passe si device inconnu détecté - ---- - -## 26. Changement mot de passe déconnecte tous les devices - -**Étant donné** que je pense que mon compte est compromis - -**Quand** je change mon mot de passe - -**Alors** tous mes devices sont déconnectés immédiatement -**Et** les sessions actives dans Redis sont supprimées -**Et** je dois me reconnecter sur chaque device -**Et** cela sécurise mon compte - ---- - -## 27. Test charge - 100 000 vérifications/seconde - -**Étant donné** que 100 000 utilisateurs Premium lancent des contenus - -**Quand** chaque lancement vérifie Redis (GET active_streams:{user_id}) - -**Alors** Redis peut gérer facilement 100 000 requêtes/seconde -**Et** le temps de réponse moyen est <1ms -**Et** aucun ralentissement n'est constaté - ---- - -## 28. Test failover Redis - -**Étant donné** que le serveur Redis principal tombe en panne - -**Quand** le failover automatique vers le replica Redis s'active - -**Alors** les sessions actives peuvent être perdues temporairement (max 5 min) -**Mais** les utilisateurs peuvent relancer immédiatement -**Et** l'impact est minimal (pas de perte de données critiques) - ---- - -## 29. Test concurrence - Lancement simultané 2 devices - -**Étant donné** que je lance exactement au même instant sur iPhone et iPad - -**Quand** les 2 requêtes arrivent en parallèle au serveur - -**Alors** Redis utilise un lock (SETNX) pour atomicité -**Et** 1 seul device gagne (par exemple iPhone) -**Et** l'autre device (iPad) reçoit immédiatement une erreur -**Et** l'utilisateur peut retry sur iPad si souhaité - ---- - -## 30. Nettoyage automatique sessions expirées - -**Étant donné** que 1000 sessions Redis ont expiré (TTL atteint) - -**Quand** Redis supprime automatiquement ces entrées - -**Alors** la mémoire est libérée -**Et** les nouveaux streams peuvent démarrer sans conflit -**Et** aucune intervention manuelle n'est nécessaire - ---- - - - - - -
- - -# Offre et tarification Premium -> *En tant qu'utilisateur* -> *Je veux pouvoir souscrire à un abonnement Premium* -> *Afin de profiter d'une expérience sans publicité avec des avantages exclusifs* - -**31 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'utilisateur -## 1. Formule mensuelle à 4.99€/mois - -**Étant donné** que je consulte les offres Premium - -**Quand** je vois la formule mensuelle - -**Alors** le prix affiché est 4.99€/mois -**Et** il n'y a aucune réduction -**Et** le prix effectif par mois est 4.99€ - ---- - -## 2. Formule annuelle à 49.99€/an (2 mois offerts) - -**Étant donné** que je consulte les offres Premium - -**Quand** je vois la formule annuelle - -**Alors** le prix affiché est 49.99€/an -**Et** l'économie affichée est "2 mois offerts" -**Et** le prix effectif par mois est 4.16€ -**Et** je vois le badge "Meilleure offre" - ---- - -## 3. Calcul économie formule annuelle - -**Étant donné** que la formule mensuelle coûte 4.99€/mois - -**Quand** je calcule le coût annuel en mensuel - -**Alors** 12 mois × 4.99€ = 59.88€/an -**Et** la formule annuelle coûte 49.99€ -**Et** l'économie est de 9.89€ (≈ 2 mois gratuits) -**Et** la réduction est de 16.5% - ---- - -## 4. Pas d'essai gratuit disponible - -**Étant donné** que je consulte les offres Premium - -**Quand** je recherche une option "Essai gratuit" - -**Alors** aucune option d'essai gratuit n'est proposée -**Et** je dois payer dès le premier jour pour accéder au Premium - ---- - -## 5. Justification absence essai gratuit - Anti-abus vacances - -**Étant donné** que RoadWave ne propose pas d'essai gratuit - -**Quand** un utilisateur envisage un road trip de 14 jours - -**Alors** il ne peut pas s'abonner pour l'essai gratuit puis annuler -**Et** cela évite les inscriptions opportunistes -**Et** protège les revenus des créateurs - ---- - -## 6. Justification absence essai gratuit - Protection revenus créateurs - -**Étant donné** qu'un utilisateur Premium écoute des contenus - -**Quand** il génère des écoutes dès le jour 1 - -**Alors** les créateurs sont rémunérés immédiatement (70% de 4.99€) -**Et** il n'y a pas de "période gratuite" sans rémunération créateurs - ---- - -## 7. Justification absence essai gratuit - Simplicité - -**Étant donné** que RoadWave gère les abonnements - -**Quand** il n'y a pas d'essai gratuit - -**Alors** pas de gestion complexe de période trial -**Et** pas de workflow de conversion trial → payant -**Et** cela réduit la complexité technique - ---- - -## 8. Justification absence essai gratuit - Engagement - -**Étant donné** qu'un utilisateur paie dès le début - -**Quand** il souscrit à Premium - -**Alors** il est plus engagé qu'un utilisateur en essai gratuit -**Et** le taux de churn est généralement plus faible -**Et** la lifetime value (LTV) est plus élevée - ---- - -## 9. Pas de partage familial au MVP - -**Étant donné** que je consulte les offres Premium - -**Quand** je recherche une option "Famille" ou "Partage" - -**Alors** aucune option de partage familial n'est disponible -**Et** seuls les abonnements individuels sont proposés - ---- - -## 10. Justification absence partage familial - Complexité technique - -**Étant donné** que le partage familial nécessite: - - | fonctionnalité | complexité | - |---|---| - | Gestion invitations | Moyenne | - | Validation liens famille | Moyenne | - | Limite devices par membre | Élevée | - | Dashboard admin famille | Élevée | - - -**Quand** RoadWave évalue le ROI - -**Alors** le coût dev/support est trop élevé pour le MVP -**Et** la fonctionnalité est reportée post-MVP - ---- - -## 11. Justification absence partage familial - Risque abus - -**Étant donné** qu'une offre famille permet 5-6 membres - -**Quand** il n'y a pas de vérification stricte de lien familial - -**Alors** des "familles" de 6 inconnus pourraient se former -**Et** cela réduirait fortement les revenus (6 personnes pour 1 abonnement) - ---- - -## 12. Justification absence partage familial - Cible individuelle - -**Étant donné** que RoadWave cible principalement les conducteurs - -**Quand** chaque conducteur utilise l'app individuellement en voiture - -**Alors** le besoin de partage familial est limité -**Et** la plupart des utilisateurs sont des individus (pas des familles) - ---- - -## 13. Post-MVP - Offre Famille à 9.99€/mois pour 5 comptes - -**Étant donné** que RoadWave envisage une offre Famille post-MVP - -**Quand** la fonctionnalité est spécifiée - -**Alors** le prix serait 9.99€/mois pour 5 comptes -**Et** cela représente 2€/mois/personne -**Mais** cette offre n'est pas disponible au MVP - ---- - -## 14. Comparaison tarif - Spotify à 10.99€/mois - -**Étant donné** que Spotify Premium coûte 10.99€/mois - -**Quand** RoadWave fixe son prix à 4.99€/mois - -**Alors** RoadWave est 54.5% moins cher que Spotify -**Et** cela positionne RoadWave comme très accessible - ---- - -## 15. Comparaison tarif - YouTube Premium à 11.99€/mois - -**Étant donné** que YouTube Premium coûte 11.99€/mois - -**Quand** RoadWave fixe son prix à 4.99€/mois - -**Alors** RoadWave est 58.4% moins cher que YouTube Premium -**Et** cela est un argument commercial fort - ---- - -## 16. Comparaison tarif - Apple Music à 10.99€/mois - -**Étant donné** qu'Apple Music coûte 10.99€/mois - -**Quand** RoadWave fixe son prix à 4.99€/mois - -**Alors** RoadWave est 54.5% moins cher qu'Apple Music -**Et** cela attire les utilisateurs sensibles au prix - ---- - -## 17. Justification tarif bas - Cible conducteurs quotidiens - -**Étant donné** que RoadWave cible les trajets quotidiens domicile-travail - -**Quand** le prix est fixé à 4.99€/mois - -**Alors** c'est un budget raisonnable pour un conducteur -**Et** équivalent à ~1-2 cafés/mois -**Et** psychologiquement acceptable pour un usage quotidien - ---- - -## 18. Justification formule annuelle - Engagement long terme - -**Étant donné** que la formule annuelle offre 2 mois gratuits - -**Quand** un utilisateur souscrit pour 1 an - -**Alors** il s'engage sur le long terme -**Et** RoadWave sécurise 49.99€ de revenus immédiatement -**Et** le cash flow est amélioré - ---- - -## 19. Justification formule annuelle - Réduction churn - -**Étant donné** qu'un utilisateur paie 49.99€ pour l'année - -**Quand** il envisage d'arrêter après 3 mois - -**Alors** il a déjà payé pour 12 mois -**Et** il continuera probablement à utiliser l'app -**Et** le taux de churn est réduit significativement - ---- - -## 20. Affichage comparatif des deux formules - -**Étant donné** que je consulte la page Premium - -**Quand** je vois les deux formules côte à côte - -**Alors** je vois: - ---- - -## 21. Mise en avant formule annuelle - -**Étant donné** que je consulte la page Premium - -**Quand** je vois les deux formules - -**Alors** la formule annuelle a un badge "Meilleure offre" ⭐ -**Et** elle est visuellement mise en avant (bordure colorée, taille plus grande) -**Et** l'économie de 2 mois est affichée en gros -**Et** cela incite à choisir la formule annuelle - ---- - -## 22. Lien "Pourquoi pas d'essai gratuit ?" en FAQ - -**Étant donné** que je consulte la page Premium - -**Quand** je clique sur "FAQ" - -**Alors** je vois une question "Pourquoi pas d'essai gratuit ?" -**Et** la réponse explique: - ---- - -## 23. A/B test formule annuelle (post-MVP) - -**Étant donné** que RoadWave veut optimiser la conversion annuelle - -**Quand** un A/B test est lancé - -**Alors** groupe A voit "2 mois offerts" (économie en durée) -**Et** groupe B voit "Économisez 9.89€" (économie en argent) -**Et** les taux de souscription sont mesurés -**Et** le message le plus performant est déployé - ---- - -## 24. Promo temporaire exceptionnelle (Black Friday, etc.) - -**Étant donné** que c'est le Black Friday - -**Quand** une promo temporaire est activée - -**Alors** la formule annuelle peut passer à 39.99€/an (au lieu de 49.99€) -**Et** l'économie affichée est "4 mois offerts !" -**Et** la promo dure 3 jours uniquement -**Et** cela génère un pic de souscriptions - ---- - -## 25. Code promo partenariat influenceur - -**Étant donné** qu'un influenceur promeut RoadWave - -**Quand** il partage un code promo "INFLUENCEUR20" - -**Alors** les utilisateurs obtiennent -20% sur le premier mois (3.99€ au lieu de 4.99€) -**Et** le code est valable 1 mois -**Et** les conversions sont trackées par code promo - ---- - -## 26. Statistiques admin - Répartition formules - -**Étant donné** qu'un admin consulte les métriques d'abonnements - -**Quand** il accède au dashboard - -**Alors** il voit: - - | métrique | valeur | - |---|---| - | Abonnés Premium total | 12,547 | - | Abonnés mensuels | 7,234 (58%) | - | Abonnés annuels | 5,313 (42%) | - | Revenus mensuels récurrents | 58,890€ | - -**Et** ces données aident à piloter la stratégie tarifaire - ---- - -## 27. Calcul revenus mensuels récurrents (MRR) - -**Étant donné** que RoadWave a: - - | formule | nombre abonnés | prix | - |---|---|---| - | Mensuel | 7,234 | 4.99€/mois | - | Annuel | 5,313 | 49.99€/an | - - -**Quand** le MRR est calculé - -**Alors** MRR mensuel = 7,234 × 4.99€ = 36,098€ -**Et** MRR annuel ramené au mois = 5,313 × 49.99€ / 12 = 22,139€ -**Et** MRR total = 58,237€/mois - ---- - -## 28. Projection revenus annuels (ARR) - -**Étant donné** que le MRR est de 58,237€ - -**Quand** l'ARR est calculé - -**Alors** ARR = 58,237€ × 12 = 698,844€/an -**Et** cela aide à évaluer la valorisation de l'entreprise - ---- - -## 29. Affichage prix TTC (TVA incluse) - -**Étant donné** que RoadWave est une plateforme française - -**Quand** les prix sont affichés - -**Alors** tous les prix sont TTC (TVA 20% incluse) -**Et** le prix 4.99€ inclut déjà la TVA -**Et** cela respecte la réglementation française - ---- - -## 30. Performance page Premium avec cache - -**Étant donné** que la page Premium est consultée fréquemment - -**Quand** un utilisateur charge la page - -**Alors** les prix et avantages sont servis depuis un cache CDN -**Et** le temps de chargement est <200ms -**Et** cela garantit une expérience fluide - ---- - -## 31. Localisation prix selon pays (post-MVP) - -**Étant donné** que RoadWave se lance à l'international post-MVP - -**Quand** un utilisateur se connecte depuis l'Allemagne - -**Alors** les prix peuvent être ajustés (ex: 4.99€ en France, 4.49€ en Pologne) -**Et** cela respecte le pouvoir d'achat local -**Mais** cette fonctionnalité n'est pas au MVP (France uniquement) - ---- - - - - - -
- - -# Profil créateur -> *En tant qu'utilisateur de RoadWave* -> *Je veux consulter les profils des créateurs* -> *Afin de découvrir leur contenu et décider de m'abonner* - -**31 scénarios** (28 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -## 1. URL du profil créateur - -**Étant donné** un créateur avec le pseudo "paris_stories" - -**Quand** l'utilisateur accède au profil - -**Alors** l'URL est "https://roadwave.fr/@paris_stories" - ---- - -## 2. Informations principales du profil - -**Étant donné** un créateur "@paris_stories" avec les informations suivantes: - - | champ | valeur | - |---|---| - | photo | avatar_120x120.jpg | - | pseudo | paris_stories | - | badge_vérifié | true | - | bio | Histoires et anecdotes de Paris | - | abonnés | 1200 | - | contenus | 42 | - | durée_totale | 18h | - | écoutes_totales | 54000 | - - -**Quand** le profil est affiché - -**Alors** les éléments suivants sont visibles: - - | élément | valeur affichée | - |---|---| - | Photo profil | 120×120 px | - | @pseudo | @paris_stories | - | Badge vérifié | ✓ | - | Bio | Histoires et... | - | Nombre abonnés | 1.2K abonnés | - | Nombre contenus | 42 contenus | - | Durée totale | 18h de contenu créé | - | Écoutes totales | 54K écoutes totales | - - ---- - -## 3. 📋 Plan: Arrondi des statistiques publiques - -**Étant donné** un créateur avec - -**Quand** le profil est affiché - -**Alors** la valeur affichée est "" - -**📊 Exemples de données:** - -| métrique | valeur_exacte | valeur_affichée | -|---|---|---| -| abonnés | 342 | 342 | -| abonnés | 1200 | 1.2K | -| abonnés | 54000 | 54K | -| abonnés | 1200000 | 1.2M | -| écoutes | 842 | 842 | -| écoutes | 5400 | 5.4K | -| écoutes | 142000 | 142K | -| écoutes | 2100000 | 2.1M | -| durée (heures) | 18 | 18h | -| durée (heures) | 142 | 142h | - ---- - -## 4. Bio avec markdown basique - -**Étant donné** un créateur avec la bio suivante en markdown: - -**Quand** le profil est affiché - -**Alors** le texte en gras "Histoires de Paris" est formaté -**Et** le texte en italique "Nouveau contenu chaque semaine" est formaté -**Et** le lien "https://paris-stories.fr" est cliquable - ---- - -## 5. Limitation de la bio à 300 caractères - -**Étant donné** un créateur qui entre une bio de 350 caractères - -**Quand** la bio est sauvegardée - -**Alors** seuls les 300 premiers caractères sont conservés -**Et** un message "Maximum 300 caractères" s'affiche - ---- - -## 6. Boutons d'action principaux - -**Étant donné** que l'utilisateur consulte un profil créateur - -**Quand** la page est chargée - -**Alors** les boutons suivants sont visibles: - - | bouton | action | - |---|---| - | S'abonner | Abonnement au créateur | - | Partager profil | Menu de partage | - | ••• | Menu contextuel | - - ---- - -## 7. Menu contextuel du profil [•••] - -**Étant donné** que l'utilisateur clique sur le bouton [•••] - -**Quand** le menu s'ouvre - -**Alors** les options suivantes sont disponibles: - - | option | description | - |---|---| - | Partager profil | Partager le lien du profil | - | Signaler profil | Signaler spam ou usurpation d'identité | - | Bloquer créateur | Masquer tous les contenus du créateur | - - ---- - -## 8. Liste des contenus du créateur - -**Étant donné** un créateur avec 3 contenus publiés - -**Quand** le profil est affiché - -**Alors** chaque contenu affiche: - - | élément | exemple | - |---|---| - | Cover image | Image 16:9 | - | Titre | Balade à Paris | - | Durée et écoutes | 12 min · 🎧 2.3K | - | Localisation | 📍 Paris | - | Bouton lecture | ▶️ | - - ---- - -## 9. 📋 Plan: Options de tri des contenus - -**Étant donné** un créateur avec 10 contenus publiés - -**Quand** l'utilisateur sélectionne le tri "" - -**Alors** les contenus sont triés par - -**📊 Exemples de données:** - -| option_tri | critère | -|---|---| -| Plus récents | Date publication DESC (défaut) | -| Plus populaires | Écoutes × facteur temporel (90 jours) | -| Plus anciens | Date publication ASC | - ---- - -## 10. Filtrage des contenus par tag - -**Étant donné** un créateur avec des contenus taggés "Voyage", "Histoire", "Gastronomie" - -**Quand** l'utilisateur filtre par tags "Voyage, Histoire" - -**Alors** seuls les contenus avec ces tags sont affichés -**Et** le nombre de résultats est indiqué "12 contenus" - ---- - -## 11. Recherche locale dans le profil - -**Étant donné** que l'utilisateur consulte le profil de "@paris_stories" -**Et** que le créateur a publié 50 contenus - -**Quand** l'utilisateur entre "Montmartre" dans la barre de recherche - -**Alors** la recherche s'effectue sur les titres et descriptions -**Et** seuls les contenus correspondants sont affichés -**Et** le placeholder indique "Rechercher dans les contenus de @paris_stories" - ---- - -## 12. Chargement paginé des contenus - -**Étant donné** un créateur avec 100 contenus publiés - -**Quand** le profil est affiché - -**Alors** 20 contenus sont chargés initialement -**Et** un bouton "Charger plus" est visible en bas de page - -**Quand** l'utilisateur clique sur "Charger plus" - -**Alors** 20 contenus supplémentaires sont chargés - ---- - -## 13. Informations publiques visibles par tous - -**Étant donné** que l'utilisateur consulte un profil créateur - -**Alors** les informations suivantes sont publiques: - - | information | visible | - |---|---| - | Photo et pseudo | ✅ | - | Badge vérifié | ✅ | - | Bio | ✅ | - | Nombre abonnés | ✅ | - | Nombre contenus | ✅ | - | Durée totale créée | ✅ | - | Écoutes totales | ✅ | - - ---- - -## 14. Informations privées non visibles - -**Étant donné** que l'utilisateur consulte un profil créateur - -**Alors** les informations suivantes sont privées: - - | information | visible | - |---|---| - | Liste des abonnés | ❌ | - | Revenus | ❌ | - | Localisation précise | ❌ | - | Email | ❌ | - - ---- - -## 15. Dashboard créateur avec métriques privées - -**Étant donné** que le créateur "@paris_stories" consulte son propre dashboard - -**Quand** la page statistiques est affichée - -**Alors** les métriques suivantes sont accessibles: - - | métrique | type | - |---|---| - | Taux complétion moyen | 78% | - | Évolution abonnés | Graphique | - | Écoutes par contenu | Tableau | - | Revenus | Dashboard | - | Taux conversion Premium | Pourcentage | - | Démographie (âge/zone) | Agrégée | - - ---- - -## 16. Graphique d'évolution des abonnés - -**Étant donné** que le créateur consulte son dashboard - -**Quand** il sélectionne la période "30 jours" - -**Alors** un graphique d'évolution des abonnés est affiché -**Et** les périodes disponibles sont: - - | période | - |---| - | 30j | - | 90j | - | 1 an | - - ---- - -## 17. Tableau détaillé des écoutes par contenu - -**Étant donné** un créateur avec 10 contenus publiés - -**Quand** il consulte le tableau des performances - -**Alors** chaque contenu affiche: - - | métrique | exemple | - |---|---| - | Titre | Balade | - | Écoutes totales | 2300 | - | Écoutes complètes >80% | 1840 | - | Taux complétion | 80% | - | Likes | 420 | - | Partages | 56 | - - ---- - -## 18. Affichage du badge vérifié - -**Étant donné** un créateur vérifié "@paris_stories" - -**Quand** son profil est affiché - -**Alors** le badge bleu "✓" est accolé au pseudo -**Et** un tooltip "Compte vérifié" s'affiche au survol - ---- - -## 19. Badge vérifié visible partout - -**Étant donné** un créateur vérifié "@paris_stories" - -**Alors** le badge "✓" est affiché dans: - - | emplacement | - |---| - | Page profil | - | Player en lecture | - | Résultats de recherche | - | Notifications | - - ---- - -## 20. 📋 Plan: Attribution automatique du badge selon critères - -**Étant donné** un créateur avec - -**Quand** les conditions sont validées - -**Alors** le badge vérifié est attribué - -**📊 Exemples de données:** - -| critère | automatique | -|---|---| -| KYC Mangopay validé | Oui | -| ≥10K abonnés + compte >6 mois | Oui | -| Célébrité / Média officiel | Manuel | - ---- - -## 21. Attribution automatique via KYC - -**Étant donné** un créateur qui complète son KYC Mangopay - -**Quand** les documents sont validés - -**Alors** le badge vérifié est attribué automatiquement -**Et** une notification "Votre compte est maintenant vérifié ✓" est envoyée - ---- - -## 22. Attribution automatique à 10K abonnés - -**Étant donné** un créateur avec 9999 abonnés et un compte de 7 mois - -**Quand** il atteint 10000 abonnés - -**Alors** le badge vérifié est attribué automatiquement -**Et** une notification de félicitations est envoyée - ---- - -## 23. Demande manuelle de vérification (célébrité) - -**Étant donné** un créateur reconnu publiquement - -**Quand** il soumet le formulaire de demande de vérification - -**Alors** une requête est créée pour l'équipe RoadWave -**Et** l'équipe vérifie l'identité sous 48-72h -**Et** le badge est attribué si validation réussie - ---- - -## 24. Retrait du badge en cas de suspension - -**Étant donné** un créateur vérifié avec le badge "✓" - -**Quand** sa monétisation est suspendue - -**Alors** le badge vérifié est retiré temporairement -**Et** le badge est restauré après levée de la suspension - ---- - -## 25. Retrait définitif du badge pour strikes multiples - -**Étant donné** un créateur vérifié avec 3 strikes actifs - -**Quand** un 4ème strike est appliqué (ban) - -**Alors** le badge vérifié est retiré définitivement -**Et** le compte est banni - ---- - -## 26. Retrait du badge pour usurpation d'identité - -**Étant donné** un créateur vérifié qui usurpe l'identité d'une célébrité - -**Quand** la fraude est détectée - -**Alors** le badge est retiré immédiatement -**Et** le compte est banni -**Et** une enquête est ouverte - ---- - -## 27. Profil créateur supprimé - -**Étant donné** qu'un utilisateur tente d'accéder à "@deleted_user" - -**Quand** la page est chargée - -**Alors** un message "Ce profil n'existe pas ou a été supprimé" s'affiche - ---- - -## 28. Blocage d'un créateur - -**Étant donné** que l'utilisateur bloque le créateur "@spam_account" - -**Quand** l'utilisateur consulte son flux de recommandations - -**Alors** aucun contenu de "@spam_account" n'est affiché -**Et** le créateur n'apparaît plus dans les recherches - ---- - -## 29. Déblocage d'un créateur - -**Étant donné** que l'utilisateur a bloqué "@paris_stories" - -**Quand** il accède à ses paramètres "Comptes bloqués" -**Et** qu'il débloque "@paris_stories" - -**Alors** les contenus du créateur réapparaissent dans les recommandations - ---- - -## 30. Signalement d'un profil pour spam - -**Étant donné** que l'utilisateur signale le profil "@spam_account" - -**Quand** il sélectionne la raison "Spam" - -**Alors** le signalement est envoyé à la modération -**Et** un message de confirmation s'affiche -**Et** le profil reste visible jusqu'à décision de modération - ---- - -## 31. Signalement pour usurpation d'identité - -**Étant donné** que l'utilisateur signale le profil "@fake_celebrity" - -**Quand** il sélectionne "Usurpation d'identité" -**Et** qu'il fournit une preuve - -**Alors** le signalement est priorisé (priorité HAUTE) -**Et** l'équipe modération traite sous 24h - ---- - - - - - -
- - -# Création de campagnes publicitaires -> *En tant que publicitaire* -> *Je veux créer des campagnes avec ciblage précis et maîtrise du budget* -> *Afin d'optimiser mes investissements publicitaires* - -**30 scénarios** (27 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un compte publicitaire est créé et vérifié -## 1. Création d'une campagne publicitaire complète - -**Étant donné** que je suis connecté en tant que publicitaire - -**Quand** je crée une nouvelle campagne avec les paramètres: - - | Paramètre | Valeur | - |---|---| - | Budget total | 300€ | - | Date début | 2026-02-01 | - | Date fin | 2026-02-14 | - | Zone géographique | Département du Var | - | Plages horaires | 7h-9h, 17h-19h | - | Tags ciblés | Automobile, Voyage | - | Tranche d'âge | 18+ | - - -**Alors** la campagne est créée avec succès -**Et** le budget quotidien calculé est de 21.43€/jour -**Et** les diffusions estimées sont de ~430 écoutes complètes -**Et** un statut "En attente de validation" est assigné - ---- - -## 2. Budget minimum 50€ requis - -**Étant donné** que je crée une nouvelle campagne - -**Quand** je définis un budget de 40€ - -**Alors** une erreur s'affiche: "Budget minimum requis: 50€" -**Et** la campagne n'est pas créée - ---- - -## 3. Budget de 50€ exactement accepté - -**Étant donné** que je crée une nouvelle campagne - -**Quand** je définis un budget de 50€ - -**Alors** la campagne est créée avec succès -**Et** aucune erreur n'est affichée - ---- - -## 4. Calcul automatique du budget quotidien - -**Étant donné** une campagne avec: - - | Budget total | 300€ | - |---|---| - | Durée | 14 j | - - -**Quand** le système calcule le budget quotidien - -**Alors** le budget/jour est de 21.43€ -**Et** le nombre estimé de diffusions/jour est de 430 (à 0.05€/écoute) - ---- - -## 5. Ciblage géographique point GPS précis - -**Étant donné** que je crée une campagne - -**Quand** je sélectionne "Point GPS" avec coordonnées (43.1234, 5.9234) -**Et** que je définis un rayon de 5km - -**Alors** la campagne cible uniquement les utilisateurs dans ce rayon -**Et** la zone est représentée par un cercle sur la carte - ---- - -## 6. Ciblage géographique ville - -**Étant donné** que je crée une campagne - -**Quand** je sélectionne "Ville" et choisis "Marseille" - -**Alors** la campagne cible tous les utilisateurs dans la commune de Marseille -**Et** les limites administratives sont affichées sur la carte - ---- - -## 7. Ciblage géographique département - -**Étant donné** que je crée une campagne - -**Quand** je sélectionne "Département" et choisis "Var (83)" - -**Alors** la campagne cible tout le département du Var -**Et** une estimation de population cible est affichée - ---- - -## 8. Ciblage géographique région - -**Étant donné** que je crée une campagne - -**Quand** je sélectionne "Région" et choisis "Provence-Alpes-Côte d'Azur" - -**Alors** la campagne cible toute la région PACA -**Et** l'estimation de population cible est mise à jour - ---- - -## 9. Ciblage géographique national - -**Étant donné** que je crée une campagne - -**Quand** je sélectionne "National" - -**Alors** la campagne cible tous les utilisateurs en France -**Et** aucune limite géographique n'est appliquée - ---- - -## 10. Ciblage horaire plages multiples - -**Étant donné** que je crée une campagne - -**Quand** je définis les plages horaires: - - | Plage | - |---| - | 7h-9h | - | 12h-14h | - | 17h-19h | - - -**Alors** la publicité est diffusée uniquement pendant ces plages -**Et** elle n'est jamais diffusée en dehors (ex: 10h, 15h, 20h) - ---- - -## 11. Ciblage horaire toute la journée - -**Étant donné** que je crée une campagne - -**Quand** je ne définis aucune plage horaire spécifique - -**Alors** la publicité est diffusée 24h/24 -**Et** aucune restriction horaire n'est appliquée - ---- - -## 12. Ciblage par centres d'intérêt - -**Étant donné** que je crée une campagne pour un garage automobile - -**Quand** je sélectionne les tags: - - | Tag | - |---| - | Automobile | - | Mécanique | - | Sport | - - -**Alors** la publicité est prioritaire pour les utilisateurs avec jauges élevées sur ces tags -**Et** elle peut quand même être diffusée à d'autres utilisateurs (ciblage non exclusif) - ---- - -## 13. Classification d'âge obligatoire - -**Étant donné** que je crée une campagne - -**Quand** j'essaie de valider sans sélectionner une tranche d'âge - -**Alors** une erreur s'affiche: "Classification d'âge obligatoire" -**Et** les options proposées sont: - - | Option | - |---| - | Tout public | - | 13+ | - | 16+ | - | 18+ | - - ---- - -## 14. Upload audio publicitaire formats acceptés - -**Étant donné** que je crée une campagne - -**Quand** j'upload un fichier audio format MP3 - -**Alors** le fichier est accepté - -**Quand** j'upload un fichier audio format AAC (.aac ou .m4a) - -**Alors** le fichier est accepté - -**Quand** j'upload un fichier audio format WAV - -**Alors** une erreur s'affiche: "Format non supporté. Utilisez MP3 ou AAC" - ---- - -## 15. Durée audio publicitaire validée - -**Étant donné** que je crée une campagne - -**Quand** j'upload un audio de 8 secondes - -**Alors** une erreur s'affiche: "Durée minimale: 10 secondes" - -**Quand** j'upload un audio de 65 secondes - -**Alors** une erreur s'affiche: "Durée maximale: 60 secondes" - -**Quand** j'upload un audio de 30 secondes - -**Alors** le fichier est accepté - ---- - -## 16. Prépaiement obligatoire via Mangopay - -**Étant donné** que j'ai configuré une campagne à 300€ - -**Quand** j'arrive à l'étape de paiement - -**Alors** je dois payer les 300€ avant validation -**Et** le paiement est traité via Mangopay -**Et** seule la carte bancaire est acceptée - ---- - -## 17. Recharge automatique optionnelle - -**Étant donné** que j'ai une campagne active - -**Quand** je configure la recharge automatique à 10% du budget - -**Alors** si le budget restant passe sous 30€ (10% de 300€) -**Et** que la campagne recharge automatiquement 100€ -**Et** ma carte bancaire est débitée de 100€ -**Et** le budget total passe à 130€ - ---- - -## 18. Désactivation recharge automatique - -**Étant donné** que j'ai activé la recharge automatique - -**Quand** je désactive cette option - -**Alors** aucune recharge ne se produit automatiquement -**Et** la campagne s'arrête quand le budget atteint 0€ - ---- - -## 19. Étalement budget sur période longue - -**Étant donné** une campagne avec: - - | Budget total | 1000€ | - |---|---| - | Durée | 30 j | - - -**Quand** le système calcule l'étalement - -**Alors** le budget/jour est de 33.33€ -**Et** si le budget se consomme plus vite (ex: 50€/jour) -**Alors** une alerte "Budget épuisé dans 10 jours" est envoyée - ---- - -## 20. Estimation population cible selon zone - -**Étant donné** que je sélectionne la zone "Marseille" - -**Quand** le système calcule la population cible - -**Alors** l'estimation affichée est "~15 000 utilisateurs potentiels" -**Et** un message "Estimation basée sur utilisateurs actifs dans la zone" s'affiche - ---- - -## 21. Campagne avec date de début différée - -**Étant donné** que je crée une campagne - -**Quand** je définis la date de début au 2026-03-01 (dans 1 mois) - -**Alors** la campagne a le statut "Programmée" -**Et** elle démarre automatiquement le 2026-03-01 à 00h00 -**Et** le budget n'est pas consommé avant cette date - ---- - -## 22. Interface self-service accessible - -**Étant donné** que je suis un publicitaire - -**Quand** j'accède à l'interface publicitaire - -**Alors** je peux créer une campagne sans contact commercial RoadWave -**Et** toutes les options sont configurables en autonomie -**Et** un tutoriel guidé est disponible (première utilisation) - ---- - -## 23. Aperçu zone ciblée sur carte interactive - -**Étant donné** que je configure une zone géographique - -**Quand** je sélectionne "Département du Var" - -**Alors** une carte Leaflet affiche les limites du département en surbrillance -**Et** un compteur "~50 000 utilisateurs actifs" est affiché -**Et** je peux zoomer/dézoomer pour visualiser la zone - ---- - -## 24. Tags multiples pour ciblage affiné - -**Étant donné** que je crée une campagne pour un restaurant - -**Quand** je sélectionne les tags: - - | Tag | - |---| - | Gastronomie | - | Tourisme | - | Famille | - - -**Alors** la publicité est prioritaire pour utilisateurs intéressés par ces 3 thèmes -**Et** le score de ciblage combine les 3 jauges d'intérêt - ---- - -## 25. Validation des dates de campagne - -**Étant donné** que je crée une campagne - -**Quand** je définis une date de début postérieure à la date de fin - -**Alors** une erreur s'affiche: "Date de fin doit être après date de début" -**Et** la campagne n'est pas créée - ---- - -## 26. Durée minimale de campagne - -**Étant donné** que je crée une campagne - -**Quand** je définis une durée de moins de 24 heures - -**Alors** une erreur s'affiche: "Durée minimale: 1 jour" -**Et** je dois ajuster les dates - ---- - -## 27. Durée maximale de campagne - -**Étant donné** que je crée une campagne - -**Quand** je définis une durée de plus de 90 jours - -**Alors** une erreur s'affiche: "Durée maximale: 90 jours" -**Et** je dois ajuster les dates ou créer plusieurs campagnes - ---- - -## 28. 📋 Plan: Calcul budget quotidien selon durée - -**Étant donné** une campagne avec un budget de € - -**Quand** la durée est de jours - -**Alors** le budget quotidien est de €/jour - -**📊 Exemples de données:** - -| budget | duree | budget_jour | -|---|---|---| -| 100 | 10 | 10.00 | -| 300 | 14 | 21.43 | -| 500 | 30 | 16.67 | -| 1000 | 60 | 16.67 | - ---- - -## 29. 📋 Plan: Estimation diffusions selon budget - -**Étant donné** un budget quotidien de € - -**Quand** le coût par écoute complète est 0.05€ - -**Alors** le nombre estimé de diffusions/jour est - -**📊 Exemples de données:** - -| budget_jour | diffusions | -|---|---| -| 10.00 | 200 | -| 21.43 | 429 | -| 50.00 | 1000 | -| 100.00 | 2000 | - ---- - -## 30. 📋 Plan: Formats audio acceptés/rejetés - -**Étant donné** que j'upload un fichier - -**Quand** le format est - -**Alors** le résultat est - -**📊 Exemples de données:** - -| fichier | format | resultat | -|---|---|---| -| pub.mp3 | MP3 | accepté | -| pub.aac | AAC | accepté | -| pub.m4a | AAC | accepté | -| pub.wav | WAV | rejeté | -| pub.ogg | OGG | rejeté | -| pub.flac | FLAC | rejeté | - ---- - - - - - -
- - -# Caractéristiques et facturation des publicités -> *En tant que système RoadWave* -> *Je veux appliquer des règles précises de durée, skippabilité et facturation* -> *Afin d'équilibrer expérience utilisateur et rentabilité publicitaire* - -**32 scénarios** (29 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur gratuit écoute du contenu -## 1. Durée minimale 10 secondes - -**Étant donné** qu'un publicitaire uploade une publicité de 8 secondes - -**Quand** le système valide la durée - -**Alors** une erreur s'affiche: "Durée minimale: 10 secondes" -**Et** l'upload est rejeté - ---- - -## 2. Durée maximale 60 secondes - -**Étant donné** qu'un publicitaire uploade une publicité de 65 secondes - -**Quand** le système valide la durée - -**Alors** une erreur s'affiche: "Durée maximale: 60 secondes" -**Et** l'upload est rejeté - ---- - -## 3. Durée recommandée 15-30 secondes - -**Étant donné** qu'un publicitaire crée une campagne - -**Quand** il voit les recommandations - -**Alors** un message s'affiche: - ---- - -## 4. Publicité de 10 secondes acceptée - -**Étant donné** qu'un publicitaire uploade une publicité de 10 secondes - -**Quand** le système valide la durée - -**Alors** le fichier est accepté -**Et** aucune erreur n'est affichée - ---- - -## 5. Publicité de 60 secondes acceptée - -**Étant donné** qu'un publicitaire uploade une publicité de 60 secondes - -**Quand** le système valide la durée - -**Alors** le fichier est accepté -**Et** un avertissement s'affiche: "⚠️ Durée longue: taux de skip potentiellement élevé" - ---- - -## 6. Délai minimum skippable 5 secondes par défaut - -**Étant donné** qu'une publicité de 30 secondes démarre -**Et** que le délai minimal est configuré à 5 secondes - -**Quand** j'écoute pendant 3 secondes - -**Alors** le bouton "Passer" n'est pas visible -**Et** je dois attendre 2 secondes supplémentaires - -**Quand** j'atteins 5 secondes d'écoute - -**Alors** le bouton "Passer" apparaît -**Et** je peux cliquer pour passer au contenu suivant - ---- - -## 7. Délai minimum paramétrable admin (3 secondes) - -**Étant donné** que l'admin configure le délai à 3 secondes -**Et** qu'une publicité démarre - -**Quand** j'écoute pendant 3 secondes - -**Alors** le bouton "Passer" apparaît immédiatement -**Et** je peux skipper - ---- - -## 8. Délai minimum paramétrable admin (10 secondes) - -**Étant donné** que l'admin configure le délai à 10 secondes -**Et** qu'une publicité démarre - -**Quand** j'écoute pendant 9 secondes - -**Alors** le bouton "Passer" n'est toujours pas visible - -**Quand** j'atteins 10 secondes - -**Alors** le bouton "Passer" apparaît - ---- - -## 9. Facturation écoute complète (>80%) - 0.05€ - -**Étant donné** qu'une publicité de 30 secondes est diffusée - -**Quand** j'écoute pendant 25 secondes (83%) - -**Alors** l'écoute est considérée comme "complète" -**Et** le publicitaire est facturé 0.05€ -**Et** le compteur "écoutes complètes" s'incrémente - ---- - -## 10. Facturation écoute complète exactement 80% - -**Étant donné** qu'une publicité de 30 secondes est diffusée - -**Quand** j'écoute pendant exactement 24 secondes (80%) - -**Alors** l'écoute est considérée comme "complète" -**Et** le publicitaire est facturé 0.05€ - ---- - -## 11. Facturation skip après délai minimal - 0.02€ - -**Étant donné** qu'une publicité de 30 secondes est diffusée -**Et** que le délai minimal est 5 secondes - -**Quand** j'écoute pendant 10 secondes (33%) -**Et** que je clique sur "Passer" - -**Alors** l'écoute est considérée comme "partielle" -**Et** le publicitaire est facturé 0.02€ - ---- - -## 12. Facturation skip immédiat (<5s) - 0€ - -**Étant donné** qu'une publicité de 30 secondes est diffusée -**Et** que le délai minimal est 5 secondes - -**Quand** j'écoute pendant 3 secondes -**Et** que je clique sur "Suivant" (pas de bouton skip encore) - -**Alors** l'écoute est considérée comme "non engagée" -**Et** le publicitaire n'est PAS facturé (0€) - ---- - -## 13. Comptabilisation écoute complète à 79% - -**Étant donné** qu'une publicité de 30 secondes est diffusée - -**Quand** j'écoute pendant 23 secondes (77%) - -**Alors** l'écoute est considérée comme "partielle" (pas complète) -**Et** le publicitaire est facturé 0.02€ - ---- - -## 14. Comptabilisation écoute complète à 100% - -**Étant donné** qu'une publicité de 30 secondes est diffusée - -**Quand** j'écoute les 30 secondes complètes (100%) - -**Alors** l'écoute est considérée comme "complète" -**Et** le publicitaire est facturé 0.05€ - ---- - -## 15. Budget consommé selon mix écoutes - -**Étant donné** qu'une campagne à 300€ a généré: - - | Type écoute | Nombre | Coût unitaire | Total | - |---|---|---|---| - | Complète (>80%) | 4000 | 0.05€ | 200€ | - | Partielle (5-80%) | 2000 | 0.02€ | 40€ | - | Skip immédiat | 1000 | 0€ | 0€ | - - -**Quand** je calcule le budget consommé - -**Alors** le total est 240€ -**Et** il reste 60€ de budget disponible - ---- - -## 16. Affichage compteur secondes restantes - -**Étant donné** qu'une publicité de 30s démarre -**Et** que le délai minimal est 5s - -**Quand** j'écoute pendant 2 secondes - -**Alors** un compteur s'affiche: "Passer dans 3s..." - -**Quand** j'atteins 5 secondes - -**Alors** le compteur disparaît -**Et** le bouton "Passer la publicité" s'affiche - ---- - -## 17. Progress bar publicité visible - -**Étant donné** qu'une publicité de 30s est en lecture - -**Quand** 10 secondes se sont écoulées - -**Alors** la progress bar affiche 33% (10/30) -**Et** l'indicateur temporel affiche "0:10 / 0:30" -**Et** l'utilisateur visualise la progression - ---- - -## 18. Message "Publicité" clairement affiché - -**Étant donné** qu'une publicité démarre - -**Quand** l'audio commence - -**Alors** un badge "Publicité" est affiché en haut de l'écran -**Et** la durée totale est indiquée: "Publicité (30s)" -**Et** la transparence est maximale (utilisateur sait que c'est une pub) - ---- - -## 19. Transition fluide après publicité - -**Étant donné** qu'une publicité de 30s se termine - -**Quand** la lecture atteint 30 secondes - -**Alors** le délai de transition de 2s démarre -**Et** le contenu normal suivant est annoncé -**Et** l'enchaînement est naturel (même UX que entre contenus) - ---- - -## 20. Like autorisé sur publicité - -**Étant donné** qu'une publicité est en lecture -**Et** que le véhicule est à l'arrêt - -**Quand** je clique sur le bouton cœur - -**Alors** un like explicite (+2%) est enregistré -**Et** mes jauges d'intérêt sont mises à jour selon les tags de la pub -**Et** le publicitaire voit un compteur "Likes" incrémenté - ---- - -## 21. Abonnement autorisé sur publicité - -**Étant donné** qu'une publicité est diffusée par un créateur -**Et** que le véhicule est à l'arrêt - -**Quand** je clique sur "S'abonner" - -**Alors** l'abonnement est enregistré (+5% jauges) -**Et** le publicitaire bénéficie de l'engagement fort -**Et** cela compte comme une conversion majeure - ---- - -## 22. Bouton skip visible et accessible - -**Étant donné** qu'une publicité a dépassé le délai minimal - -**Quand** le bouton "Passer" s'affiche - -**Alors** il est positionné en bas à droite de l'écran -**Et** il a une taille de clic confortable (44×44px minimum iOS) -**Et** il est clairement visible (contraste élevé) - ---- - -## 23. Analytics tracking précis par type - -**Étant donné** qu'une publicité est diffusée - -**Quand** un événement se produit - -**Alors** il est tracké en temps réel: - - | Événement | Données enregistrées | - |---|---| - | Impression | timestamp, user_id, pub_id, zone_geo | - | Écoute complète | durée_ecoutee, pourcentage, coût (0.05€) | - | Skip après délai | durée_ecoutee, pourcentage, coût (0.02€) | - | Skip immédiat | durée_ecoutee, pourcentage, coût (0€) | - | Like | timestamp, tags impactés | - | Abonnement | timestamp, creator_id | - - ---- - -## 24. Recommandation sweet spot 15-30s - -**Étant donné** les statistiques RoadWave globales: - - | Durée pub | Taux complétion moyen | - |---|---| - | 10s | 65% | - | 15s | 55% | - | 30s | 45% | - | 45s | 30% | - | 60s | 20% | - - -**Quand** un publicitaire consulte les recommandations - -**Alors** le sweet spot affiché est "15-30 secondes" -**Et** l'explication est "Meilleur compromis engagement/message" - ---- - -## 25. Optimisation durée selon taux de skip campagne - -**Étant donné** qu'une campagne de 60s a un taux de skip de 85% - -**Quand** le publicitaire consulte les recommandations - -**Alors** le système suggère: - ---- - -## 26. Coût effectif moyen (CEM) calculé - -**Étant donné** une campagne avec: - - | Type écoute | Nombre | Coût unitaire | Total | - |---|---|---|---| - | Complète | 2000 | 0.05€ | 100€ | - | Partielle | 3000 | 0.02€ | 60€ | - | Skip immédiat | 1000 | 0€ | 0€ | - - -**Quand** je calcule le coût effectif moyen - -**Alors** CEM = 160€ / 6000 impressions = 0.027€/impression -**Et** cette métrique aide à comparer avec CPM industrie - ---- - -## 27. Publicité non skippable interdite - -**Étant donné** qu'un publicitaire demande "Publicité non skippable" - -**Quand** il configure sa campagne - -**Alors** cette option n'existe pas -**Et** toutes les publicités sont obligatoirement skippables après 5s minimum - ---- - -## 28. Délai minimal jamais <3 secondes - -**Étant donné** qu'un admin essaie de configurer le délai à 2 secondes - -**Quand** il valide le paramètre - -**Alors** une erreur s'affiche: "Délai minimal: 3 secondes minimum" - ---- - -## 29. Délai minimal jamais >10 secondes - -**Étant donné** qu'un admin essaie de configurer le délai à 15 secondes - -**Quand** il valide le paramètre - -**Alors** une erreur s'affiche: "Délai maximal: 10 secondes maximum" - ---- - -## 30. 📋 Plan: Facturation selon durée écoutée - -**Étant donné** qu'une publicité de 30s est diffusée - -**Quand** j'écoute pendant s (%) - -**Alors** le type d'écoute est -**Et** le coût facturé est € - -**📊 Exemples de données:** - -| duree | pourcentage | type | cout | -|---|---|---|---| -| 3 | 10 | skip immédiat | 0 | -| 5 | 17 | partielle | 0.02 | -| 10 | 33 | partielle | 0.02 | -| 20 | 67 | partielle | 0.02 | -| 24 | 80 | complète | 0.05 | -| 27 | 90 | complète | 0.05 | -| 30 | 100 | complète | 0.05 | - ---- - -## 31. 📋 Plan: Budget consommé selon distribution écoutes - -**Étant donné** écoutes complètes à 0.05€ -**Et** écoutes partielles à 0.02€ -**Et** skips immédiats à 0€ - -**Quand** je calcule le budget total consommé - -**Alors** le résultat est € - -**📊 Exemples de données:** - -| completes | partielles | skips | budget_total | -|---|---|---|---| -| 1000 | 500 | 100 | 60 | -| 2000 | 1000 | 500 | 120 | -| 5000 | 2000 | 1000 | 290 | -| 0 | 1000 | 0 | 20 | -| 1000 | 0 | 0 | 50 | - ---- - -## 32. 📋 Plan: Apparition bouton skip selon délai configuré - -**Étant donné** que le délai minimal est configuré à s - -**Quand** j'écoute pendant s - -**Alors** le bouton "Passer" est - -**📊 Exemples de données:** - -| delai | temps_ecoute | visible | -|---|---|---| -| 5 | 3 | non visible | -| 5 | 5 | visible | -| 5 | 10 | visible | -| 10 | 8 | non visible | -| 10 | 10 | visible | -| 3 | 2 | non visible | -| 3 | 3 | visible | - ---- - - - - - -
- - -# Gestion du budget et alertes publicitaires -> *En tant que publicitaire* -> *Je veux suivre en temps réel mon budget et recevoir des alertes* -> *Afin de maîtriser mes dépenses et optimiser mes campagnes* - -**30 scénarios** (27 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un compte publicitaire est connecté -> **Et** qu'une campagne active est en cours -## 1. Dashboard budget temps réel - -**Étant donné** que ma campagne a un budget de 300€ -**Et** que j'ai consommé 220€ - -**Quand** je consulte le dashboard budget - -**Alors** je vois: - - | Métrique | Valeur | - |---|---| - | Budget total | 300€ | - | Budget consommé | 220€ | - | Budget restant | 80€ | - | Pourcentage | 73% consommé | - - ---- - -## 2. Jauge visuelle budget consommé - -**Étant donné** que j'ai consommé 220€ sur 300€ - -**Quand** je consulte le dashboard - -**Alors** une jauge de progression affiche 73% -**Et** la couleur est orange (seuil 50-80%) -**Et** un indicateur "80€ restants" est affiché clairement - ---- - -## 3. Couleur jauge selon seuil - -**Étant donné** un budget de 300€ - -**Quand** j'ai consommé 150€ (50%) - -**Alors** la jauge est verte - -**Quand** j'ai consommé 240€ (80%) - -**Alors** la jauge est orange - -**Quand** j'ai consommé 285€ (95%) - -**Alors** la jauge est rouge -**Et** un message "Budget presque épuisé" s'affiche - ---- - -## 4. Projection épuisement budget - -**Étant donné** que j'ai consommé 220€ en 10 jours -**Et** qu'il reste 4 jours de campagne - -**Quand** le système calcule la projection - -**Alors** la consommation quotidienne moyenne est 22€/jour -**Et** la projection affiche "Budget épuisé dans 3.6 jours" -**Et** un avertissement "Campagne s'arrêtera avant la fin prévue" s'affiche - ---- - -## 5. Projection avec budget suffisant - -**Étant donné** que j'ai consommé 100€ en 10 jours -**Et** qu'il reste 4 jours de campagne -**Et** que le budget total est 300€ - -**Quand** le système calcule la projection - -**Alors** la consommation quotidienne moyenne est 10€/jour -**Et** la projection affiche "Budget suffisant pour toute la campagne" -**Et** le budget restant estimé à la fin est 160€ - ---- - -## 6. Alerte 80% budget consommé - -**Étant donné** que mon budget est de 300€ - -**Quand** je consomme 240€ (80%) - -**Alors** je reçois immédiatement un email: -**Et** une notification push est envoyée -**Et** une notification in-app s'affiche - ---- - -## 7. Alerte 90% budget consommé - -**Étant donné** que mon budget est de 300€ - -**Quand** je consomme 270€ (90%) - -**Alors** je reçois immédiatement un email: - ---- - -## 8. Alerte budget épuisé (100%) - -**Étant donné** que mon budget est de 300€ - -**Quand** je consomme les 300€ (100%) - -**Alors** je reçois immédiatement un email: -**Et** la campagne est automatiquement mise en pause -**Et** plus aucune diffusion ne se produit - ---- - -## 9. Pause manuelle de campagne - -**Étant donné** que ma campagne est active -**Et** qu'il reste 150€ de budget - -**Quand** je clique sur "Mettre en pause" - -**Alors** le statut passe à "En pause" -**Et** les diffusions s'arrêtent immédiatement -**Et** le budget de 150€ est conservé -**Et** je peux réactiver la campagne plus tard - ---- - -## 10. Reprise campagne pausée - -**Étant donné** que ma campagne est en pause -**Et** qu'il reste 150€ de budget - -**Quand** je clique sur "Reprendre la campagne" - -**Alors** le statut passe à "Active" -**Et** les diffusions reprennent immédiatement -**Et** le budget restant de 150€ continue de se consommer - ---- - -## 11. Prolongation campagne avec recharge - -**Étant donné** que ma campagne se termine dans 2 jours -**Et** qu'il reste 20€ de budget - -**Quand** je clique sur "Prolonger la campagne" -**Et** que j'ajoute 200€ supplémentaires - -**Alors** le budget total passe à 220€ -**Et** la date de fin peut être prolongée de 10 jours -**Et** un nouveau paiement Mangopay de 200€ est traité - ---- - -## 12. Recharge automatique activée - -**Étant donné** que j'ai configuré la recharge automatique -**Et** que le seuil est fixé à 10% (30€ sur budget 300€) -**Et** que le montant de recharge est 100€ - -**Quand** le budget restant passe sous 30€ - -**Alors** une recharge automatique de 100€ est déclenchée -**Et** ma carte bancaire est débitée via Mangopay -**Et** le budget total passe à budget_restant + 100€ -**Et** je reçois un email de confirmation - ---- - -## 13. Échec recharge automatique (carte expirée) - -**Étant donné** que la recharge automatique est activée -**Et** que ma carte bancaire a expiré - -**Quand** le budget passe sous le seuil de 10% - -**Alors** la recharge automatique échoue -**Et** je reçois un email urgent: -**Et** la campagne continue jusqu'à épuisement du budget restant - ---- - -## 14. Modification ciblage si budget <50% consommé - -**Étant donné** que j'ai consommé 120€ sur 300€ (40%) - -**Quand** j'essaie de modifier le ciblage géographique - -**Alors** la modification est autorisée -**Et** le ciblage est mis à jour immédiatement -**Et** les nouvelles diffusions utilisent le nouveau ciblage - ---- - -## 15. Blocage modification ciblage si budget >50% consommé - -**Étant donné** que j'ai consommé 180€ sur 300€ (60%) - -**Quand** j'essaie de modifier le ciblage géographique - -**Alors** une erreur s'affiche: - ---- - -## 16. Modification audio nécessite nouvelle validation - -**Étant donné** que ma campagne est active - -**Quand** je veux modifier le fichier audio - -**Alors** un message s'affiche: - ---- - -## 17. Modification plages horaires autorisée - -**Étant donné** que ma campagne cible 7h-9h et 17h-19h - -**Quand** je modifie pour cibler 12h-14h aussi - -**Alors** la modification est appliquée immédiatement -**Et** les diffusions suivantes incluent la nouvelle plage -**Et** aucune re-validation n'est nécessaire - ---- - -## 18. Historique consommation budget jour par jour - -**Étant donné** que ma campagne a duré 10 jours - -**Quand** je consulte l'historique - -**Alors** je vois un graphique avec: - - | Jour | Consommation | Cumulé | - |---|---|---| - | 1 | 22€ | 22€ | - | 2 | 25€ | 47€ | - | 3 | 20€ | 67€ | - | ... | ... | ... | - | 10 | 18€ | 220€ | - -**Et** je peux identifier les pics de consommation - ---- - -## 19. Notification fin de campagne programmée - -**Étant donné** que ma campagne se termine le 14/02 - -**Quand** la date de fin est atteinte - -**Alors** je reçois un email: - ---- - -## 20. Remboursement budget non utilisé - -**Étant donné** que ma campagne avait 300€ de budget -**Et** qu'elle s'est terminée avec 280€ consommés - -**Quand** la campagne se termine (date ou épuisement) - -**Alors** un remboursement de 20€ est initié via Mangopay -**Et** le délai est de 5-7 jours ouvrés -**Et** je reçois une notification de confirmation - ---- - -## 21. Aucun remboursement si budget entièrement consommé - -**Étant donné** que ma campagne avait 300€ de budget -**Et** qu'elle s'est terminée avec 300€ consommés - -**Quand** la campagne se termine - -**Alors** aucun remboursement n'est initié -**Et** le message final indique "Budget entièrement utilisé" - ---- - -## 22. Statistiques comparatives budget vs objectif - -**Étant donné** que j'avais défini un objectif de 5000 impressions -**Et** que mon budget était 300€ - -**Quand** je consulte les statistiques finales - -**Alors** je vois: - - | Métrique | Objectif | Réalisé | Écart | - |---|---|---|---| - | Impressions | 5000 | 6000 | +20% | - | Budget | 300€ | 280€ | -7% | - | Coût/impression | 0.06€ | 0.047€ | -22% | - -**Et** une analyse "✅ Objectifs dépassés avec budget optimisé" - ---- - -## 23. Export rapport financier détaillé - -**Étant donné** que je veux analyser mes dépenses - -**Quand** je clique sur "Exporter rapport financier" - -**Alors** je télécharge un CSV avec: - - | Colonne | - |---| - | Date/Heure | - | Type écoute | - | Coût unitaire | - | Zone géographique | - | Utilisateur (anonyme) | - | Durée écoutée | - -**Et** je peux l'importer dans Excel pour analyses - ---- - -## 24. Tableau de bord multi-campagnes - -**Étant donné** que j'ai 3 campagnes actives - -**Quand** je consulte la vue d'ensemble - -**Alors** je vois un tableau récapitulatif: - - | Campagne | Budget | Consommé | % | Jours restants | Projection | - |---|---|---|---|---|---| - | A | 300€ | 220€ | 73 | 4j | Suffisant | - | B | 500€ | 480€ | 96 | 10j | Épuisé 2j | - | C | 200€ | 50€ | 25 | 20j | Suffisant | - -**Et** un badge alerte rouge sur la campagne B - ---- - -## 25. Alerte consolidée multi-campagnes - -**Étant donné** que j'ai 5 campagnes actives -**Et** que 2 campagnes ont >80% budget consommé - -**Quand** je reçois les notifications - -**Alors** un email consolidé unique est envoyé: -**Et** je ne reçois pas 2 emails séparés (évite spam) - ---- - -## 26. Configuration seuils alertes personnalisés - -**Étant donné** que je configure mes préférences d'alerte - -**Quand** je définis les seuils: - - | Seuil | Valeur | - |---|---| - | Alerte 1 | 70% | - | Alerte 2 | 85% | - | Alerte 3 | 95% | - - -**Alors** je reçois des alertes à 70%, 85% et 95% -**Et** non aux seuils par défaut 80%, 90%, 100% - ---- - -## 27. Désactivation alertes email - -**Étant donné** que je préfère uniquement les notifications in-app - -**Quand** je désactive les alertes email dans mes préférences - -**Alors** je ne reçois plus d'emails d'alerte budget -**Mais** les notifications in-app continuent -**Et** les alertes critiques (échec paiement) sont toujours envoyées par email - ---- - -## 28. 📋 Plan: Couleur jauge selon pourcentage consommé - -**Étant donné** un budget de 300€ - -**Quand** j'ai consommé € (%) - -**Alors** la couleur de la jauge est - -**📊 Exemples de données:** - -| montant | pourcentage | couleur | -|---|---|---| -| 100 | 33 | verte | -| 150 | 50 | verte | -| 180 | 60 | orange | -| 240 | 80 | orange | -| 270 | 90 | rouge | -| 285 | 95 | rouge | -| 300 | 100 | rouge | - ---- - -## 29. 📋 Plan: Projection épuisement selon consommation - -**Étant donné** un budget de 300€ -**Et** une consommation actuelle de € -**Et** une durée écoulée de jours - -**Quand** je calcule la consommation quotidienne moyenne - -**Alors** elle est de €/jour -**Et** le budget sera épuisé dans jours - -**📊 Exemples de données:** - -| consomme | jours_ecoules | conso_jour | jours_restants | -|---|---|---|---| -| 100 | 5 | 20 | 10 | -| 200 | 10 | 20 | 5 | -| 150 | 10 | 15 | 10 | -| 270 | 12 | 22.5 | 1.3 | - ---- - -## 30. 📋 Plan: Alertes envoyées selon seuils - -**Étant donné** un budget de 500€ - -**Quand** je consomme € (%) - -**Alors** je reçois une alerte - -**📊 Exemples de données:** - -| montant | pourcentage | niveau | -|---|---|---| -| 350 | 70 | aucune | -| 400 | 80 | alerte 80% | -| 450 | 90 | alerte 90% | -| 500 | 100 | budget épuisé | - ---- - - - - - -
- - -# Insertion et fréquence des publicités -> *En tant que système RoadWave* -> *Je veux insérer les publicités de manière équilibrée et non intrusive* -> *Afin de préserver l'expérience utilisateur tout en monétisant* - -**31 scénarios** (28 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un utilisateur gratuit est connecté -## 1. Fréquence par défaut 1 pub / 5 contenus - -**Étant donné** que la fréquence par défaut est configurée à 1/5 -**Et** que je suis un utilisateur gratuit - -**Quand** j'écoute 5 contenus - -**Alors** 1 publicité est insérée après le 5ème contenu - -**Quand** j'écoute 10 contenus - -**Alors** 2 publicités sont insérées (après les contenus 5 et 10) - ---- - -## 2. Aucune publicité pour utilisateurs Premium - -**Étant donné** que je suis un utilisateur Premium - -**Quand** j'écoute 100 contenus - -**Alors** aucune publicité n'est insérée -**Et** je bénéficie d'une expérience sans interruption publicitaire - ---- - -## 3. Fréquence paramétrable par admin (1/3) - -**Étant donné** que l'admin configure la fréquence à 1/3 -**Et** que je suis un utilisateur gratuit - -**Quand** j'écoute 6 contenus - -**Alors** 2 publicités sont insérées (après contenus 3 et 6) - ---- - -## 4. Fréquence paramétrable par admin (1/10) - -**Étant donné** que l'admin configure la fréquence à 1/10 -**Et** que je suis un utilisateur gratuit - -**Quand** j'écoute 20 contenus - -**Alors** 2 publicités sont insérées (après contenus 10 et 20) - ---- - -## 5. Jamais d'interruption d'un contenu en cours - -**Étant donné** que j'écoute un contenu de 10 minutes -**Et** que je suis à 5 minutes de lecture -**Et** qu'une publicité devrait être insérée selon la fréquence - -**Quand** le système vérifie l'insertion - -**Alors** la publicité attend la fin du contenu actuel -**Et** elle s'insère pendant le délai de transition (2s) -**Et** le contenu n'est jamais interrompu - ---- - -## 6. Insertion entre deux contenus uniquement - -**Étant donné** que le contenu "A" se termine -**Et** que le délai de transition de 2s démarre - -**Quand** le système détecte qu'une publicité doit être insérée - -**Alors** le message "Publicité (30s)" s'affiche -**Et** la publicité démarre après les 2 secondes -**Et** l'enchaînement est naturel et fluide - ---- - -## 7. Rotation limite 3 fois/jour par utilisateur - -**Étant donné** qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui - -**Quand** le système sélectionne une nouvelle publicité à diffuser - -**Alors** la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui -**Et** une autre publicité "B" est sélectionnée -**Et** cela évite la saturation publicitaire - ---- - -## 8. Compteur de diffusions par pub et par utilisateur - -**Étant donné** qu'un utilisateur écoute la pub "RestaurantX" - -**Quand** la diffusion se termine - -**Alors** un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente -**Et** le TTL est de 24h (reset à minuit) - -**Quand** le compteur atteint 3 - -**Alors** la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui - ---- - -## 9. Limite max 6 pubs/heure par utilisateur - -**Étant donné** qu'un utilisateur a entendu 6 publicités dans la dernière heure - -**Quand** le système devrait insérer une 7ème pub - -**Alors** l'insertion est reportée à l'heure suivante -**Et** un compteur horaire Redis "pub:user:123:hourly" est vérifié -**Et** cela évite le spam publicitaire - ---- - -## 10. Ciblage géographique prioritaire - Point GPS - -**Étant donné** qu'une publicité cible un point GPS à 2km de ma position -**Et** qu'une autre publicité cible ma ville entière - -**Quand** le système sélectionne une publicité - -**Alors** la publicité point GPS est priorisée (score géo plus élevé) -**Et** le ciblage précis est favorisé - ---- - -## 11. Ciblage géographique prioritaire - Hiérarchie - -**Étant donné** que 4 publicités sont éligibles: - - | Publicité | Zone | Distance | - |---|---|---| - | A | Point GPS | 1km | - | B | Ville | 0km | - | C | Département | 0km | - | D | National | N/A | - - -**Quand** le système sélectionne selon priorité géographique - -**Alors** l'ordre de priorité est: A > B > C > D -**Et** la publicité A (Point GPS, la plus précise) est diffusée - ---- - -## 12. Ciblage centres d'intérêt secondaire - -**Étant donné** que 2 publicités ciblent ma zone géographique: - - | Publicité | Tags | Mes jauges | - |---|---|---| - | A | Automobile | 80% | - | B | Voyage | 40% | - - -**Quand** le système applique le score centres d'intérêt - -**Alors** la publicité A est favorisée (meilleur match jauges) -**Et** le ciblage thématique affine la sélection - ---- - -## 13. Ciblage horaire strict - -**Étant donné** qu'une campagne cible uniquement 7h-9h -**Et** qu'il est 10h30 - -**Quand** le système sélectionne une publicité - -**Alors** cette campagne n'est PAS éligible -**Et** seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées - ---- - -## 14. Ciblage horaire pendant plage active - -**Étant donné** qu'une campagne cible 7h-9h et 17h-19h -**Et** qu'il est 8h15 - -**Quand** le système sélectionne une publicité - -**Alors** cette campagne est éligible -**Et** elle peut être diffusée - ---- - -## 15. Normalisation volume audio -14 LUFS - -**Étant donné** qu'une publicité est uploadée avec volume trop élevé (-6 LUFS) - -**Quand** le système encode l'audio via FFmpeg - -**Alors** le volume est normalisé automatiquement à -14 LUFS -**Et** le publicitaire reçoit une notification "Volume audio ajusté pour conformité" -**Et** cela évite l'effet "pub trop forte" frustrant - ---- - -## 16. Validation volume audio lors encodage - -**Étant donné** qu'une publicité est soumise - -**Quand** FFmpeg encode le fichier - -**Alors** une commande loudnorm est appliquée: -**Et** le fichier final respecte le standard broadcast -14 LUFS - ---- - -## 17. Sélection aléatoire si critères équivalents - -**Étant donné** que 3 publicités ont le même score géo -**Et** qu'elles ont toutes des jauges centres d'intérêt équivalentes -**Et** qu'aucune n'a été diffusée 3 fois aujourd'hui - -**Quand** le système sélectionne une publicité - -**Alors** une sélection aléatoire équitable est faite -**Et** chaque campagne a 33% de chances d'être diffusée - ---- - -## 18. Exclusion publicités avec budget épuisé - -**Étant donné** qu'une campagne "A" a épuisé son budget -**Et** qu'une campagne "B" a encore du budget disponible - -**Quand** le système sélectionne une publicité - -**Alors** seule la campagne "B" est éligible -**Et** la campagne "A" est automatiquement exclue - ---- - -## 19. Exclusion publicités hors dates de campagne - -**Étant donné** qu'une campagne "A" est programmée du 01/02 au 14/02 -**Et** que nous sommes le 20/01 - -**Quand** le système sélectionne une publicité - -**Alors** la campagne "A" n'est pas éligible -**Et** seules les campagnes actives aujourd'hui sont considérées - ---- - -## 20. Publicité visible uniquement dans zone géographique - -**Étant donné** qu'une publicité cible "Marseille uniquement" -**Et** que je suis à Lyon - -**Quand** le système sélectionne une publicité - -**Alors** cette publicité n'est jamais éligible pour moi -**Et** je ne la verrai jamais tant que je reste à Lyon - ---- - -## 21. Tracking compteur horaire avec TTL - -**Étant donné** qu'un utilisateur entend une pub à 10h05 - -**Quand** le compteur horaire est incrémenté - -**Alors** la clé Redis "pub:user:123:hourly:2026012110" est créée -**Et** le TTL est de 1 heure (expire à 11h05) -**Et** le système compte les pubs dans la fenêtre glissante d'1h - ---- - -## 22. Reset compteur quotidien à minuit - -**Étant donné** qu'un utilisateur a entendu la pub "A" 3 fois le 20/01 - -**Quand** minuit passe et on est le 21/01 - -**Alors** le compteur "pub:A:user:123:count" est expiré (TTL 24h) -**Et** l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois - ---- - -## 23. Aucune pub si aucune campagne éligible - -**Étant donné** qu'aucune campagne n'a de budget disponible - -**Quand** le système devrait insérer une publicité - -**Alors** aucune pub n'est insérée -**Et** l'enchaînement de contenus continue normalement -**Et** le prochain contenu démarre directement - ---- - -## 24. Priorisation campagnes avec budget important restant - -**Étant donné** que 2 campagnes sont éligibles: - - | Campagne | Budget restant | Jours restants | - |---|---|---| - | A | 500€ | 2j | - | B | 50€ | 10j | - - -**Quand** le système applique la priorisation budgétaire - -**Alors** la campagne A est légèrement favorisée (urgence dépense) -**Et** cela aide à épuiser les budgets avant fin de campagne - ---- - -## 25. Log des sélections pour analytics - -**Étant donné** qu'une publicité "RestaurantX" est sélectionnée - -**Quand** elle est diffusée à l'utilisateur "123" - -**Alors** un événement est loggé en base: - - | Champ | Valeur | - |---|---| - | pub_id | RestaurantX | - | user_id | 123 | - | timestamp | 2026-01-21 10:30 | - | zone_geo | Marseille | - | score_geo | 0.85 | - | score_interet | 0.70 | - -**Et** cela permet l'analytics publicitaire - ---- - -## 26. Détection changement statut utilisateur (gratuit → premium) - -**Étant donné** que je suis un utilisateur gratuit -**Et** que j'entends des publicités - -**Quand** je souscris à Premium - -**Alors** le système détecte le changement de statut immédiatement -**Et** plus aucune publicité n'est insérée dès le prochain contenu -**Et** mon expérience devient sans pub instantanément - ---- - -## 27. Interface admin pour ajuster fréquence globale - -**Étant donné** que je suis admin RoadWave - -**Quand** j'accède aux paramètres publicitaires - -**Alors** je peux ajuster le curseur de fréquence: - - | Option | Fréquence | - |---|---| - | 1/3 | Haute (agressif) | - | 1/5 | Standard (défaut) | - | 1/7 | Modérée | - | 1/10 | Faible | - -**Et** le changement s'applique en temps réel à tous les utilisateurs - ---- - -## 28. A/B testing fréquence sur cohortes utilisateurs - -**Étant donné** que l'admin active un test A/B - -**Quand** 50% des utilisateurs ont fréquence 1/5 -**Et** 50% des utilisateurs ont fréquence 1/7 - -**Alors** les métriques sont trackées séparément: - - | Cohorte | Fréquence | Taux désabonnement | Revenus/user | - |---|---|---|---| - | A | 1/5 | 2.5% | 0.50€ | - | B | 1/7 | 1.8% | 0.40€ | - -**Et** l'admin peut identifier la fréquence optimale - ---- - -## 29. 📋 Plan: Insertion publicité selon fréquence - -**Étant donné** que la fréquence est - -**Quand** j'écoute contenus - -**Alors** publicités sont insérées - -**📊 Exemples de données:** - -| frequence | contenus | pubs | -|---|---|---| -| 1/3 | 9 | 3 | -| 1/5 | 10 | 2 | -| 1/5 | 25 | 5 | -| 1/7 | 14 | 2 | -| 1/10 | 30 | 3 | - ---- - -## 30. 📋 Plan: Priorité géographique selon type zone - -**Étant donné** qu'une publicité cible - -**Quand** le système calcule le score géographique - -**Alors** la priorité est - -**📊 Exemples de données:** - -| type_zone | score | -|---|---| -| Point GPS | 1.0 | -| Ville | 0.8 | -| Département | 0.6 | -| Région | 0.4 | -| National | 0.2 | - ---- - -## 31. 📋 Plan: Exclusion publicité selon compteur quotidien - -**Étant donné** qu'une publicité a été entendue fois aujourd'hui - -**Quand** le système vérifie l'éligibilité - -**Alors** la publicité est - -**📊 Exemples de données:** - -| fois | eligible | -|---|---| -| 0 | éligible | -| 1 | éligible | -| 2 | éligible | -| 3 | non éligible | -| 4 | non éligible | - ---- - - - - - -
- - -# Métriques d'engagement et dashboard publicitaire -> *En tant que publicitaire* -> *Je veux consulter des métriques détaillées en temps réel* -> *Afin d'optimiser mes campagnes et mesurer leur ROI* - -**27 scénarios** (24 standards, 3 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un compte publicitaire est connecté -> **Et** qu'une campagne active est en cours -## 1. Dashboard temps réel avec métriques essentielles - -**Étant donné** que ma campagne a généré 1000 diffusions - -**Quand** je consulte le dashboard - -**Alors** je vois les métriques suivantes mises à jour en temps réel: - - | Métrique | Valeur | - |---|---| - | Impressions | 1000 | - | Écoutes complètes (>80%) | 400 | - | Taux d'écoute complète | 40% | - | Taux de skip | 60% | - | Durée moyenne d'écoute | 18s | - | Likes | 25 | - | Abonnements | 5 | - | Coût par écoute | 0.05€ | - - ---- - -## 2. Calcul impressions totales - -**Étant donné** que ma publicité a été diffusée 2500 fois - -**Quand** je consulte le dashboard - -**Alors** le compteur "Impressions" affiche 2500 -**Et** il s'incrémente en temps réel à chaque nouvelle diffusion - ---- - -## 3. Calcul écoutes complètes (>80%) - -**Étant donné** que ma publicité de 30s a été: - - | Durée écoutée | Nombre | - |---|---| - | 25s (83%) | 300 | - | 20s (67%) | 200 | - | 10s (33%) | 150 | - | 5s (17%) | 50 | - - -**Quand** je consulte les écoutes complètes - -**Alors** le compteur affiche 300 (uniquement ≥80%) -**Et** le taux d'écoute complète est de 43% (300/700) - ---- - -## 4. Calcul taux de skip - -**Étant donné** 1000 diffusions totales -**Et** 400 écoutes complètes - -**Quand** je consulte le taux de skip - -**Alors** il affiche 60% ((1000-400)/1000) -**Et** il est calculé comme (total - complètes) / total - ---- - -## 5. Durée moyenne d'écoute calculée - -**Étant donné** que ma publicité de 30s a été écoutée: - - | Durée | Nombre d'utilisateurs | - |---|---| - | 30s | 400 | - | 20s | 300 | - | 10s | 200 | - | 5s | 100 | - - -**Quand** je consulte la durée moyenne - -**Alors** le calcul est: (30×400 + 20×300 + 10×200 + 5×100) / 1000 -**Et** le résultat affiché est 21s - ---- - -## 6. Métriques de likes sur publicité - -**Étant donné** que 50 utilisateurs ont liké ma publicité - -**Quand** je consulte le dashboard - -**Alors** le compteur "Likes" affiche 50 -**Et** un taux de like de 5% est calculé (50/1000 impressions) -**Et** cela indique une forte appréciation du contenu - ---- - -## 7. Métriques d'abonnements générés - -**Étant donné** que 10 utilisateurs se sont abonnés après avoir entendu ma pub - -**Quand** je consulte le dashboard - -**Alors** le compteur "Abonnements" affiche 10 -**Et** un taux de conversion de 1% est calculé (10/1000) -**Et** cela représente un engagement très fort - ---- - -## 8. Calcul coût par écoute (CPE) - -**Étant donné** que j'ai dépensé 200€ -**Et** obtenu 4000 écoutes complètes - -**Quand** je consulte le coût par écoute - -**Alors** le CPE affiché est 0.05€ (200/4000) -**Et** il correspond au tarif standard RoadWave - ---- - -## 9. Répartition géographique avec heatmap - -**Étant donné** que ma campagne cible le département du Var -**Et** que j'ai 1000 diffusions réparties: - - | Zone | Diffusions | Pourcentage | - |---|---|---| - | Toulon | 400 | 40% | - | Hyères | 250 | 25% | - | Fréjus | 200 | 20% | - | Autres | 150 | 15% | - - -**Quand** je consulte la heatmap géographique - -**Alors** une carte Leaflet affiche les zones avec intensité proportionnelle -**Et** Toulon apparaît en rouge foncé (forte concentration) -**Et** les autres villes en dégradé orange/jaune - ---- - -## 10. Répartition horaire avec graphique - -**Étant donné** que ma campagne cible les plages 7h-9h et 17h-19h -**Et** que j'ai 1000 diffusions: - - | Plage horaire | Diffusions | - |---|---| - | 7h-8h | 300 | - | 8h-9h | 250 | - | 17h-18h | 280 | - | 18h-19h | 170 | - - -**Quand** je consulte le graphique horaire - -**Alors** un histogramme Chart.js affiche les 4 barres -**Et** je peux identifier que 7h-8h est le pic d'écoute -**Et** optimiser mes futures campagnes sur cette plage - ---- - -## 11. Taux de complétion par tranche d'âge - -**Étant donné** que ma campagne est Tout Public -**Et** que j'ai des écoutes sur différentes tranches: - - | Tranche d'âge | Écoutes complètes | Total diffusions | Taux | - |---|---|---|---| - | 18-24 ans | 120 | 400 | 30% | - | 25-34 ans | 200 | 400 | 50% | - | 35-44 ans | 80 | 200 | 40% | - - -**Quand** je consulte l'analyse par âge - -**Alors** je vois que les 25-34 ans ont le meilleur taux (50%) -**Et** je peux cibler cette tranche pour mes prochaines campagnes - ---- - -## 12. Comparatif de campagnes A/B testing - -**Étant donné** que j'ai 2 campagnes actives: - - | Campagne | Budget | Écoutes complètes | Taux | CPE | - |---|---|---|---|---| - | A | 300€ | 4000 | 40% | 0.075€ | - | B | 300€ | 6000 | 60% | 0.05€ | - - -**Quand** je consulte le comparatif - -**Alors** je vois que la campagne B performe mieux -**Et** le tableau recommande "Campagne B: +50% écoutes, -33% CPE" -**Et** je peux allouer plus de budget à la campagne B - ---- - -## 13. Export données CSV pour analyse externe - -**Étant donné** que je veux analyser mes données dans Excel - -**Quand** je clique sur "Exporter CSV" - -**Alors** je télécharge un fichier avec les colonnes: - - | Colonne | - |---| - | Date | - | Heure | - | Zone géographique | - | Tranche d'âge | - | Durée écoute | - | Skip (Oui/Non) | - | Like (Oui/Non) | - | Abonnement (Oui/Non) | - -**Et** je peux faire des analyses personnalisées - ---- - -## 14. Export graphiques interactifs - -**Étant donné** que je consulte le dashboard - -**Quand** je clique sur un graphique Chart.js - -**Alors** je peux zoomer/filtrer interactivement -**Et** je peux exporter le graphique en PNG -**Et** l'image est en haute résolution pour présentations - ---- - -## 15. Rapport PDF automatique fin de campagne - -**Étant donné** que ma campagne de 14 jours se termine - -**Quand** la date de fin est atteinte - -**Alors** un rapport PDF est généré automatiquement -**Et** il contient: - - | Section | - |---| - | Résumé exécutif | - | Métriques clés | - | Graphiques de performance | - | Heatmap géographique | - | Répartition horaire | - | Analyse tranches d'âge | - | Recommandations optimisation | - -**Et** je reçois un email avec le PDF en pièce jointe - ---- - -## 16. Métriques temps réel rafraîchies automatiquement - -**Étant donné** que je consulte le dashboard à 10h00 - -**Quand** une nouvelle diffusion se produit à 10h01 - -**Alors** les métriques sont rafraîchies automatiquement (polling 30s) -**Et** je vois les nouveaux chiffres sans recharger la page -**Et** un badge "Mis à jour il y a 15s" s'affiche - ---- - -## 17. Alertes performance personnalisées - -**Étant donné** que je configure une alerte "Taux de skip >70%" -**Et** que ma campagne atteint 72% de skip - -**Quand** le seuil est dépassé - -**Alors** je reçois un email d'alerte: - ---- - -## 18. Benchmark vs moyennes RoadWave - -**Étant donné** que ma campagne a 45% d'écoutes complètes - -**Quand** je consulte le benchmark - -**Alors** je vois "Votre taux: 45% | Moyenne RoadWave: 40%" -**Et** un badge "📊 Performance: +12% vs moyenne" s'affiche -**Et** je sais que ma campagne performe au-dessus de la moyenne - ---- - -## 19. Coût total consommé vs budget - -**Étant donné** que j'ai un budget de 300€ -**Et** que j'ai consommé 220€ - -**Quand** je consulte le dashboard - -**Alors** je vois une jauge "Budget consommé: 73%" (220/300) -**Et** le montant restant "80€ restants" -**Et** une projection "Épuisé dans 3 jours à ce rythme" - ---- - -## 20. Répartition coûts par type d'écoute - -**Étant donné** que j'ai dépensé 200€ avec: - - | Type d'écoute | Nombre | Coût unitaire | Total | - |---|---|---|---| - | Écoute complète | 3000 | 0.05€ | 150€ | - | Skip après 5s | 2000 | 0.02€ | 40€ | - | Skip immédiat | 500 | 0€ | 0€ | - - -**Quand** je consulte la répartition - -**Alors** un graphique camembert affiche: - - | Segment | Pourcentage | - |---|---| - | Écoutes complètes | 75% (150€) | - | Skips partiels | 20% (40€) | - | Skips immédiats | 5% (0€) | - - ---- - -## 21. Évolution performance dans le temps - -**Étant donné** une campagne de 30 jours - -**Quand** je consulte le graphique d'évolution - -**Alors** je vois une courbe Chart.js avec: - - | Axe | Donnée | - |---|---| - | X | Jours (1-30) | - | Y | Taux d'écoute complète (%) | - -**Et** je peux identifier les tendances (amélioration/dégradation) -**Et** les jours avec pics d'engagement - ---- - -## 22. Métriques avancées - Taux de réécoute - -**Étant donné** qu'un utilisateur a entendu ma pub 3 fois -**Et** qu'il l'a écoutée complètement les 3 fois - -**Quand** je consulte les métriques avancées - -**Alors** le "Taux de réécoute" affiche 100% -**Et** cela indique que le contenu n'est pas perçu comme spam -**Et** les utilisateurs tolèrent bien la répétition - ---- - -## 23. Recommandations automatiques d'optimisation - -**Étant donné** que ma campagne a un taux de skip de 75% -**Et** que la durée moyenne d'écoute est de 8s sur 30s - -**Quand** je consulte les recommandations - -**Alors** le système suggère: - ---- - -## 24. Suivi multi-campagnes avec vue consolidée - -**Étant donné** que j'ai 3 campagnes actives simultanément - -**Quand** je consulte la vue consolidée - -**Alors** je vois un tableau récapitulatif: - - | Campagne | Budget | Dépensé | Diffusions | Taux complète | CPE | - |---|---|---|---|---|---| - | A | 300€ | 220€ | 4000 | 40% | 0.05€ | - | B | 500€ | 150€ | 3000 | 60% | 0.05€ | - | C | 200€ | 180€ | 3600 | 35% | 0.05€ | - -**Et** je peux comparer les performances d'un coup d'œil - ---- - -## 25. 📋 Plan: Calcul taux d'écoute complète - -**Étant donné** diffusions totales -**Et** écoutes complètes (≥80%) - -**Quand** je calcule le taux - -**Alors** le résultat est % - -**📊 Exemples de données:** - -| total | completes | taux | -|---|---|---| -| 1000 | 400 | 40 | -| 2000 | 1200 | 60 | -| 500 | 100 | 20 | -| 1000 | 850 | 85 | - ---- - -## 26. 📋 Plan: Calcul coût par écoute (CPE) - -**Étant donné** un budget dépensé de € -**Et** écoutes complètes - -**Quand** je calcule le CPE - -**Alors** le résultat est € - -**📊 Exemples de données:** - -| depense | ecoutes | cpe | -|---|---|---| -| 100 | 2000 | 0.05 | -| 300 | 6000 | 0.05 | -| 50 | 1000 | 0.05 | -| 500 | 10000 | 0.05 | - ---- - -## 27. 📋 Plan: Classification performance vs benchmark - -**Étant donné** un taux d'écoute complète de % -**Et** une moyenne RoadWave de 40% - -**Quand** je compare à la moyenne - -**Alors** la performance est - -**📊 Exemples de données:** - -| taux | classification | -|---|---| -| 60 | Excellente (+50%) | -| 50 | Bonne (+25%) | -| 40 | Moyenne | -| 30 | Faible (-25%) | -| 20 | Très faible (-50%) | - ---- - - - - - -
- - -# Validation et modération des publicités -> *En tant que modérateur RoadWave* -> *Je veux valider manuellement toutes les publicités avant diffusion* -> *Afin de garantir la qualité et la légalité des contenus publicitaires* - -**29 scénarios** (27 standards, 2 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** qu'un modérateur RoadWave est connecté -## 1. Validation manuelle obligatoire avant diffusion - -**Étant donné** qu'un publicitaire a créé une campagne -**Et** que le paiement de 300€ a été effectué - -**Quand** la campagne est soumise - -**Alors** elle passe en statut "En attente de validation" -**Et** elle est ajoutée à la file d'attente des modérateurs -**Et** la diffusion ne démarre PAS avant validation manuelle -**Et** le publicitaire reçoit un email "Votre campagne est en cours de validation (24-48h)" - ---- - -## 2. Délai de validation 24-48h ouvrées - -**Étant donné** qu'une campagne est soumise le lundi 10h - -**Quand** le modérateur la valide le mardi 15h - -**Alors** le délai est de 29h (dans les 48h ouvrées) -**Et** le publicitaire reçoit une notification "Votre campagne est approuvée" - ---- - -## 3. Validation dépassant 48h avec notification - -**Étant donné** qu'une campagne est soumise le lundi 10h - -**Quand** 48h ouvrées se sont écoulées -**Et** que la campagne n'est toujours pas validée - -**Alors** le publicitaire reçoit un email automatique: -**Et** un modérateur senior est assigné automatiquement - ---- - -## 4. Acceptation de campagne publicitaire - -**Étant donné** qu'une campagne est en attente de validation -**Et** que l'audio respecte toutes les règles - -**Quand** le modérateur clique sur "Approuver" - -**Alors** le statut passe à "Approuvée" -**Et** la campagne démarre à la date programmée -**Et** le publicitaire reçoit un email de confirmation -**Et** le budget commence à être consommé dès le début - ---- - -## 5. Refus de campagne avec motif détaillé - -**Étant donné** qu'une campagne contient du contenu alcool - -**Quand** le modérateur clique sur "Refuser" -**Et** qu'il sélectionne le motif "Contenu interdit: Alcool" -**Et** qu'il ajoute le commentaire "La publicité pour l'alcool est interdite en France" - -**Alors** le statut passe à "Refusée" -**Et** le publicitaire reçoit un email détaillé avec: - - | Champ | Valeur | - |---|---| - | Motif | Contenu interdit: Alcool | - | Commentaire | La publicité pour l'alcool est interdite en France | - | Action requise | Modifier votre contenu et soumettre à nouveau | - -**Et** un remboursement automatique de 300€ est déclenché - ---- - -## 6. Remboursement automatique après refus - -**Étant donné** qu'une campagne à 500€ est refusée - -**Quand** le statut passe à "Refusée" - -**Alors** un remboursement Mangopay de 500€ est initié automatiquement -**Et** le délai de remboursement est de 5-7 jours ouvrés -**Et** le publicitaire reçoit un email "Remboursement en cours" - ---- - -## 7. Contenus interdits - Alcool - -**Étant donné** qu'une publicité mentionne "Whisky premium 40°" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Alcool" - ---- - -## 8. Contenus interdits - Tabac - -**Étant donné** qu'une publicité mentionne "Cigarettes électroniques" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Tabac/Vape" - ---- - -## 9. Contenus interdits - Jeux d'argent - -**Étant donné** qu'une publicité mentionne "Gagnez 10 000€ - Paris sportifs" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Jeux d'argent" - ---- - -## 10. Contenus interdits - Politique pendant campagne électorale - -**Étant donné** qu'une publicité politique est soumise -**Et** que nous sommes en période de campagne électorale officielle - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Publicité politique (période électorale)" - ---- - -## 11. Contenus interdits - Contenu sexuel - -**Étant donné** qu'une publicité contient des propos sexuellement explicites - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Contenu sexuel" - ---- - -## 12. Contenus interdits - Violence - -**Étant donné** qu'une publicité contient des descriptions violentes - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser la campagne -**Et** sélectionner le motif "Contenu interdit: Violence" - ---- - -## 13. Contenu légal autorisé - Commerce local - -**Étant donné** qu'une publicité pour un restaurant local dit "Découvrez notre menu du jour" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit approuver la campagne - ---- - -## 14. Contenu légal autorisé - Service professionnel - -**Étant donné** qu'une publicité pour un garage dit "Révision complète à partir de 99€" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit approuver la campagne - ---- - -## 15. Critères de validation - Qualité audio - -**Étant donné** qu'une publicité a une qualité audio très basse (bruits, saturation) - -**Quand** le modérateur écoute l'audio - -**Alors** il peut refuser avec le motif "Qualité audio insuffisante" -**Et** recommander "Veuillez soumettre un fichier audio de meilleure qualité" - ---- - -## 16. Critères de validation - Classification d'âge correcte - -**Étant donné** qu'une publicité contient du langage familier -**Et** qu'elle est classée "Tout public" - -**Quand** le modérateur écoute l'audio - -**Alors** il peut refuser avec le motif "Classification d'âge incorrecte" -**Et** recommander "Reclasser en 13+ minimum" - ---- - -## 17. Critères de validation - Respect réglementation française - -**Étant donné** qu'une publicité fait des promesses mensongères "Perdez 10kg en 1 semaine" - -**Quand** le modérateur écoute l'audio - -**Alors** il doit refuser avec le motif "Non-conformité réglementaire: Publicité mensongère" - ---- - -## 18. File d'attente modération priorisée - -**Étant donné** que 10 campagnes sont en attente de validation -**Et** que la campagne A a été soumise il y a 40h -**Et** que la campagne B a été soumise il y a 2h - -**Quand** le modérateur consulte sa file - -**Alors** la campagne A apparaît en premier (priorité temporelle) -**Et** un badge "Urgente - >40h" est affiché - ---- - -## 19. Dashboard modération - Vue d'ensemble - -**Étant donné** que je suis modérateur - -**Quand** j'accède au dashboard modération publicités - -**Alors** je vois: - - | Métrique | Exemple valeur | - |---|---| - | Campagnes en attente | 5 | - | Délai moyen de validation | 28h | - | Campagnes validées aujourd'hui | 12 | - | Campagnes refusées aujourd'hui | 3 | - | Taux d'acceptation | 80% | - - ---- - -## 20. Transcription automatique pour aide modération - -**Étant donné** qu'une publicité audio est soumise - -**Quand** le système traite l'audio - -**Alors** une transcription automatique est générée via Whisper -**Et** elle est affichée au modérateur pour faciliter la revue -**Et** elle permet une recherche par mots-clés (alcool, tabac, etc.) - ---- - -## 21. Détection automatique mots-clés interdits - -**Étant donné** qu'une publicité audio est soumise - -**Quand** la transcription contient "whisky" ou "vodka" - -**Alors** un flag automatique "⚠️ Alcool détecté" est ajouté -**Et** la campagne est priorisée pour validation manuelle rapide -**Et** le modérateur est alerté du contenu potentiellement interdit - ---- - -## 22. Historique modération publicitaire - -**Étant donné** qu'un publicitaire a eu 2 campagnes refusées - -**Quand** il soumet une 3ème campagne - -**Alors** le modérateur voit l'historique: - - | Date | Statut | Motif | - |---|---|---| - | 2026-01-15 | Refusée | Contenu interdit: Alcool | - | 2026-01-20 | Refusée | Qualité audio faible | - -**Et** il peut en tenir compte dans sa décision - ---- - -## 23. Appel possible après refus - -**Étant donné** que ma campagne a été refusée pour "Classification incorrecte" - -**Quand** je conteste la décision via le formulaire d'appel - -**Alors** un modérateur senior revoit la campagne -**Et** il peut approuver si la classification est en fait correcte -**Et** le délai de réponse est de 48-72h - ---- - -## 24. Notification temps réel pour modérateurs - -**Étant donné** que je suis modérateur connecté - -**Quand** une nouvelle campagne est soumise - -**Alors** je reçois une notification in-app -**Et** le compteur "Campagnes en attente" s'incrémente en temps réel -**Et** je peux cliquer pour consulter immédiatement - ---- - -## 25. Statistiques conformité par catégorie - -**Étant donné** que je suis admin modération - -**Quand** je consulte les statistiques mensuelles - -**Alors** je vois les motifs de refus: - - | Motif | Nombre | Pourcentage | - |---|---|---| - | Alcool | 15 | 30% | - | Qualité audio | 12 | 24% | - | Classification erronée | 10 | 20% | - | Publicité mensongère | 8 | 16% | - | Autres | 5 | 10% | - - ---- - -## 26. Export rapport modération - -**Étant donné** que je suis modérateur senior - -**Quand** j'exporte le rapport mensuel - -**Alors** je reçois un fichier CSV avec: - - | Colonne | - |---| - | Campagne ID | - | Publicitaire | - | Date soumission | - | Date décision | - | Statut | - | Motif (si refus) | - | Modérateur | - -**Et** je peux l'analyser dans Excel - ---- - -## 27. Validation partielle avec demande modification - -**Étant donné** qu'une campagne a un contenu acceptable -**Mais** que la classification d'âge est incorrecte - -**Quand** le modérateur clique sur "Demander modification" - -**Alors** le publicitaire reçoit un email: -**Et** le statut devient "Modification requise" -**Et** le publicitaire peut modifier sans repayer - ---- - -## 28. 📋 Plan: Contenus interdits automatiquement détectés - -**Étant donné** qu'une publicité contient le mot - -**Quand** la transcription automatique est analysée - -**Alors** un flag est ajouté -**Et** le motif de refus suggéré est - -**📊 Exemples de données:** - -| mot_cle | flag | motif | -|---|---|---| -| whisky | ⚠️ Alcool | Contenu interdit: Alcool | -| vodka | ⚠️ Alcool | Contenu interdit: Alcool | -| cigarette | ⚠️ Tabac | Contenu interdit: Tabac | -| casino | ⚠️ Jeux argent | Contenu interdit: Jeux | -| paris sportifs | ⚠️ Jeux argent | Contenu interdit: Jeux | - ---- - -## 29. 📋 Plan: Délais de validation selon soumission - -**Étant donné** qu'une campagne est soumise à - -**Quand** elle est validée heures plus tard - -**Alors** le statut est - -**📊 Exemples de données:** - -| jour | heure | delai | conformite | -|---|---|---|---| -| Lundi | 10h | 24 | Dans les délais (24h) | -| Lundi | 10h | 48 | Dans les délais (48h) | -| Lundi | 10h | 50 | Hors délais (>48h) | -| Vendredi | 16h | 72 | Dans les délais (we) | - ---- - - - - - -
- - -# Architecture technique radio live -> *En tant que système* -> *Je veux gérer efficacement les flux audio en temps réel* -> *Afin d'assurer une diffusion stable et scalable des lives* - -**24 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'infrastructure RoadWave est opérationnelle -> **Et** que les serveurs Go avec Pion WebRTC sont actifs -## 1. Ingestion WebRTC du flux créateur - -**Étant donné** qu'un créateur démarre un live depuis son application mobile - -**Quand** le flux audio WebRTC (Opus 48 kbps) arrive sur le serveur - -**Alors** le serveur Go avec Pion WebRTC accepte la connexion -**Et** le flux est traité en temps réel - ---- - -## 2. Conversion temps réel Opus vers segments HLS - -**Étant donné** qu'un flux WebRTC Opus est reçu par le serveur - -**Quand** le serveur traite le flux - -**Alors** FFmpeg convertit en segments HLS (.ts) -**Et** un fichier manifest .m3u8 est généré et mis à jour régulièrement -**Et** les segments ont une durée de 2 secondes chacun - ---- - -## 3. Distribution via Bunny CDN - -**Étant donné** que les segments HLS sont générés - -**Quand** un auditeur demande à rejoindre le live - -**Alors** le manifest .m3u8 est servi via Bunny CDN -**Et** les segments .ts sont cachés sur le CDN -**Et** la distribution est globale avec latence minimale - ---- - -## 4. Lecture HLS native sur mobile iOS - -**Étant donné** qu'un auditeur iOS rejoint un live - -**Quand** l'application charge le flux HLS - -**Alors** le player natif AVPlayer gère la lecture -**Et** le buffer de 15 secondes est appliqué automatiquement -**Et** la qualité s'adapte selon la connexion - ---- - -## 5. Lecture HLS native sur mobile Android - -**Étant donné** qu'un auditeur Android rejoint un live - -**Quand** l'application charge le flux HLS - -**Alors** le player natif ExoPlayer gère la lecture -**Et** le buffer de 15 secondes est configuré -**Et** la qualité s'adapte selon la connexion - ---- - -## 6. Enregistrement parallèle du flux pour replay - -**Étant donné** qu'un live est en cours - -**Alors** un processus parallèle enregistre le flux Opus raw -**Et** l'enregistrement est stocké temporairement sur le serveur -**Et** l'enregistrement est indépendant de la diffusion HLS - ---- - -## 7. Traitement post-live asynchrone - -**Étant donné** qu'un live vient de se terminer - -**Quand** le processus post-live démarre - -**Alors** un job asynchrone est créé dans la queue Redis -**Et** un worker Go récupère le job -**Et** le worker exécute FFmpeg pour les conversions - ---- - -## 8. Conversion Opus raw vers MP3 256 kbps - -**Étant donné** qu'un worker traite un job post-live - -**Quand** la conversion démarre - -**Alors** FFmpeg convertit Opus raw en MP3 256 kbps -**Et** la normalisation audio à -14 LUFS est appliquée -**Et** les silences prolongés (>3 secondes) sont détectés et nettoyés - ---- - -## 9. Génération segments HLS pour le replay - -**Étant donné** que le MP3 256 kbps est généré - -**Quand** le worker crée les segments HLS - -**Alors** des segments .ts de 10 secondes sont créés -**Et** un manifest .m3u8 est généré -**Et** les segments sont uploadés vers le stockage Bunny - ---- - -## 10. Publication automatique du replay - -**Étant donné** que tous les segments HLS sont uploadés - -**Quand** le worker finalise le job - -**Alors** une entrée de contenu "replay" est créée en base PostgreSQL -**Et** le titre est "[REPLAY] [Titre live original]" -**Et** le type géographique est "Géo-neutre" -**Et** le replay est immédiatement disponible pour les auditeurs - ---- - -## 11. Suppression automatique fichier Opus raw après 7 jours - -**Étant donné** qu'un replay est publié depuis 7 jours - -**Quand** le job de nettoyage quotidien s'exécute - -**Alors** le fichier Opus raw est supprimé du stockage -**Et** seul le MP3 256 kbps et les segments HLS sont conservés -**Et** l'espace de stockage est libéré - ---- - -## 12. Scalabilité horizontale des workers de conversion - -**Étant donné** que 50 lives se terminent simultanément - -**Quand** les jobs post-live sont créés - -**Alors** les workers Go disponibles traitent les jobs en parallèle -**Et** si tous les workers sont occupés, les jobs attendent en queue Redis -**Et** de nouveaux workers peuvent être lancés automatiquement (Kubernetes) - ---- - -## 13. Limitation du nombre de lives simultanés (MVP) - -**Étant donné** que l'infrastructure MVP est configurée pour 100 lives simultanés -**Et** que 100 lives sont actuellement en cours - -**Quand** un nouveau créateur essaie de démarrer un live - -**Alors** la demande est refusée avec le code erreur 503 -**Et** le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" est retourné -**Et** la demande peut être mise en queue prioritaire si créateur Premium - ---- - -## 14. Monitoring des ressources serveur en temps réel - -**Étant donné** que plusieurs lives sont en cours - -**Alors** le système monitore en temps réel: - - | métrique | seuil alerte | - |---|---| - | CPU utilisation | >80% | - | Mémoire utilisation | >85% | - | Bande passante upload | >80% capacité | - | Nombre connexions WebRTC | >90 | - | Latence moyenne CDN | >200ms | - -**Et** si un seuil est dépassé, une alerte est envoyée à l'équipe technique - ---- - -## 15. Calcul du coût de bande passante CDN - -**Étant donné** qu'un live a 100 auditeurs simultanés -**Et** que la qualité est 48 kbps Opus - -**Quand** le live dure 1 heure - -**Alors** la bande passante totale est d'environ 2.16 GB -**Et** le coût Bunny CDN est d'environ 0.02€ (tarif ~0.01€/GB) -**Et** ces métriques sont enregistrées pour facturation créateur si nécessaire - ---- - -## 16. Cache CDN des segments HLS - -**Étant donné** qu'un live est diffusé via Bunny CDN - -**Quand** un segment .ts est généré - -**Alors** le segment est uploadé vers Bunny origin -**Et** Bunny CDN cache le segment sur ses edge servers -**Et** les auditeurs suivants récupèrent le segment depuis le cache -**Et** la charge sur le serveur origin est réduite de ~90% - ---- - -## 17. Gestion de la latence WebRTC créateur - -**Étant donné** qu'un créateur diffuse avec une connexion 4G - -**Quand** la latence réseau augmente ponctuellement - -**Alors** le buffer côté serveur absorbe les fluctuations -**Et** la qualité peut être réduite temporairement (48 kbps → 32 kbps) -**Et** un warning est affiché au créateur si la connexion est trop instable - ---- - -## 18. Détection automatique de la musique protégée (post-MVP) - -**Étant donné** qu'un live contient de la musique en arrière-plan - -**Quand** le système d'audio fingerprint analyse le flux - -**Alors** une empreinte audio est calculée toutes les 30 secondes -**Et** l'empreinte est comparée à une base de données de contenus protégés -**Et** si une correspondance est trouvée, un warning est envoyé au créateur -**Et** si le créateur ne corrige pas sous 30 secondes, le live peut être arrêté - ---- - -## 19. Stockage des métadonnées de live en PostgreSQL - -**Étant donné** qu'un créateur démarre un live - -**Alors** les métadonnées suivantes sont enregistrées: - - | champ | exemple valeur | - |---|---| - | live_id | uuid v4 | - | creator_id | uuid créateur | - | title | "Mon super live" | - | started_at | timestamp UTC | - | zone_geo | "Île-de-France" | - | tags | ["Actualité", "Tech"] | - | classification_age | "Tout public" | - -**Et** ces données sont indexées pour recherche et analytics - ---- - -## 20. Cache Redis pour compteurs temps réel - -**Étant donné** qu'un live est en cours - -**Alors** Redis stocke les compteurs temps réel: - - | clé Redis | valeur exemple | - |---|---| - | live:[live_id]:listeners | 247 | - | live:[live_id]:likes | 89 | - | live:[live_id]:reports | 0 | - -**Et** ces compteurs sont mis à jour toutes les 2 secondes -**Et** les compteurs sont persistés en PostgreSQL toutes les 60 secondes - ---- - -## 21. Heartbeat auditeurs pour compteur précis - -**Étant donné** qu'un auditeur écoute un live - -**Alors** l'application envoie un heartbeat toutes les 10 secondes -**Et** le heartbeat met à jour le timestamp dans Redis -**Et** si aucun heartbeat n'est reçu pendant 30 secondes, l'auditeur est retiré du compteur - ---- - -## 22. Gestion des pannes serveur pendant un live - -**Étant donné** qu'un live est en cours sur serveur A - -**Quand** le serveur A tombe en panne - -**Alors** Kubernetes redémarre automatiquement un pod -**Mais** le live en cours est perdu (pas de failover temps réel en MVP) -**Et** le créateur voit le message "Connexion perdue. Veuillez redémarrer le live" -**Et** les auditeurs voient "Le live est terminé suite à un problème technique" - ---- - -## 23. Backup automatique des enregistrements live - -**Étant donné** qu'un live est enregistré en Opus raw - -**Quand** l'enregistrement dépasse 10 minutes - -**Alors** un backup incrémental est créé toutes les 10 minutes -**Et** le backup est stocké sur un stockage secondaire (S3-compatible) -**Et** en cas de crash serveur, le live peut être récupéré jusqu'au dernier backup - ---- - -## 24. Logs et audit trail des lives - -**Étant donné** qu'un live démarre, se déroule et se termine - -**Alors** tous les événements sont loggés: - - | événement | détails enregistrés | - |---|---| - | Démarrage live | timestamp, creator_id, zone_geo | - | Auditeur rejoint | timestamp, user_id, position GPS | - | Auditeur quitte | timestamp, user_id, durée écoute | - | Signalement | timestamp, user_id, catégorie | - | Fin live | timestamp, durée totale, stats finales | - -**Et** ces logs sont conservés 90 jours pour analytics et conformité RGPD - ---- - - - - - -
- - -# Arrêt du live -> *En tant que créateur* -> *Je veux arrêter ma diffusion en direct de manière contrôlée* -> *Afin de terminer proprement mon live et générer un replay automatiquement* - -**19 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant que créateur -> **Et** que je diffuse actuellement un live -## 1. Arrêt manuel avec compte à rebours 5 secondes - -**Quand** j'appuie sur le bouton "Arrêter live" - -**Alors** un compte à rebours de 5 secondes démarre -**Et** je vois le message "Ce live se termine dans 5... 4... 3... 2... 1" -**Et** un bouton "Annuler" est affiché pendant le décompte -**Et** l'audio du compte à rebours est diffusé aux auditeurs - ---- - -## 2. Annulation du compte à rebours - -**Étant donné** que j'ai appuyé sur "Arrêter live" -**Et** que le compte à rebours affiche "3 secondes" - -**Quand** j'appuie sur "Annuler" - -**Alors** le compte à rebours s'arrête -**Et** le live continue normalement -**Et** aucune notification n'est envoyée aux auditeurs - ---- - -## 3. Arrêt effectif après compte à rebours - -**Étant donné** que le compte à rebours est à 0 - -**Alors** le live s'arrête -**Et** la diffusion aux auditeurs se termine -**Et** le message "Live terminé" s'affiche -**Et** le processus de traitement post-live démarre automatiquement - ---- - -## 4. Déconnexion créateur courte (moins de 60 secondes) - -**Étant donné** que je diffuse un live - -**Quand** ma connexion est perdue pendant 30 secondes - -**Alors** les auditeurs voient le message "Connexion créateur perdue, reconnexion en cours..." -**Et** le live continue de bufferer -**Et** quand ma connexion revient, le live reprend normalement - ---- - -## 5. Déconnexion créateur longue (60 secondes ou plus) - -**Étant donné** que je diffuse un live - -**Quand** ma connexion est perdue pendant 60 secondes - -**Alors** le live s'arrête automatiquement -**Et** les auditeurs voient le message "Le live est terminé suite à une coupure de connexion" -**Et** le processus de traitement post-live démarre - ---- - -## 6. Enregistrement automatique pendant le live - -**Étant donné** que je diffuse un live - -**Alors** mon flux audio est enregistré en continu -**Et** le format d'enregistrement est Opus raw -**Et** l'enregistrement est stocké temporairement sur le serveur - ---- - -## 7. Génération automatique du replay après arrêt - -**Étant donné** que mon live vient de se terminer -**Et** que l'option "Publier replay automatiquement" est activée (par défaut) - -**Quand** le traitement post-live démarre - -**Alors** un job asynchrone est créé -**Et** le job effectue les opérations suivantes: - - | opération | détail | - |---|---| - | Conversion format | Opus raw → MP3 256 kbps | - | Génération segments HLS | Segments .ts pour streaming | - | Normalisation volume | -14 LUFS | - | Détection silences prolongés | Nettoyage automatique | - - ---- - -## 8. Publication du replay - -**Étant donné** que le traitement post-live est terminé - -**Alors** le replay est publié automatiquement sous 5 à 10 minutes -**Et** le titre est "[REPLAY] [Titre live original]" -**Et** la zone de diffusion est la même que le live -**Et** les tags sont identiques au live -**Et** la classification d'âge est identique -**Et** le type géographique est "Géo-neutre" (contenu pérenne) - ---- - -## 9. Notification de disponibilité du replay aux auditeurs - -**Étant donné** que le replay de mon live est publié - -**Quand** un auditeur qui a écouté le live se reconnecte - -**Alors** il voit une notification in-app "Le replay de [Titre] est disponible" - ---- - -## 10. Option désactivation publication automatique replay - -**Étant donné** que je configure un nouveau live - -**Quand** je désactive l'option "Publier replay automatiquement" -**Et** que je démarre puis arrête le live - -**Alors** le live est enregistré -**Mais** le replay n'est pas publié automatiquement -**Et** je peux décider manuellement de le publier plus tard - ---- - -## 11. Suppression manuelle du replay après publication - -**Étant donné** que mon live a généré un replay publié - -**Quand** j'accède à mes contenus - -**Alors** je vois le replay dans ma liste -**Et** je peux le supprimer comme n'importe quel contenu - -**Quand** je supprime le replay - -**Alors** le fichier source Opus raw est supprimé immédiatement - ---- - -## 12. Conservation fichier source Opus raw - -**Étant donné** que mon live est terminé -**Et** que le replay est publié - -**Alors** le fichier Opus raw est conservé pendant 7 jours -**Et** après 7 jours, le fichier raw est supprimé automatiquement -**Et** seul le MP3 256 kbps est conservé - ---- - -## 13. Modification du replay interdite - -**Étant donné** que mon live a généré un replay publié - -**Quand** j'essaie de modifier l'audio du replay - -**Alors** l'action est refusée -**Et** je vois le message "Les replays ne peuvent pas être modifiés pour garantir l'intégrité de l'enregistrement" -**Et** je peux uniquement modifier les métadonnées (titre, description) - ---- - -## 14. Statistiques du live disponibles après arrêt - -**Étant donné** que mon live est terminé - -**Quand** j'accède aux statistiques - -**Alors** je vois: - - | métrique | exemple valeur | - |---|---| - | Durée totale | 1h 23min | - | Nombre d'auditeurs max | 247 | - | Nombre d'auditeurs moyen | 183 | - | Nombre de likes | 89 | - | Nombre d'abonnements | 12 | - | Signalements reçus | 0 | - - ---- - -## 15. Live terminé avec signalements en cours - -**Étant donné** que mon live a reçu 3 signalements pendant la diffusion - -**Quand** le live se termine - -**Alors** le replay n'est pas publié automatiquement -**Et** le contenu est en attente de modération -**Et** je vois le message "Votre replay sera publié après vérification suite aux signalements reçus" -**Et** un modérateur doit valider ou refuser le replay sous 24h - ---- - -## 16. Arrêt forcé par un modérateur - -**Étant donné** que je diffuse un live -**Et** qu'un modérateur détecte du contenu interdit - -**Quand** le modérateur clique sur "Arrêter le live immédiatement" - -**Alors** le live s'arrête sans compte à rebours -**Et** je vois le message "Votre live a été interrompu par la modération" -**Et** je reçois une notification détaillant la raison -**Et** le replay n'est pas publié -**Et** le fichier source est conservé 30 jours pour appel - ---- - -## 17. Métriques de bande passante pendant le live - -**Étant donné** que je diffuse un live -**Et** que 100 auditeurs écoutent simultanément - -**Alors** la bande passante consommée est d'environ 4.8 Mbps via CDN -**Et** le coût estimé Bunny CDN est d'environ 0.02€ par heure de diffusion -**Et** je peux voir ces métriques en temps réel dans l'interface créateur - ---- - -## 18. Live sans auditeurs pendant 5 minutes - -**Étant donné** que je diffuse un live -**Et** qu'aucun auditeur n'écoute depuis 5 minutes - -**Alors** je vois un message d'information "Aucun auditeur actuellement connecté" -**Mais** le live continue normalement -**Et** je peux choisir de continuer ou d'arrêter - ---- - -## 19. Qualité audio du replay supérieure au live - -**Étant donné** que mon live était diffusé en Opus 48 kbps - -**Quand** le replay est généré - -**Alors** le replay est encodé en MP3 256 kbps -**Et** la qualité audio du replay est supérieure au live -**Et** la taille du fichier est optimisée pour le stockage long terme - ---- - - - - - -
- - -# Comportement auditeur pendant un live -> *En tant qu'auditeur* -> *Je veux écouter des lives de manière stable* -> *Afin de profiter du contenu en temps réel sans coupures* - -**27 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant qu'auditeur -> **Et** qu'un créateur diffuse actuellement un live -## 1. Rejoindre un live avec buffer de synchronisation 15 secondes - -**Quand** je clique sur "Rejoindre le live" - -**Alors** la connexion au flux HLS s'établit -**Et** je commence à écouter avec un décalage de 15 secondes par rapport au créateur -**Et** le buffer de 15 secondes garantit une lecture stable - ---- - -## 2. Justification du buffer 15 secondes - -**Étant donné** les alternatives de buffer possibles: - - | buffer | stabilité 3G | stabilité 4G | décalage perceptible | décision | - |---|---|---|---|---| - | 5s | Faible | Moyenne | Non | ❌ | - | 10s | Moyenne | Bonne | Non | ❌ | - | 15s | Bonne | Excellente | Léger acceptable | ✅ | - | 20s+ | Excellente | Excellente | Oui | ❌ | - - -**Alors** le buffer optimal est 15 secondes - ---- - -## 3. Lecture stable sur réseau 3G - -**Étant donné** que je suis sur réseau 3G -**Et** que j'écoute un live - -**Quand** des micro-coupures réseau surviennent - -**Alors** le buffer de 15 secondes absorbe les coupures -**Et** la lecture continue sans interruption perceptible - ---- - -## 4. Lecture stable sur réseau 4G - -**Étant donné** que je suis sur réseau 4G -**Et** que j'écoute un live - -**Alors** la lecture est fluide -**Et** le buffer de 15 secondes prévient les coupures lors de changement de cellule - ---- - -## 5. Continuation du live en sortant de la zone géographique - -**Étant donné** que j'écoute un live régional "Île-de-France" -**Et** que je suis situé en Île-de-France - -**Quand** je me déplace et sors du département - -**Alors** le live continue de jouer normalement -**Et** je peux écouter jusqu'à la fin naturelle du live -**Et** après la fin du live, l'algorithme propose du contenu correspondant à ma nouvelle position - ---- - -## 6. Abonné dans la zone reçoit notification push - -**Étant donné** que je suis abonné au créateur "JeanDupont" -**Et** que je suis situé en Île-de-France - -**Quand** "JeanDupont" démarre un live en Île-de-France - -**Alors** je reçois une notification push "🔴 JeanDupont est en direct : [Titre du live]" -**Et** quand je tape sur la notification, l'app s'ouvre et le live démarre immédiatement - ---- - -## 7. Abonné hors zone ne reçoit pas de notification - -**Étant donné** que je suis abonné au créateur "JeanDupont" -**Et** que je suis situé à Lyon - -**Quand** "JeanDupont" démarre un live en Île-de-France - -**Alors** je ne reçois pas de notification push -**Et** cela évite la frustration de ne pas pouvoir écouter un live hors zone - ---- - -## 8. Découverte d'un live via l'algorithme de recommandation - -**Étant donné** que je suis dans la zone géographique du live -**Et** que je navigue dans l'app avec "Suivant" - -**Quand** l'algorithme propose un live en cours - -**Alors** je vois l'indicateur "🔴 EN DIRECT" -**Et** je peux choisir de le rejoindre ou de passer au suivant - ---- - -## 9. Reconnexion rapide après coupure réseau (moins de 90 secondes) - -**Étant donné** que j'écoute un live - -**Quand** je perds ma connexion réseau pendant 45 secondes -**Et** que je retrouve ma connexion - -**Alors** je reprends le live au moment actuel (pas au buffer ancien) -**Et** le saut temporel est transparent (pas de message d'erreur) -**Et** je ne rate que quelques secondes de contenu - ---- - -## 10. Reconnexion longue après coupure réseau (90 secondes ou plus) - -**Étant donné** que j'écoute un live - -**Quand** je perds ma connexion réseau pendant 90 secondes -**Et** que je retrouve ma connexion - -**Alors** je vois le message "Live en cours perdu, passage au contenu suivant" -**Et** l'algorithme propose automatiquement le contenu suivant -**Et** je peux manuellement revenir au live s'il est toujours en cours - ---- - -## 11. Interactions disponibles pendant le live - Like - -**Étant donné** que j'écoute un live -**Et** que mon véhicule est à l'arrêt - -**Quand** je clique sur le bouton "❤️ Like" - -**Alors** le like est enregistré immédiatement -**Et** le compteur de likes visible par le créateur s'incrémente -**Et** ma jauge d'intérêt pour les tags du live augmente de +2% - ---- - -## 12. Interactions disponibles pendant le live - Abonnement - -**Étant donné** que j'écoute un live -**Et** que je ne suis pas encore abonné au créateur - -**Quand** je clique sur le bouton "S'abonner" - -**Alors** je m'abonne au créateur -**Et** ma jauge d'intérêt pour tous les tags du créateur augmente de +5% -**Et** je recevrai des notifications pour ses prochains lives - ---- - -## 13. Interactions disponibles pendant le live - Skip - -**Étant donné** que j'écoute un live - -**Quand** j'appuie sur "Suivant" (ou commande au volant) - -**Alors** je quitte le live immédiatement -**Et** l'algorithme propose le contenu suivant -**Et** si j'ai écouté moins de 10 secondes, ma jauge d'intérêt diminue de -0.5% - ---- - -## 14. Commande Précédent désactivée pendant un live - -**Étant donné** que j'écoute un live - -**Quand** j'appuie sur "Précédent" (ou commande au volant) - -**Alors** rien ne se passe -**Et** un message d'information s'affiche brièvement "Précédent non disponible sur les lives" - ---- - -## 15. Chat en direct désactivé (décision définitive) - -**Étant donné** que j'écoute un live - -**Alors** aucune interface de chat n'est disponible -**Et** je ne peux pas envoyer de messages au créateur -**Et** je ne peux pas voir de messages d'autres auditeurs -**Et** cette fonctionnalité ne sera jamais implémentée - ---- - -## 16. Réactions emoji désactivées (décision définitive) - -**Étant donné** que j'écoute un live - -**Alors** aucune réaction emoji n'est disponible -**Et** je ne peux pas envoyer d'emoji en temps réel -**Et** cette fonctionnalité ne sera jamais implémentée - ---- - -## 17. Message d'information sur l'absence de chat - -**Étant donné** que j'écoute mon premier live - -**Quand** j'accède à l'interface du live - -**Alors** je vois un bandeau informatif "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement." -**Et** ce bandeau n'apparaît qu'une seule fois (première expérience) - ---- - -## 18. Signalement d'un live en cours - -**Étant donné** que j'écoute un live -**Et** que le contenu me semble inapproprié - -**Quand** je clique sur le bouton "Signaler" - -**Alors** je vois les catégories de signalement: - - | catégorie | - |---| - | Haine et violence | - | Contenu sexuel | - | Illégalité | - | Droits d'auteur | - | Désinformation dangereuse | - | Harcèlement | - | Autre | - -**Et** quand je sélectionne une catégorie -**Alors** le signalement est envoyé en priorité selon la catégorie -**Et** un modérateur peut écouter le live en temps réel si besoin - ---- - -## 19. Statistiques visibles par les auditeurs pendant le live - -**Étant donné** que j'écoute un live - -**Quand** je consulte les informations du live - -**Alors** je vois: - - | information | exemple valeur | - |---|---| - | Nombre d'auditeurs | 247 personnes | - | Durée du live | 1h 23min | - | Nom du créateur | @JeanDupont | - | Zone de diffusion | Île-de-France | - | Tags | Actualité, Société | - -**Mais** je ne vois pas les likes ou autres métriques détaillées - ---- - -## 20. Compteur d'auditeurs arrondi pour préserver la vie privée - -**Étant donné** que j'écoute un live avec exactement 247 auditeurs - -**Quand** je consulte le nombre d'auditeurs - -**Alors** je vois "~250 auditeurs" (arrondi à la dizaine supérieure) - ---- - -## 21. Qualité audio adaptative pendant le live - -**Étant donné** que j'écoute un live - -**Quand** ma connexion passe de 4G à 3G - -**Alors** la qualité audio s'adapte automatiquement -**Et** je passe de 48 kbps à 24 kbps Opus -**Et** la transition est transparente sans coupure - ---- - -## 22. Consommation de données pendant un live - -**Étant donné** que j'écoute un live en qualité standard 48 kbps -**Et** que j'écoute pendant 1 heure - -**Alors** j'ai consommé environ 21.6 MB de données mobiles -**Et** cette consommation est affichée dans les paramètres de l'app - ---- - -## 23. Lecture du replay après la fin du live - -**Étant donné** que j'écoute un live depuis 30 minutes - -**Quand** le créateur arrête le live - -**Alors** je vois le message "Le live est terminé. Le replay sera disponible dans quelques minutes" -**Et** le contenu suivant est automatiquement proposé après 2 secondes - ---- - -## 24. Notification de disponibilité du replay - -**Étant donné** que j'ai écouté un live jusqu'à la fin -**Et** que le replay est publié 8 minutes plus tard - -**Quand** je rouvre l'application - -**Alors** je vois une notification in-app "Le replay de [Titre] est maintenant disponible" -**Et** je peux cliquer pour l'écouter immédiatement - ---- - -## 25. Aucune publicité pendant un live pour utilisateurs gratuits - -**Étant donné** que je suis un utilisateur gratuit -**Et** que j'écoute un live - -**Alors** aucune publicité n'est insérée pendant le live -**Et** la publicité apparaît seulement entre le live et le contenu suivant - ---- - -## 26. Détection de contexte voiture pendant un live - -**Étant donné** que j'écoute un live -**Et** que ma vitesse est supérieure à 10 km/h - -**Alors** l'interface tactile est désactivée pour la sécurité -**Et** seules les commandes au volant sont actives (Play/Pause/Suivant) - ---- - -## 27. Détection de contexte piéton pendant un live - -**Étant donné** que j'écoute un live -**Et** que ma vitesse est inférieure à 5 km/h - -**Alors** l'interface tactile complète est disponible -**Et** je peux liker, m'abonner, signaler via l'écran tactile - ---- - - - - - -
- - -# Démarrage d'un live -> *En tant que créateur* -> *Je veux démarrer une diffusion en direct* -> *Afin de partager du contenu audio en temps réel avec mes auditeurs* - -**20 scénarios** - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'API RoadWave est disponible -> **Et** que je suis connecté en tant que créateur vérifié -> **Et** que j'ai les permissions de diffusion live -## 1. Vérifications pré-live réussies - -**Étant donné** que ma connexion upload est supérieure à 1 Mbps -**Et** que j'ai autorisé l'accès au microphone -**Et** que j'ai défini une zone de diffusion "Île-de-France" - -**Quand** je lance les vérifications pré-live - -**Alors** toutes les vérifications sont validées -**Et** je peux démarrer le live - ---- - -## 2. Échec pré-live avec connexion insuffisante - -**Étant donné** que ma connexion upload est de 0.5 Mbps - -**Quand** je lance les vérifications pré-live - -**Alors** je vois un warning "Connexion insuffisante pour garantir une diffusion stable (minimum 1 Mbps)" -**Et** je peux choisir de continuer quand même ou d'annuler - ---- - -## 3. Échec pré-live sans autorisation microphone - -**Étant donné** que je n'ai pas autorisé l'accès au microphone - -**Quand** j'essaie de démarrer un live - -**Alors** je vois le message "Accès au microphone requis pour démarrer un live" -**Et** je suis redirigé vers les paramètres système - ---- - -## 4. Échec pré-live sans zone de diffusion définie - -**Étant donné** que je n'ai pas défini de zone de diffusion - -**Quand** j'essaie de démarrer un live - -**Alors** je vois le message "Veuillez définir une zone de diffusion avant de démarrer" -**Et** je suis redirigé vers le formulaire de configuration du live - ---- - -## 5. Démarrage live avec buffer 15 secondes - -**Étant donné** que toutes les vérifications pré-live sont validées - -**Quand** j'appuie sur "Démarrer live" - -**Alors** je vois le message "Live démarre dans 15s... Testez votre micro" -**Et** un compte à rebours de 15 secondes s'affiche -**Et** mon flux audio est enregistré pendant ces 15 secondes -**Et** le live n'est pas encore visible publiquement - ---- - -## 6. Live devient public après buffer initial - -**Étant donné** que j'ai démarré un live -**Et** que le buffer de 15 secondes s'est écoulé - -**Alors** le live devient public -**Et** les auditeurs peuvent le rejoindre -**Et** les abonnés dans la zone reçoivent une notification push - ---- - -## 7. Notification push aux abonnés dans la zone géographique - -**Étant donné** que j'ai 1000 abonnés au total -**Et** que 300 abonnés sont situés en Île-de-France -**Et** que 700 abonnés sont situés hors Île-de-France - -**Quand** mon live en Île-de-France devient public - -**Alors** 300 abonnés reçoivent une notification push "🔴 [Mon pseudo] est en direct : [Titre live]" -**Et** 700 abonnés ne reçoivent pas de notification - ---- - -## 8. Configuration métadonnées obligatoires pour un live - -**Quand** je configure un nouveau live - -**Alors** je dois renseigner: - - | champ | format | validation | - |---|---|---| - | Titre | 5-100 caractères | Obligatoire | - | Tags | 1-3 centres intérêt | Sélection liste prédéfinie | - | Classification âge | Enum | Tout public / 13+ / 16+ / 18+ | - | Zone diffusion | Geo | Ville / Département / Région / National | - - ---- - -## 9. Validation échouée avec titre trop court - -**Quand** j'essaie de créer un live avec le titre "Live" - -**Alors** la validation échoue -**Et** je vois le message "Le titre doit contenir entre 5 et 100 caractères" - ---- - -## 10. Validation échouée sans tags - -**Étant donné** que j'ai rempli tous les champs sauf les tags - -**Quand** j'essaie de démarrer le live - -**Alors** la validation échoue -**Et** je vois le message "Veuillez sélectionner entre 1 et 3 centres d'intérêt" - ---- - -## 11. Limite de durée 8 heures - -**Étant donné** que mon live dure depuis 7 heures et 30 minutes - -**Alors** je vois un warning "Votre live se terminera dans 30 min" -**Et** le message est affiché de manière non intrusive - ---- - -## 12. Arrêt automatique à 8 heures - -**Étant donné** que mon live dure depuis 8 heures - -**Alors** le live s'arrête automatiquement -**Et** je vois le message "Durée maximale atteinte (8 heures). Vous pouvez redémarrer un nouveau live si nécessaire" -**Et** le processus de traitement post-live démarre - ---- - -## 13. Diffusion contenu interdit - Concert en direct - -**Étant donné** que je diffuse un concert en direct depuis une salle -**Et** qu'un auditeur signale le contenu pour "Violation droits d'auteur" - -**Quand** un modérateur écoute le live -**Et** qu'il confirme la violation - -**Alors** le live est arrêté immédiatement -**Et** je reçois un Strike 2 (suspension 7 jours) -**Et** je vois le message "Votre live a été interrompu pour violation des droits d'auteur" -**Et** le replay n'est pas publié - ---- - -## 14. Diffusion contenu interdit - Événement sportif payant - -**Étant donné** que je diffuse un match de football avec droits TV -**Et** que le contenu est détecté par l'IA audio fingerprint - -**Quand** la détection est confirmée - -**Alors** le live est arrêté immédiatement -**Et** je reçois un Strike 2 (suspension 7 jours) - ---- - -## 15. Diffusion contenu violent - -**Étant donné** que je diffuse du contenu violent (agression physique) -**Et** que 5 auditeurs signalent le contenu - -**Quand** un modérateur vérifie en temps réel -**Et** confirme la violence - -**Alors** le live est coupé immédiatement -**Et** mon compte est banni définitivement -**Et** les autorités sont notifiées - ---- - -## 16. Détection musique protégée en arrière-plan - -**Étant donné** que mon live contient de la musique protégée en fond - -**Quand** l'IA audio fingerprint détecte la violation après 2 minutes - -**Alors** je reçois un avertissement en direct "Musique protégée détectée. Veuillez couper le son ou risquez un arrêt du live" -**Et** j'ai 30 secondes pour corriger -**Et** si je ne corrige pas, le live est arrêté avec Strike 1 - ---- - -## 17. Signalement pendant un live - -**Étant donné** que je diffuse un live -**Et** qu'un auditeur clique sur "Signaler" - -**Quand** l'auditeur sélectionne la catégorie "Harcèlement" - -**Alors** le signalement est envoyé en priorité HAUTE -**Et** un modérateur peut écouter le live en temps réel -**Et** le live continue pendant l'écoute de vérification - ---- - -## 18. Dépassement nombre de lives simultanés autorisés (limite plateforme) - -**Étant donné** que la plateforme héberge actuellement 2000 lives simultanés -**Et** que c'est la limite de l'infrastructure actuelle - -**Quand** j'essaie de démarrer un nouveau live - -**Alors** je vois le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" -**Et** ma demande est mise en file d'attente prioritaire si je suis créateur Premium - ---- - -## 19. Premier live d'un nouveau créateur - -**Étant donné** que je n'ai jamais diffusé de live auparavant -**Et** que j'ai moins de 3 contenus validés - -**Quand** j'essaie de démarrer mon premier live - -**Alors** je vois le message "Les lives sont disponibles après validation de vos 3 premiers contenus" -**Et** le bouton "Démarrer live" est désactivé - ---- - -## 20. Créateur avec score de confiance faible - -**Étant donné** que j'ai 2 strikes actifs - -**Quand** j'essaie de démarrer un live - -**Alors** je vois le message "Fonctionnalité live temporairement indisponible suite à vos sanctions" -**Et** je dois attendre la fin de ma suspension - ---- - - - - - -
- - -# Recherche de contenu -> *En tant qu'utilisateur de RoadWave* -> *Je veux rechercher des contenus audio par mots-clés, localisation et filtres* -> *Afin de trouver facilement le contenu qui m'intéresse* - -**55 scénarios** (49 standards, 6 plans) - ---- - -> **Contexte commun à tous les scénarios** -> -> **Étant donné** que l'application RoadWave est démarrée -> **Et** que l'utilisateur "jean@example.com" est connecté -## 1. Recherche full-text basique - -**Étant donné** que la base contient les contenus suivants: - - | titre | description | créateur | - |---|---|---| - | Balade à Paris | Visite du quartier Latin | @paris_stories | - | Secrets de Montmartre | Histoire de la butte | @explore_paris | - | Voyage en Normandie | Découverte des plages | @voyages_fr | - - -**Quand** l'utilisateur recherche "paris" - -**Alors** 2 résultats sont retournés -**Et** les résultats incluent "Balade à Paris" -**Et** les résultats incluent "Secrets de Montmartre" - ---- - -## 2. Recherche avec stemming français - -**Étant donné** un contenu avec le titre "Voyage en Bretagne" - -**Quand** l'utilisateur recherche "voyages" - -**Alors** le contenu "Voyage en Bretagne" est trouvé -**Et** le stemming a transformé "voyages" en racine "voyag" - ---- - -## 3. 📋 Plan: Stemming français sur différentes formes - -**Étant donné** un contenu avec le mot "" - -**Quand** l'utilisateur recherche "" - -**Alors** le contenu est trouvé grâce au stemming français - -**📊 Exemples de données:** - -| mot_original | recherche | -|---|---| -| voyage | voyages | -| voyager | voyage | -| balades | balade | -| historique | histoire | - ---- - -## 4. Recherche avec accents ignorés - -**Étant donné** un contenu avec le titre "Découverte de l'Élysée" - -**Quand** l'utilisateur recherche "decouverte elysee" - -**Alors** le contenu est trouvé -**Et** les accents sont normalisés automatiquement - ---- - -## 5. Champs indexés avec pondération - -**Étant donné** les contenus suivants: - - | titre | description | créateur | tags | - |---|---|---|---| - | Voyage Paris | Balade sympa | @user1 | Tourisme | - | Balade Lyon | Voyage en ville | @paris_guide | Voyage | - - -**Quand** l'utilisateur recherche "paris" - -**Alors** "Voyage Paris" est en première position -**Et** "@paris_guide" apparaît en second - ---- - -## 6. Ranking par pertinence et popularité - -**Étant donné** les contenus suivants: - - | titre | écoutes | rang_texte | - |---|---|---| - | Balade Paris | 50000 | 0.8 | - | Paris la nuit | 1000 | 0.9 | - - -**Quand** l'utilisateur recherche "paris" - -**Alors** le score final combine rang_texte × (1 + log(écoutes + 1)) -**Et** "Balade Paris" est mieux classé grâce à sa popularité - ---- - -## 7. Autocomplete pendant la frappe - -**Étant donné** que l'utilisateur commence à taper "par" - -**Quand** 3 caractères sont saisis - -**Alors** des suggestions apparaissent: - - | suggestion | - |---| - | paris | - | parc naturel | - | parvis notre-dame | - -**Et** le top 5 des suggestions est affiché - ---- - -## 8. Historique des 10 dernières recherches - -**Étant donné** que l'utilisateur a effectué les recherches suivantes: - - | recherche | date | - |---|---| - | voyage paris | 2026-01-20 | - | audio-guide louvre | 2026-01-19 | - | podcast automobile | 2026-01-18 | - - -**Quand** l'utilisateur ouvre la barre de recherche - -**Alors** les 10 dernières recherches sont affichées -**Et** elles sont triées par date décroissante - ---- - -## 9. Correction automatique si aucun résultat - -**Étant donné** que l'utilisateur recherche "ballade paris" (faute d'orthographe) -**Et** qu'aucun résultat n'est trouvé - -**Quand** la page de résultats s'affiche - -**Alors** une suggestion "Essayez plutôt : balade paris" est affichée - ---- - -## 10. Recherches populaires suggérées - -**Étant donné** qu'aucun résultat n'est trouvé pour une recherche - -**Quand** la page s'affiche - -**Alors** des suggestions populaires sont affichées: - - | suggestion | - |---| - | balade paris | - | audio-guide louvre | - | visite montmartre | - - ---- - -## 11. Saisie d'un lieu avec autocomplete - -**Étant donné** que l'utilisateur ouvre le filtre "Lieu" - -**Quand** il tape "Louv" - -**Alors** Nominatim retourne des suggestions: - - | suggestion | type | - |---|---| - | Musée du Louvre, Paris | monument | - | Louvres, Val-d'Oise | commune | - - ---- - -## 12. Sélection d'un lieu et définition du rayon - -**Étant donné** que l'utilisateur sélectionne "Paris, France" -**Et** que les coordonnées sont (48.8566, 2.3522) - -**Quand** il définit un rayon de 50 km - -**Alors** la recherche PostGIS utilise ST_DWithin avec 50000 mètres - ---- - -## 13. 📋 Plan: Recherche géographique avec différents rayons - -**Étant donné** un contenu à 30 km de Paris - -**Quand** l'utilisateur recherche autour de Paris avec un rayon de - -**Alors** le contenu est - -**📊 Exemples de données:** - -| rayon | résultat | -|---|---| -| 20 km | non trouvé | -| 50 km | trouvé | -| 100 km | trouvé | - ---- - -## 14. Utilisation de "Autour de moi" (GPS actuel) - -**Étant donné** que l'utilisateur active le GPS -**Et** que sa position est (48.8566, 2.3522) - -**Quand** il sélectionne "Autour de moi" - -**Alors** la recherche utilise ses coordonnées GPS actuelles -**Et** un rayon par défaut de 10 km est appliqué - ---- - -## 15. Curseur de rayon avec limites - -**Étant donné** que l'utilisateur ouvre le curseur de rayon - -**Quand** il ajuste le curseur - -**Alors** les valeurs disponibles vont de 5 km à 500 km -**Et** la valeur s'affiche en temps réel "50 km" - ---- - -## 16. Affichage de la distance dans les résultats - -**Étant donné** une recherche géographique autour de Paris -**Et** un contenu à 2.3 km de distance - -**Quand** les résultats sont affichés - -**Alors** la distance "À 2.3 km" est indiquée pour chaque résultat - ---- - -## 17. 📋 Plan: Tri par proximité géographique - -**Étant donné** des contenus à différentes distances de Paris: - - | contenu | distance | - |---|---| - | Louvre Guide | 0.5 km | - | Tour Eiffel | 2.0 km | - | Versailles | 20 km | - - -**Quand** l'utilisateur trie par "Proximité" - -**Alors** les résultats sont affichés dans l'ordre: - - | position | contenu | - |---|---| - | 1 | Louvre Guide | - | 2 | Tour Eiffel | - | 3 | Versailles | - - ---- - -## 18. Géocodage avec Nominatim (MVP) - -**Étant donné** que l'application est en phase MVP - -**Quand** une requête de géocodage est effectuée - -**Alors** l'API publique Nominatim est utilisée -**Et** le rate limit de 1 req/s est respecté - ---- - -## 19. Géocodage avec fallback Mapbox - -**Étant donné** que Nominatim ne retourne aucun résultat - -**Quand** l'application tente un fallback - -**Alors** l'API Mapbox Geocoding est utilisée -**Et** le coût de 0.50€ / 1000 requêtes est appliqué - ---- - -## 20. Ouverture du panneau de filtres - -**Étant donné** que l'utilisateur est sur la page de recherche - -**Quand** il clique sur "Filtres" - -**Alors** un panneau latéral s'ouvre -**Et** 7 catégories de filtres sont affichées: - - | catégorie | - |---| - | Type de contenu | - | Durée | - | Classification âge | - | Géo-pertinence | - | Tags | - | Date de publication | - | Abonnement | - - ---- - -## 21. Filtre par type de contenu (multi-sélection) - -**Étant donné** que l'utilisateur ouvre les filtres - -**Quand** il sélectionne: - - | type | - |---| - | Contenu court | - | Audio-guide | - - -**Alors** seuls ces types de contenus sont recherchés -**Et** les podcasts et radios live sont exclus - ---- - -## 22. 📋 Plan: Filtre par durée - -**Étant donné** un contenu de minutes - -**Quand** l'utilisateur filtre par "" - -**Alors** le contenu est - -**📊 Exemples de données:** - -| durée | tranche | résultat | -|---|---|---| -| 3 | <5 min | trouvé | -| 3 | 5-15 min | non trouvé | -| 10 | 5-15 min | trouvé | -| 20 | 15-30 min | trouvé | -| 45 | >30 min | trouvé | - ---- - -## 23. Filtre par classification âge - -**Étant donné** des contenus avec différentes classifications: - - | contenu | classification | - |---|---| - | Conte enfants | Tout public | - | Podcast news | 13+ | - | Débat politique | 16+ | - - -**Quand** l'utilisateur filtre "Tout public" - -**Alors** seul "Conte enfants" est affiché - ---- - -## 24. Filtre par géo-pertinence - -**Étant donné** des contenus avec différents types géo: - - | contenu | type_geo | - |---|---| - | Guide Louvre | Ancré | - | Podcast Paris | Contextuel | - | News nationales | Neutre | - - -**Quand** l'utilisateur filtre "Ancré, Contextuel" - -**Alors** "Guide Louvre" et "Podcast Paris" sont affichés -**Et** "News nationales" est exclu - ---- - -## 25. Filtre par tags (multi-sélection) - -**Étant donné** des contenus taggés: - - | contenu | tags | - |---|---| - | Voyage en Italie | Voyage, Gastronomie | - | Histoire de Rome | Voyage, Histoire | - | Économie italienne | Économie | - - -**Quand** l'utilisateur sélectionne les tags "Voyage, Histoire" - -**Alors** "Histoire de Rome" est en priorité (2 tags correspondants) -**Et** "Voyage en Italie" est affiché (1 tag correspondant) -**Et** "Économie italienne" est exclu - ---- - -## 26. 📋 Plan: Filtre par date de publication - -**Étant donné** un contenu publié il y a - -**Quand** l'utilisateur filtre par "" - -**Alors** le contenu est - -**📊 Exemples de données:** - -| délai | période | résultat | -|---|---|---| -| 12 heures | Dernières 24h | trouvé | -| 3 jours | Cette semaine | trouvé | -| 15 jours | Ce mois | trouvé | -| 8 mois | Cette année | trouvé | -| 2 ans | Toutes dates | trouvé | -| 2 ans | Cette année | non trouvé | - ---- - -## 27. Filtre par type d'abonnement - -**Étant donné** des contenus gratuits et Premium: - - | contenu | type | - |---|---| - | Balade Paris | Gratuit | - | Visite VIP Louvre | Premium | - - -**Quand** l'utilisateur filtre "Premium uniquement 👑" - -**Alors** seul "Visite VIP Louvre" est affiché - ---- - -## 28. Combinaison de filtres multiples (AND logic) - -**Étant donné** que l'utilisateur applique les filtres: - - | filtre | valeur | - |---|---| - | Type | Audio-guide | - | Durée | 5-15 min | - | Tags | Voyage | - | Classification | Tout public | - - -**Quand** la recherche est lancée - -**Alors** seuls les contenus respectant TOUS les critères sont affichés - ---- - -## 29. Réinitialisation des filtres - -**Étant donné** que l'utilisateur a appliqué 5 filtres différents - -**Quand** il clique sur "Réinitialiser" - -**Alors** tous les filtres sont désactivés -**Et** la recherche affiche tous les résultats - ---- - -## 30. Sauvegarde d'une recherche - -**Étant donné** que l'utilisateur a appliqué plusieurs filtres - -**Quand** il clique sur "💾 Sauvegarder cette recherche" -**Et** qu'il entre le nom "Podcasts voyage Paris" - -**Alors** la recherche est sauvegardée -**Et** elle apparaît dans l'onglet "Recherches sauvegardées" - ---- - -## 31. Limite de 5 recherches sauvegardées - -**Étant donné** que l'utilisateur a déjà 5 recherches sauvegardées - -**Quand** il tente de sauvegarder une 6ème recherche - -**Alors** un message d'erreur s'affiche -**Et** il doit supprimer une recherche existante avant d'en ajouter une nouvelle - ---- - -## 32. Notifications pour recherches sauvegardées - -**Étant donné** une recherche sauvegardée "Podcasts voyage Paris" -**Et** que l'utilisateur a activé les notifications - -**Quand** 3 nouveaux contenus correspondants sont publiés - -**Alors** une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée - ---- - -## 33. 📋 Plan: Options de tri des résultats - -**Étant donné** une recherche avec plusieurs résultats - -**Quand** l'utilisateur sélectionne le tri "