using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using System;
using System.Runtime.InteropServices;

/// <summary>
/// Popup d'identification avec deux étapes : email puis mot de passe
/// </summary>
public class LoginPopup : MonoBehaviour
{
    [Header("UI References")]
    private GameObject popupPanel;
    private GameObject headerPanel; // Panneau d'entête
    private GameObject emailContainer;
    private GameObject passwordContainer;
    private GameObject errorContainer;
    private TMP_InputField emailInput;
    private TMP_InputField passwordInput;
    private Button submitButton;
    private TextMeshProUGUI errorText;
    private TextMeshProUGUI titleText;
    private TextMeshProUGUI subtitleText; // Sous-titre
    private TextMeshProUGUI descriptionText; // Texte descriptif
    private TextMeshProUGUI linkText;
    private string currentEmail;
    private string currentFirstName; // Prénom de l'utilisateur récupéré lors du preconnect
    
    // Références aux images de fond des inputs pour le clignotement
    private Image emailInputBg;
    private Image passwordInputBg;
    private Coroutine emailBlinkCoroutine;
    private Coroutine passwordBlinkCoroutine;
    
    // Références pour la modale Seroni Connect
    private GameObject seroniConnectModal;
    private GameObject seroniConnectModalBackground;
    private bool seroniConnectModalCloseArmed;
    
    // Référence au conteneur de boutons pour mise à jour après chargement config
    private GameObject buttonsContainer;
    
    // Police Anton pour les textes
    private TMP_FontAsset antonFont;

    // Position par défaut du conteneur d'erreur (pour pouvoir l'ajuster ponctuellement sur l'étape mot de passe)
    private Vector2 errorContainerDefaultAnchoredPosition;
    private bool hasErrorContainerDefaultAnchoredPosition;

    // Décalage (vers le bas) spécifique au message "mot de passe incorrect"
    private const float PASSWORD_INCORRECT_ERROR_Y_OFFSET = 20f;

    // Sprites pour le bouton afficher/masquer mot de passe (évite les emojis cassés en WebGL)
    private Sprite passwordEyeOpenSprite;
    private Sprite passwordEyeSlashedSprite;
    private Texture2D passwordEyeOpenTexture;
    private Texture2D passwordEyeSlashedTexture;
    
    // Listes pour tracker les ressources créées dynamiquement (nettoyage WebGL)
    private readonly List<Texture2D> dynamicTextures = new List<Texture2D>();
    private readonly List<Sprite> dynamicSprites = new List<Sprite>();

    [Header("Configuration")]
    // URLs récupérées depuis general-config.json via GeneralConfigManager

    [Header("Visual")]
    public Color panelBackgroundColor = new Color(0.95f, 0.9f, 0.85f, 0.98f); // Couleur beige/sable
    public Color headerBackgroundColor = new Color(0.859f, 0.765f, 0.718f, 1f); // #dbc3b7
    public Color buttonNormalColor = new Color(0.58f, 0.4f, 0.7f, 1f); // Bouton violet
    public Color buttonHoverColor = new Color(0.48f, 0.3f, 0.6f, 1f);
    public Color errorTextColor = new Color(0.9f, 0.2f, 0.2f, 1f);
    public Color linkTextColor = new Color(0.2f, 0.4f, 0.8f, 1f);
    public float cornerRadius = 75f; // Rayon des coins arrondis (augmenté de 50%)

    private Canvas canvas;
    private Action onLoginSuccess;
    private Action onClose;
    
    // Variables pour les inputs HTML sur mobile
    private bool useMobileInputs = false;
    private string emailInputHtmlId = "unity-email-input";
    private string passwordInputHtmlId = "unity-password-input";
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    // Déclarations des fonctions JavaScript pour les inputs HTML natifs
    [DllImport("__Internal")]
    private static extern bool Mobile_IsMobile();
    
    [DllImport("__Internal")]
    private static extern int Mobile_CreateInput(string id, float x, float y, float width, float height, string placeholder, string inputType, float fontSize);
    
    [DllImport("__Internal")]
    private static extern int Mobile_ShowInput(string id, bool show);
    
    [DllImport("__Internal")]
    private static extern int Mobile_SetInputPosition(string id, float x, float y, float width, float height);
    
    [DllImport("__Internal")]
    private static extern int Mobile_GetInputValue(string id, System.IntPtr valuePtr, int maxLen);
    
    [DllImport("__Internal")]
    private static extern int Mobile_SetInputValue(string id, string value);
    
    [DllImport("__Internal")]
    private static extern int Mobile_RemoveInput(string id);
    #endif

    public void Initialize(Action onSuccess = null, Action onCloseCallback = null)
    {
        try
        {
            onLoginSuccess = onSuccess;
            onClose = onCloseCallback;
            
            LoadAntonFont();
            
            #if UNITY_WEBGL && !UNITY_EDITOR
            // DÉSACTIVÉ : Les inputs HTML natifs causent des doublons sur Firefox
            // Les inputs Unity fonctionnent correctement avec WebGLClickReceiver
            useMobileInputs = false;
            
            /* ANCIEN CODE - Désactivé car causait des doublons
            try
            {
                useMobileInputs = Mobile_IsMobile();
            }
            catch (System.Exception)
            {
                useMobileInputs = false;
            }
            */
            #endif
            
            CreatePopup();
            
            if (popupPanel != null) popupPanel.SetActive(true);
            if (canvas != null) canvas.gameObject.SetActive(true);
            
            StartCoroutine(UpdateTextsFromConfig());
            ShowEmailStep();
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[LoginPopup] Erreur Initialize: {e.Message}");
        }
    }
    
    private void LoadAntonFont()
    {
        // Charger la police Anton depuis Resources
        antonFont = Resources.Load<TMP_FontAsset>("Fonts/Anton-Regular SDF");
        if (antonFont == null)
        {
            // Essayer depuis StreamingAssets/fonts
            antonFont = Resources.Load<TMP_FontAsset>("fonts/Anton-Regular SDF");
        }
        if (antonFont == null)
        {
            Debug.LogWarning("[LoginPopup] Police Anton non trouvée dans Resources");
        }
    }
    
    private IEnumerator UpdateTextsFromConfig()
    {
        // Attendre que la configuration soit chargée
        if (GeneralConfigManager.Instance != null)
        {
            yield return GeneralConfigManager.Instance.WaitForConfigLoaded();
        }
        else
        {
            // Attendre un peu si le manager n'existe pas encore
            float timeout = 5f;
            float elapsed = 0f;
            while (GeneralConfigManager.Instance == null && elapsed < timeout)
            {
                yield return new WaitForSeconds(0.1f);
                elapsed += 0.1f;
            }
            if (GeneralConfigManager.Instance != null)
            {
                yield return GeneralConfigManager.Instance.WaitForConfigLoaded();
            }
        }
        
        // Mettre à jour les textes
        var config = GeneralConfigManager.Instance?.GetConfig();
        if (config == null)
        {
            Debug.LogWarning("[LoginPopup] Configuration non disponible");
            yield break;
        }
        
        if (config.connexionPanel == null)
        {
            Debug.LogWarning("[LoginPopup] connexionPanel est null dans la configuration");
            yield break;
        }
        
        if (subtitleText != null)
        {
            subtitleText.text = config.connexionPanel.connexionText ?? "";
            
            if (config.connexionPanel.connexionTextFontSize > 0)
            {
                subtitleText.fontSize = config.connexionPanel.connexionTextFontSize;
            }
            
            if (!string.IsNullOrEmpty(config.connexionPanel.connexionTextColor))
            {
                if (ColorUtility.TryParseHtmlString(config.connexionPanel.connexionTextColor, out Color textColor))
                {
                    subtitleText.color = textColor;
                }
            }
            
            RectTransform subtitleRect = subtitleText.GetComponent<RectTransform>();
            if (subtitleRect != null && config.connexionPanel.connexionTextTopMargin > 0)
            {
                subtitleRect.anchoredPosition = new Vector2(0, -config.connexionPanel.connexionTextTopMargin);
            }
        }
        // Le texte descriptif n'est plus utilisé, on peut le laisser vide ou le supprimer
        if (descriptionText != null)
        {
            descriptionText.text = "";
        }
        
        // Mettre à jour les boutons avec les styles configurés
        UpdateButtonsFromConfig(config);
    }

    private void CreatePopup()
    {
        // TOUJOURS créer un nouveau Canvas dédié pour la popup (pas réutiliser un existant)
        GameObject canvasObj = new GameObject("LoginPopupCanvas");
        canvasObj.SetActive(true); // S'assurer que le canvas est actif dès le début
        canvas = canvasObj.AddComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        canvas.overrideSorting = true;
        canvas.sortingOrder = 32767; // Maximum pour être au-dessus de tout (max int16)
        canvas.enabled = true;
        
        CanvasScaler scaler = canvasObj.AddComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(1920, 1080);
        scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
        scaler.matchWidthOrHeight = 0.5f;
        
        GraphicRaycaster raycaster = canvasObj.AddComponent<GraphicRaycaster>();
        raycaster.enabled = true; // S'assurer que le raycaster est activé
        raycaster.blockingObjects = GraphicRaycaster.BlockingObjects.None; // Ne pas bloquer le rendu
        
        // Configuration spécifique pour WebGL
        #if UNITY_WEBGL && !UNITY_EDITOR
        // S'assurer que le canvas peut recevoir les événements
        canvas.overridePixelPerfect = false;
        #endif
        
        // S'assurer qu'un EventSystem existe pour les interactions
        EnsureEventSystem();

        // Créer un fond sombre qui couvre tout l'écran pour bloquer les clics en arrière-plan
        CreateDarkBackground();
        
        // Récupérer la configuration du panneau
        var config = GeneralConfigManager.Instance?.GetConfig();
        var panelConfig = config?.connexionPanel;
        var panelStyle = config?.panelStyles?.GetStyle(panelConfig?.panelStyle ?? "defaultPanel");
        
        // Dimensions du panneau (depuis config ou valeurs par défaut)
        float panelWidth = panelConfig?.width ?? 750;
        float panelHeight = panelConfig?.height ?? 450;
        
        // Couleurs du panneau (depuis le style ou valeurs par défaut)
        Color bgColor = panelStyle != null ? HexToColor(panelStyle.backgroundColor) : panelBackgroundColor;
        float radius = panelStyle?.cornerRadius ?? cornerRadius;
        
        Debug.Log($"[LoginPopup] Configuration panneau - Style: {panelConfig?.panelStyle}, Taille: {panelWidth}x{panelHeight}");
        
        // Créer un conteneur pour tout (ombre + panneau)
        GameObject panelContainer = new GameObject("PanelContainer");
        panelContainer.transform.SetParent(canvas.transform, false);
        
        RectTransform containerRect = panelContainer.AddComponent<RectTransform>();
        containerRect.anchorMin = new Vector2(0.5f, 0.5f);
        containerRect.anchorMax = new Vector2(0.5f, 0.5f);
        containerRect.pivot = new Vector2(0.5f, 0.5f);
        containerRect.sizeDelta = new Vector2(panelWidth, panelHeight);
        containerRect.anchoredPosition = Vector2.zero;
        
        // S'assurer que le conteneur est au-dessus du fond sombre
        if (darkBackground != null)
        {
            panelContainer.transform.SetAsLastSibling();
        }
        
        // Créer l'ombre comme un élément séparé DERRIÈRE le panneau
        if (panelStyle?.shadow != null && panelStyle.shadow.enabled)
        {
            GameObject shadowObj = new GameObject("PanelShadow");
            shadowObj.transform.SetParent(panelContainer.transform, false);
            
            RectTransform shadowRect = shadowObj.AddComponent<RectTransform>();
            shadowRect.anchorMin = Vector2.zero;
            shadowRect.anchorMax = Vector2.one;
            shadowRect.sizeDelta = Vector2.zero;
            // Décaler l'ombre selon la config
            shadowRect.anchoredPosition = new Vector2(panelStyle.shadow.offsetX, -panelStyle.shadow.offsetY);
            
            Image shadowImage = shadowObj.AddComponent<Image>();
            Color shadowColor = HexToColor(panelStyle.shadow.color);
            shadowImage.sprite = CreateRoundedColorSprite((int)panelWidth, (int)panelHeight, radius, shadowColor);
            shadowImage.color = Color.white;
            shadowImage.raycastTarget = false;
            
            // L'ombre doit être en premier (derrière le panneau)
            shadowObj.transform.SetAsFirstSibling();
            
            Debug.Log($"[LoginPopup] Ombre panneau créée: {panelStyle.shadow.color}, offset: ({panelStyle.shadow.offsetX}, {panelStyle.shadow.offsetY})");
        }
        
        // Créer le panneau principal
        popupPanel = new GameObject("LoginPopup");
        popupPanel.SetActive(true);
        popupPanel.transform.SetParent(panelContainer.transform, false);

        RectTransform panelRect = popupPanel.AddComponent<RectTransform>();
        // Centrer le panneau et lui donner une taille fixe (au lieu du mode stretch)
        panelRect.anchorMin = new Vector2(0.5f, 0.5f);
        panelRect.anchorMax = new Vector2(0.5f, 0.5f);
        panelRect.pivot = new Vector2(0.5f, 0.5f);
        panelRect.sizeDelta = new Vector2(panelWidth, panelHeight);
        panelRect.anchoredPosition = Vector2.zero;

        // Image de fond avec angles arrondis
        Image panelImage = popupPanel.AddComponent<Image>();
        panelImage.raycastTarget = true; // Permet de bloquer les clics en arrière-plan
        panelImage.type = Image.Type.Simple;
        
        // Créer une texture avec des coins arrondis et la couleur du style
        panelImage.sprite = CreateRoundedColorSprite((int)panelWidth, (int)panelHeight, radius, bgColor);
        panelImage.color = Color.white; // La couleur est dans le sprite
        
        // Ajouter un Mask pour masquer tout ce qui dépasse des coins arrondis
        Mask panelMask = popupPanel.AddComponent<Mask>();
        panelMask.showMaskGraphic = true; // Afficher l'image du panneau
        
        // S'assurer que l'image est visible
        panelImage.enabled = true;
        panelImage.SetAllDirty(); // Forcer la mise à jour du rendu
        Canvas.ForceUpdateCanvases(); // Forcer la mise à jour de tous les canvas
        Debug.Log($"[LoginPopup] Panneau créé - Position: {panelRect.anchoredPosition}, Taille: {panelRect.sizeDelta} ({panelWidth}x{panelHeight}), Actif: {popupPanel.activeSelf}, ImageEnabled: {panelImage.enabled}");

        // Bordure supprimée pour un look plus moderne

        // Entête avec fond coloré
        CreateHeader();

        // Conteneur pour l'étape email
        emailContainer = CreateStepContainer("EmailContainer");
        
        // Conteneur pour l'étape mot de passe
        passwordContainer = CreateStepContainer("PasswordContainer");
        
        // Conteneur pour les erreurs
        errorContainer = CreateErrorContainer();
        
        // Créer les boutons en bas du panneau
        CreateButtonsAtBottom();
        
        // Désactiver les managers de scène pour bloquer les interactions
        DisableSceneManagers();
    }
    
    private GameObject darkBackground;
    
    private void CreateDarkBackground()
    {
        darkBackground = new GameObject("DarkBackground");
        darkBackground.transform.SetParent(canvas.transform, false);
        
        RectTransform rect = darkBackground.AddComponent<RectTransform>();
        rect.anchorMin = Vector2.zero;
        rect.anchorMax = Vector2.one;
        rect.offsetMin = Vector2.zero;
        rect.offsetMax = Vector2.zero;
        
        Image image = darkBackground.AddComponent<Image>();
        image.color = new Color(0f, 0f, 0f, 0.5f); // Fond semi-transparent (50% d'opacité)
        image.raycastTarget = true; // IMPORTANT : Bloquer tous les raycasts vers la scène
        
        // Ajouter un Button pour permettre la fermeture en cliquant sur le fond (optionnel)
        // Pour l'instant, on ne permet pas de fermer en cliquant sur le fond
    }
    
    private MainSceneManager mainSceneManager;
    private MapManager mapManager;
    
    private void DisableSceneManagers()
    {
        // Désactiver le MainSceneManager s'il existe
        mainSceneManager = FindFirstObjectByType<MainSceneManager>();
        if (mainSceneManager != null)
        {
            mainSceneManager.enabled = false;
        }
        
        // Désactiver le MapManager s'il existe
        mapManager = FindFirstObjectByType<MapManager>();
        if (mapManager != null)
        {
            mapManager.enabled = false;
        }
    }
    
    private void EnableSceneManagers()
    {
        // Réactiver le MainSceneManager s'il existe
        if (mainSceneManager != null)
        {
            mainSceneManager.enabled = true;
        }
        
        // Réactiver le MapManager s'il existe
        if (mapManager != null)
        {
            mapManager.enabled = true;
        }
    }

