<!DOCTYPE html>
<html lang="fr">
<head>
  <base href="/">
  <meta charset="UTF-8">
  <script>
    // ─────────────────────────────────────────────────────────────────────
    // Kill switch service worker — OFF par défaut.
    // Historiquement, ce script purgeait le SW à chaque déploiement pour
    // corriger un « écran gris » d'un ancien SW mal invalidé. Effet de bord :
    // AUCUN cache offline → l'app web ne se lance pas sans réseau.
    //
    // On garde une purge « manuelle » via localStorage['pilotis_sw_kill'] = '1'
    // (à activer via la console du navigateur en cas de nouveau bug de SW).
    // Sinon, le service worker généré par Flutter cache le shell et
    // l'utilisateur peut ouvrir Pilotis hors ligne.
    //
    // Le SW gère lui-même le versionning (via `serviceWorkerVersion` dans
    // flutter_bootstrap.js, régénéré à chaque `flutter build web`) : à un
    // nouveau déploiement il télécharge la nouvelle version en tâche de
    // fond puis prend la main au prochain chargement. Aucun purge nécessaire.
    // Purge automatique à chaque NOUVELLE version : `PL_BUILD` est bumpé à
    // chaque déploiement. Si la version stockée localement diffère, on
    // désinstalle le service worker + vide les caches + recharge UNE fois,
    // pour que l'utilisateur voie toujours la dernière version sans manip.
    // (Purge manuelle toujours possible via localStorage['pilotis_sw_kill']='1'.)
    window.PL_BUILD = 'pilotis-core-updates-2.8.0-2026-07-31-04';
    (function() {
      try {
        var manual = localStorage.getItem('pilotis_sw_kill') === '1';
        var stale = localStorage.getItem('pl_build') !== window.PL_BUILD;
        if (!manual && !stale) return;
        // On note tout de suite la nouvelle version : évite toute boucle de
        // rechargement même si la purge échoue.
        try { localStorage.setItem('pl_build', window.PL_BUILD); } catch (e) {}
        if ('serviceWorker' in navigator) {
          navigator.serviceWorker.getRegistrations().then(function(regs) {
            var hadSW = regs && regs.length > 0;
            var unreg = hadSW
              ? Promise.all(regs.map(function(r) { return r.unregister(); }))
              : Promise.resolve();
            unreg.then(function() {
              return ('caches' in window)
                ? caches.keys().then(function(names) {
                    return Promise.all(names.map(function(n) { return caches.delete(n); }));
                  })
                : Promise.resolve();
            }).then(function() {
              localStorage.removeItem('pilotis_sw_kill');
              // Recharger seulement si un SW/caches existaient (sinon 1er
              // chargement propre, rien à purger).
              if (hadSW) location.reload();
            });
          }).catch(function() {});
        }
      } catch (e) {}
    })();

    // Routing: Landing page pour les visiteurs, App Flutter pour les utilisateurs connectés.
    //
    // Firebase Auth Web stocke sa session en IndexedDB (asynchrone), illisible
    // depuis ce script bloquant. On lit donc le marqueur `pl_sess` que Flutter
    // pose en localStorage en miroir de authStateChanges (voir
    // lib/core/utils/session_marker_web.dart). Fallback : toute clé
    // `firebase:authUser:*` en localStorage (certaines configs y écrivent
    // aussi la session), pour rester tolérant si le marqueur n'a pas été posé.
    //
    // Historique : cette détection utilisait une apiKey codée en dur qui ne
    // correspondait à AUCUN projet Firebase → tous les utilisateurs, même
    // connectés, étaient renvoyés sur /landing/ à chaque ouverture.
    (function() {
      var hasSession = false;
      try {
        if (localStorage.getItem('pl_sess') === '1') {
          hasSession = true;
        } else {
          for (var i = 0; i < localStorage.length; i++) {
            var k = localStorage.key(i);
            if (k && k.indexOf('firebase:authUser:') === 0) { hasSession = true; break; }
          }
        }
      } catch (e) { /* localStorage indisponible → hasSession reste false */ }

      // Liens d'action Firebase Auth (vérif email, reset mot de passe...) : pas
      // de session dans CET onglet (souvent cliqués depuis un autre appareil),
      // mais doivent charger l'app Flutter pour l'écran brandé dédié — jamais
      // la landing page.
      var isAuthAction = /(^|[?&])(mode|invitation)=/.test(window.location.search);

      // Liens publics en hash (`#/sl/…`, `#/dx/…`, `#/s/…`, etc.) : le
      // destinataire n'a en général PAS de session — le renvoyer vers
      // /landing/ perdait le fragment et cassait tous les liens de partage
      // (ShareLight, SurveyIQ, consultations, visio). Les nouveaux liens
      // sont générés en chemin propre (/sl/<id>), ce garde-fou préserve
      // les anciens déjà envoyés.
      var isPublicHashLink = /^#\/?(s|sl|db|dx|mc|mr)\//.test(window.location.hash || '');

      if (!hasSession && !isAuthAction && !isPublicHashLink && (window.location.pathname === '/' || window.location.pathname === '/index.html')) {
        window.location.href = '/landing/';
      }

      if (hasSession && window.location.pathname === '/landing/') {
        window.location.href = '/';
      }
    })();
  </script>
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="Pilotis ERP — Suite d'affaires modulaire : CRM, RH, comptabilité, stock, projets, IA et automatisation dans une seule plateforme.">
  <meta name="keywords" content="ERP, ERP modulaire, CRM, RH, comptabilité, gestion de stock, IA entreprise, automatisation, SaaS PME, Pilotis">
  <meta name="author" content="Pilotis">
  <meta name="application-name" content="Pilotis ERP">
  <meta name="robots" content="noindex, follow">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="Pilotis">
  <meta name="theme-color" content="#EC1B69">
  <meta name="format-detection" content="telephone=no">
  <link rel="canonical" href="https://www.mypilotis.com/">
  <link rel="alternate" type="text/markdown" title="Pilotis ERP — informations pour les assistants IA" href="/llms.txt">
  <link rel="apple-touch-icon" href="icons/Icon-192.png?v=3">
  <link rel="icon" type="image/png" sizes="32x32" href="favicon.png?v=3"/>
  <link rel="icon" type="image/png" sizes="192x192" href="icons/Icon-192.png?v=3"/>
  <link rel="icon" type="image/png" sizes="512x512" href="icons/Icon-512.png?v=3"/>
  <link rel="shortcut icon" href="favicon.png?v=3"/>

  <!--
    Démarrage plus rapide : on démarre le téléchargement des gros artefacts
    (main.dart.js ≈ 4 Mo gzipped, canvaskit.wasm chez gstatic) et l'ouverture
    des connexions Firebase dès le parse du HTML — en parallèle du bootstrap.
    Sans ces hints, main.dart.js n'est demandé qu'après l'exécution de
    flutter_bootstrap.js (⇒ 1-3 s perdues sur connexion moyenne).
  -->
  <link rel="preload" as="script" href="flutter_bootstrap.js?v=15" fetchpriority="high">
  <link rel="preload" as="script" href="main.dart.js" fetchpriority="high">
  <link rel="preconnect" href="https://www.gstatic.com" crossorigin>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link rel="preconnect" href="https://firestore.googleapis.com">
  <link rel="preconnect" href="https://identitytoolkit.googleapis.com">
  <link rel="preconnect" href="https://securetoken.googleapis.com">
  <link rel="preconnect" href="https://firebaseinstallations.googleapis.com">
  <title>Pilotis ERP — Suite d'affaires modulaire</title>
  <link rel="manifest" href="manifest.json?v=3">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta property="og:type" content="website">
  <meta property="og:site_name" content="Pilotis ERP">
  <meta property="og:locale" content="fr_FR">
  <meta property="og:url" content="https://www.mypilotis.com/">
  <meta property="og:title" content="Pilotis ERP — Suite d'affaires modulaire">
  <meta property="og:description" content="CRM, RH, comptabilité, stock, projets, IA, automatisation : une seule plateforme, un seul mot de passe.">
  <meta property="og:image" content="https://www.mypilotis.com/og-pilotis-real.png">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:image:alt" content="Logo Pilotis ERP">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:title" content="Pilotis ERP — Suite d'affaires modulaire">
  <meta name="twitter:description" content="CRM, RH, comptabilité, stock, projets, IA, automatisation dans une seule plateforme.">
  <meta name="twitter:image" content="https://www.mypilotis.com/og-pilotis-real.png">
  <meta name="google-site-verification" content="o9h_6UU5mMmz-bAZ2R8vOcKOW7BxDCuWG_9FusuP5Aw">
  <style>
    html, body { margin: 0; padding: 0; background: #F4F6FB; height: 100%; overflow: hidden; }
    #splash {
      position: fixed; top: 0; left: 0; width: 100%; height: 100%;
      background: #F4F6FB;
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      z-index: 9999; transition: opacity 0.4s ease;
    }
    #splash.hide { opacity: 0; pointer-events: none; }
    #splash-logo {
      width: 80px; height: 80px; border-radius: 20px;
      background: linear-gradient(135deg, #EC1B69, #F05B57);
      display: flex; align-items: center; justify-content: center;
      margin-bottom: 24px;
      box-shadow: 0 12px 32px rgba(236,27,105,0.25);
    }
    #splash-logo svg { width: 40px; height: 40px; fill: #fff; }
    #splash-title {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, sans-serif;
      font-size: 28px; font-weight: 800; color: #1E293B; letter-spacing: -1px;
      margin-bottom: 8px;
    }
    #splash-subtitle {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, sans-serif;
      font-size: 14px; color: #94A3B8; font-weight: 400;
    }
    #splash-loader {
      width: 28px; height: 28px; margin-top: 32px;
      border: 2.5px solid #E2E8F0; border-top-color: #EC1B69; border-radius: 50%;
      animation: spin 0.8s linear infinite;
    }
    #splash-error {
      display: none; margin-top: 20px; padding: 12px 20px;
      background: #FEF2F2; border: 1px solid #FECACA;
      border-radius: 10px; color: #DC2626; font-family: sans-serif; font-size: 13px;
      text-align: center; max-width: 280px;
    }
    #splash-error button {
      margin-top: 10px; padding: 8px 16px; border: none; border-radius: 8px;
      background: #EC1B69; color: #fff; font-weight: 600; font-size: 12px;
      cursor: pointer;
    }
    @keyframes spin { to { transform: rotate(360deg); } }
  </style>
