CORRECTIF « SOLDE EN ENTÊTE (client ET super administrateur), JOURNAL DU ROUTEUR, PHOTO RB951, PAGES VPN, EXPIRATION DES TICKETS ».
Il REMPLACE le zip « toilettage et expiration » (il en contient tous les fichiers) : installez celui-ci seul. Aucune migration. Votre fichier .env n'est PAS touché.

=== NOUVEAU : SOLDE DANS L'ENTÊTE POUR VOUS AUSSI
 Dans l'entête (à gauche de la recherche) :
   - administrateur d'un espace CLIENT : le solde de son portefeuille (vert ; ambre quand son espace n'a plus de forfait actif), un clic mène à son portefeuille ;
   - SUPER ADMINISTRATEUR (vous) : le TOTAL des portefeuilles de vos clients (bleu, « 3 000 FCFA clients »), un clic mène à la liste des portefeuilles ; quand vous « entrez » dans l'espace d'un client, le solde de CE client
     (un clic mène à son journal). Votre espace principal n'a ni portefeuille ni forfait : il n'est jamais compté.
   - jamais pour les autres rôles.

=== NOUVEAU : « CE ROUTEUR EST HORS LIGNE : CONNEXION COUPÉE PENDANT « /log/print… » » (votre capture)
 Cause : le routeur répondait très bien (identité, horloge, processeur), mais la lecture de son JOURNAL est coupée par ce MikroTik, et cette seule coupure le faisait déclarer « hors ligne ».
 Maintenant :
   - une lecture coupée APRÈS que le routeur a accepté l'identification ne le déclare plus hors ligne (seule la raison est notée) ; coupé avant l'identification, il reste « hors ligne » ;
   - si la lecture du journal avec liste de champs est coupée, elle est refaite sans liste de champs (cas déjà rencontré avec la lecture système de ce routeur).
 Si le journal reste vide sur ce routeur, le message indique la commande coupée ; envoyez-le moi.

=== 1. SOLDE DANS L'ENTÊTE : voir « NOUVEAU » ci-dessus.

=== 2. PHOTO DU RB951Ui-2HnD
 Cause (vérifiée sur la vraie fiche de MikroTik) : la page du produit s'appelle .../product/RB951Ui-2HnD (casse et tiret d'origine), alors que le site cherchait seulement « rb951ui_2hnd », écriture des produits récents.
 Maintenant : l'écriture d'origine est essayée d'abord pour les noms sans espace, l'écriture en minuscules d'abord pour les noms avec espaces (« hAP ax lite »). Une image trouvée est gardée 30 jours. En cas de refus ou de panne de
 mikrotik.com, on n'insiste pas avec une autre écriture (nouvel essai 10 minutes plus tard). La miniature du RB951 apparaît au prochain affichage de sa fiche ; diagnostic : php artisan routers:images RB951Ui-2HnD
 Non vérifié depuis VOTRE hébergeur (il doit pouvoir joindre mikrotik.com) : la commande ci-dessus le dit.