    private void CreateHeader()
    {
        // Récupérer la configuration
        var config = GeneralConfigManager.Instance?.GetConfig();
        var panelConfig = config?.connexionPanel;
        var panelStyle = config?.panelStyles?.GetStyle(panelConfig?.panelStyle ?? "defaultPanel");
        var headerStyle = panelStyle?.header;
        
        // Paramètres du header (depuis config ou valeurs par défaut)
        float headerHeight = headerStyle?.height ?? 105;
        Color headerBgColor = headerStyle != null ? HexToColor(headerStyle.backgroundColor) : headerBackgroundColor;
        float headerRadius = headerStyle?.cornerRadius ?? cornerRadius;
        float panelWidth = panelConfig?.width ?? 750;
        
        // Paramètres du titre (depuis config ou valeurs par défaut)
        string titleStr = panelConfig?.title ?? "CONNEXION";
        Color titleColor = !string.IsNullOrEmpty(panelConfig?.titleColor) ? HexToColor(panelConfig.titleColor) : new Color(0.486f, 0.420f, 0.388f, 1f);
        float titleFontSize = panelConfig?.titleFontSize ?? 48;
        string titleFontWeight = panelConfig?.titleFontWeight ?? "bold";
        string titleFontName = panelConfig?.titleFontName ?? "Anton-Regular SDF";
        
        Debug.Log($"[LoginPopup] Header config - Height: {headerHeight}, Title: {titleStr}, Font: {titleFontName}, Weight: {titleFontWeight}");
        
        // Panneau d'entête avec fond coloré
        headerPanel = new GameObject("Header");
        headerPanel.transform.SetParent(popupPanel.transform, false);

        RectTransform headerRect = headerPanel.AddComponent<RectTransform>();
        headerRect.anchorMin = new Vector2(0, 1);
        headerRect.anchorMax = new Vector2(1, 1);
        headerRect.pivot = new Vector2(0.5f, 1);
        headerRect.sizeDelta = new Vector2(0, headerHeight);
        headerRect.anchoredPosition = Vector2.zero;

        // Image de fond de l'entête avec coins arrondis en haut
        Image headerImage = headerPanel.AddComponent<Image>();
        headerImage.raycastTarget = false;
        headerImage.type = Image.Type.Simple;
        headerImage.sprite = CreateHeaderColorSprite((int)panelWidth, (int)headerHeight, headerRadius, headerBgColor);
        headerImage.color = Color.white;

        // Titre
        GameObject titleObj = new GameObject("Title");
        titleObj.transform.SetParent(headerPanel.transform, false);

        RectTransform titleRect = titleObj.AddComponent<RectTransform>();
        titleRect.anchorMin = new Vector2(0, 0);
        titleRect.anchorMax = new Vector2(1, 1);
        titleRect.pivot = new Vector2(0.5f, 0.5f);
        titleRect.sizeDelta = Vector2.zero;
        titleRect.anchoredPosition = Vector2.zero;

        titleText = titleObj.AddComponent<TextMeshProUGUI>();
        titleText.text = titleStr;
        
        // Charger la police configurée (priorité à la police spécifiée)
        TMP_FontAsset titleFont = null;
        
        // Si une police est spécifiée dans la config, l'utiliser
        if (!string.IsNullOrEmpty(titleFontName))
        {
            titleFont = Resources.Load<TMP_FontAsset>($"Fonts/{titleFontName}");
            if (titleFont == null)
            {
                titleFont = Resources.Load<TMP_FontAsset>(titleFontName);
            }
        }
        
        // Si pas de police spécifiée ou non trouvée, utiliser la police par défaut
        if (titleFont == null)
        {
            titleFont = GeneralConfigManager.Instance?.GetDefaultFont();
        }
        
        // Appliquer la police
        if (titleFont != null)
        {
            titleText.font = titleFont;
        }
        
        titleText.fontSize = titleFontSize;
        titleText.color = titleColor;
        titleText.alignment = TextAlignmentOptions.Center;
        titleText.fontStyle = (titleFontWeight == "bold") ? FontStyles.Bold : FontStyles.Normal;
        titleText.raycastTarget = false;

        // Bouton de fermeture (croix) - DÉSACTIVÉ : l'utilisateur ne doit pas pouvoir fermer le panel d'identification
        // GameObject closeButtonObj = new GameObject("CloseButton");
        // closeButtonObj.transform.SetParent(headerPanel.transform, false);
        //
        // RectTransform closeButtonRect = closeButtonObj.AddComponent<RectTransform>();
        // closeButtonRect.anchorMin = new Vector2(1, 0.5f);
        // closeButtonRect.anchorMax = new Vector2(1, 0.5f);
        // closeButtonRect.pivot = new Vector2(1, 0.5f);
        // closeButtonRect.sizeDelta = new Vector2(40, 40);
        // closeButtonRect.anchoredPosition = new Vector2(-10, 0);
        //
        // Image closeButtonImage = closeButtonObj.AddComponent<Image>();
        // closeButtonImage.color = new Color(1f, 1f, 1f, 0.3f);
        // closeButtonImage.raycastTarget = true;
        // closeButtonImage.type = Image.Type.Sliced;
        // closeButtonImage.sprite = CreateRoundedSprite(40, 40, 20f); // Cercle
        //
        // Button closeButton = closeButtonObj.AddComponent<Button>();
        // closeButton.targetGraphic = closeButtonImage;
        // closeButton.interactable = true;
        // closeButton.onClick.AddListener(() => {
        //     Close();
        // });
        //
        // // Texte du bouton (X)
        // GameObject closeTextObj = new GameObject("CloseText");
        // closeTextObj.transform.SetParent(closeButtonObj.transform, false);
        //
        // RectTransform closeTextRect = closeTextObj.AddComponent<RectTransform>();
        // closeTextRect.anchorMin = Vector2.zero;
        // closeTextRect.anchorMax = Vector2.one;
        // closeTextRect.sizeDelta = Vector2.zero;
        //
        // TextMeshProUGUI closeText = closeTextObj.AddComponent<TextMeshProUGUI>();
        // closeText.text = "X"; // Lettre X simple au lieu de l'emoji
        // closeText.fontSize = 24;
        // closeText.color = new Color(0.3f, 0.25f, 0.2f, 1f);
        // closeText.alignment = TextAlignmentOptions.Center;
        // closeText.fontStyle = FontStyles.Bold;
        // closeText.raycastTarget = false;

        // Texte connexionText (en haut de la partie centrale)
        GameObject subtitleObj = new GameObject("Subtitle");
        subtitleObj.transform.SetParent(popupPanel.transform, false);

        // Récupérer la configuration du panneau de connexion
        var connexionPanelConfig = GeneralConfigManager.Instance?.GetConfig()?.connexionPanel;
        
        // Récupérer les valeurs depuis la config avec fallbacks
        string connexionTextValue = connexionPanelConfig?.connexionText ?? "";
        float connexionTextFontSize = connexionPanelConfig?.connexionTextFontSize ?? 30f;
        string connexionTextFontName = connexionPanelConfig?.connexionTextFontName ?? "Lato-Regular SDF";
        float connexionTextTopMargin = connexionPanelConfig?.connexionTextTopMargin ?? 120f;
        string connexionTextColorHex = connexionPanelConfig?.connexionTextColor ?? "#5a3d73";
        
        // Parser la couleur
        Color connexionTextColor = new Color(0.35f, 0.25f, 0.45f, 1f); // Fallback violet
        if (!string.IsNullOrEmpty(connexionTextColorHex))
        {
            ColorUtility.TryParseHtmlString(connexionTextColorHex, out connexionTextColor);
        }

        RectTransform subtitleRect = subtitleObj.AddComponent<RectTransform>();
        subtitleRect.anchorMin = new Vector2(0, 1);
        subtitleRect.anchorMax = new Vector2(1, 1);
        subtitleRect.pivot = new Vector2(0.5f, 1);
        subtitleRect.sizeDelta = new Vector2(-60, 60);
        // Positionner selon la marge configurée
        subtitleRect.anchoredPosition = new Vector2(0, -connexionTextTopMargin);

        subtitleText = subtitleObj.AddComponent<TextMeshProUGUI>();
        subtitleText.text = connexionTextValue;
        subtitleText.fontSize = connexionTextFontSize;
        subtitleText.color = connexionTextColor;
        subtitleText.alignment = TextAlignmentOptions.Center;
        subtitleText.fontStyle = FontStyles.Normal;
        subtitleText.raycastTarget = false;
        
        // Appliquer la font configurée
        TMP_FontAsset connexionFont = Resources.Load<TMP_FontAsset>($"Fonts/{connexionTextFontName}");
        if (connexionFont != null)
        {
            subtitleText.font = connexionFont;
        }
        
        Debug.Log($"[LoginPopup] connexionText configuré: text='{connexionTextValue}', fontSize={connexionTextFontSize}, fontName={connexionTextFontName}, topMargin={connexionTextTopMargin}, color={connexionTextColorHex}");

        // Texte descriptif (utilisé pour les autres étapes, pas pour l'étape email)
        GameObject descriptionObj = new GameObject("Description");
        descriptionObj.transform.SetParent(popupPanel.transform, false);

        RectTransform descriptionRect = descriptionObj.AddComponent<RectTransform>();
        descriptionRect.anchorMin = new Vector2(0, 1);
        descriptionRect.anchorMax = new Vector2(1, 1);
        descriptionRect.pivot = new Vector2(0.5f, 1);
        descriptionRect.sizeDelta = new Vector2(-40, 40);
        descriptionRect.anchoredPosition = new Vector2(0, -170);

        descriptionText = descriptionObj.AddComponent<TextMeshProUGUI>();
        descriptionText.text = ""; // Vide pour l'étape email
        descriptionText.fontSize = 16;
        descriptionText.color = new Color(0.3f, 0.3f, 0.3f, 1f);
        descriptionText.alignment = TextAlignmentOptions.Center;
        descriptionText.raycastTarget = false;
        descriptionObj.SetActive(false); // Caché par défaut pour l'étape email
    }

    private void CreateTitle()
    {
        GameObject titleObj = new GameObject("Title");
        titleObj.transform.SetParent(popupPanel.transform, false);

        RectTransform titleRect = titleObj.AddComponent<RectTransform>();
        titleRect.anchorMin = new Vector2(0, 1);
        titleRect.anchorMax = new Vector2(1, 1);
        titleRect.pivot = new Vector2(0.5f, 1);
        titleRect.sizeDelta = new Vector2(0, 50);
        titleRect.anchoredPosition = new Vector2(0, -10);

        titleText = titleObj.AddComponent<TextMeshProUGUI>();
        titleText.text = "CONNEXION";
        if (antonFont != null)
        {
            titleText.font = antonFont;
        }
        titleText.fontSize = 28;
        titleText.color = new Color(0.2f, 0.2f, 0.2f, 1f);
        titleText.alignment = TextAlignmentOptions.Center;
        titleText.fontStyle = FontStyles.Bold;
        titleText.raycastTarget = false; // Ne pas bloquer les clics
    }

    private GameObject CreateStepContainer(string name)
    {
        GameObject container = new GameObject(name);
        container.transform.SetParent(popupPanel.transform, false);

        RectTransform containerRect = container.AddComponent<RectTransform>();
        containerRect.anchorMin = new Vector2(0, 0);
        containerRect.anchorMax = new Vector2(1, 1);
        containerRect.sizeDelta = Vector2.zero;
        // Le conteneur doit laisser de la place pour les boutons en bas (68px + 30px marge = 98px)
        containerRect.offsetMin = new Vector2(60, 98);
        containerRect.offsetMax = new Vector2(-60, -180);

        VerticalLayoutGroup layout = container.AddComponent<VerticalLayoutGroup>();
        layout.spacing = 30;
        layout.padding = new RectOffset(15, 15, 15, 15);
        layout.childAlignment = TextAnchor.MiddleCenter;
        layout.childControlWidth = false; // Ne pas contrôler la largeur pour respecter la config
        layout.childControlHeight = false;
        layout.childForceExpandWidth = false; // Ne pas forcer l'expansion pour respecter la largeur configurée
        layout.childForceExpandHeight = false;

        container.SetActive(false);
        return container;
    }

    private GameObject CreateErrorContainer()
    {
        GameObject container = new GameObject("ErrorContainer");
        container.transform.SetParent(popupPanel.transform, false);

        RectTransform containerRect = container.AddComponent<RectTransform>();
        containerRect.anchorMin = new Vector2(0, 0);
        containerRect.anchorMax = new Vector2(1, 0);
        containerRect.pivot = new Vector2(0.5f, 0);
        containerRect.sizeDelta = new Vector2(0, 40);
        containerRect.anchoredPosition = new Vector2(0, 60);

        errorText = container.AddComponent<TextMeshProUGUI>();
        errorText.text = "";
        errorText.fontSize = 16;
        errorText.color = errorTextColor;
        errorText.alignment = TextAlignmentOptions.Center;
        errorText.textWrappingMode = TMPro.TextWrappingModes.Normal;
        errorText.raycastTarget = false; // Ne pas bloquer les clics

        // Mémoriser la position "par défaut" pour pouvoir la restaurer dans ShowError()
        errorContainerDefaultAnchoredPosition = containerRect.anchoredPosition;
        hasErrorContainerDefaultAnchoredPosition = true;

        container.SetActive(false);
        return container;
    }