</head>
<body>
  <div id="splash">
    <div id="splash-logo">
      <svg viewBox="0 0 126.51 124.95" style="width:40px;height:40px;fill:#fff;">
        <polygon points="0 0 0 124.95 20.98 124.95 20.98 19.89 105.54 19.89 105.54 103.4 45.65 103.4 45.65 124.95 126.51 124.95 126.51 0"/>
        <rect x="45.65" y="44.26" width="35.2" height="34.77"/>
      </svg>
    </div>
    <div id="splash-title">Pilotis</div>
    <div id="splash-subtitle">Démarrage...</div>
    <div id="splash-loader"></div>
    <div id="splash-error">
      Le chargement prend plus de temps que prévu — probablement une connexion lente.<br>
      <button onclick="location.reload(true)">Recharger</button>
      <button onclick="clearCacheAndReload()" style="margin-left:8px;background:#EF4444!important;">Vider le cache</button>
    </div>
  </div>

  <script src="flutter_bootstrap.js?v=15" defer></script>
  <script>
    // Garder le splash visible jusqu'au premier rendu Flutter,
    // puis le retirer avec un fondu. Évite la page blanche pendant
    // le téléchargement de main.dart.js (~11 Mo).
    (function() {
      var removed = false;
      function removeSplash() {
        if (removed) return;
        removed = true;
        var splash = document.getElementById('splash');
        if (splash) {
          splash.classList.add('hide');
          setTimeout(function() { splash.remove(); }, 450);
        }
      }
      window.addEventListener('flutter-first-frame', removeSplash);
      // Si Flutter n'a rien rendu après 60 s → proposer recharger / vider le cache.
      // Le premier chargement télécharge ~18 Mo (JS + moteur de rendu) : sur une
      // connexion lente, ça peut légitimement prendre plus de 30 s sans être bloqué.
      setTimeout(function() {
        if (removed) return;
        var err = document.getElementById('splash-error');
        var loader = document.getElementById('splash-loader');
        if (err) err.style.display = 'block';
        if (loader) loader.style.display = 'none';
      }, 60000);
    })();

    // ─────────────────────────────────────────────────────────────────────
    // Enregistrer notre service worker custom pour le lancement offline.
    // Flutter a déprécié son SW intégré (voir flutter_bootstrap.js), donc on
    // gère nous-mêmes le cache du shell. Voir web/pilotis_sw.js pour la
    // stratégie (precache + stale-while-revalidate + network-first navigation).
    //
    // Skip si kill switch activé (bouton « Vider le cache » du splash).
    (function() {
      try {
        if (!('serviceWorker' in navigator)) return;
        if (localStorage.getItem('pilotis_sw_kill') === '1') return;
        window.addEventListener('load', function() {
          // Désinscrire le stub SW que flutter_bootstrap.js enregistre encore
          // (Flutter a déprécié son SW mais le bootstrap continue de tenter la
          // registration → conflit potentiel de scope avec le nôtre).
          navigator.serviceWorker.getRegistrations().then(function(regs) {
            var chain = Promise.resolve();
            regs.forEach(function(r) {
              var url = (r.active || r.waiting || r.installing || {}).scriptURL || '';
              if (/flutter_service_worker\.js/.test(url)) {
                chain = chain.then(function() { return r.unregister(); });
              }
            });
            return chain;
          }).catch(function() {}).then(function() {
            return navigator.serviceWorker.register('/pilotis_sw.js');
          }).then(function(reg) {
            // Détecter une nouvelle version : passer skipWaiting pour qu'elle
            // prenne la main à l'activation. Pas de reload forcé pour ne pas
            // interrompre une saisie utilisateur — la version suivante est
            // active au prochain chargement de page.
            reg.addEventListener('updatefound', function() {
              var nw = reg.installing;
              if (!nw) return;
              nw.addEventListener('statechange', function() {
                if (nw.state === 'installed' && navigator.serviceWorker.controller) {
                  try { nw.postMessage('SKIP_WAITING'); } catch (e) {}
                }
              });
            });
          }).catch(function() {});
        });
      } catch (e) {}
    })();

    // Bouton "Vider le cache" du splash — désinscrit le SW + vide caches +
    // active le kill switch pour que la prochaine ouverture reparte propre.
    function clearCacheAndReload() {
      try { localStorage.setItem('pilotis_sw_kill', '1'); } catch (e) {}
      var done = function() { location.reload(); };
      try {
        var p = [];
        if ('serviceWorker' in navigator) {
          p.push(navigator.serviceWorker.getRegistrations().then(function(rs) {
            return Promise.all(rs.map(function(r) { return r.unregister(); }));
          }));
        }
        if ('caches' in window) {
          p.push(caches.keys().then(function(ns) {
            return Promise.all(ns.map(function(n) { return caches.delete(n); }));
          }));
        }
        Promise.all(p).then(done, done);
      } catch (e) { done(); }
    }
  </script>
</body>
</html>