=== 3. PAGES VPN REFAITES (client, administration, configuration API, audit)
 Des cartes lisibles à la place des tableaux ; les boutons et les textes sont visibles. Cause des textes blancs invisibles : votre feuille de style compilée ne contenait pas des classes comme bg-violet-600 ou bg-emerald-600.
 Page client : « Mes VPN » (état, expiration, jours restants, téléphone et MikroTik côte à côte avec en ligne / hors ligne, dernière connexion, trafic, télécharger, révoquer, activer), puis « Acheter un VPN ».
 Le tunnel MikroTik ne demande plus d'activer d'abord un téléphone (comme votre modification de VpnProvisioningService, que je n'ai pas touchée). Vos textes sur le paiement par portefeuille sont conservés.
 Administration : chiffres clés, trafic, filtres, une carte par appareil et par souscription ; « Traiter un ancien dossier en attente » conservé.

=== 4. FEUILLE DE STYLE RECONSTRUITE (tout le site)
 Toute classe de style utilisée dans une page mais absente du fichier compilé ne produit AUCUN style. J'ai reconstruit la feuille de style à partir de TOUTES vos pages : nouveau fichier public/build/assets/app-B4XNtZ0O.css et
 public/build/manifest.json mis à jour. Votre app.js n'est pas remplacé (comportement inchangé). Les anciens fichiers restent en place : pour revenir en arrière, remettez l'ancien manifest.json.
 Si vous reconstruisez un jour vous-même (npm ci puis npm run build), le résultat est équivalent.

=== 5. EXPIRATION DES TICKETS : LA PLATEFORME S'EN CHARGE
 Principe : à la première connexion d'un ticket, le routeur écrit sa date d'expiration dans son commentaire, puis une tâche planifiée DU ROUTEUR (toutes les 2 minutes) désactive ou supprime les tickets expirés. Chez certains MikroTik cette
 tâche manque ou est en panne. Désormais la plateforme fait la même vérification par l'API, TOUTES LES 10 MINUTES, sur les routeurs en ligne (commande php artisan routers:enforce-expiry, déjà dans le planificateur) :
   1. elle lit l'horloge du routeur ; 2. elle lit les tickets ACTIVÉS dont le commentaire porte une date dépassée ; 3. selon le mode du profil (Bloquer / Supprimer) elle désactive ou supprime le ticket et COUPE sa session.
 Quelques commandes seulement par routeur, même pour des centaines de tickets (traitement par lots).
 Garde-fous : jamais un utilisateur créé à la main ni un profil sans règle de la plateforme ; mode « Ne rien faire » respecté ; ticket pas encore utilisé jamais touché ; si l'horloge du routeur est illisible ou décalée de plus de 36 h,
 RIEN n'est fait et l'écran le dit (une horloge folle ferait expirer tous les tickets) ; 500 tickets au plus par passage, le reste au passage suivant ; un ticket disparu entre-temps ne bloque jamais les autres.
 Écran : Paramètres > « Expiration des tickets » : interrupteur par espace (activé au départ), pour chaque routeur le résultat du dernier passage, et « Vérifier maintenant ».
 Un espace sans forfait actif n'est jamais traité (aucune commande vers ses routeurs). Un routeur hors ligne est ignoré jusqu'à son retour.
 À SAVOIR : l'option « supprimer le ticket N jours après son blocage » est programmée par le routeur lui-même ; si c'est sa tâche qui manque, ce nettoyage différé ne sera pas programmé (le ticket reste bloqué).
 CONSEIL AVANT DE COMPTER DESSUS : php artisan routers:enforce-expiry --dry-run montre, sans rien envoyer, ce qui serait fait sur chacun de vos routeurs.

=== 6. MES RECOMMANDATIONS POUR TOUT LE SITE
 - Après chaque modification de pages, reconstruire la feuille de style (npm run build) : c'est ce qui évite les textes invisibles ; je peux le refaire à chaque livraison.
 - Vérifier chaque mois : php artisan wallet:audit (portefeuilles cohérents) et php artisan routers:enforce-expiry --dry-run (rien d'anormal sur les routeurs).
 - Réglages e-mail : contrôler SPF et DKIM de votre domaine (cPanel > Deliverability) pour que les alertes et les e-mails de confirmation n'arrivent pas en courrier indésirable.
 - Envisager de remplacer le relais VPN tiers (vpn2.mikhmon.dev, 1,7 s de latence) par votre propre DOUAWE VPN pour vos routeurs : lectures plus rapides et plus fiables.
 - Nettoyage : php artisan site:nettoyer (liste) déplace sans rien supprimer les anciens fichiers que d'anciens zips ont pu réinstaller.

=== 7. INSTALLATION
 1. cPanel > Gestionnaire de fichiers > « public_html » > envoyer le zip > Extraire, en remplaçant les fichiers.  2. Dans wifi.douaweproshop.ci/bootstrap/cache : supprimer les fichiers .php (garder .gitignore).
 3. Vider le cache du navigateur du téléphone (la feuille de style a changé).  4. Facultatif : php artisan routers:enforce-expiry --dry-run.

=== VÉRIFICATIONS
 Suite complète de tests sur une copie de votre site. 21 tests pour l'expiration (garde-fous cassés exprès : les tests échouent), 15 pour les photos, 60 pour le VPN. Rendu vérifié dans Chrome sur téléphone : pages VPN, pastille du solde, carte d'expiration.
 NON VÉRIFIÉ : vos vrais routeurs (l'expiration est testée avec un routeur simulé) ; l'accès de votre hébergeur à mikrotik.com ; la photo du RB951 sur votre site.