    private void ShowEmailStep()
    {
        HideAllSteps();
        
        emailContainer.SetActive(true);
        titleText.text = "CONNEXION";
        errorContainer.SetActive(false);
        
        // S'assurer que le header est visible
        if (headerPanel != null)
        {
            headerPanel.SetActive(true);
        }
        
        // Supprimer les éléments créés par ShowRegistrationLink() s'ils existent
        Transform existingLink = popupPanel.transform.Find("ModifyEmailLink");
        if (existingLink != null) Destroy(existingLink.gameObject);
        Transform existingBtn = popupPanel.transform.Find("RegistrationButton");
        if (existingBtn != null) Destroy(existingBtn.gameObject);
        Transform existingTxt = popupPanel.transform.Find("RegistrationBottomText");
        if (existingTxt != null) Destroy(existingTxt.gameObject);
        
        // Supprimer les éléments créés par ShowPasswordStep/CreatePasswordInput() s'ils existent
        Transform pwdEmailDisplay = popupPanel.transform.Find("PasswordEmailDisplay");
        if (pwdEmailDisplay != null) Destroy(pwdEmailDisplay.gameObject);
        Transform pwdChangeAccount = popupPanel.transform.Find("PasswordChangeAccountLink");
        if (pwdChangeAccount != null) Destroy(pwdChangeAccount.gameObject);
        Transform pwdForgotLink = popupPanel.transform.Find("PasswordForgotLink");
        if (pwdForgotLink != null) Destroy(pwdForgotLink.gameObject);
        Transform pwdSubmitBtn = popupPanel.transform.Find("PasswordSubmitButton");
        if (pwdSubmitBtn != null) Destroy(pwdSubmitBtn.gameObject);
        Transform pwdInput = popupPanel.transform.Find("PasswordInput");
        if (pwdInput != null) Destroy(pwdInput.gameObject);
        // Réinitialiser la référence passwordInput
        passwordInput = null;
        
        // Réafficher le sous-titre et le réinitialiser à sa valeur d'origine
        if (subtitleText != null)
        {
            subtitleText.gameObject.SetActive(true);
            
            // Réinitialiser le texte et les styles depuis la config
            var connexionPanelConfig = GeneralConfigManager.Instance?.GetConfig()?.connexionPanel;
            subtitleText.text = connexionPanelConfig?.connexionText ?? "";
            subtitleText.fontSize = connexionPanelConfig?.connexionTextFontSize ?? 30f;
            subtitleText.fontStyle = FontStyles.Normal;
            
            // Réinitialiser la couleur
            string connexionTextColorHex = connexionPanelConfig?.connexionTextColor ?? "#5a3d73";
            Color connexionTextColor = new Color(0.35f, 0.25f, 0.45f, 1f);
            if (!string.IsNullOrEmpty(connexionTextColorHex))
            {
                ColorUtility.TryParseHtmlString(connexionTextColorHex, out connexionTextColor);
            }
            subtitleText.color = connexionTextColor;
            
            // Réinitialiser la font
            string connexionTextFontName = connexionPanelConfig?.connexionTextFontName ?? "Lato-Regular SDF";
            TMP_FontAsset connexionFont = Resources.Load<TMP_FontAsset>($"Fonts/{connexionTextFontName}");
            if (connexionFont != null)
            {
                subtitleText.font = connexionFont;
            }
        }
        
        // Cacher le texte descriptif pour l'étape email
        if (descriptionText != null && descriptionText.transform.parent != null)
        {
            descriptionText.transform.parent.gameObject.SetActive(false);
        }
        
        // Réafficher les boutons en bas du panneau pour l'étape email
        GameObject emailButtonsContainer = popupPanel.transform.Find("ButtonsContainer")?.gameObject;
        if (emailButtonsContainer != null)
        {
            emailButtonsContainer.SetActive(true);
        }
        
        // Cacher les boutons de l'étape mot de passe
        if (passwordButtonsContainer != null)
        {
            passwordButtonsContainer.SetActive(false);
        }

        // TOUJOURS détruire et recréer l'input email pour garantir la cohérence
        if (emailInput != null)
        {
            // Chercher et détruire l'ancien input email s'il existe
            Transform[] children = emailContainer.transform.GetComponentsInChildren<Transform>(true);
            foreach (Transform child in children)
            {
                if (child.gameObject.name == "EmailInput")
                {
                    if (child != emailContainer.transform)
                    {
                        Destroy(child.gameObject);
                    }
                }
            }
            emailInput = null;
            emailInputBg = null;
        }
        
        // Toujours créer l'input (première fois ou après destruction)
        CreateEmailInput();

        #if UNITY_WEBGL && !UNITY_EDITOR
        // Si on est sur mobile WebGL, créer AUSSI l'input HTML natif par-dessus
        if (useMobileInputs)
        {
            // Créer ou afficher l'input HTML par-dessus l'input Unity
            StartCoroutine(ShowEmailStepMobile());
        }
        #endif
        
        // S'assurer que le popupPanel est visible (à la fin pour éviter tout conflit)
        if (popupPanel != null)
        {
            popupPanel.SetActive(true);
        }
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    private IEnumerator ShowEmailStepMobile()
    {
        yield return new WaitForEndOfFrame();
        yield return new WaitForSeconds(0.1f);
        
        // Créer l'input HTML s'il n'existe pas
        bool success = false;
        float x = 0, y = 0, width = 0, height = 0;
        
        try
        {
            // Calculer la position du conteneur email dans l'écran
            RectTransform containerRect = emailContainer.GetComponent<RectTransform>();
            Vector3[] worldCorners = new Vector3[4];
            containerRect.GetWorldCorners(worldCorners);
            
            // Le conteneur a un padding, on doit trouver où serait l'input
            // L'input est généralement au centre du conteneur, avec une hauteur de 50px
            float containerWidth = worldCorners[2].x - worldCorners[0].x;
            float containerHeight = worldCorners[2].y - worldCorners[0].y;
            
            // Position approximative : au centre vertical du conteneur, largeur du conteneur moins padding
            x = worldCorners[0].x + 10f; // Padding gauche
            y = Screen.height - (worldCorners[1].y - 25f); // Centre vertical moins la moitié de la hauteur
            width = containerWidth - 20f; // Moins padding gauche et droite
            height = 50f;
            
            Mobile_CreateInput(emailInputHtmlId, x, y, width, height, "Adresse email", "email", 20f);
            success = true;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[LoginPopup] Erreur lors de la création de l'input HTML email: {e.Message}");
        }
        
        if (success)
        {
            yield return new WaitForSeconds(0.1f);
            
            try
            {
                Mobile_ShowInput(emailInputHtmlId, true);
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[LoginPopup] Erreur lors de l'affichage de l'input HTML email: {e.Message}");
            }
        }
    }
    #endif

    private void ShowPasswordStep()
    {
        HideAllSteps();
        passwordContainer.SetActive(true);
        
        // Cacher les boutons CONTINUER et SERONI CONNECT de l'étape email
        GameObject emailButtonsContainer = popupPanel.transform.Find("ButtonsContainer")?.gameObject;
        if (emailButtonsContainer != null)
        {
            emailButtonsContainer.SetActive(false);
        }
        
        // Supprimer les anciens éléments de l'écran mot de passe s'ils existent (pour recréer)
        Transform oldEmailDisplay = popupPanel.transform.Find("PasswordEmailDisplay");
        if (oldEmailDisplay != null) Destroy(oldEmailDisplay.gameObject);
        Transform oldChangeAccount = popupPanel.transform.Find("PasswordChangeAccountLink");
        if (oldChangeAccount != null) Destroy(oldChangeAccount.gameObject);
        Transform oldForgotLink = popupPanel.transform.Find("PasswordForgotLink");
        if (oldForgotLink != null) Destroy(oldForgotLink.gameObject);
        Transform oldSubmitBtn = popupPanel.transform.Find("PasswordSubmitButton");
        if (oldSubmitBtn != null) Destroy(oldSubmitBtn.gameObject);
        
        // Récupérer la configuration
        var config = GeneralConfigManager.Instance?.GetConfig();
        var passwordStepConfig = config?.connexionPanel?.passwordStep;
        
        // Mettre à jour le titre de l'entête: "BONJOUR [PRÉNOM]"
        string firstName = !string.IsNullOrEmpty(currentFirstName) ? currentFirstName.ToUpper() : "";
        titleText.text = $"BONJOUR {firstName}";
        
        // Cacher le sous-titre (subtitleText) - on n'en a plus besoin
        if (subtitleText != null)
        {
            subtitleText.gameObject.SetActive(false);
        }
        
        // Cacher le descriptionText par défaut
        if (descriptionText != null && descriptionText.transform.parent != null)
        {
            descriptionText.transform.parent.gameObject.SetActive(false);
        }
        
        errorContainer.SetActive(false);

        Debug.Log($"[LoginPopup] ⚠️ ShowPasswordStep - Début (passwordInput null: {passwordInput == null}, useMobileInputs: {useMobileInputs})");

        // TOUJOURS détruire et recréer les éléments de l'étape password
        // pour s'assurer que tous les éléments (email, liens, bouton) sont présents
        
        // Détruire les anciens éléments s'ils existent
        if (passwordInput != null)
        {
            Debug.Log("[LoginPopup] 🗑️ Destruction des anciens éléments password");
            // Chercher et détruire tous les éléments créés par CreatePasswordInput()
            Transform[] children = popupPanel.transform.GetComponentsInChildren<Transform>(true);
            foreach (Transform child in children)
            {
                if (child.gameObject.name == "PasswordEmailDisplay" ||
                    child.gameObject.name == "PasswordChangeAccountLink" ||
                    child.gameObject.name == "PasswordInput" ||
                    child.gameObject.name == "PasswordForgotLink" ||
                    child.gameObject.name == "PasswordSubmitButton")
                {
                    if (child != popupPanel.transform)
                    {
                        Debug.Log($"[LoginPopup]   - Détruit: {child.gameObject.name}");
                        Destroy(child.gameObject);
                    }
                }
            }
            passwordInput = null;
            passwordInputBg = null;
        }
        
        // Toujours créer les éléments (première fois ou après destruction)
        Debug.Log("[LoginPopup] 🔨 Appel de CreatePasswordInput()");
        CreatePasswordInput();
        Debug.Log($"[LoginPopup] ✅ CreatePasswordInput() terminé - passwordInput: {(passwordInput != null ? "OK" : "NULL")}");

        #if UNITY_WEBGL && !UNITY_EDITOR
        // Si on est sur mobile WebGL, créer AUSSI l'input HTML natif par-dessus
        if (useMobileInputs)
        {
            Debug.Log("[LoginPopup] 📱 Mode mobile WebGL - création input HTML");
            try
            {
                Mobile_ShowInput(emailInputHtmlId, false);
            }
            catch (System.Exception e)
            {
                Debug.LogWarning($"[LoginPopup] Erreur lors du masquage de l'input HTML email: {e.Message}");
            }
            
            // Créer l'input HTML password par-dessus l'input Unity
            StartCoroutine(ShowPasswordStepMobile());
        }
        #endif
        
        // S'assurer que le popupPanel est visible
        if (popupPanel != null)
        {
            popupPanel.SetActive(true);
        }
        
        Debug.Log("[LoginPopup] 🏁 ShowPasswordStep - Fin");
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    private IEnumerator ShowPasswordStepMobile()
    {
        yield return new WaitForEndOfFrame();
        yield return new WaitForSeconds(0.1f);
        
        // Créer l'input HTML s'il n'existe pas
        bool success = false;
        float x = 0, y = 0, width = 0, height = 0;
        
        try
        {
            // Calculer la position du conteneur password dans l'écran
            RectTransform containerRect = passwordContainer.GetComponent<RectTransform>();
            Vector3[] worldCorners = new Vector3[4];
            containerRect.GetWorldCorners(worldCorners);
            
            // Le conteneur a un padding, on doit trouver où serait l'input
            // L'input est généralement après le label, avec une hauteur de 50px
            float containerWidth = worldCorners[2].x - worldCorners[0].x;
            float containerHeight = worldCorners[2].y - worldCorners[0].y;
            
            // Position approximative : après le label (environ 40px du haut), largeur du conteneur moins padding
            x = worldCorners[0].x + 10f; // Padding gauche
            y = Screen.height - (worldCorners[1].y - 80f); // Après le label et le message
            width = containerWidth - 20f; // Moins padding gauche et droite
            height = 50f;
            
            Mobile_CreateInput(passwordInputHtmlId, x, y, width, height, "Entrez votre mot de passe", "password", 20f);
            success = true;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[LoginPopup] Erreur lors de la création de l'input HTML password: {e.Message}");
        }
        
        if (success)
        {
            yield return new WaitForSeconds(0.1f);
            
            try
            {
                Mobile_ShowInput(passwordInputHtmlId, true);
            }
            catch (System.Exception e)
            {
                Debug.LogError($"[LoginPopup] Erreur lors de l'affichage de l'input HTML password: {e.Message}");
            }
        }
    }
    #endif

    private void HideAllSteps()
    {
        #if UNITY_WEBGL && !UNITY_EDITOR
        // Masquer les inputs HTML sur mobile
        if (useMobileInputs)
        {
            try
            {
                Mobile_ShowInput(emailInputHtmlId, false);
                Mobile_ShowInput(passwordInputHtmlId, false);
            }
            catch (System.Exception e)
            {
                Debug.LogWarning($"[LoginPopup] Erreur lors du masquage des inputs HTML: {e.Message}");
            }
        }
        #endif
        
        if (emailContainer != null) emailContainer.SetActive(false);
        if (passwordContainer != null) passwordContainer.SetActive(false);
    }

    private void CreateEmailInput()
    {
        // Récupérer la configuration du champ email
        var config = GeneralConfigManager.Instance?.GetConfig();
        var emailConfig = config?.connexionPanel?.emailInput;
        
        float inputWidth = emailConfig?.width ?? 600;
        float inputHeight = emailConfig?.height ?? 60;
        float borderRadius = emailConfig?.borderRadius ?? 12;
        float borderWidth = emailConfig?.borderWidth ?? 1;
        Color bgColor = !string.IsNullOrEmpty(emailConfig?.backgroundColor) ? HexToColor(emailConfig.backgroundColor) : Color.white;
        Color borderColor = !string.IsNullOrEmpty(emailConfig?.borderColor) ? HexToColor(emailConfig.borderColor) : new Color(0.8f, 0.8f, 0.8f);
        Color textColor = !string.IsNullOrEmpty(emailConfig?.textColor) ? HexToColor(emailConfig.textColor) : new Color(0.2f, 0.2f, 0.2f);
        Color placeholderColor = !string.IsNullOrEmpty(emailConfig?.placeholderColor) ? HexToColor(emailConfig.placeholderColor) : new Color(0.5f, 0.5f, 0.5f);
        float fontSize = emailConfig?.fontSize ?? 24;
        
        Debug.Log($"[LoginPopup] Email input config - width: {inputWidth}, height: {inputHeight}, bgColor: {emailConfig?.backgroundColor}");
        
        // Input field Unity (desktop)
        GameObject inputObj = new GameObject("EmailInput");
        inputObj.transform.SetParent(emailContainer.transform, false);
        RectTransform inputRect = inputObj.AddComponent<RectTransform>();
        inputRect.sizeDelta = new Vector2(inputWidth, inputHeight);
        
        // Ajouter LayoutElement pour préserver la taille
        LayoutElement layoutElement = inputObj.AddComponent<LayoutElement>();
        layoutElement.preferredWidth = inputWidth;
        layoutElement.preferredHeight = inputHeight;

        Image inputBg = inputObj.AddComponent<Image>();
        inputBg.raycastTarget = true; // Permet les clics sur l'input
        inputBg.type = Image.Type.Simple;
        
        // Créer le sprite avec bordure si configurée
        if (borderWidth > 0)
        {
            inputBg.sprite = CreateInputFieldSprite((int)inputWidth, (int)inputHeight, borderRadius, bgColor, borderColor, borderWidth);
        }
        else
        {
            inputBg.sprite = CreateRoundedColorSprite((int)inputWidth, (int)inputHeight, borderRadius, bgColor);
        }
        inputBg.color = Color.white;
        emailInputBg = inputBg; // Sauvegarder la référence pour le clignotement

        emailInput = inputObj.AddComponent<TMP_InputField>();
        emailInput.interactable = true; // S'assurer que l'input est interactif
        emailInput.enabled = true;
        emailInput.readOnly = false;
        emailInput.contentType = TMP_InputField.ContentType.EmailAddress;
        emailInput.textComponent = CreateInputText(inputObj.transform, textColor, fontSize);
        emailInput.placeholder = CreatePlaceholder(inputObj.transform, "Adresse email", placeholderColor, fontSize);
        
        // Configuration spécifique pour WebGL
        #if UNITY_WEBGL && !UNITY_EDITOR
        emailInput.shouldHideMobileInput = false; // Important pour WebGL
        emailInput.shouldHideSoftKeyboard = false;
        emailInput.richText = false; // Désactiver le rich text pour WebGL
        #endif
        
        // Ajouter les listeners pour le clignotement
        emailInput.onSelect.AddListener((string text) => {
            StartInputBlink(emailInputBg, ref emailBlinkCoroutine);
        });
        emailInput.onDeselect.AddListener((string text) => {
            StopInputBlink(ref emailBlinkCoroutine);
        });
        emailInput.onValueChanged.AddListener((string text) => {
        });
        
        // Gérer la touche Enter pour soumettre
        emailInput.onSubmit.AddListener((string text) => {
            OnEmailSubmit();
        });
        
        // Forcer l'activation et le focus en WebGL après un court délai
        #if UNITY_WEBGL && !UNITY_EDITOR
        StartCoroutine(ActivateInputFieldWebGL(emailInput));
        
        // Si on est sur mobile WebGL, créer AUSSI l'input HTML natif par-dessus
        if (useMobileInputs)
        {
            // Les boutons sont créés en bas du panneau via CreateButtonsAtBottom()
            // Créer l'input HTML par-dessus l'input Unity
            StartCoroutine(ShowEmailStepMobile());
        }
        #endif
    }
    
    private void CreateButtonsAtBottom()
    {
        // Récupérer les marges depuis la config
        var config = GeneralConfigManager.Instance?.GetConfig();
        float bottomMargin = config?.connexionPanel?.buttonsBottomMargin ?? 20;
        float topMargin = config?.connexionPanel?.buttonsTopMargin ?? 0;
        
        // Conteneur pour les boutons (horizontal)
        buttonsContainer = new GameObject("ButtonsContainer");
        buttonsContainer.transform.SetParent(popupPanel.transform, false);
        
        RectTransform buttonsContainerRect = buttonsContainer.AddComponent<RectTransform>();
        buttonsContainerRect.sizeDelta = new Vector2(0, 100); // Largeur = parent, hauteur = 100px pour les boutons
        
        // Utiliser topMargin si défini (> 0), sinon utiliser bottomMargin
        if (topMargin > 0)
        {
            // Ancrer en haut
            buttonsContainerRect.anchorMin = new Vector2(0, 1);
            buttonsContainerRect.anchorMax = new Vector2(1, 1);
            buttonsContainerRect.pivot = new Vector2(0.5f, 1);
            buttonsContainerRect.anchoredPosition = new Vector2(0, -topMargin);
            Debug.Log($"[LoginPopup] Boutons email - marge haut: {topMargin}px");
        }
        else
        {
            // Ancrer en bas (comportement par défaut)
            buttonsContainerRect.anchorMin = new Vector2(0, 0);
            buttonsContainerRect.anchorMax = new Vector2(1, 0);
            buttonsContainerRect.pivot = new Vector2(0.5f, 0);
            buttonsContainerRect.anchoredPosition = new Vector2(0, bottomMargin);
            Debug.Log($"[LoginPopup] Boutons email - marge bas: {bottomMargin}px");
        }
        
        HorizontalLayoutGroup buttonsLayout = buttonsContainer.AddComponent<HorizontalLayoutGroup>();
        buttonsLayout.spacing = 22.5f;
        buttonsLayout.childAlignment = TextAnchor.MiddleCenter;
        buttonsLayout.childControlWidth = false;
        buttonsLayout.childControlHeight = true;
        buttonsLayout.childForceExpandWidth = false;
        buttonsLayout.childForceExpandHeight = false;
        buttonsLayout.padding = new RectOffset(0, 0, 0, 0);

        // Récupérer la configuration des boutons depuis general-config.json (config déjà récupéré plus haut)
        var buttonsConfig = config?.connexionPanel?.buttons;
        var buttonStyles = config?.buttonStyles;
        
        // Debug logs
        Debug.Log($"[LoginPopup] CreateButtonsAtBottom - config: {(config != null ? "OK" : "NULL")}");
        Debug.Log($"[LoginPopup] CreateButtonsAtBottom - buttonsConfig: {(buttonsConfig != null ? "OK" : "NULL")}");
        Debug.Log($"[LoginPopup] CreateButtonsAtBottom - buttonStyles: {(buttonStyles != null ? "OK" : "NULL")}");
        
        // Bouton de soumission "CONTINUER" - utiliser le style configuré ou fallback
        string continueLabel = buttonsConfig?.continueButton?.label ?? "CONTINUER";
        string continueStyleName = buttonsConfig?.continueButton?.style ?? "";
        ButtonStyleConfig continueStyle = !string.IsNullOrEmpty(continueStyleName) ? buttonStyles?.GetStyle(continueStyleName) : null;
        
        Debug.Log($"[LoginPopup] Continue button - label: {continueLabel}, styleName: {continueStyleName}, style: {(continueStyle != null ? "OK" : "NULL")}");
        
        if (continueStyle != null)
        {
            Debug.Log($"[LoginPopup] ✅ Création bouton CONTINUER avec style {continueStyleName}");
            CreateStyledButton(buttonsContainer.transform, continueLabel, continueStyle, OnEmailSubmit, true);
        }
        else
        {
            Debug.Log($"[LoginPopup] ⚠️ Fallback pour bouton CONTINUER (pas de style trouvé)");
            CreateSubmitButton(buttonsContainer.transform, continueLabel, OnEmailSubmit);
        }
        
        // Bouton "SERONI CONNECT" - utiliser le style configuré ou fallback
        string seroniLabel = buttonsConfig?.seroniConnectButton?.label ?? "SERONI CONNECT";
        string seroniStyleName = buttonsConfig?.seroniConnectButton?.style ?? "";
        ButtonStyleConfig seroniStyle = !string.IsNullOrEmpty(seroniStyleName) ? buttonStyles?.GetStyle(seroniStyleName) : null;
        
        Debug.Log($"[LoginPopup] Seroni button - label: {seroniLabel}, styleName: {seroniStyleName}, style: {(seroniStyle != null ? "OK" : "NULL")}");
        
        if (seroniStyle != null)
        {
            Debug.Log($"[LoginPopup] ✅ Création bouton SERONI avec style {seroniStyleName}");
            CreateStyledButton(buttonsContainer.transform, seroniLabel, seroniStyle, ShowSeroniConnectModal, false);
        }
        else
        {
            Debug.Log($"[LoginPopup] ⚠️ Fallback pour bouton SERONI (pas de style trouvé)");
            CreateSeroniConnectButton(buttonsContainer.transform);
        }
    }
    
    /// <summary>
    /// Met à jour les boutons avec les styles configurés une fois la config chargée
    /// </summary>
    private void UpdateButtonsFromConfig(GeneralConfig config)
    {
        if (buttonsContainer == null)
        {
            Debug.LogWarning("[LoginPopup] buttonsContainer est null, impossible de mettre à jour les boutons");
            return;
        }
        
        var buttonsConfig = config?.connexionPanel?.buttons;
        var buttonStyles = config?.buttonStyles;
        
        Debug.Log($"[LoginPopup] UpdateButtonsFromConfig - buttonsConfig: {(buttonsConfig != null ? "OK" : "NULL")}");
        Debug.Log($"[LoginPopup] UpdateButtonsFromConfig - buttonStyles: {(buttonStyles != null ? "OK" : "NULL")}");
        
        // Vérifier si on a les styles
        if (buttonStyles == null || buttonsConfig == null)
        {
            Debug.Log("[LoginPopup] Pas de styles de boutons configurés, on garde les boutons par défaut");
            return;
        }
        
        string continueStyleName = buttonsConfig?.continueButton?.style ?? "";
        string seroniStyleName = buttonsConfig?.seroniConnectButton?.style ?? "";
        
        ButtonStyleConfig continueStyle = !string.IsNullOrEmpty(continueStyleName) ? buttonStyles.GetStyle(continueStyleName) : null;
        ButtonStyleConfig seroniStyle = !string.IsNullOrEmpty(seroniStyleName) ? buttonStyles.GetStyle(seroniStyleName) : null;
        
        Debug.Log($"[LoginPopup] UpdateButtonsFromConfig - continueStyle '{continueStyleName}': {(continueStyle != null ? "OK" : "NULL")}");
        Debug.Log($"[LoginPopup] UpdateButtonsFromConfig - seroniStyle '{seroniStyleName}': {(seroniStyle != null ? "OK" : "NULL")}");
        
        // Si on a au moins un style, recréer les boutons
        if (continueStyle != null || seroniStyle != null)
        {
            Debug.Log("[LoginPopup] 🔄 Recréation des boutons avec les styles configurés...");
            
            // Supprimer les anciens boutons
            foreach (Transform child in buttonsContainer.transform)
            {
                Destroy(child.gameObject);
            }
            
            // Recréer avec les styles
            string continueLabel = buttonsConfig?.continueButton?.label ?? "CONTINUER";
            string seroniLabel = buttonsConfig?.seroniConnectButton?.label ?? "SERONI CONNECT";
            
            if (continueStyle != null)
            {
                Debug.Log($"[LoginPopup] ✅ Création bouton CONTINUER avec style {continueStyleName}");
                CreateStyledButton(buttonsContainer.transform, continueLabel, continueStyle, OnEmailSubmit, true);
            }
            else
            {
                CreateSubmitButton(buttonsContainer.transform, continueLabel, OnEmailSubmit);
            }
            
            if (seroniStyle != null)
            {
                Debug.Log($"[LoginPopup] ✅ Création bouton SERONI avec style {seroniStyleName}");
                CreateStyledButton(buttonsContainer.transform, seroniLabel, seroniStyle, ShowSeroniConnectModal, false);
            }
            else
            {
                CreateSeroniConnectButton(buttonsContainer.transform);
            }
        }
    }
    
    /// <summary>
    /// Crée un bouton stylé basé sur la configuration ButtonStyleConfig
    /// </summary>
    private void CreateStyledButton(Transform parent, string buttonText, ButtonStyleConfig style, Action onClick, bool isSubmitButton)
    {
        GameObject buttonObj = new GameObject(isSubmitButton ? "SubmitButton" : "StyledButton");
        buttonObj.transform.SetParent(parent, false);
        RectTransform buttonRect = buttonObj.AddComponent<RectTransform>();
        buttonRect.sizeDelta = new Vector2(style.width, style.height);
        
        // Ajouter LayoutElement pour préserver la taille dans le HorizontalLayoutGroup
        LayoutElement layoutElement = buttonObj.AddComponent<LayoutElement>();
        layoutElement.preferredWidth = style.width;
        layoutElement.preferredHeight = style.height;
        layoutElement.minWidth = style.width;
        layoutElement.minHeight = style.height;

        Image buttonImage = buttonObj.AddComponent<Image>();
        
        // Appliquer le dégradé ou la couleur de base
        Color startColor = HexToColor(style.gradient?.startColor ?? "#b87aff");
        Color endColor = HexToColor(style.gradient?.endColor ?? "#7b4fbf");
        
        // Pour le dégradé, on utilise une couleur moyenne (Unity UI standard ne supporte pas les dégradés natifs)
        // On crée un sprite avec dégradé ET bordure
        Color borderColor = HexToColor(style.borderColor ?? "#f5ece5");
        float borderWidth = style.borderWidth;
        
        if (style.gradient != null && style.gradient.enabled)
        {
            Debug.Log($"[LoginPopup] Création sprite dégradé: {style.width}x{style.height}, radius={style.borderRadius}, border={borderWidth}");
            Debug.Log($"[LoginPopup] Couleurs: start={style.gradient.startColor}, end={style.gradient.endColor}, border={style.borderColor}");
            buttonImage.sprite = CreateGradientSpriteWithBorder((int)style.width, (int)style.height, style.borderRadius, startColor, endColor, borderColor, borderWidth);
            buttonImage.color = Color.white; // Le sprite contient déjà les couleurs
        }
        else
        {
            buttonImage.sprite = CreateRoundedSprite((int)style.width, (int)style.height, style.borderRadius);
            buttonImage.color = startColor;
        }
        
        buttonImage.raycastTarget = true;
        buttonImage.type = Image.Type.Simple; // Simple au lieu de Sliced pour les sprites générés
        
        // Ajouter l'ombre si configurée
        if (style.shadow != null && style.shadow.enabled)
        {
            Shadow shadowComponent = buttonObj.AddComponent<Shadow>();
            shadowComponent.effectColor = HexToColor(style.shadow.color ?? "#00000040");
            shadowComponent.effectDistance = new Vector2(style.shadow.offsetX, -style.shadow.offsetY);
            Debug.Log($"[LoginPopup] 🌑 Ombre ajoutée: color={style.shadow.color}, offset=({style.shadow.offsetX}, {style.shadow.offsetY})");
        }

        Button button = buttonObj.AddComponent<Button>();
        button.targetGraphic = buttonImage;
        button.interactable = true;

        // Calculer les couleurs pour les états hover/pressed
        float hoverMultiplier = style.hover?.brightnessMultiplier ?? 1.1f;
        float pressedMultiplier = style.pressed?.brightnessMultiplier ?? 0.9f;
        
        ColorBlock colors = button.colors;
        colors.normalColor = Color.white;
        colors.highlightedColor = new Color(hoverMultiplier, hoverMultiplier, hoverMultiplier, 1f);
        colors.pressedColor = new Color(pressedMultiplier, pressedMultiplier, pressedMultiplier, 1f);
        button.colors = colors;

        // Texte du bouton
        GameObject textObj = new GameObject("Text");
        textObj.transform.SetParent(buttonObj.transform, false);
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;
        textRect.offsetMin = new Vector2(20, 10);
        textRect.offsetMax = new Vector2(-20, -10);

        TextMeshProUGUI text = textObj.AddComponent<TextMeshProUGUI>();
        text.text = buttonText;
        
        // Charger la police (priorité : style spécifié > police par défaut)
        TMP_FontAsset buttonFont = null;
        string fontFamily = style.text?.fontFamily;
        
        if (!string.IsNullOrEmpty(fontFamily))
        {
            buttonFont = Resources.Load<TMP_FontAsset>($"Fonts/{fontFamily}");
            if (buttonFont == null)
            {
                buttonFont = Resources.Load<TMP_FontAsset>(fontFamily);
            }
        }
        
        // Si pas de police spécifiée ou non trouvée, utiliser la police par défaut
        if (buttonFont == null)
        {
            buttonFont = GeneralConfigManager.Instance?.GetDefaultFont();
        }
        
        if (buttonFont != null)
        {
            text.font = buttonFont;
        }
        
        text.fontSize = style.text?.fontSize ?? 28;
        text.color = HexToColor(style.text?.color ?? "#FFFFFF");
        text.alignment = TextAlignmentOptions.Center;
        // Utiliser le fontWeight de la config via la méthode helper
        text.fontStyle = style.text?.GetFontStyle() ?? FontStyles.Normal;
        text.raycastTarget = false;

        // Stocker la référence si c'est le bouton submit
        if (isSubmitButton)
        {
            submitButton = button;
        }

        button.onClick.AddListener(() => {
            Debug.Log($"[LoginPopup] Bouton stylé cliqué: {buttonText}");
            onClick?.Invoke();
        });
        
        // Ajouter EventTrigger pour WebGL
        #if UNITY_WEBGL && !UNITY_EDITOR
        UnityEngine.EventSystems.EventTrigger trigger = buttonObj.AddComponent<UnityEngine.EventSystems.EventTrigger>();
        
        UnityEngine.EventSystems.EventTrigger.Entry entryDown = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryDown.eventID = UnityEngine.EventSystems.EventTriggerType.PointerDown;
        entryDown.callback.AddListener((data) => {
            Debug.Log($"[LoginPopup] EventTrigger PointerDown sur bouton stylé: {buttonText}");
            onClick?.Invoke();
        });
        trigger.triggers.Add(entryDown);
        
        UnityEngine.EventSystems.EventTrigger.Entry entryClick = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryClick.eventID = UnityEngine.EventSystems.EventTriggerType.PointerClick;
        entryClick.callback.AddListener((data) => {
            Debug.Log($"[LoginPopup] EventTrigger PointerClick sur bouton stylé: {buttonText}");
            onClick?.Invoke();
        });
        trigger.triggers.Add(entryClick);
        #endif
    }
    
    /// <summary>
    /// Crée un sprite avec un dégradé vertical et une bordure
    /// </summary>
    private Sprite CreateGradientSpriteWithBorder(int width, int height, float borderRadius, Color topColor, Color bottomColor, Color borderColor, float borderWidth)
    {
        Texture2D texture = new Texture2D(width, height);
        texture.filterMode = FilterMode.Bilinear;
        
        for (int y = 0; y < height; y++)
        {
            // Interpolation du dégradé (de haut en bas)
            float t = (float)y / height;
            Color gradientColor = Color.Lerp(bottomColor, topColor, t);
            
            for (int x = 0; x < width; x++)
            {
                // Calculer la distance depuis les bords pour les coins arrondis
                float distFromEdge = CalculateDistanceFromRoundedEdge(x, y, width, height, borderRadius);
                
                if (distFromEdge < 0)
                {
                    // En dehors du bouton (coin arrondi)
                    texture.SetPixel(x, y, Color.clear);
                }
                else if (distFromEdge < borderWidth)
                {
                    // Dans la bordure
                    texture.SetPixel(x, y, borderColor);
                }
                else
                {
                    // Dans le contenu (dégradé)
                    texture.SetPixel(x, y, gradientColor);
                }
            }
        }
        
        texture.Apply();
        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
        Debug.Log($"[LoginPopup] 🖼️ Sprite créé avec bordure: {width}x{height}, border={borderWidth}, sprite null? {sprite == null}");
        return sprite;
    }
    
    /// <summary>
    /// Calcule la distance depuis le bord arrondi du bouton
    /// Retourne une valeur négative si le point est en dehors, positive si à l'intérieur
    /// </summary>
    private float CalculateDistanceFromRoundedEdge(int x, int y, int width, int height, float radius)
    {
        // Distance depuis les bords droits
        float distLeft = x;
        float distRight = width - 1 - x;
        float distBottom = y;
        float distTop = height - 1 - y;
        
        // Vérifier les coins
        // Coin bas-gauche
        if (x < radius && y < radius)
        {
            float dx = radius - x;
            float dy = radius - y;
            float distFromCenter = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - distFromCenter;
        }
        // Coin bas-droit
        if (x > width - 1 - radius && y < radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = radius - y;
            float distFromCenter = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - distFromCenter;
        }
        // Coin haut-gauche
        if (x < radius && y > height - 1 - radius)
        {
            float dx = radius - x;
            float dy = y - (height - 1 - radius);
            float distFromCenter = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - distFromCenter;
        }
        // Coin haut-droit
        if (x > width - 1 - radius && y > height - 1 - radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = y - (height - 1 - radius);
            float distFromCenter = Mathf.Sqrt(dx * dx + dy * dy);
            return radius - distFromCenter;
        }
        
        // Pour les bords droits, retourner la distance minimale
        return Mathf.Min(distLeft, Mathf.Min(distRight, Mathf.Min(distBottom, distTop)));
    }
    
    /// <summary>
    /// Crée un sprite avec un dégradé vertical (sans bordure, pour compatibilité)
    /// </summary>
    private Sprite CreateGradientSprite(int width, int height, float borderRadius, Color topColor, Color bottomColor)
    {
        return CreateGradientSpriteWithBorder(width, height, borderRadius, topColor, bottomColor, Color.clear, 0);
    }
    
    /// <summary>
    /// Convertit une couleur hexadécimale en Color Unity
    /// </summary>
    private Color HexToColor(string hex)
    {
        if (string.IsNullOrEmpty(hex)) return Color.white;
        
        hex = hex.TrimStart('#');
        
        if (hex.Length == 6)
        {
            hex += "FF"; // Ajouter l'alpha si absent
        }
        
        if (hex.Length == 8)
        {
            byte r = Convert.ToByte(hex.Substring(0, 2), 16);
            byte g = Convert.ToByte(hex.Substring(2, 2), 16);
            byte b = Convert.ToByte(hex.Substring(4, 2), 16);
            byte a = Convert.ToByte(hex.Substring(6, 2), 16);
            return new Color(r / 255f, g / 255f, b / 255f, a / 255f);
        }
        
        return Color.white;
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    private void CreateEmailInputMobile()
    {
        // Cette méthode n'est plus utilisée directement, elle est appelée via ShowEmailStepMobile
        // Gardée pour compatibilité
    }
    
    private IEnumerator ShowMobileInputAfterDelay(string inputId, bool show)
    {
        yield return new WaitForSeconds(0.3f);
        try
        {
            Mobile_ShowInput(inputId, show);
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[LoginPopup] Erreur lors de l'affichage de l'input HTML: {e.Message}");
        }
    }
    #endif

    private void CreatePasswordInput()
    {
        Debug.Log("[LoginPopup] 🎨 CreatePasswordInput - Début");
        
        // Récupérer la configuration
        var config = GeneralConfigManager.Instance?.GetConfig();
        var passwordStepConfig = config?.connexionPanel?.passwordStep;
        var passwordInputConfig = passwordStepConfig?.passwordInput;
        var emailInputConfig = config?.connexionPanel?.emailInput;
        var buttonStyles = config?.buttonStyles;
        
        // Utiliser la même couleur de fond que l'email input
        Color bgColor = !string.IsNullOrEmpty(emailInputConfig?.backgroundColor) ? HexToColor(emailInputConfig.backgroundColor) : HexToColor("#ecdfd6");
        Color borderColor = !string.IsNullOrEmpty(emailInputConfig?.borderColor) ? HexToColor(emailInputConfig.borderColor) : HexToColor("#d9bdae");
        float borderRadius = emailInputConfig?.borderRadius ?? 12;
        float borderWidth = emailInputConfig?.borderWidth ?? 2;
        
        float inputWidth = passwordInputConfig?.width ?? 500;
        float inputHeight = passwordInputConfig?.height ?? 60;
        Color textColor = !string.IsNullOrEmpty(passwordInputConfig?.textColor) ? HexToColor(passwordInputConfig.textColor) : new Color(0.2f, 0.2f, 0.2f);
        Color placeholderColor = !string.IsNullOrEmpty(passwordInputConfig?.placeholderColor) ? HexToColor(passwordInputConfig.placeholderColor) : new Color(0.5f, 0.5f, 0.5f);
        string placeholderText = "Mot de passe";
        float fontSize = passwordInputConfig?.fontSize ?? 24;
        bool showToggleButton = passwordInputConfig?.showToggleButton ?? true;
        Color toggleButtonColor = !string.IsNullOrEmpty(passwordInputConfig?.toggleButtonColor) ? HexToColor(passwordInputConfig.toggleButtonColor) : HexToColor("#64477f");
        
        Color purpleTextColor = HexToColor("#64477D");
        TMP_FontAsset latoRegularFont = Resources.Load<TMP_FontAsset>("Fonts/Lato-Regular SDF");
        TMP_FontAsset latoBoldFont = Resources.Load<TMP_FontAsset>("Fonts/Lato-Bold SDF");
        
        // Positions depuis le haut du panel
        float startY = 160f; // Après le header
        float spacing = 15f;
        float currentY = startY;

        Debug.Log($"[LoginPopup] 📧 Création affichage email: {currentEmail}");
        // 1. Afficher l'email saisi
        GameObject emailDisplayObj = new GameObject("PasswordEmailDisplay");
        emailDisplayObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform emailDisplayRect = emailDisplayObj.AddComponent<RectTransform>();
        emailDisplayRect.anchorMin = new Vector2(0.5f, 1f);
        emailDisplayRect.anchorMax = new Vector2(0.5f, 1f);
        emailDisplayRect.pivot = new Vector2(0.5f, 1f);
        emailDisplayRect.anchoredPosition = new Vector2(0, -currentY);
        emailDisplayRect.sizeDelta = new Vector2(500, 35);
        
        TextMeshProUGUI emailDisplayText = emailDisplayObj.AddComponent<TextMeshProUGUI>();
        emailDisplayText.text = currentEmail;
        emailDisplayText.fontSize = 24;
        emailDisplayText.color = purpleTextColor;
        emailDisplayText.alignment = TextAlignmentOptions.Center;
        emailDisplayText.raycastTarget = false;
        if (latoBoldFont != null) emailDisplayText.font = latoBoldFont;
        Debug.Log($"[LoginPopup] ✅ Email display créé: '{emailDisplayText.text}'");
        
        currentY += 35 + spacing;
        
        Debug.Log("[LoginPopup] 🔗 Création lien 'Changez de compte'");
        // 2. Lien "Ce n'est pas vous ? Changez de compte"
        GameObject changeAccountObj = new GameObject("PasswordChangeAccountLink");
        changeAccountObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform changeAccountRect = changeAccountObj.AddComponent<RectTransform>();
        changeAccountRect.anchorMin = new Vector2(0.5f, 1f);
        changeAccountRect.anchorMax = new Vector2(0.5f, 1f);
        changeAccountRect.pivot = new Vector2(0.5f, 1f);
        changeAccountRect.anchoredPosition = new Vector2(0, -currentY);
        changeAccountRect.sizeDelta = new Vector2(500, 30);
        
        TextMeshProUGUI changeAccountText = changeAccountObj.AddComponent<TextMeshProUGUI>();
        changeAccountText.text = "Ce n'est pas vous ? Changez de compte";
        changeAccountText.fontSize = 18;
        changeAccountText.color = purpleTextColor;
        changeAccountText.alignment = TextAlignmentOptions.Center;
        changeAccountText.raycastTarget = true;
        if (latoRegularFont != null) changeAccountText.font = latoRegularFont;
        
        // Ligne de soulignement sous "Changez de compte"
        GameObject underline1 = new GameObject("Underline");
        underline1.transform.SetParent(changeAccountObj.transform, false);
        RectTransform underline1Rect = underline1.AddComponent<RectTransform>();
        underline1Rect.anchorMin = new Vector2(0.5f, 0f);
        underline1Rect.anchorMax = new Vector2(0.5f, 0f);
        underline1Rect.pivot = new Vector2(0.5f, 0f);
        underline1Rect.anchoredPosition = new Vector2(68, 3); // Décalé vers la droite pour "Changez de compte"
        underline1Rect.sizeDelta = new Vector2(142, 1);
        Image underline1Img = underline1.AddComponent<Image>();
        underline1Img.color = purpleTextColor;
        underline1Img.raycastTarget = false;
        
        Button changeAccountButton = changeAccountObj.AddComponent<Button>();
        changeAccountButton.targetGraphic = null;
        changeAccountButton.onClick.AddListener(() => ShowEmailStep());
        
        currentY += 30 + spacing + 10;

        // 3. Champ mot de passe
        GameObject inputObj = new GameObject("PasswordInput");
        inputObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform inputRect = inputObj.AddComponent<RectTransform>();
        inputRect.anchorMin = new Vector2(0.5f, 1f);
        inputRect.anchorMax = new Vector2(0.5f, 1f);
        inputRect.pivot = new Vector2(0.5f, 1f);
        inputRect.anchoredPosition = new Vector2(0, -currentY);
        inputRect.sizeDelta = new Vector2(inputWidth, inputHeight);

        Image inputBg = inputObj.AddComponent<Image>();
        inputBg.raycastTarget = true;
        inputBg.type = Image.Type.Simple;
        
        if (borderWidth > 0)
        {
            inputBg.sprite = CreateInputFieldSprite((int)inputWidth, (int)inputHeight, borderRadius, bgColor, borderColor, borderWidth);
        }
        else
        {
            inputBg.sprite = CreateRoundedColorSprite((int)inputWidth, (int)inputHeight, borderRadius, bgColor);
        }
        inputBg.color = Color.white;
        passwordInputBg = inputBg;

        passwordInput = inputObj.AddComponent<TMP_InputField>();
        passwordInput.interactable = true;
        passwordInput.enabled = true;
        passwordInput.readOnly = false;
        passwordInput.contentType = TMP_InputField.ContentType.Password;
        passwordInput.textComponent = CreateInputText(inputObj.transform, new Vector2(15, 5), new Vector2(-55, -5), textColor, fontSize);
        passwordInput.placeholder = CreatePlaceholder(inputObj.transform, placeholderText, new Vector2(15, 5), new Vector2(-55, -5), placeholderColor, fontSize);
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        passwordInput.shouldHideMobileInput = false;
        passwordInput.shouldHideSoftKeyboard = false;
        passwordInput.richText = false;
        #endif
        
        passwordInput.onSelect.AddListener((string text) => {
            StartInputBlink(passwordInputBg, ref passwordBlinkCoroutine);
        });
        passwordInput.onDeselect.AddListener((string text) => {
            StopInputBlink(ref passwordBlinkCoroutine);
        });
        passwordInput.onSubmit.AddListener((string text) => {
            OnPasswordSubmit();
        });
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        StartCoroutine(ActivateInputFieldWebGL(passwordInput));
        #endif

        if (showToggleButton)
        {
            CreatePasswordToggleButton(inputObj.transform, toggleButtonColor);
        }
        
        currentY += inputHeight + spacing;

        // 4. Lien "Mot de passe oublié"
        GameObject forgotPasswordObj = new GameObject("PasswordForgotLink");
        forgotPasswordObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform forgotRect = forgotPasswordObj.AddComponent<RectTransform>();
        forgotRect.anchorMin = new Vector2(0.5f, 1f);
        forgotRect.anchorMax = new Vector2(0.5f, 1f);
        forgotRect.pivot = new Vector2(0.5f, 1f);
        forgotRect.anchoredPosition = new Vector2(0, -currentY);
        forgotRect.sizeDelta = new Vector2(300, 30);
        
        TextMeshProUGUI forgotPasswordText = forgotPasswordObj.AddComponent<TextMeshProUGUI>();
        forgotPasswordText.text = "Mot de passe oublié ?";
        forgotPasswordText.fontSize = 18;
        forgotPasswordText.color = purpleTextColor;
        forgotPasswordText.alignment = TextAlignmentOptions.Center;
        forgotPasswordText.raycastTarget = true;
        if (latoRegularFont != null) forgotPasswordText.font = latoRegularFont;
        
        // Ligne de soulignement
        GameObject underline2 = new GameObject("Underline");
        underline2.transform.SetParent(forgotPasswordObj.transform, false);
        RectTransform underline2Rect = underline2.AddComponent<RectTransform>();
        underline2Rect.anchorMin = new Vector2(0.5f, 0f);
        underline2Rect.anchorMax = new Vector2(0.5f, 0f);
        underline2Rect.pivot = new Vector2(0.5f, 0f);
        underline2Rect.anchoredPosition = new Vector2(0, 3);
        underline2Rect.sizeDelta = new Vector2(155, 1);
        Image underline2Img = underline2.AddComponent<Image>();
        underline2Img.color = purpleTextColor;
        underline2Img.raycastTarget = false;
        
        Button forgotPasswordButton = forgotPasswordObj.AddComponent<Button>();
        forgotPasswordButton.targetGraphic = null;
        forgotPasswordButton.onClick.AddListener(() => OpenForgotPasswordLink());
        
        currentY += 30 + spacing + 10;

        // 5. Bouton "JE ME CONNECTE"
        ButtonStyleConfig submitStyle = buttonStyles?.GetStyle("validationDefault");
        float btnWidth = submitStyle?.width ?? 300f;
        float btnHeight = submitStyle?.height ?? 80f;
        
        GameObject btnObj = new GameObject("PasswordSubmitButton");
        btnObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform btnRect = btnObj.AddComponent<RectTransform>();
        btnRect.anchorMin = new Vector2(0.5f, 1f);
        btnRect.anchorMax = new Vector2(0.5f, 1f);
        btnRect.pivot = new Vector2(0.5f, 1f);
        btnRect.anchoredPosition = new Vector2(0, -currentY);
        btnRect.sizeDelta = new Vector2(btnWidth, btnHeight);
        
        Image btnImage = btnObj.AddComponent<Image>();
        if (submitStyle?.gradient != null && submitStyle.gradient.enabled)
        {
            Color startColor = HexToColor(submitStyle.gradient.startColor ?? "#b87aff");
            Color endColor = HexToColor(submitStyle.gradient.endColor ?? "#7b4fbf");
            Color btnBorderColor = HexToColor(submitStyle.borderColor ?? "#f5ece5");
            btnImage.sprite = CreateGradientSpriteWithBorder((int)btnWidth, (int)btnHeight, submitStyle.borderRadius, startColor, endColor, btnBorderColor, submitStyle.borderWidth);
            btnImage.color = Color.white;
        }
        else
        {
            btnImage.sprite = CreateRoundedSprite((int)btnWidth, (int)btnHeight, 35f);
            btnImage.color = HexToColor("#b87aff");
        }
        btnImage.raycastTarget = true;
        
        if (submitStyle?.shadow != null && submitStyle.shadow.enabled)
        {
            Shadow shadow = btnObj.AddComponent<Shadow>();
            shadow.effectColor = HexToColor(submitStyle.shadow.color ?? "#00000040");
            shadow.effectDistance = new Vector2(submitStyle.shadow.offsetX, -submitStyle.shadow.offsetY);
        }
        
        Button btn = btnObj.AddComponent<Button>();
        btn.targetGraphic = btnImage;
        btn.onClick.AddListener(() => OnPasswordSubmit());
        
        GameObject txtObj = new GameObject("Text");
        txtObj.transform.SetParent(btnObj.transform, false);
        RectTransform txtRect = txtObj.AddComponent<RectTransform>();
        txtRect.anchorMin = Vector2.zero;
        txtRect.anchorMax = Vector2.one;
        txtRect.sizeDelta = Vector2.zero;
        
        TextMeshProUGUI btnTxt = txtObj.AddComponent<TextMeshProUGUI>();
        btnTxt.text = "JE ME CONNECTE";
        btnTxt.fontSize = submitStyle?.text?.fontSize ?? 28f;
        btnTxt.color = HexToColor(submitStyle?.text?.color ?? "#FFFFFF");
        btnTxt.alignment = TextAlignmentOptions.Center;
        btnTxt.raycastTarget = false;
        // Utiliser le fontWeight de la config
        btnTxt.fontStyle = submitStyle?.text?.GetFontStyle() ?? FontStyles.Normal;
        
        string fontName = submitStyle?.text?.fontFamily ?? "Anton-Regular SDF";
        TMP_FontAsset btnFont = Resources.Load<TMP_FontAsset>($"Fonts/{fontName}");
        if (btnFont != null) btnTxt.font = btnFont;
        
        // Pas besoin du passwordButtonsContainer externe
        passwordButtonsContainer = null;

        Debug.Log("[LoginPopup] ✅ CreatePasswordInput - Fin - Tous les éléments créés:");
        Debug.Log($"[LoginPopup]    - PasswordEmailDisplay: {(GameObject.Find("PasswordEmailDisplay") != null ? "OK" : "ABSENT")}");
        Debug.Log($"[LoginPopup]    - PasswordChangeAccountLink: {(GameObject.Find("PasswordChangeAccountLink") != null ? "OK" : "ABSENT")}");
        Debug.Log($"[LoginPopup]    - PasswordInput: {(passwordInput != null ? "OK" : "ABSENT")}");
        Debug.Log($"[LoginPopup]    - PasswordForgotLink: {(GameObject.Find("PasswordForgotLink") != null ? "OK" : "ABSENT")}");
        Debug.Log($"[LoginPopup]    - PasswordSubmitButton: {(GameObject.Find("PasswordSubmitButton") != null ? "OK" : "ABSENT")}");
    }
    
    private GameObject passwordButtonsContainer;
    
    private void CreatePasswordStepButtons()
    {
        // Récupérer la configuration
        var config = GeneralConfigManager.Instance?.GetConfig();
        var passwordStepConfig = config?.connexionPanel?.passwordStep;
        var buttonStyles = config?.buttonStyles;
        float topMargin = passwordStepConfig?.buttonsTopMargin ?? 410f;
        
        // Conteneur pour le bouton (centré)
        passwordButtonsContainer = new GameObject("PasswordButtonsContainer");
        passwordButtonsContainer.transform.SetParent(popupPanel.transform, false);
        
        RectTransform buttonsContainerRect = passwordButtonsContainer.AddComponent<RectTransform>();
        buttonsContainerRect.sizeDelta = new Vector2(0, 100);
        buttonsContainerRect.anchorMin = new Vector2(0, 1);
        buttonsContainerRect.anchorMax = new Vector2(1, 1);
        buttonsContainerRect.pivot = new Vector2(0.5f, 1);
        buttonsContainerRect.anchoredPosition = new Vector2(0, -topMargin);
        
        HorizontalLayoutGroup buttonsLayout = passwordButtonsContainer.AddComponent<HorizontalLayoutGroup>();
        buttonsLayout.spacing = 22.5f;
        buttonsLayout.childAlignment = TextAnchor.MiddleCenter;
        buttonsLayout.childControlWidth = false;
        buttonsLayout.childControlHeight = true;
        buttonsLayout.childForceExpandWidth = false;
        buttonsLayout.childForceExpandHeight = false;
        
        // Bouton "JE ME CONNECTE" avec le style validationDefault
        string submitLabel = "JE ME CONNECTE";
        ButtonStyleConfig submitStyle = buttonStyles?.GetStyle("validationDefault");
        
        if (submitStyle != null)
        {
            CreateStyledButton(passwordButtonsContainer.transform, submitLabel, submitStyle, OnPasswordSubmit, true);
        }
        else
        {
            CreateSubmitButton(passwordButtonsContainer.transform, submitLabel, OnPasswordSubmit);
        }
        
        // Cacher par défaut (sera affiché dans ShowPasswordStep)
        passwordButtonsContainer.SetActive(false);
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    private void CreatePasswordInputMobile()
    {
        // Cette méthode n'est plus utilisée directement, elle est appelée via ShowPasswordStepMobile
        // Gardée pour compatibilité
    }
    #endif

    private void CreatePasswordToggleButton(Transform parent)
    {
        CreatePasswordToggleButton(parent, HexToColor("#64477f"));
    }
    
    private void CreatePasswordToggleButton(Transform parent, Color activeColor)
    {
        EnsurePasswordToggleSprites();

        GameObject toggleButtonObj = new GameObject("ToggleVisibilityButton");
        toggleButtonObj.transform.SetParent(parent, false);

        RectTransform toggleButtonRect = toggleButtonObj.AddComponent<RectTransform>();
        toggleButtonRect.anchorMin = new Vector2(1, 0.5f);
        toggleButtonRect.anchorMax = new Vector2(1, 0.5f);
        toggleButtonRect.pivot = new Vector2(1, 0.5f);
        toggleButtonRect.sizeDelta = new Vector2(40, 40);
        toggleButtonRect.anchoredPosition = new Vector2(-10, 0);

        Image toggleButtonImage = toggleButtonObj.AddComponent<Image>();
        // Fond transparent, l'icône est portée par un Image enfant
        toggleButtonImage.color = Color.clear;
        toggleButtonImage.raycastTarget = true;

        Button toggleButton = toggleButtonObj.AddComponent<Button>();
        toggleButton.targetGraphic = toggleButtonImage;
        toggleButton.interactable = true;

        ColorBlock colors = toggleButton.colors;
        colors.normalColor = new Color(1f, 1f, 1f, 0f); // Transparent par défaut
        colors.highlightedColor = new Color(0.95f, 0.95f, 0.95f, 1f);
        colors.pressedColor = new Color(0.9f, 0.9f, 0.9f, 1f);
        toggleButton.colors = colors;

        // Icône (Image) - stable sur WebGL
        GameObject iconObj = new GameObject("Icon");
        iconObj.transform.SetParent(toggleButtonObj.transform, false);

        RectTransform iconRect = iconObj.AddComponent<RectTransform>();
        iconRect.anchorMin = Vector2.zero;
        iconRect.anchorMax = Vector2.one;
        iconRect.sizeDelta = Vector2.zero;
        iconRect.anchoredPosition = Vector2.zero;

        Image iconImage = iconObj.AddComponent<Image>();
        iconImage.raycastTarget = false;
        iconImage.preserveAspect = true;
        // Par défaut (mot de passe masqué)
        iconImage.sprite = passwordEyeSlashedSprite;
        iconImage.color = new Color(0.5f, 0.5f, 0.5f, 1f);

        // Variable pour suivre l'état de visibilité
        bool isPasswordVisible = false;

        // Action du bouton
        toggleButton.onClick.AddListener(() => {
            isPasswordVisible = !isPasswordVisible;

            if (isPasswordVisible)
            {
                // Afficher le mot de passe en clair
                passwordInput.contentType = TMP_InputField.ContentType.Standard;
                iconImage.sprite = passwordEyeOpenSprite;
                iconImage.color = activeColor; // Couleur configurée pour indiquer l'état actif
            }
            else
            {
                // Masquer le mot de passe
                passwordInput.contentType = TMP_InputField.ContentType.Password;
                iconImage.sprite = passwordEyeSlashedSprite;
                iconImage.color = new Color(0.5f, 0.5f, 0.5f, 1f); // Gris par défaut
            }

            // IMPORTANT : En WebGL, il faut forcer la reconstruction du champ pour que le changement soit visible
            passwordInput.ForceLabelUpdate();
            
            #if UNITY_WEBGL && !UNITY_EDITOR
            string tempText = passwordInput.text;
            passwordInput.text = "";
            passwordInput.text = tempText;
            #endif
        });
    }

    private void EnsurePasswordToggleSprites()
    {
        if (passwordEyeOpenSprite != null && passwordEyeSlashedSprite != null) return;

        passwordEyeOpenTexture = CreateEyeIconTexture(size: 64, slashed: false);
        passwordEyeSlashedTexture = CreateEyeIconTexture(size: 64, slashed: true);

        passwordEyeOpenSprite = Sprite.Create(
            passwordEyeOpenTexture,
            new Rect(0, 0, passwordEyeOpenTexture.width, passwordEyeOpenTexture.height),
            new Vector2(0.5f, 0.5f),
            100f
        );
        passwordEyeSlashedSprite = Sprite.Create(
            passwordEyeSlashedTexture,
            new Rect(0, 0, passwordEyeSlashedTexture.width, passwordEyeSlashedTexture.height),
            new Vector2(0.5f, 0.5f),
            100f
        );
    }

    private Texture2D CreateEyeIconTexture(int size, bool slashed)
    {
        // Icône "œil" en blanc (à teinter via Image.color), fond transparent
        Texture2D tex = new Texture2D(size, size, TextureFormat.RGBA32, false);
        tex.filterMode = FilterMode.Bilinear;
        tex.wrapMode = TextureWrapMode.Clamp;

        Color clear = new Color(1f, 1f, 1f, 0f);
        Color white = new Color(1f, 1f, 1f, 1f);
        Color[] pixels = new Color[size * size];
        for (int i = 0; i < pixels.Length; i++) pixels[i] = clear;

        float cx = (size - 1) * 0.5f;
        float cy = (size - 1) * 0.5f;

        // Ellipse (contour)
        float rx = size * 0.34f;
        float ry = size * 0.20f;
        float thickness = Mathf.Max(1.5f, size * 0.035f);

        // Pupille
        float pr = size * 0.07f;

        // Slash
        float slashThickness = Mathf.Max(2f, size * 0.05f);

        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float dx = (x - cx);
                float dy = (y - cy);

                // Normalisation ellipse
                float ex = dx / rx;
                float ey = dy / ry;
                float d = Mathf.Sqrt(ex * ex + ey * ey);

                // Contour ellipse ~ d == 1
                float contour = Mathf.Abs(d - 1f) * Mathf.Min(rx, ry);
                bool onContour = contour <= thickness;

                // Pupille
                bool onPupil = (dx * dx + dy * dy) <= (pr * pr);

                bool onSlash = false;
                if (slashed)
                {
                    // Ligne diagonale (haut-gauche -> bas-droite)
                    // Distance point->ligne y = x (dans un repère centré)
                    float dist = Mathf.Abs(dy - dx) / Mathf.Sqrt(2f);
                    // limiter la slash à la zone de l'œil
                    bool within = Mathf.Abs(dy) < ry * 1.4f && Mathf.Abs(dx) < rx * 1.4f;
                    onSlash = within && dist <= slashThickness;
                }

                if (onContour || onPupil || onSlash)
                {
                    pixels[y * size + x] = white;
                }
            }
        }

        tex.SetPixels(pixels);
        tex.Apply(false, false);
        return tex;
    }

    private TextMeshProUGUI CreateInputText(Transform parent)
    {
        return CreateInputText(parent, new Vector2(10, 5), new Vector2(-10, -5));
    }
    
    private TextMeshProUGUI CreateInputText(Transform parent, Color textColor, float fontSize)
    {
        return CreateInputText(parent, new Vector2(15, 5), new Vector2(-15, -5), textColor, fontSize);
    }

    private TextMeshProUGUI CreateInputText(Transform parent, Vector2 offsetMin, Vector2 offsetMax)
    {
        return CreateInputText(parent, offsetMin, offsetMax, new Color(0.2f, 0.2f, 0.2f, 1f), 30);
    }
    
    private TextMeshProUGUI CreateInputText(Transform parent, Vector2 offsetMin, Vector2 offsetMax, Color textColor, float fontSize)
    {
        GameObject textObj = new GameObject("Text");
        textObj.transform.SetParent(parent, false);
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;
        textRect.offsetMin = offsetMin;
        textRect.offsetMax = offsetMax;

        TextMeshProUGUI text = textObj.AddComponent<TextMeshProUGUI>();
        
        // Appliquer la police par défaut
        var defaultFont = GeneralConfigManager.Instance?.GetDefaultFont();
        if (defaultFont != null)
        {
            text.font = defaultFont;
        }
        
        text.fontSize = fontSize;
        text.color = textColor;
        text.alignment = TextAlignmentOptions.Left;
        text.raycastTarget = false; // Ne pas bloquer les clics sur l'input
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        // En WebGL, s'assurer que le texte peut être sélectionné
        text.textWrappingMode = TMPro.TextWrappingModes.NoWrap;
        #endif
        
        return text;
    }

    private TextMeshProUGUI CreatePlaceholder(Transform parent, string placeholderText)
    {
        return CreatePlaceholder(parent, placeholderText, new Vector2(10, 5), new Vector2(-10, -5));
    }
    
    private TextMeshProUGUI CreatePlaceholder(Transform parent, string placeholderText, Color placeholderColor, float fontSize)
    {
        return CreatePlaceholder(parent, placeholderText, new Vector2(15, 5), new Vector2(-15, -5), placeholderColor, fontSize);
    }

    private TextMeshProUGUI CreatePlaceholder(Transform parent, string placeholderText, Vector2 offsetMin, Vector2 offsetMax)
    {
        return CreatePlaceholder(parent, placeholderText, offsetMin, offsetMax, new Color(0.5f, 0.5f, 0.5f, 1f), 30);
    }
    
    private TextMeshProUGUI CreatePlaceholder(Transform parent, string placeholderText, Vector2 offsetMin, Vector2 offsetMax, Color placeholderColor, float fontSize)
    {
        GameObject placeholderObj = new GameObject("Placeholder");
        placeholderObj.transform.SetParent(parent, false);
        RectTransform placeholderRect = placeholderObj.AddComponent<RectTransform>();
        placeholderRect.anchorMin = Vector2.zero;
        placeholderRect.anchorMax = Vector2.one;
        placeholderRect.sizeDelta = Vector2.zero;
        placeholderRect.offsetMin = offsetMin;
        placeholderRect.offsetMax = offsetMax;

        TextMeshProUGUI placeholder = placeholderObj.AddComponent<TextMeshProUGUI>();
        
        // Appliquer la police par défaut
        var defaultFont = GeneralConfigManager.Instance?.GetDefaultFont();
        if (defaultFont != null)
        {
            placeholder.font = defaultFont;
        }
        
        placeholder.text = placeholderText;
        placeholder.fontSize = fontSize;
        placeholder.color = placeholderColor;
        placeholder.alignment = TextAlignmentOptions.Left;
        placeholder.fontStyle = FontStyles.Normal; // Pas d'italique
        placeholder.raycastTarget = false; // Ne pas bloquer les clics sur l'input
        return placeholder;
    }
    
    /// <summary>
    /// Crée un sprite pour un champ input avec fond et bordure
    /// </summary>
    private Sprite CreateInputFieldSprite(int width, int height, float radius, Color bgColor, Color borderColor, float borderWidth)
    {
        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float distFromEdge = CalculateDistanceFromRoundedEdgeFloat(x, y, width, height, radius);
                
                if (distFromEdge < 0)
                {
                    // En dehors (transparent)
                    pixels[y * width + x] = Color.clear;
                }
                else if (distFromEdge < borderWidth)
                {
                    // Dans la bordure
                    pixels[y * width + x] = borderColor;
                }
                else
                {
                    // Dans le fond
                    pixels[y * width + x] = bgColor;
                }
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);

        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
        dynamicSprites.Add(sprite);
        return sprite;
    }
    
    private float CalculateDistanceFromRoundedEdgeFloat(int x, int y, int width, int height, float radius)
    {
        float distLeft = x;
        float distRight = width - 1 - x;
        float distBottom = y;
        float distTop = height - 1 - y;
        
        // Coin bas-gauche
        if (x < radius && y < radius)
        {
            float dx = radius - x;
            float dy = radius - y;
            return radius - Mathf.Sqrt(dx * dx + dy * dy);
        }
        // Coin bas-droit
        if (x > width - 1 - radius && y < radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = radius - y;
            return radius - Mathf.Sqrt(dx * dx + dy * dy);
        }
        // Coin haut-gauche
        if (x < radius && y > height - 1 - radius)
        {
            float dx = radius - x;
            float dy = y - (height - 1 - radius);
            return radius - Mathf.Sqrt(dx * dx + dy * dy);
        }
        // Coin haut-droit
        if (x > width - 1 - radius && y > height - 1 - radius)
        {
            float dx = x - (width - 1 - radius);
            float dy = y - (height - 1 - radius);
            return radius - Mathf.Sqrt(dx * dx + dy * dy);
        }
        
        return Mathf.Min(distLeft, Mathf.Min(distRight, Mathf.Min(distBottom, distTop)));
    }

    private void CreateSeroniConnectButton(Transform parent)
    {
        GameObject buttonObj = new GameObject("SeroniConnectButton");
        buttonObj.transform.SetParent(parent, false);
        RectTransform buttonRect = buttonObj.AddComponent<RectTransform>();
        buttonRect.sizeDelta = new Vector2(290, 216); // Hauteur doublée (108 * 2)

        Image buttonImage = buttonObj.AddComponent<Image>();
        // Couleur beige/marron pour le bouton SERONI CONNECT
        Color seroniButtonColor = new Color(0.8f, 0.7f, 0.6f, 1f); // Beige/marron
        buttonImage.color = seroniButtonColor;
        buttonImage.raycastTarget = true;
        buttonImage.type = Image.Type.Sliced;
        buttonImage.sprite = CreateRoundedSprite(290, 108, 15f);

        Button button = buttonObj.AddComponent<Button>();
        button.targetGraphic = buttonImage;
        button.interactable = true;

        ColorBlock colors = button.colors;
        colors.normalColor = seroniButtonColor;
        colors.highlightedColor = new Color(0.7f, 0.6f, 0.5f, 1f);
        colors.pressedColor = new Color(0.6f, 0.5f, 0.4f, 1f);
        button.colors = colors;

        GameObject textObj = new GameObject("Text");
        textObj.transform.SetParent(buttonObj.transform, false);
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;
        // Padding de 40px
        textRect.offsetMin = new Vector2(40, 40);
        textRect.offsetMax = new Vector2(-40, -40);

        TextMeshProUGUI text = textObj.AddComponent<TextMeshProUGUI>();
        text.text = "SERONI CONNECT ?";
        if (antonFont != null)
        {
            text.font = antonFont;
        }
        text.fontSize = 22;
        text.color = Color.white;
        text.alignment = TextAlignmentOptions.Center;
        text.fontStyle = FontStyles.Bold;
        text.raycastTarget = false;

        button.onClick.AddListener(() => {
            ShowSeroniConnectModal();
        });
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        UnityEngine.EventSystems.EventTrigger trigger = buttonObj.AddComponent<UnityEngine.EventSystems.EventTrigger>();
        
        UnityEngine.EventSystems.EventTrigger.Entry entryDown = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryDown.eventID = UnityEngine.EventSystems.EventTriggerType.PointerDown;
        entryDown.callback.AddListener((data) => {
            ShowSeroniConnectModal();
        });
        trigger.triggers.Add(entryDown);
        
        UnityEngine.EventSystems.EventTrigger.Entry entryClick = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryClick.eventID = UnityEngine.EventSystems.EventTriggerType.PointerClick;
        entryClick.callback.AddListener((data) => {
            ShowSeroniConnectModal();
        });
        trigger.triggers.Add(entryClick);
        #endif
    }
    
    private void ShowSeroniConnectModal()
    {
        // Récupérer le texte depuis la configuration
        string seroniText = GeneralConfigManager.Instance?.GetConfig()?.connexionPanel?.seroniconnectText ?? "";
        // Nettoyer les retours ligne HTML présents dans la config (TMP ne gère pas </br>)
        seroniText = seroniText.Replace("</br>", "\n").Replace("<br>", "\n");
        
        // Si la modale existe déjà, la réutiliser
        if (seroniConnectModal != null)
        {
            seroniConnectModal.SetActive(true);
            seroniConnectModalBackground?.SetActive(true);
            // Mettre à jour le texte
            TextMeshProUGUI existingModalText = seroniConnectModal.transform.Find("Content/ContentText")?.GetComponent<TextMeshProUGUI>();
            if (existingModalText != null)
            {
                existingModalText.text = seroniText;
            }
            return;
        }
        
        // Utiliser le style informationPanel
        var generalConfig = GeneralConfigManager.Instance?.GetConfig();
        var panelStyle = generalConfig?.panelStyles?.GetStyle("informationPanel");
        if (panelStyle == null)
        {
            Debug.LogWarning("[LoginPopup] panelStyles.informationPanel introuvable, annulation de la modale SERONI CONNECT.");
            return;
        }

        float modalWidth = panelStyle.width > 0 ? panelStyle.width : 600f;
        float modalHeight = panelStyle.height > 0 ? panelStyle.height : 400f;

        // Overlay sombre
        seroniConnectModalBackground = new GameObject("SeroniConnectModalBackground");
        seroniConnectModalBackground.transform.SetParent(canvas.transform, false);

        RectTransform bgRect = seroniConnectModalBackground.AddComponent<RectTransform>();
        bgRect.anchorMin = Vector2.zero;
        bgRect.anchorMax = Vector2.one;
        bgRect.offsetMin = Vector2.zero;
        bgRect.offsetMax = Vector2.zero;

        bool overlayEnabled = panelStyle.overlay?.enabled ?? true;
        string overlayColorHex = panelStyle.overlay?.color ?? "#000000";
        float overlayOpacity = panelStyle.overlay?.opacity ?? 0.75f;

        Image bgImage = seroniConnectModalBackground.AddComponent<Image>();
        Color overlayColor = HexToColor(overlayColorHex);
        bgImage.color = overlayEnabled ? new Color(overlayColor.r, overlayColor.g, overlayColor.b, overlayOpacity) : new Color(0, 0, 0, 0);
        bgImage.raycastTarget = true;

        // ⚠️ Important : éviter que le même clic qui ouvre la modale la ferme immédiatement.
        seroniConnectModalCloseArmed = false;
        StartCoroutine(ArmSeroniConnectModalCloseNextFrame());

        UnityEngine.EventSystems.EventTrigger trigger = seroniConnectModalBackground.AddComponent<UnityEngine.EventSystems.EventTrigger>();
        UnityEngine.EventSystems.EventTrigger.Entry clickEntry = new UnityEngine.EventSystems.EventTrigger.Entry();
        clickEntry.eventID = UnityEngine.EventSystems.EventTriggerType.PointerClick;
        clickEntry.callback.AddListener((evt) =>
        {
            if (!seroniConnectModalCloseArmed) return;
            HideSeroniConnectModal();
        });
        trigger.triggers.Add(clickEntry);

        // Panneau stylé
        var panel = StyledPanelBuilder.CreatePanelFromStyle(canvas.transform, panelStyle, modalWidth, modalHeight, "SeroniConnectInformationPanel");
        if (panel == null || panel.panelRect == null)
        {
            Debug.LogWarning("[LoginPopup] Impossible de créer informationPanel pour SERONI CONNECT.");
            HideSeroniConnectModal();
            return;
        }

        seroniConnectModal = panel.panelObject;
        panel.panelRect.anchorMin = new Vector2(0.5f, 0.5f);
        panel.panelRect.anchorMax = new Vector2(0.5f, 0.5f);
        panel.panelRect.pivot = new Vector2(0.5f, 0.5f);
        panel.panelRect.anchoredPosition = Vector2.zero;

        // Mettre au premier plan
        seroniConnectModalBackground.transform.SetAsLastSibling();
        seroniConnectModal.transform.SetAsLastSibling();

        // Header : titre + bouton fermer
        if (panel.headerObject != null && panelStyle.header != null && panelStyle.header.enabled)
        {
            float closeSize = panelStyle.header.closeButton?.size ?? 70f;
            float closeMarginRight = panelStyle.header.closeButton?.marginRight ?? 30f;

            // Titre centré par rapport à toute la largeur du panel
            GameObject titleObj = new GameObject("Title");
            titleObj.transform.SetParent(panel.headerObject.transform, false);
            RectTransform titleRect = titleObj.AddComponent<RectTransform>();
            titleRect.anchorMin = new Vector2(0, 0);
            titleRect.anchorMax = new Vector2(1, 1);
            float reservedSide = closeSize + closeMarginRight;
            titleRect.offsetMin = new Vector2(20 + reservedSide, 0);
            titleRect.offsetMax = new Vector2(-(20 + reservedSide), 0);

            TextMeshProUGUI headerTitle = titleObj.AddComponent<TextMeshProUGUI>();
            headerTitle.text = "SERONI CONNECT";
            headerTitle.alignment = TextAlignmentOptions.Center;
            headerTitle.fontSize = panelStyle.header.titleFontSize > 0 ? panelStyle.header.titleFontSize : 50f;
            headerTitle.color = HexToColor(panelStyle.header.titleColor ?? "#64477f");
            headerTitle.fontStyle = FontStyles.Bold;
            headerTitle.raycastTarget = false;

            if (!string.IsNullOrEmpty(panelStyle.header.titleFontName))
            {
                TMP_FontAsset font = Resources.Load<TMP_FontAsset>($"Fonts/{panelStyle.header.titleFontName}");
                if (font == null) font = Resources.Load<TMP_FontAsset>($"Fonts & Materials/{panelStyle.header.titleFontName}");
                if (font != null) headerTitle.font = font;
            }

            // Bouton fermer (png UI_fermer)
            GameObject closeObj = new GameObject("CloseButton");
            closeObj.transform.SetParent(panel.headerObject.transform, false);
            RectTransform closeRect = closeObj.AddComponent<RectTransform>();
            closeRect.anchorMin = new Vector2(1, 0.5f);
            closeRect.anchorMax = new Vector2(1, 0.5f);
            closeRect.pivot = new Vector2(1, 0.5f);
            closeRect.sizeDelta = new Vector2(closeSize, closeSize);
            closeRect.anchoredPosition = new Vector2(-closeMarginRight, 0);

            Image closeImg = closeObj.AddComponent<Image>();
            closeImg.color = Color.white;
            closeImg.raycastTarget = true;
            closeImg.type = Image.Type.Simple;

            Button closeBtn = closeObj.AddComponent<Button>();
            closeBtn.transition = Selectable.Transition.ColorTint;
            closeBtn.onClick.AddListener(HideSeroniConnectModal);

            string closeImageUrl = panelStyle.header.closeButton?.imageUrl ?? "UI_fermer.png";
            string closeUrl = GeneralConfigManager.Instance != null ? GeneralConfigManager.Instance.GetUIUrl(closeImageUrl) : closeImageUrl;
            if (RemoteSpriteCache.Instance != null)
            {
                StartCoroutine(RemoteSpriteCache.Instance.GetOrLoad(
                    closeUrl,
                    onSuccess: (sp) => { if (closeImg != null) closeImg.sprite = sp; },
                    onError: (err) => { Debug.LogWarning($"[LoginPopup] CloseButton sprite load failed: {err}"); }
                ));
            }
        }

        // Content : texte
        if (panel.contentContainer != null)
        {
            GameObject textObj = new GameObject("ContentText");
            textObj.transform.SetParent(panel.contentContainer.transform, false);
            RectTransform textRect = textObj.AddComponent<RectTransform>();
            textRect.anchorMin = Vector2.zero;
            textRect.anchorMax = Vector2.one;
            textRect.offsetMin = new Vector2(50, 40);
            textRect.offsetMax = new Vector2(-50, -30);

            TextMeshProUGUI contentText = textObj.AddComponent<TextMeshProUGUI>();
            contentText.text = seroniText;
            contentText.alignment = TextAlignmentOptions.Center;
            contentText.textWrappingMode = TextWrappingModes.Normal;
            contentText.raycastTarget = false;

            if (panelStyle.content != null)
            {
                contentText.fontSize = panelStyle.content.fontSize > 0 ? panelStyle.content.fontSize : 22f;
                contentText.color = HexToColor(panelStyle.content.textColor ?? "#64477f");

                if (!string.IsNullOrEmpty(panelStyle.content.fontName))
                {
                    TMP_FontAsset font = Resources.Load<TMP_FontAsset>($"Fonts/{panelStyle.content.fontName}");
                    if (font == null) font = Resources.Load<TMP_FontAsset>($"Fonts & Materials/{panelStyle.content.fontName}");
                    if (font != null) contentText.font = font;
                }
            }
            else
            {
                contentText.fontSize = 22f;
                contentText.color = new Color(0.39f, 0.28f, 0.5f, 1f);
            }
        }
    }
    
    private void HideSeroniConnectModal()
    {
        seroniConnectModalCloseArmed = false;
        if (seroniConnectModal != null)
        {
            seroniConnectModal.SetActive(false);
        }
        if (seroniConnectModalBackground != null)
        {
            seroniConnectModalBackground.SetActive(false);
        }
    }

    private IEnumerator ArmSeroniConnectModalCloseNextFrame()
    {
        yield return null; // attendre 1 frame
        seroniConnectModalCloseArmed = true;
    }

    private void CreateSubmitButton(Transform parent, string buttonText, Action onClick)
    {
        GameObject buttonObj = new GameObject("SubmitButton");
        buttonObj.transform.SetParent(parent, false);
        RectTransform buttonRect = buttonObj.AddComponent<RectTransform>();
        buttonRect.sizeDelta = new Vector2(290, 216); // Hauteur doublée (108 * 2)

        Image buttonImage = buttonObj.AddComponent<Image>();
        buttonImage.color = buttonNormalColor;
        buttonImage.raycastTarget = true; // Permet les clics
        buttonImage.type = Image.Type.Sliced;
        buttonImage.sprite = CreateRoundedSprite(290, 108, 15f); // Coins légèrement arrondis pour le bouton

        submitButton = buttonObj.AddComponent<Button>();
        submitButton.targetGraphic = buttonImage;
        submitButton.interactable = true; // S'assurer que le bouton est interactif

        ColorBlock colors = submitButton.colors;
        colors.normalColor = buttonNormalColor;
        colors.highlightedColor = buttonHoverColor;
        colors.pressedColor = buttonHoverColor;
        submitButton.colors = colors;

        GameObject textObj = new GameObject("Text");
        textObj.transform.SetParent(buttonObj.transform, false);
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;
        // Padding de 40px
        textRect.offsetMin = new Vector2(40, 40);
        textRect.offsetMax = new Vector2(-40, -40);

        TextMeshProUGUI text = textObj.AddComponent<TextMeshProUGUI>();
        text.text = buttonText;
        if (antonFont != null)
        {
            text.font = antonFont;
        }
        text.fontSize = 22;
        text.color = Color.white;
        text.alignment = TextAlignmentOptions.Center;
        text.fontStyle = FontStyles.Bold;
        text.raycastTarget = false; // Ne pas bloquer les clics sur le bouton

        submitButton.onClick.AddListener(() => {
            Debug.Log($"[LoginPopup] Bouton cliqué: {buttonText}");
            onClick?.Invoke();
        });
        
        // Ajouter un EventTrigger pour WebGL (fallback si le Button ne fonctionne pas)
        #if UNITY_WEBGL && !UNITY_EDITOR
        UnityEngine.EventSystems.EventTrigger trigger = buttonObj.AddComponent<UnityEngine.EventSystems.EventTrigger>();
        
        // PointerDown
        UnityEngine.EventSystems.EventTrigger.Entry entryDown = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryDown.eventID = UnityEngine.EventSystems.EventTriggerType.PointerDown;
        entryDown.callback.AddListener((data) => {
            Debug.Log($"[LoginPopup] EventTrigger PointerDown sur bouton: {buttonText}");
            onClick?.Invoke();
        });
        trigger.triggers.Add(entryDown);
        
        // PointerClick
        UnityEngine.EventSystems.EventTrigger.Entry entryClick = new UnityEngine.EventSystems.EventTrigger.Entry();
        entryClick.eventID = UnityEngine.EventSystems.EventTriggerType.PointerClick;
        entryClick.callback.AddListener((data) => {
            Debug.Log($"[LoginPopup] EventTrigger PointerClick sur bouton: {buttonText}");
            onClick?.Invoke();
        });
        trigger.triggers.Add(entryClick);
        #endif
    }

    private void OnEmailSubmit()
    {
        string email = "";
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        if (useMobileInputs)
        {
            // Récupérer la valeur depuis l'input HTML
            email = GetMobileInputValue(emailInputHtmlId);
        }
        else
        #endif
        {
            // Récupérer la valeur depuis l'input Unity
            if (emailInput != null)
            {
                email = emailInput.text.Trim();
            }
        }
        
        if (string.IsNullOrEmpty(email))
        {
            ShowError("Veuillez entrer une adresse email");
            return;
        }

        currentEmail = email;
        StartCoroutine(CheckEmail(email));
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    private string GetMobileInputValue(string inputId)
    {
        try
        {
            // Allouer de la mémoire pour la chaîne
            int maxLen = 256;
            System.IntPtr valuePtr = Marshal.AllocHGlobal(maxLen);
            
            try
            {
                int len = Mobile_GetInputValue(inputId, valuePtr, maxLen);
                if (len > 0)
                {
                    string value = Marshal.PtrToStringUTF8(valuePtr, len);
                    return value?.Trim() ?? "";
                }
            }
            finally
            {
                Marshal.FreeHGlobal(valuePtr);
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"[LoginPopup] Erreur lors de la récupération de la valeur de l'input HTML: {e.Message}");
        }
        
        return "";
    }
    #endif

    private void OnPasswordSubmit()
    {
        string password = "";
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        if (useMobileInputs)
        {
            // Récupérer la valeur depuis l'input HTML
            password = GetMobileInputValue(passwordInputHtmlId);
        }
        else
        #endif
        {
            // Récupérer la valeur depuis l'input Unity
            if (passwordInput != null)
            {
                password = passwordInput.text;
            }
        }
        
        if (string.IsNullOrEmpty(password))
        {
            ShowError("Veuillez entrer votre mot de passe");
            return;
        }

        StartCoroutine(Login(currentEmail, password));
    }

    private IEnumerator CheckEmail(string email)
    {
        if (submitButton != null) submitButton.interactable = false;
        ShowError("Vérification en cours...");

        string preconnectUrl = GeneralConfigManager.Instance?.GetAuthPreconnectUrl() ?? "";
        
        if (string.IsNullOrEmpty(preconnectUrl))
        {
            ShowError("Erreur de configuration : URL de préconnexion non définie dans general-config.json");
            if (submitButton != null) submitButton.interactable = true;
            yield break;
        }
        string url = $"{preconnectUrl}?email={UnityWebRequest.EscapeURL(email)}";
        
        using (UnityWebRequest www = UnityWebRequest.Get(url))
        {
            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                ShowError($"Erreur de connexion: {www.error}");
                if (submitButton != null) submitButton.interactable = true;
                yield break;
            }

            string jsonResponse = www.downloadHandler.text;
            PreconnectResponse response = JsonUtility.FromJson<PreconnectResponse>(jsonResponse);

            if (response != null && response.status == "success" && response.data != null)
            {
                // Stocker le prénom de l'utilisateur
                currentFirstName = response.data.first_name ?? "";
                Debug.Log($"[LoginPopup] Email reconnu, prénom: {currentFirstName}");
                
                // Email reconnu, masquer l'erreur et afficher l'étape mot de passe
                if (errorContainer != null)
                {
                    errorContainer.SetActive(false);
                }
                Debug.Log("[LoginPopup] Email reconnu, affichage de l'étape mot de passe");
                // S'assurer que le panneau et le canvas sont visibles
                if (popupPanel != null)
                {
                    popupPanel.SetActive(true);
                    Debug.Log("[LoginPopup] Panneau activé");
                }
                if (canvas != null)
                {
                    canvas.gameObject.SetActive(true);
                    Debug.Log("[LoginPopup] Canvas activé");
                }
                ShowPasswordStep();
            }
            else
            {
                // Email non reconnu, afficher le lien d'inscription
                ShowRegistrationLink();
            }
        }

        if (submitButton != null) submitButton.interactable = true;
    }

    private IEnumerator Login(string email, string password)
    {
        if (submitButton != null) submitButton.interactable = false;
        ShowError("Connexion en cours...");

        LoginRequest requestData = new LoginRequest
        {
            email = email,
            password = password
        };

        string jsonData = JsonUtility.ToJson(requestData);
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);

        string loginUrl = GeneralConfigManager.Instance?.GetAuthLoginUrl() ?? "";
        
        if (string.IsNullOrEmpty(loginUrl))
        {
            ShowError("Erreur de configuration : URL de connexion non définie dans general-config.json");
            if (submitButton != null) submitButton.interactable = true;
            yield break;
        }
        using (UnityWebRequest www = new UnityWebRequest(loginUrl, "POST"))
        {
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");

            yield return www.SendWebRequest();

            if (www.result != UnityWebRequest.Result.Success)
            {
                // Cas courant: mauvais mot de passe => HTTP 401/403 (ProtocolError côté UnityWebRequest)
                if (www.responseCode == 401 || www.responseCode == 403)
                {
                    ShowPasswordIncorrectError();
                }
                else
                {
                    ShowError($"Erreur de connexion: {www.error}");
                }
                if (submitButton != null) submitButton.interactable = true;
                yield break;
            }

            string jsonResponse = www.downloadHandler.text;
            LoginResponse response = JsonUtility.FromJson<LoginResponse>(jsonResponse);

            if (response != null && response.status == "success" && response.data != null)
            {
                // Connexion réussie, stocker les données
                if (UserDataManager.Instance != null)
                {
                    // S'assurer que l'email est stocké (il n'est peut-être pas dans la réponse)
                    if (string.IsNullOrEmpty(response.data.email))
                    {
                        response.data.email = currentEmail;
                    }
                    UserDataManager.Instance.StoreUserData(response.data);
                }

                // Notifier le parent si on est dans une iframe
                if (IframeAuthManager.Instance != null)
                {
                    IframeAuthManager.Instance.NotifyParentOfLogin();
                }

                // Fermer la popup après connexion réussie
                onLoginSuccess?.Invoke();
                Close();
            }
            else
            {
                ShowPasswordIncorrectError();
                if (submitButton != null) submitButton.interactable = true;
            }
        }
    }

    private void ShowRegistrationLink()
    {
        Debug.Log("[LoginPopup] 🔄 ShowRegistrationLink appelé");
        
        // Ne pas appeler HideAllSteps() - juste masquer ce qu'il faut
        if (emailContainer != null) emailContainer.SetActive(false);
        if (passwordContainer != null) passwordContainer.SetActive(false);
        
        // Mettre à jour l'entête
        titleText.text = "CRÉATION DE COMPTE";
        
        // Réutiliser subtitleText pour le message principal
        if (subtitleText != null)
        {
            subtitleText.gameObject.SetActive(true);
            subtitleText.text = "Votre mail n'est pas reconnu, créez un compte ou modifiez votre mail si vous possédez déjà un compte chez nous.";
            subtitleText.fontSize = 25;
            subtitleText.color = new Color(0.4f, 0.28f, 0.5f, 1f);
            subtitleText.fontStyle = FontStyles.Normal;
            
            TMP_FontAsset latoBoldFont = Resources.Load<TMP_FontAsset>("Fonts/Lato-Bold SDF");
            if (latoBoldFont != null)
            {
                subtitleText.font = latoBoldFont;
            }
        }
        
        // Afficher descriptionText avec le message de redirection
        if (descriptionText != null)
        {
            descriptionText.transform.parent.gameObject.SetActive(true);
            descriptionText.text = "Vous serez redirigé vers la page d'inscription pour commencer votre aventure.";
            descriptionText.fontSize = 16;
            descriptionText.color = new Color(0.4f, 0.28f, 0.5f, 1f);
            descriptionText.alignment = TextAlignmentOptions.Center;
        }
        
        errorContainer.SetActive(false);
        
        // Masquer les boutons email, afficher les boutons password (qu'on va réutiliser)
        GameObject emailButtonsContainer = popupPanel.transform.Find("ButtonsContainer")?.gameObject;
        if (emailButtonsContainer != null)
        {
            emailButtonsContainer.SetActive(false);
        }
        
        // Créer le bouton CRÉER UN COMPTE directement sur le panel
        var config = GeneralConfigManager.Instance?.GetConfig();
        float topMargin = config?.connexionPanel?.buttonsTopMargin ?? 410f;
        
        // Supprimer les anciens éléments s'ils existent
        Transform existingLink = popupPanel.transform.Find("ModifyEmailLink");
        if (existingLink != null) Destroy(existingLink.gameObject);
        Transform existingBtn = popupPanel.transform.Find("RegistrationButton");
        if (existingBtn != null) Destroy(existingBtn.gameObject);
        Transform existingTxt = popupPanel.transform.Find("RegistrationBottomText");
        if (existingTxt != null) Destroy(existingTxt.gameObject);
        
        // Créer le lien "Modifier mon email" (entre le texte et le bouton)
        GameObject modifyLinkObj = new GameObject("ModifyEmailLink");
        modifyLinkObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform modifyLinkRect = modifyLinkObj.AddComponent<RectTransform>();
        modifyLinkRect.anchorMin = new Vector2(0.5f, 1f);
        modifyLinkRect.anchorMax = new Vector2(0.5f, 1f);
        modifyLinkRect.pivot = new Vector2(0.5f, 0.5f);
        modifyLinkRect.anchoredPosition = new Vector2(0, -topMargin + 120); // Au-dessus du bouton
        modifyLinkRect.sizeDelta = new Vector2(300, 40);
        
        TextMeshProUGUI modifyLinkText = modifyLinkObj.AddComponent<TextMeshProUGUI>();
        modifyLinkText.text = "<u>Modifier mon email</u>";
        modifyLinkText.fontSize = 22;
        modifyLinkText.color = new Color(0.4f, 0.28f, 0.5f, 1f);
        modifyLinkText.alignment = TextAlignmentOptions.Center;
        modifyLinkText.raycastTarget = true;
        
        TMP_FontAsset latoFont = Resources.Load<TMP_FontAsset>("Fonts/Lato-Regular SDF");
        if (latoFont != null) modifyLinkText.font = latoFont;
        
        Button modifyLinkButton = modifyLinkObj.AddComponent<Button>();
        modifyLinkButton.targetGraphic = null;
        modifyLinkButton.onClick.AddListener(() => ShowEmailStep());
        
        // Créer le bouton
        var buttonStyles = config?.buttonStyles;
        ButtonStyleConfig createAccountStyle = buttonStyles?.GetStyle("validationDefault");
        float btnWidth = createAccountStyle?.width ?? 300f;
        float btnHeight = createAccountStyle?.height ?? 80f;
        
        GameObject btnObj = new GameObject("RegistrationButton");
        btnObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform btnRect = btnObj.AddComponent<RectTransform>();
        btnRect.anchorMin = new Vector2(0.5f, 1f);
        btnRect.anchorMax = new Vector2(0.5f, 1f);
        btnRect.pivot = new Vector2(0.5f, 0.5f);
        btnRect.anchoredPosition = new Vector2(0, -topMargin);
        btnRect.sizeDelta = new Vector2(btnWidth, btnHeight);
        
        Image btnImage = btnObj.AddComponent<Image>();
        if (createAccountStyle?.gradient != null && createAccountStyle.gradient.enabled)
        {
            Color startColor = HexToColor(createAccountStyle.gradient.startColor ?? "#b87aff");
            Color endColor = HexToColor(createAccountStyle.gradient.endColor ?? "#7b4fbf");
            Color borderColor = HexToColor(createAccountStyle.borderColor ?? "#f5ece5");
            btnImage.sprite = CreateGradientSpriteWithBorder((int)btnWidth, (int)btnHeight, createAccountStyle.borderRadius, startColor, endColor, borderColor, createAccountStyle.borderWidth);
            btnImage.color = Color.white;
        }
        else
        {
            btnImage.sprite = CreateRoundedSprite((int)btnWidth, (int)btnHeight, 35f);
            btnImage.color = buttonNormalColor;
        }
        btnImage.raycastTarget = true;
        
        // Ombre
        if (createAccountStyle?.shadow != null && createAccountStyle.shadow.enabled)
        {
            Shadow shadow = btnObj.AddComponent<Shadow>();
            shadow.effectColor = HexToColor(createAccountStyle.shadow.color ?? "#00000040");
            shadow.effectDistance = new Vector2(createAccountStyle.shadow.offsetX, -createAccountStyle.shadow.offsetY);
        }
        
        Button btn = btnObj.AddComponent<Button>();
        btn.targetGraphic = btnImage;
        btn.onClick.AddListener(() => OpenRegistrationLink());
        
        // Texte du bouton
        GameObject txtObj = new GameObject("Text");
        txtObj.transform.SetParent(btnObj.transform, false);
        RectTransform txtRect = txtObj.AddComponent<RectTransform>();
        txtRect.anchorMin = Vector2.zero;
        txtRect.anchorMax = Vector2.one;
        txtRect.sizeDelta = Vector2.zero;
        
        TextMeshProUGUI btnTxt = txtObj.AddComponent<TextMeshProUGUI>();
        btnTxt.text = "CRÉER UN COMPTE";
        btnTxt.fontSize = createAccountStyle?.text?.fontSize ?? 28f;
        btnTxt.color = HexToColor(createAccountStyle?.text?.color ?? "#FFFFFF");
        btnTxt.alignment = TextAlignmentOptions.Center;
        btnTxt.raycastTarget = false;
        
        string fontName = createAccountStyle?.text?.fontFamily ?? "Anton-Regular SDF";
        TMP_FontAsset btnFont = Resources.Load<TMP_FontAsset>($"Fonts/{fontName}");
        if (btnFont != null) btnTxt.font = btnFont;
        
        // Texte sous le bouton
        GameObject bottomTextObj = new GameObject("RegistrationBottomText");
        bottomTextObj.transform.SetParent(popupPanel.transform, false);
        
        RectTransform bottomTextRect = bottomTextObj.AddComponent<RectTransform>();
        bottomTextRect.anchorMin = new Vector2(0.5f, 1f);
        bottomTextRect.anchorMax = new Vector2(0.5f, 1f);
        bottomTextRect.pivot = new Vector2(0.5f, 1f);
        bottomTextRect.anchoredPosition = new Vector2(0, -topMargin - btnHeight/2 - 20);
        bottomTextRect.sizeDelta = new Vector2(600, 60);
        
        TextMeshProUGUI bottomText = bottomTextObj.AddComponent<TextMeshProUGUI>();
        bottomText.text = "Vous serez redirigé vers la page d'inscription\npour commencer votre aventure.";
        bottomText.fontSize = 22;
        bottomText.color = new Color(0.4f, 0.28f, 0.5f, 1f);
        bottomText.alignment = TextAlignmentOptions.Center;
        bottomText.raycastTarget = false;
        
        TMP_FontAsset latoRegularFont = Resources.Load<TMP_FontAsset>("Fonts/Lato-Regular SDF");
        if (latoRegularFont != null) bottomText.font = latoRegularFont;
        
        Debug.Log("[LoginPopup] ✅ ShowRegistrationLink terminé - bouton créé");
    }

    private void ShowError(string message)
    {
        if (errorContainer != null && errorText != null)
        {
            // Restaurer une présentation "standard" pour les erreurs génériques
            errorText.fontStyle = FontStyles.Normal;
            errorText.color = errorTextColor;
            RectTransform rt = errorContainer.GetComponent<RectTransform>();
            if (rt != null && hasErrorContainerDefaultAnchoredPosition)
            {
                rt.anchoredPosition = errorContainerDefaultAnchoredPosition;
            }

            errorText.text = message;
            errorContainer.SetActive(true);
        }
    }

    private void ShowPasswordIncorrectError()
    {
        if (errorContainer == null || errorText == null) return;

        // Descendre légèrement le message sur l'étape mot de passe
        RectTransform rt = errorContainer.GetComponent<RectTransform>();
        if (rt != null)
        {
            // Fallback si jamais CreateErrorContainer n'a pas encore mémorisé (sécurité)
            if (!hasErrorContainerDefaultAnchoredPosition)
            {
                errorContainerDefaultAnchoredPosition = rt.anchoredPosition;
                hasErrorContainerDefaultAnchoredPosition = true;
            }

            rt.anchoredPosition = new Vector2(
                errorContainerDefaultAnchoredPosition.x,
                errorContainerDefaultAnchoredPosition.y - PASSWORD_INCORRECT_ERROR_Y_OFFSET
            );
        }

        // Afficher en gras, et ne pas relayer le message brut de l'endpoint
        errorText.fontStyle = FontStyles.Bold;
        errorText.color = errorTextColor;
        errorText.text = "<b>mot de passe incorrect.</b>";
        errorContainer.SetActive(true);
    }

    private void ShowPasswordResetInfo(string message)
    {
        if (errorContainer == null || errorText == null) return;

        // Même position (un peu plus bas) que "mot de passe incorrect"
        RectTransform rt = errorContainer.GetComponent<RectTransform>();
        if (rt != null)
        {
            if (!hasErrorContainerDefaultAnchoredPosition)
            {
                errorContainerDefaultAnchoredPosition = rt.anchoredPosition;
                hasErrorContainerDefaultAnchoredPosition = true;
            }

            rt.anchoredPosition = new Vector2(
                errorContainerDefaultAnchoredPosition.x,
                errorContainerDefaultAnchoredPosition.y - PASSWORD_INCORRECT_ERROR_Y_OFFSET
            );
        }

        // Gras + bleu
        errorText.fontStyle = FontStyles.Bold;
        errorText.color = linkTextColor;
        errorText.text = $"<b>{message}</b>";
        errorContainer.SetActive(true);
    }

    public void Show()
    {
        if (popupPanel != null)
        {
            popupPanel.SetActive(true);
        }
        
        if (canvas != null)
        {
            canvas.gameObject.SetActive(true);
            // S'assurer que le canvas est bien au premier plan
            canvas.sortingOrder = 80000;
        }
        
        // S'assurer que l'EventSystem est correctement configuré
        EnsureEventSystem();
        
        // En WebGL, forcer l'activation des champs après un court délai
        #if UNITY_WEBGL && !UNITY_EDITOR
        StartCoroutine(EnsureInputFieldsActiveWebGL());
        #endif
        
    }
    
    #if UNITY_WEBGL && !UNITY_EDITOR
    /// <summary>
    /// Coroutine pour s'assurer que les champs de saisie sont actifs en WebGL
    /// </summary>
    private IEnumerator EnsureInputFieldsActiveWebGL()
    {
        yield return new WaitForSeconds(0.2f);
        
        // S'assurer que l'EventSystem est actif
        UnityEngine.EventSystems.EventSystem eventSystem = FindFirstObjectByType<UnityEngine.EventSystems.EventSystem>();
        if (eventSystem != null)
        {
            eventSystem.enabled = true;
            eventSystem.gameObject.SetActive(true);
            #if ENABLE_INPUT_SYSTEM
            var inputModule = eventSystem.GetComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
            if (inputModule != null)
            {
                inputModule.enabled = true;
            }
            else
            {
                inputModule = eventSystem.gameObject.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
                inputModule.enabled = true;
            }
            #endif
        }
        else
        {
            Debug.LogError("[LoginPopup] ❌ Aucun EventSystem trouvé en WebGL !");
        }
        
        if (emailInput != null && emailContainer != null && emailContainer.activeInHierarchy)
        {
            emailInput.interactable = true;
            emailInput.enabled = true;
            emailInput.readOnly = false;
            emailInput.gameObject.SetActive(true);
            // Forcer la mise à jour
            emailInput.ForceLabelUpdate();
            
            // En WebGL, forcer le focus après un délai supplémentaire
            yield return new WaitForSeconds(0.1f);
            if (emailInput != null)
            {
                emailInput.ActivateInputField();
            }
        }
        
        if (passwordInput != null && passwordContainer != null && passwordContainer.activeInHierarchy)
        {
            passwordInput.interactable = true;
            passwordInput.enabled = true;
            passwordInput.readOnly = false;
            passwordInput.gameObject.SetActive(true);
            // Forcer la mise à jour
            passwordInput.ForceLabelUpdate();
            
            // En WebGL, forcer le focus après un délai supplémentaire
            yield return new WaitForSeconds(0.1f);
            if (passwordInput != null)
            {
                passwordInput.ActivateInputField();
            }
        }
    }
    
    /// <summary>
    /// Coroutine pour activer un champ de saisie en WebGL
    /// </summary>
    private IEnumerator ActivateInputFieldWebGL(TMP_InputField inputField)
    {
        yield return new WaitForSeconds(0.2f);
        
        if (inputField != null)
        {
            inputField.interactable = true;
            inputField.enabled = true;
            inputField.readOnly = false;
            
            // S'assurer que le GameObject parent est actif
            if (inputField.gameObject != null)
            {
                inputField.gameObject.SetActive(true);
            }
            
            // Forcer la mise à jour
            inputField.ForceLabelUpdate();
            
            // En WebGL, activer le champ pour qu'il puisse recevoir les événements clavier
            yield return new WaitForSeconds(0.1f);
            if (inputField != null)
            {
                inputField.ActivateInputField();
            }
        }
    }
    #endif

    private void EnsureEventSystem()
    {
        // Vérifier si un EventSystem existe
        UnityEngine.EventSystems.EventSystem eventSystem = FindFirstObjectByType<UnityEngine.EventSystems.EventSystem>();
        
        if (eventSystem == null)
        {
            GameObject eventSystemObj = new GameObject("LoginPopupEventSystem");
            eventSystem = eventSystemObj.AddComponent<UnityEngine.EventSystems.EventSystem>();
            DontDestroyOnLoad(eventSystemObj); // S'assurer qu'il persiste
            
            #if ENABLE_INPUT_SYSTEM
            // Utiliser InputSystemUIInputModule si le nouveau Input System est activé
            var inputModule = eventSystemObj.GetComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
            if (inputModule == null)
            {
                inputModule = eventSystemObj.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
            }
            // S'assurer que le module est activé
            if (inputModule != null)
            {
                inputModule.enabled = true;
            }
            #else
            // Utiliser StandaloneInputModule pour l'ancien Input System
            var standaloneModule = eventSystemObj.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>();
            if (standaloneModule == null)
            {
                standaloneModule = eventSystemObj.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
            }
            // S'assurer que le module est activé
            if (standaloneModule != null)
            {
                standaloneModule.enabled = true;
            }
            #endif
        }
        else
        {
            // S'assurer que l'EventSystem existant a le bon module et est actif
            eventSystem.enabled = true;
            
            #if ENABLE_INPUT_SYSTEM
            // Si un StandaloneInputModule traîne (ancien système), le retirer: ça casse le nouveau Input System et peut provoquer des exceptions.
            var strayStandalone = eventSystem.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>();
            if (strayStandalone != null)
            {
                strayStandalone.enabled = false;
                Destroy(strayStandalone);
            }

            var inputModule = eventSystem.GetComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
            if (inputModule == null)
            {
                inputModule = eventSystem.gameObject.AddComponent<UnityEngine.InputSystem.UI.InputSystemUIInputModule>();
            }
            if (inputModule != null)
            {
                inputModule.enabled = true;
            }
            #else
            var standaloneModule = eventSystem.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>();
            if (standaloneModule == null)
            {
                standaloneModule = eventSystem.gameObject.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
            }
            if (standaloneModule != null)
            {
                standaloneModule.enabled = true;
            }
            #endif
        }
    }

    private void OpenRegistrationLink()
    {
        string url = GeneralConfigManager.Instance?.GetRegistrationUrl() ?? "";
        
        if (string.IsNullOrEmpty(url))
        {
            Debug.LogError("[LoginPopup] ❌ URL d'inscription non définie dans general-config.json");
            return;
        }
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        // En WebGL, utiliser window.open pour ouvrir dans un nouvel onglet
        Application.ExternalCall("window.open", url, "_blank");
        #else
        // Dans l'éditeur ou standalone, Application.OpenURL devrait fonctionner
        Application.OpenURL(url);
        #endif
    }

    private void OpenForgotPasswordLink()
    {
        // Nouveau comportement: appeler l'endpoint {baseUrl}/api/auth/reset-password
        // en POST avec { "email": "{{email}}" }
        string email = (currentEmail ?? "").Trim();
        if (string.IsNullOrEmpty(email))
        {
            ShowError("Veuillez entrer une adresse email");
            return;
        }

        StartCoroutine(RequestPasswordReset(email));
    }

    private IEnumerator RequestPasswordReset(string email)
    {
        // Éviter les double-clics sur le lien
        Button forgotBtn = popupPanel != null ? popupPanel.transform.Find("PasswordForgotLink")?.GetComponent<Button>() : null;
        if (forgotBtn != null) forgotBtn.interactable = false;

        ShowError("Envoi de l'email de réinitialisation...");

        string resetUrl = GeneralConfigManager.Instance?.GetAuthResetPasswordUrl() ?? "";
        if (string.IsNullOrEmpty(resetUrl))
        {
            ShowError("Erreur de configuration : URL de réinitialisation non définie");
            if (forgotBtn != null) forgotBtn.interactable = true;
            yield break;
        }

        ResetPasswordRequest requestData = new ResetPasswordRequest { email = email };
        string jsonData = JsonUtility.ToJson(requestData);
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);

        using (UnityWebRequest www = new UnityWebRequest(resetUrl, "POST"))
        {
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");

            yield return www.SendWebRequest();

            // On ne doit pas exposer la réponse brute (potentiellement verbeuse) à l'utilisateur.
            // UX standard: message générique quel que soit le résultat (évite d'indiquer si l'email existe).
            bool ok = (www.result == UnityWebRequest.Result.Success) && (www.responseCode >= 200 && www.responseCode < 300);
            if (!ok)
            {
                Debug.LogWarning($"[LoginPopup] Reset-password échoué (HTTP {www.responseCode}): {www.error}");
            }

            ShowPasswordResetInfo("Si un compte existe pour cet email, vous recevrez un lien de réinitialisation.");
        }

        if (forgotBtn != null) forgotBtn.interactable = true;
    }

    public void Close()
    {
        // Fermer la modale Seroni Connect si elle est ouverte
        HideSeroniConnectModal();
        
        #if UNITY_WEBGL && !UNITY_EDITOR
        // Supprimer les inputs HTML sur mobile
        if (useMobileInputs)
        {
            try
            {
                Mobile_RemoveInput(emailInputHtmlId);
                Mobile_RemoveInput(passwordInputHtmlId);
            }
            catch (System.Exception e)
            {
                Debug.LogWarning($"[LoginPopup] Erreur lors de la suppression des inputs HTML: {e.Message}");
            }
        }
        #endif
        
        // Réactiver les managers de scène
        EnableSceneManagers();
        
        // Détruire le fond sombre
        if (darkBackground != null)
        {
            Destroy(darkBackground);
            darkBackground = null;
        }
        
        // Détruire la modale Seroni Connect
        if (seroniConnectModal != null)
        {
            Destroy(seroniConnectModal);
            seroniConnectModal = null;
        }
        if (seroniConnectModalBackground != null)
        {
            Destroy(seroniConnectModalBackground);
            seroniConnectModalBackground = null;
        }
        
        // Notifier le callback de fermeture avant de détruire
        onClose?.Invoke();
        
        // Détruire le panneau
        if (popupPanel != null)
        {
            Destroy(popupPanel);
            popupPanel = null;
        }
        
        // Détruire le canvas (qui contient tout)
        if (canvas != null && canvas.gameObject != null)
        {
            Destroy(canvas.gameObject);
            canvas = null;
        }

        // Nettoyer les sprites/textures générés pour l'icône œil
        if (passwordEyeOpenSprite != null) Destroy(passwordEyeOpenSprite);
        if (passwordEyeSlashedSprite != null) Destroy(passwordEyeSlashedSprite);
        if (passwordEyeOpenTexture != null) Destroy(passwordEyeOpenTexture);
        if (passwordEyeSlashedTexture != null) Destroy(passwordEyeSlashedTexture);
        passwordEyeOpenSprite = null;
        passwordEyeSlashedSprite = null;
        passwordEyeOpenTexture = null;
        passwordEyeSlashedTexture = null;
        
        // Détruire le GameObject du script
        if (gameObject != null)
        {
            Destroy(gameObject);
        }
    }

    /// <summary>
    /// Démarre l'effet de clignotement pour un champ de saisie
    /// </summary>
    private void StartInputBlink(Image inputBg, ref Coroutine blinkCoroutine)
    {
        // Arrêter le clignotement précédent s'il existe
        if (blinkCoroutine != null)
        {
            StopCoroutine(blinkCoroutine);
        }
        
        // Démarrer le nouveau clignotement
        blinkCoroutine = StartCoroutine(BlinkInputField(inputBg));
    }

    /// <summary>
    /// Arrête l'effet de clignotement pour un champ de saisie
    /// </summary>
    private void StopInputBlink(ref Coroutine blinkCoroutine)
    {
        if (blinkCoroutine != null)
        {
            StopCoroutine(blinkCoroutine);
            blinkCoroutine = null;
        }
    }

    /// <summary>
    /// Coroutine qui fait clignoter le fond d'un champ de saisie
    /// </summary>
    private IEnumerator BlinkInputField(Image inputBg)
    {
        if (inputBg == null) yield break;
        
        Color normalColor = Color.white;
        Color highlightColor = new Color(0.8f, 0.9f, 1f, 1f); // Bleu clair pour indiquer le focus
        float blinkSpeed = 1.5f; // Vitesse de clignotement (cycles par seconde)
        
        try
        {
            while (true)
            {
                float time = 0f;
                float halfCycle = 1f / blinkSpeed / 2f; // Demi-cycle
                
                // Transition vers la couleur highlight
                while (time < halfCycle)
                {
                    if (inputBg == null) yield break;
                    time += Time.deltaTime;
                    float t = time / halfCycle;
                    inputBg.color = Color.Lerp(normalColor, highlightColor, t);
                    yield return null;
                }
                
                // Transition vers la couleur normale
                time = 0f;
                while (time < halfCycle)
                {
                    if (inputBg == null) yield break;
                    time += Time.deltaTime;
                    float t = time / halfCycle;
                    inputBg.color = Color.Lerp(highlightColor, normalColor, t);
                    yield return null;
                }
            }
        }
        finally
        {
            // S'assurer que la couleur revient à la normale quand la coroutine s'arrête
            if (inputBg != null)
            {
                inputBg.color = normalColor;
            }
        }
    }

    /// <summary>
    /// Crée un sprite avec des coins arrondis pour le panneau principal
    /// </summary>
    private Sprite CreateRoundedSprite(int width, int height, float radius)
    {
        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float alpha = 1f;

                // Coin supérieur gauche
                if (x < radius && y > height - radius)
                {
                    float dx = radius - x;
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin supérieur droit
                else if (x > width - radius && y > height - radius)
                {
                    float dx = x - (width - radius);
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin inférieur gauche
                else if (x < radius && y < radius)
                {
                    float dx = radius - x;
                    float dy = radius - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin inférieur droit
                else if (x > width - radius && y < radius)
                {
                    float dx = x - (width - radius);
                    float dy = radius - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }

                pixels[y * width + x] = new Color(1f, 1f, 1f, alpha);
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);

        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f, 0, SpriteMeshType.FullRect, new Vector4(radius, radius, radius, radius));
        dynamicSprites.Add(sprite);
        return sprite;
    }

    /// <summary>
    /// Crée un sprite avec des coins arrondis uniquement en haut pour l'entête
    /// </summary>
    private Sprite CreateRoundedHeaderSprite(int width, int height, float radius)
    {
        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float alpha = 1f;

                // Coin supérieur gauche
                if (x < radius && y > height - radius)
                {
                    float dx = radius - x;
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin supérieur droit
                else if (x > width - radius && y > height - radius)
                {
                    float dx = x - (width - radius);
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }

                pixels[y * width + x] = new Color(1f, 1f, 1f, alpha);
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);

        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f, 0, SpriteMeshType.FullRect, new Vector4(radius, radius, 0, 0));
        dynamicSprites.Add(sprite);
        return sprite;
    }

    /// <summary>
    /// Crée un sprite avec des coins arrondis et une couleur spécifique
    /// </summary>
    private Sprite CreateRoundedColorSprite(int width, int height, float radius, Color fillColor)
    {
        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float alpha = 1f;

                // Coin supérieur gauche
                if (x < radius && y > height - radius)
                {
                    float dx = radius - x;
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin supérieur droit
                else if (x > width - radius && y > height - radius)
                {
                    float dx = x - (width - radius);
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin inférieur gauche
                else if (x < radius && y < radius)
                {
                    float dx = radius - x;
                    float dy = radius - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin inférieur droit
                else if (x > width - radius && y < radius)
                {
                    float dx = x - (width - radius);
                    float dy = radius - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }

                pixels[y * width + x] = new Color(fillColor.r, fillColor.g, fillColor.b, fillColor.a * alpha);
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);

        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
        dynamicSprites.Add(sprite);
        return sprite;
    }

    /// <summary>
    /// Crée un sprite pour le header avec coins arrondis en haut et une couleur spécifique
    /// </summary>
    private Sprite CreateHeaderColorSprite(int width, int height, float radius, Color fillColor)
    {
        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                float alpha = 1f;

                // Coin supérieur gauche
                if (x < radius && y > height - radius)
                {
                    float dx = radius - x;
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Coin supérieur droit
                else if (x > width - radius && y > height - radius)
                {
                    float dx = x - (width - radius);
                    float dy = (height - radius) - y;
                    float distance = Mathf.Sqrt(dx * dx + dy * dy);
                    if (distance > radius)
                        alpha = 0f;
                    else
                        alpha = 1f - Mathf.Clamp01((distance - radius + 1) / 1);
                }
                // Pas de coins arrondis en bas pour le header

                pixels[y * width + x] = new Color(fillColor.r, fillColor.g, fillColor.b, fillColor.a * alpha);
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();
        dynamicTextures.Add(texture);

        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, width, height), new Vector2(0.5f, 0.5f), 100f);
        dynamicSprites.Add(sprite);
        return sprite;
    }
    
    void OnDestroy()
    {
        CleanupDynamicResources();
    }
    
    /// <summary>
    /// Nettoie toutes les ressources créées dynamiquement (important pour WebGL)
    /// </summary>
    void CleanupDynamicResources()
    {
        foreach (var sprite in dynamicSprites)
        {
            if (sprite != null) Destroy(sprite);
        }
        dynamicSprites.Clear();
        
        foreach (var texture in dynamicTextures)
        {
            if (texture != null) Destroy(texture);
        }
        dynamicTextures.Clear();
        
        Debug.Log("[LoginPopup] 🧹 Ressources dynamiques nettoyées");
    }

}

