/**
 * mio.land - Composants UX partagés
 * Source de vérité pour tous les éléments d'interface utilisateur
 * 
 * Usage: <link rel="stylesheet" href="/ux/ux-components.css?<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/ux/ux-components.css') ?>">
 * 
 * Ce fichier est conçu pour être embeddable dans des exports HTML standalone.
 * Toutes les icônes sont en SVG inline, jamais d'emojis ou caractères graphiques.
 * 
 * PERSONNALISATION PAR APP:
 * Les apps peuvent définir --accent dans leur CSS pour personnaliser:
 * - La couleur du toast undo (bouton + barre de progression)
 * Exemple: :root { --accent: #7C4DFF; }
 */

/* ============================
   VARIABLES UX (à ne pas surcharger)
   ============================ */

:root {
    /* Couleurs de boutons standard */
    --ux-btn-default: #2ECC71;
    --ux-btn-default-hover: #27AE60;
    --ux-btn-primary: #3498DB;
    --ux-btn-primary-hover: #2980B9;
    --ux-btn-secondary: rgba(255, 255, 255, 0.08);
    --ux-btn-secondary-hover: rgba(255, 255, 255, 0.15);
    --ux-btn-danger: #E74C3C;
    --ux-btn-danger-hover: #C0392B;
    --ux-btn-purple: #8b5cf6;
    --ux-btn-purple-hover: #7c3aed;
    --ux-btn-pink: #ec4899;
    --ux-btn-pink-hover: #db2777;
    --ux-btn-orange: #F39C12;
    --ux-btn-orange-hover: #E67E22;
    --ux-btn-cyan: #00BCD4;
    --ux-btn-cyan-hover: #00ACC1;
    
    /* Couleurs de feedback */
    --ux-success: #2ECC71;
    --ux-error: #E74C3C;
    --ux-warning: #F39C12;
    --ux-info: #3498DB;
    
    /* Transitions */
    --ux-transition-fast: 0.15s ease;
    --ux-transition-normal: 0.25s ease;
}

/* ============================
   MARQUE PRODUCTIVIA
   ============================
   Style officiel du logotype "Productiv<span class="brand-ia">IA</span>".
   Source de verite : productivia.ca (_css/main.css).
   Usage obligatoire dans toute interface visible mentionnant la marque.
   - .brand-ia : applique le style officiel des lettres "IA" (taille reduite + decalage subtil vers le haut)
   - .brand-lock : empeche un retour a la ligne entre "Productiv" et "IA"
   Pattern HTML : <span class="brand-lock">Productiv<span class="brand-ia">IA</span></span>
   ============================ */

.brand-ia {
    display: inline-block;
    font-size: 0.85em;
    transform: translate(0px, -0.20em);
    pointer-events: none;
}

.brand-lock {
    white-space: nowrap;        /* empeche le retour a la ligne */
    display: inline-block;      /* evite certaines cesures bizarres */
    word-break: normal;         /* annule break-all sur des parents */
    overflow-wrap: normal;      /* annule anywhere / break-word */
    hyphens: none;              /* pas de cesure automatique */
}

/* ============================
   INPUTS GLOBAUX
   ============================ */

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
}

/* ============================
   ICONES DECLARATIVES (data-icon)
   ----------------------------
   Conteneur d'icone hydrate par UX.hydrateIcons (voir ux-components.js).
   Usage : <i data-icon="alert-circle"></i>
   La couleur suit currentColor, la taille suit la police (1em) sauf
   data-icon-size. Source unique : app Icones.
   ============================ */
[data-icon] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    vertical-align: middle;
    flex-shrink: 0;
}

/* Listes déroulantes natives : harmoniser avec les champs (thème système + couleurs) */
select:not([multiple]) {
    color-scheme: dark;
}

select option {
    background-color: var(--bg-tertiary, #1a1a25);
    color: var(--text-primary, #f0f0f5);
}

select option:checked,
select option:hover {
    background-color: var(--bg-hover, rgba(255, 255, 255, 0.08));
    color: var(--text-primary, #f0f0f5);
}

/* Sélecteur stylé (liste HTML = même look que le champ) — activer avec data-ux-custom-select sur <select> */
.ux-select-wrap {
    position: relative;
    width: 100%;
    max-width: 100%;
}

select.ux-select-native-hidden {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

.ux-select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 12px 40px 12px 16px;
    box-sizing: border-box;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 10px;
    color: var(--text-primary, #f0f0f5);
    font-family: inherit;
    font-size: 14px;
    font-weight: 400;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--transition-fast, 0.15s ease), background var(--transition-fast, 0.15s ease);
    user-select: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238888a0' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
}

.ux-select-trigger:hover {
    border-color: var(--border-hover, rgba(255, 255, 255, 0.12));
}

.ux-select-trigger:focus {
    outline: none;
    border-color: var(--border-focus, var(--accent-purple, #8b5cf6));
    background-color: rgba(139, 92, 246, 0.05);
}

.ux-select-trigger[aria-expanded="true"] {
    border-color: var(--border-focus, var(--accent-purple, #8b5cf6));
}

.ux-select-trigger-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ux-select-dropdown {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    z-index: 4000;
    margin-top: 4px;
    padding: 0;
    background: var(--bg-secondary, #12121a);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 10px;
    box-shadow: var(--shadow-lg, 0 10px 40px rgba(0, 0, 0, 0.45));
    max-height: min(420px, 60vh);
    max-width: min(360px, calc(100vw - 16px));
    overflow-x: hidden;
    overflow-y: auto;
    animation: uxSelectOpen 0.12s ease;
}

.ux-select-dropdown.ux-select-open {
    display: block;
}

.ux-select-dropdown:focus {
    outline: none;
}

.ux-select-dropdown.ux-select-flip {
    bottom: calc(100% + 4px);
    top: auto;
    margin-top: 0;
    margin-bottom: 4px;
}

/* Variante OS-level (rendue par os/app.js handleUXSelectOpen) :
   le contenu et le scroll sont deportes dans un wrapper interne pour que
   la scrollbar webkit soit clippee proprement par le border-radius du
   container externe. */
.ux-select-dropdown-os {
    overflow: hidden !important;
    padding: 0;
}
.ux-select-scroll {
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    /* Petit padding interne pour que les options ne touchent pas les
       arrondis du container et que la scrollbar reste a l'interieur. */
    padding: 4px 0;
    /* La hauteur max est posee par JS (positionList) en sync avec celle
       du container externe. */
}
.ux-select-scroll::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
.ux-select-scroll::-webkit-scrollbar-track {
    background: transparent;
}
.ux-select-scroll::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.18);
    border-radius: 0;
    border: 2px solid transparent;
    background-clip: padding-box;
}
.ux-select-scroll::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.32);
    background-clip: padding-box;
}
.ux-select-scroll::-webkit-scrollbar-button { display: none !important; }

@keyframes uxSelectOpen {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
}

.ux-select-option {
    padding: 10px 16px;
    font-size: 14px;
    color: var(--text-primary, #f0f0f5);
    cursor: pointer;
    transition: background 0.1s ease;
    user-select: none;
}

.ux-select-option:hover,
.ux-select-option.ux-select-option-active {
    background: var(--bg-hover, rgba(255, 255, 255, 0.06));
}

.ux-select-option.is-selected {
    color: var(--accent-purple, #8b5cf6);
    font-weight: 500;
}

.ux-select-option[disabled] {
    opacity: 0.45;
    cursor: default;
    pointer-events: none;
}

.ux-select-optgroup-label {
    padding: 8px 16px 4px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary, #a0aec0);
    cursor: default;
    user-select: none;
    opacity: 0.7;
}

.ux-select-optgroup-label:not(:first-child) {
    margin-top: 4px;
    border-top: 1px solid var(--border, rgba(255, 255, 255, 0.06));
    padding-top: 10px;
}

.ux-select-option-grouped {
    padding-left: 24px;
}

/* ============================
   MENU D'ACTIONS (popover déclenché par bouton)
   ----------------------------
   Aligné sur .ux-select-dropdown. Pattern utilisé p.ex. par le bouton "+"
   dans la zone de saisie de l'Assistant. Différent de .ux-context-menu
   (clic droit) et de <select data-ux-custom-select>.

   ATTENTION : ne JAMAIS poser padding/border/background/border-radius/
   box-shadow via style inline ou cssText en JS — ces déclarations
   battent le CSS et masquent silencieusement les corrections de style.
   Le JS ne doit positionner que (position, top, left, width, max-height,
   z-index, display).
   ============================ */

.ux-action-menu-wrap {
    position: relative;
    display: inline-block;
}

.ux-action-menu {
    display: none;
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 4000;
    min-width: 240px;
    margin: 0;
    padding: 0;
    background: var(--bg-secondary, #12121a);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 10px;
    box-shadow: var(--shadow-lg, 0 10px 40px rgba(0, 0, 0, 0.45));
    overflow: hidden;
    animation: uxSelectOpen 0.12s ease;
}

.ux-action-menu.open {
    display: block;
}

.ux-action-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    box-sizing: border-box;
    margin: 0;
    padding: 10px 16px;
    border: none;
    border-radius: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    color: var(--text-primary, #f0f0f5);
    font-family: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
    user-select: none;
    transition: background 0.1s ease;
}

.ux-action-menu-item:hover,
.ux-action-menu-item:focus-visible {
    background: var(--bg-hover, rgba(255, 255, 255, 0.06));
    outline: none;
}

.ux-action-menu-item.is-danger {
    color: var(--error, #ef4444);
}

.ux-action-menu-item.is-danger:hover,
.ux-action-menu-item.is-danger:focus-visible {
    background: rgba(239, 68, 68, 0.12);
}

.ux-action-menu-item[disabled] {
    opacity: 0.45;
    cursor: default;
    pointer-events: none;
}

.ux-action-menu-item svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    stroke: currentColor;
    stroke-width: 2;
    fill: none;
}

.ux-action-menu-separator {
    height: 1px;
    margin: 4px 0;
    background: var(--border, rgba(255, 255, 255, 0.08));
}

/* ============================
   BOUTONS STANDARD
   ============================ */

.ux-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 20px;
    border: none;
    border-radius: 10px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--ux-transition-fast);
    user-select: none;
    white-space: nowrap;
}

.ux-btn svg {
    width: 16px;
    height: 16px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    flex-shrink: 0;
}

/* Bouton par défaut = Vert (action principale) */
.ux-btn-default, .ux-btn {
    background: var(--ux-btn-default);
    color: white;
}
.ux-btn-default:hover, .ux-btn:hover {
    background: var(--ux-btn-default-hover);
    transform: translateY(-1px);
}

/* Bouton primaire = Bleu */
.ux-btn-primary {
    background: var(--ux-btn-primary);
    color: white;
}
.ux-btn-primary:hover {
    background: var(--ux-btn-primary-hover);
    transform: translateY(-1px);
}

/* Bouton secondaire */
.ux-btn-secondary {
    background: var(--ux-btn-secondary);
    color: var(--text-primary, #f0f0f5);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
}
.ux-btn-secondary:hover {
    background: var(--ux-btn-secondary-hover);
    border-color: var(--border-hover, rgba(255, 255, 255, 0.15));
}

/* Bouton danger */
.ux-btn-danger {
    background: var(--ux-btn-danger);
    color: white;
}
.ux-btn-danger:hover {
    background: var(--ux-btn-danger-hover);
    transform: translateY(-1px);
}

/* Variations de couleur */
.ux-btn-purple {
    background: var(--ux-btn-purple);
    color: white;
}
.ux-btn-purple:hover {
    background: var(--ux-btn-purple-hover);
    transform: translateY(-1px);
}

.ux-btn-pink {
    background: var(--ux-btn-pink);
    color: white;
}
.ux-btn-pink:hover {
    background: var(--ux-btn-pink-hover);
    transform: translateY(-1px);
}

.ux-btn-orange {
    background: var(--ux-btn-orange);
    color: white;
}
.ux-btn-orange:hover {
    background: var(--ux-btn-orange-hover);
    transform: translateY(-1px);
}

.ux-btn-cyan {
    background: var(--ux-btn-cyan);
    color: white;
}
.ux-btn-cyan:hover {
    background: var(--ux-btn-cyan-hover);
    transform: translateY(-1px);
}

/* Tailles de boutons */
.ux-btn-sm {
    padding: 8px 14px;
    font-size: 13px;
}

.ux-btn-lg {
    padding: 16px 28px;
    font-size: 16px;
}

/* Bouton icone-only (carre, contient un seul SVG) :
   on force le centrage parfait + un padding compact symetrique. */
.ux-btn-icon {
    justify-content: center;
    align-items: center;
    box-sizing: border-box;
    gap: 0;
    padding: 8px;
    line-height: 1;
    flex: 0 0 auto;
}
.ux-btn-icon > svg {
    display: block;
    margin: 0;
}

/* ============================
   SLIDE-TO-DELETE
   
   Principe anti double-clic :
   1. Premier clic → "Confirmer?" apparaît à droite du bouton
   2. Le container s'agrandit → pousse les boutons adjacents vers la gauche
   3. Deuxième clic → exécute l'action (le bouton "Modifier" a bougé)
   
   Structure HTML requise:
   <div class="ux-slide-delete-container">
       <button class="ux-slide-delete-btn">Supprimer</button>
       <span class="ux-slide-delete-confirm">Confirmer?</span>
   </div>
   ============================ */

.ux-slide-delete-container {
    display: inline-flex;
    align-items: center;
    height: 38px;
}

.ux-slide-delete-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 9px 14px;
    background: var(--ux-btn-danger);
    border: none;
    border-radius: 8px;
    color: white;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s ease;
    user-select: none;
    white-space: nowrap;
    flex-shrink: 0;
}

.ux-slide-delete-btn:hover {
    background: var(--ux-btn-danger-hover);
}

.ux-slide-delete-btn svg {
    width: 14px;
    height: 14px;
    stroke: currentColor;
    stroke-width: 2;
    fill: none;
    flex-shrink: 0;
}

.ux-slide-delete-confirm {
    color: #f87171;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    max-width: 0;
    opacity: 0;
    padding: 0;
    transition: max-width 0.3s cubic-bezier(0.4, 0, 0.2, 1), 
                opacity 0.2s ease 0.1s,
                padding 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* État révélé :
   - Le texte "Confirmer?" apparaît à droite du bouton
   - Le container s'agrandit → pousse les boutons adjacents vers la gauche
*/
.ux-slide-delete-container.revealed .ux-slide-delete-confirm {
    max-width: 90px;
    opacity: 1;
    padding-left: 10px;
}

/* ============================
   SLIDE TO CONFIRM (style iPhone "slide to unlock")

   Slider draggable de gauche a droite pour confirmer une action critique.
   Utilisation via UX.slideToConfirm({ container, label, color, onConfirm }).

   Structure HTML (generee par UX.slideToConfirm) :
   <div class="ux-slide-to-confirm">
       <div class="ux-stc-track">
           <div class="ux-stc-fill"></div>
           <div class="ux-stc-label">Glisser pour confirmer</div>
           <div class="ux-stc-handle"><svg>...</svg></div>
       </div>
   </div>
   ============================ */

.ux-slide-to-confirm {
    --ux-stc-color: #dc2626;
    --ux-stc-height: 52px;
    --ux-stc-handle-size: 44px;
    width: 100%;
    user-select: none;
    -webkit-user-select: none;
}

.ux-slide-to-confirm .ux-stc-track {
    position: relative;
    height: var(--ux-stc-height);
    border-radius: calc(var(--ux-stc-height) / 2);
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    overflow: hidden;
    display: flex;
    align-items: center;
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
}

.ux-slide-to-confirm .ux-stc-fill {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: calc(var(--ux-stc-handle-size) + 8px);
    background: var(--ux-stc-color);
    border-radius: calc(var(--ux-stc-height) / 2);
    z-index: 1;
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.ux-slide-to-confirm .ux-stc-label {
    position: relative;
    z-index: 2;
    width: 100%;
    text-align: center;
    color: var(--text-secondary, #a0aec0);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.2px;
    pointer-events: none;
    padding: 0 calc(var(--ux-stc-handle-size) + 12px);
    box-sizing: border-box;
    transition: opacity 0.25s ease, color 0.2s ease;
}

.ux-slide-to-confirm .ux-stc-handle {
    position: absolute;
    top: 50%;
    left: 4px;
    width: var(--ux-stc-handle-size);
    height: var(--ux-stc-handle-size);
    margin-top: calc(var(--ux-stc-handle-size) / -2);
    border-radius: 50%;
    background: var(--ux-stc-color);
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    z-index: 3;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.2);
    touch-action: none;
    outline: none;
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.15s ease;
}

.ux-slide-to-confirm .ux-stc-handle:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.ux-slide-to-confirm .ux-stc-handle:focus-visible {
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.25), 0 2px 8px rgba(0, 0, 0, 0.35);
}

.ux-slide-to-confirm.dragging .ux-stc-handle,
.ux-slide-to-confirm .ux-stc-handle:active {
    cursor: grabbing;
}

.ux-slide-to-confirm .ux-stc-handle svg {
    width: 22px;
    height: 22px;
}

.ux-slide-to-confirm.confirmed .ux-stc-label {
    color: #ffffff;
    opacity: 1;
}

/* ============================
   CHECKBOXES & TOGGLES
   ============================ */

.ux-checkbox-group,
.ux-toggle-group {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ux-checkbox-item,
.ux-toggle-item {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    user-select: none;
}

.ux-checkbox-item input,
.ux-toggle-item input {
    position: fixed;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
}

.ux-checkbox-custom {
    width: 22px;
    height: 22px;
    border: 2px solid rgba(255, 255, 255, 0.08);
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--ux-transition-fast), border-color var(--ux-transition-fast);
    flex-shrink: 0;
}

.ux-checkbox-item input:checked + .ux-checkbox-custom {
    background: var(--ux-btn-default);
    border-color: var(--ux-btn-default);
}

.ux-checkbox-custom::after {
    content: '';
    width: 12px;
    height: 12px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    opacity: 0;
    transition: opacity var(--ux-transition-fast);
}

.ux-checkbox-item input:checked + .ux-checkbox-custom::after {
    opacity: 1;
}

/* Radio (meme principe que la case : input masque + pastille personnalisee) */
.ux-radio-group {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ux-radio-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    cursor: pointer;
    user-select: none;
}

.ux-radio-item input {
    position: fixed;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
}

.ux-radio-custom {
    width: 22px;
    height: 22px;
    border: 2px solid rgba(255, 255, 255, 0.22);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color var(--ux-transition-fast), background var(--ux-transition-fast);
    flex-shrink: 0;
    margin-top: 2px;
    box-sizing: border-box;
}

.ux-radio-item input:checked + .ux-radio-custom {
    border-color: var(--ux-btn-default);
    background: rgba(46, 204, 113, 0.12);
}

.ux-radio-custom::after {
    content: '';
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--ux-btn-default);
    opacity: 0;
    transform: scale(0.5);
    transition: opacity var(--ux-transition-fast), transform var(--ux-transition-fast);
}

.ux-radio-item input:checked + .ux-radio-custom::after {
    opacity: 1;
    transform: scale(1);
}

/* Toggle switch */
.ux-toggle-switch {
    width: 44px;
    height: 24px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    position: relative;
    transition: all var(--ux-transition-fast);
    flex-shrink: 0;
}

.ux-toggle-switch::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    background: #8888a0;
    border-radius: 50%;
    transition: all var(--ux-transition-fast);
}

.ux-toggle-item input:checked + .ux-toggle-switch {
    background: rgba(46, 204, 113, 0.3);
}

.ux-toggle-item input:checked + .ux-toggle-switch::after {
    left: 22px;
    background: var(--ux-btn-default);
}

/* ============================
   DIALOGUES
   ============================ */

.ux-dialog-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 10000;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.ux-dialog-overlay.visible {
    opacity: 1;
}

.ux-dialog-overlay.closing {
    opacity: 0;
}

.ux-dialog {
    position: absolute;
    /* Largeur maitrisee : le dialogue s'adapte a son contenu (largeur auto)
       avec un minimum confortable et un maximum raisonnable. Les dialogues qui
       ont besoin d'etre plus larges (tableaux, listes, editeurs) doivent definir
       leur propre largeur via `dialogClass` (ex. .mon-app-dlg .ux-dialog). */
    width: fit-content;
    min-width: min(360px, calc(100vw - 32px));
    max-width: min(480px, calc(100vw - 32px));
    max-height: 88vh;
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary, #12121a);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 16px;
    box-shadow: 0 30px 100px rgba(0, 0, 0, 0.5);
    overflow: hidden;
    transform: scale(0.95);
    transition: transform 0.2s ease;
}

.ux-dialog-overlay.visible .ux-dialog {
    transform: scale(1);
}

.ux-dialog-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 16px;
    background: var(--bg-tertiary, #1a1a25);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    cursor: grab;
    flex-shrink: 0;
}

.ux-dialog-header:active {
    cursor: grabbing;
}

.ux-dialog-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--ux-btn-purple);
}

.ux-dialog-close {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: all var(--ux-transition-fast);
}

.ux-dialog-close:hover {
    background: rgba(239, 68, 68, 0.2);
}

.ux-dialog-close svg {
    width: 16px;
    height: 16px;
    stroke: #8888a0;
}

.ux-dialog-close:hover svg {
    stroke: var(--ux-btn-danger);
}

.ux-dialog-content {
    padding: 24px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-primary, #d0d0d8);
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}

.ux-dialog-content p {
    margin: 0;
}

/* Largeur intelligente des dialogues : un message simple (texte seul, rendu
   dans un <p> enfant direct du contenu) se lit sur quelques lignes au lieu
   d'une seule ligne tres longue. Les dialogues riches definissent leur propre
   largeur via leur contenu (divs, champs, textarea) et ne sont pas affectes. */
.ux-dialog-content > p {
    max-width: 460px;
}

.ux-dialog-content label {
    color: var(--text-secondary, #a0aec0);
}

/* Neutraliser le fond blanc impose par l'autofill du navigateur (Chrome, Edge)
   sur les champs reconnus (courriel, mot de passe...). Sans cela, le champ passe
   en fond blanc des qu'une valeur connue est saisie/proposee, ce qui casse le
   theme sombre. L'astuce : un box-shadow interne opaque couvre le fond force, et
   text-fill-color garde le texte lisible. La transition tres longue empeche le
   flash blanc au focus. */
.ux-dialog-content input:-webkit-autofill,
.ux-dialog-content input:-webkit-autofill:hover,
.ux-dialog-content input:-webkit-autofill:focus,
.ux-dialog-content input:-webkit-autofill:active,
.ux-dialog-content textarea:-webkit-autofill {
    -webkit-text-fill-color: var(--text-primary, #f0f0f5);
    -webkit-box-shadow: 0 0 0 1000px var(--bg-secondary, #12121a) inset;
    box-shadow: 0 0 0 1000px var(--bg-secondary, #12121a) inset;
    caret-color: var(--text-primary, #f0f0f5);
    border-color: var(--border, rgba(255, 255, 255, 0.08));
    transition: background-color 99999s ease-in-out 0s;
}

/* ============================
   SAISIE DE CODE (PIN / OTP) — une case par chiffre (UX.attachPinInputs)
   ============================ */
.ux-pin-inputs {
    display: flex;
    gap: 8px;
    justify-content: center;
    flex-wrap: nowrap;
}

.ux-pin-input {
    width: 44px;
    height: 52px;
    text-align: center;
    font-size: 22px;
    font-weight: 600;
    background: rgba(255, 255, 255, 0.05);
    border: 2px solid var(--border, rgba(255, 255, 255, 0.12));
    border-radius: 10px;
    color: var(--text-primary, #f0f0f5);
    font-family: 'Courier New', monospace;
    caret-color: var(--accent, #4a9eff);
    transition: border-color var(--ux-transition-fast), background var(--ux-transition-fast);
}

.ux-pin-input:focus {
    outline: none;
    border-color: var(--accent, #4a9eff);
    background: rgba(255, 255, 255, 0.08);
}

.ux-dialog-footer {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    padding: 12px 20px 16px;
    flex-shrink: 0;
}

/* Bouton pousse a gauche (ex: Supprimer), le reste reste a droite */
.ux-dialog-footer .ux-dialog-btn-left {
    margin-right: auto;
}

.ux-dialog-btn {
    padding: 10px 20px;
    border-radius: 10px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--ux-transition-fast);
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-primary, #f0f0f5);
}

.ux-dialog-btn:hover {
    background: rgba(255, 255, 255, 0.1);
}

.ux-dialog-btn.primary {
    background: var(--ux-btn-default);
    border: none;
    color: white;
}

.ux-dialog-btn.primary:hover {
    background: var(--ux-btn-default-hover);
}

.ux-dialog-btn.danger {
    background: rgba(231, 76, 60, 0.2);
    border-color: rgba(231, 76, 60, 0.3);
    color: var(--ux-btn-danger);
}

.ux-dialog-btn.danger:hover {
    background: var(--ux-btn-danger);
    color: white;
}

/* ============================
   INFO BAR / TOAST  (UX.infoBar) — MODE FLOTTANT
   ----------------------------
   Toast flottant en OVERLAY dans un coin de la fenetre :
   - jamais full-width, jamais colle bord a bord,
   - ne POUSSE JAMAIS le contenu de l'app (aucune reservation d'espace,
     aucun padding sur <body>, aucun redimensionnement),
   - n'occupe aucune place dans le flux (position: fixed, hors flux),
   - s'empile dans le coin choisi (bottom-right par defaut).
   Le toast peut recouvrir une petite partie du contenu : chaque app
   choisit le coin (UX.infoBar.setDefaultPosition / option position) pour
   ne masquer ni commande, ni bouton, ni texte important.
   ============================ */

.ux-infobar-stack {
    position: fixed;
    z-index: 20000;
    display: flex;
    gap: 10px;
    max-width: min(420px, calc(100vw - 32px));
    pointer-events: none;
}

.ux-infobar-stack--bottom-right  { right: 20px; bottom: 20px; flex-direction: column-reverse; align-items: flex-end; }
.ux-infobar-stack--bottom-left   { left: 20px;  bottom: 20px; flex-direction: column-reverse; align-items: flex-start; }
.ux-infobar-stack--top-right     { right: 20px; top: 20px;    flex-direction: column;         align-items: flex-end; }
.ux-infobar-stack--top-left      { left: 20px;  top: 20px;    flex-direction: column;         align-items: flex-start; }
.ux-infobar-stack--bottom-center { left: 50%; transform: translateX(-50%); bottom: 20px; flex-direction: column-reverse; align-items: center; }
.ux-infobar-stack--top-center    { left: 50%; transform: translateX(-50%); top: 20px;    flex-direction: column;         align-items: center; }

.ux-infobar {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    max-width: 420px;
    padding: 12px 14px;
    background: var(--bg-secondary, #161b22);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-left: 3px solid var(--ux-info);
    border-radius: 10px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
    font-size: 14px;
    line-height: 1.4;
    color: var(--text-primary, #f0f0f5);
    pointer-events: auto;
    user-select: none;
    box-sizing: border-box;
    opacity: 0;
    transition: opacity .2s ease, transform .24s cubic-bezier(.16, 1, .3, 1);
    will-change: transform, opacity;
}

/* Sens d'entree selon le coin d'ancrage (glissement depuis le bord) */
.ux-infobar-stack--bottom-right .ux-infobar,
.ux-infobar-stack--top-right .ux-infobar    { transform: translateX(24px); }
.ux-infobar-stack--bottom-left .ux-infobar,
.ux-infobar-stack--top-left .ux-infobar     { transform: translateX(-24px); }
.ux-infobar-stack--top-center .ux-infobar   { transform: translateY(-24px); }
.ux-infobar-stack--bottom-center .ux-infobar{ transform: translateY(24px); }

.ux-infobar.ux-infobar--visible {
    opacity: 1;
    transform: translate(0, 0);
}

/* La sortie reutilise le transform de base du coin (retrait du --visible)
   et eteint l'opacite. */
.ux-infobar--leaving {
    opacity: 0;
}

.ux-infobar__icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    margin-top: 1px;
    stroke: currentColor;
}

.ux-infobar__message {
    flex: 1 1 auto;
    min-width: 0;
    word-wrap: break-word;
    overflow-wrap: anywhere;
}

.ux-infobar__actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.ux-infobar__action {
    appearance: none;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    padding: 5px 10px;
    border-radius: 6px;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, filter .15s ease;
}

.ux-infobar__action:hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.25);
}

.ux-infobar__action--primary {
    background: var(--accent, var(--ux-btn-primary));
    border-color: transparent;
    color: #fff;
}

.ux-infobar__action--primary:hover {
    filter: brightness(1.12);
    background: var(--accent, var(--ux-btn-primary));
    border-color: transparent;
}

.ux-infobar__action--danger {
    background: var(--ux-error);
    border-color: transparent;
    color: #fff;
}

.ux-infobar__action--danger:hover {
    filter: brightness(1.12);
    background: var(--ux-error);
    border-color: transparent;
}

.ux-infobar__close {
    appearance: none;
    background: transparent;
    border: none;
    color: var(--text-secondary, rgba(255,255,255,0.55));
    width: 26px;
    height: 26px;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: background .15s ease, color .15s ease;
}

.ux-infobar__close svg {
    width: 14px;
    height: 14px;
}

.ux-infobar__close:hover {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-primary, #fff);
}

.ux-infobar--success { border-left-color: var(--ux-success); }
.ux-infobar--success .ux-infobar__icon { color: var(--ux-success); }

.ux-infobar--error { border-left-color: var(--ux-error); }
.ux-infobar--error .ux-infobar__icon { color: var(--ux-error); }

.ux-infobar--info { border-left-color: var(--ux-info); }
.ux-infobar--info .ux-infobar__icon { color: var(--ux-info); }

.ux-infobar--warning { border-left-color: var(--ux-warning); }
.ux-infobar--warning .ux-infobar__icon { color: var(--ux-warning); }

/* ============================
   PANEL RESIZER (redimensionnement de panneaux)
   ============================ */

/**
 * Composant de panneau redimensionnable
 * 
 * Usage:
 *   <div class="ux-resizable-layout">
 *       <aside class="ux-panel ux-panel-left" id="left-panel">...</aside>
 *       <div class="ux-resizer ux-resizer-left" id="left-resizer"></div>
 *       <main class="ux-panel-main">...</main>
 *       <div class="ux-resizer ux-resizer-right" id="right-resizer"></div>
 *       <aside class="ux-panel ux-panel-right" id="right-panel">...</aside>
 *   </div>
 * 
 * JavaScript:
 *   const leftResizer = UX.resizer({
 *       resizerId: 'left-resizer',
 *       panelId: 'left-panel',
 *       side: 'left',
 *       minWidth: 200,
 *       maxWidth: 500,
 *       storageKey: 'myapp_left_panel',
 *       collapsible: true
 *   });
 */

/* Layout conteneur */
.ux-resizable-layout {
    display: flex;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

/* Panneaux latéraux */
.ux-panel {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bg-secondary, #12121a);
    flex-shrink: 0;
    /* Pas de transition par défaut - géré par .ux-panel-animating pour collapse/expand */
}

.ux-panel-left {
    width: 280px;
    min-width: 200px;
    border-right: 1px solid var(--border, rgba(255, 255, 255, 0.08));
}

.ux-panel-right {
    width: 320px;
    min-width: 200px;
    border-left: 1px solid var(--border, rgba(255, 255, 255, 0.08));
}

/* Zone principale centrale */
.ux-panel-main {
    flex: 1;
    min-width: 200px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* Barre de redimensionnement verticale */
.ux-resizer {
    width: 6px;
    background: var(--ux-resizer-bg, rgba(255, 255, 255, 0.06));
    cursor: col-resize;
    flex-shrink: 0;
    position: relative;
    transition: background 0.2s ease;
    -webkit-user-select: none;
    user-select: none;
    z-index: 10;
    /* Tactile : le redimensionnement doit recevoir les pointermove. */
    touch-action: none;
}

.ux-resizer:hover,
.ux-resizer.ux-resizer-dragging {
    background: var(--accent, #8b5cf6);
}

/* Indicateur visuel central - barre verticale */
.ux-resizer::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 3px;
    height: 40px;
    background: var(--ux-resizer-color, var(--accent, #8b5cf6));
    opacity: 0.7;
    border-radius: 2px;
    transition: all 0.2s ease;
}

.ux-resizer:hover::after,
.ux-resizer.ux-resizer-dragging::after {
    background: rgba(255, 255, 255, 0.9);
    opacity: 1;
    height: 60px;
    box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}

/* État fermé du panneau - GAUCHE */
.ux-resizer-left.ux-resizer-collapsed {
    background: linear-gradient(90deg, var(--accent, #8b5cf6), rgba(139, 92, 246, 0.7));
    cursor: pointer;
    width: 8px;
}

.ux-resizer-left.ux-resizer-collapsed::after {
    width: 3px;
    height: 50px;
    background: rgba(255, 255, 255, 0.7);
    opacity: 1;
}

/* Chevron indiquant qu'on peut réouvrir - vers la droite */
.ux-resizer-left.ux-resizer-collapsed::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, calc(-50% - 35px));
    width: 0;
    height: 0;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-left: 6px solid rgba(255, 255, 255, 0.8);
}

/* État fermé du panneau - DROITE */
.ux-resizer-right.ux-resizer-collapsed {
    background: linear-gradient(270deg, var(--accent, #8b5cf6), rgba(139, 92, 246, 0.7));
    cursor: pointer;
    width: 8px;
}

.ux-resizer-right.ux-resizer-collapsed::after {
    width: 3px;
    height: 50px;
    background: rgba(255, 255, 255, 0.7);
    opacity: 1;
}

/* Chevron indiquant qu'on peut réouvrir - vers la gauche */
.ux-resizer-right.ux-resizer-collapsed::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, calc(-50% - 35px));
    width: 0;
    height: 0;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-right: 6px solid rgba(255, 255, 255, 0.8);
}

.ux-resizer.ux-resizer-collapsed:hover {
    background: var(--accent-hover, #7c3aed);
    filter: brightness(1.1);
}

/* Panneau collapsé */
.ux-panel-collapsed {
    min-width: 0 !important;
    width: 0 !important;
    overflow: hidden;
    flex: none !important;
    border: none !important;
}

.ux-panel-collapsed > * {
    opacity: 0;
    pointer-events: none;
}

/* Animation de transition */
.ux-panel-animating {
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1), 
                min-width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

/* PERFORMANCE: Désactiver les transitions pendant le drag */
.ux-panel-resizing,
.ux-panel-resizing * {
    transition: none !important;
}

/* Empêcher la sélection de texte pendant le resize */
body.ux-resizing,
body.ux-resizing * {
    user-select: none !important;
    -webkit-user-select: none !important;
    cursor: col-resize !important;
}

/* Désactiver les pointer-events sur les iframes pendant le resize */
body.ux-resizing iframe {
    pointer-events: none !important;
}

/* Tooltip au survol du resizer fermé */
.ux-resizer.ux-resizer-collapsed[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    padding: 6px 12px;
    background: var(--bg-tertiary, #1a1a25);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
    border-radius: 6px;
    font-size: 12px;
    color: var(--text-primary, #f0f0f5);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
    z-index: 100;
    width: auto;
    height: auto;
}

.ux-resizer-left.ux-resizer-collapsed[data-tooltip]::after {
    left: calc(100% + 8px);
}

.ux-resizer-right.ux-resizer-collapsed[data-tooltip]::after {
    right: calc(100% + 8px);
}

.ux-resizer.ux-resizer-collapsed:hover[data-tooltip]::after {
    opacity: 1;
}

/* ============================
   RESPONSIVE
   ============================ */

@media (max-width: 768px) {
    .ux-dialog {
        min-width: auto;
        width: calc(100vw - 32px);
        max-width: 400px;
    }
    
    .ux-infobar-stack {
        width: 100%;
    }
    .ux-infobar {
        font-size: 13px;
    }
    .ux-infobar--visible {
        padding: 9px 12px;
    }
}

@media (max-width: 480px) {
    .ux-dialog-footer {
        flex-direction: column;
    }
    
    .ux-dialog-btn {
        width: 100%;
        justify-content: center;
        text-align: center;
    }
}

/* ============================
   APP LOADER (écran de chargement global)
   ============================ */

/**
 * Loader plein écran pour les apps
 * 
 * Usage:
 *   1. Ajouter la classe "app-loading" au <body>
 *   2. Ajouter le HTML du loader (ou utiliser UX.showLoader())
 *   3. Appeler UX.appReady() quand les données sont chargées
 * 
 * Exemple HTML:
 *   <body class="app-loading">
 *       <div class="ux-app-loader">
 *           <div class="ux-app-loader-spinner"></div>
 *       </div>
 *       <!-- contenu de l'app -->
 *   </body>
 */

/* État de chargement - cache tout le contenu sauf le loader */
body.app-loading > *:not(.ux-app-loader):not(script):not(link):not(style) {
    visibility: hidden !important;
    opacity: 0 !important;
    pointer-events: none !important;
}

body.app-loading .ux-app-loader {
    visibility: visible !important;
    opacity: 1 !important;
}

/* Transition fluide à la fin du chargement
   IMPORTANT: Exclure les éléments qui gèrent leur propre opacité (toasts, modals, overlays)
   Sinon l'animation force opacity:1 et override leur état par défaut (opacity:0).
   CRITIQUE: .ux-tooltip DOIT être exclu. L'infobulle est un enfant direct de body
   dont l'état caché EST opacity:0 ; une animation terminée avec fill-mode:forwards
   épingle opacity:1 au niveau animation de la cascade et écrase DEFINITIVEMENT la
   règle .ux-tooltip{opacity:0} -> bulle impossible à masquer (bug « le tooltip ne
   disparaît jamais » observé 3 jours en production sur 120secondes et admin/logs). */
body.app-loaded > *:not(.ux-app-loader):not(.toast-notification):not(.modal):not(.ux-infobar-stack):not(.ux-dialog-overlay):not(.auth-overlay):not(.ux-tooltip) {
    animation: uxAppFadeIn 0.3s ease forwards;
}

@keyframes uxAppFadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Container du loader */
.ux-app-loader {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-primary, #0a0a0f);
    z-index: 99999;
    transition: opacity 0.3s ease, visibility 0.3s ease;
}

/* Animation de disparition */
.ux-app-loader.hiding {
    opacity: 0;
    visibility: hidden;
}

/* Spinner animé */
.ux-app-loader-spinner {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    position: relative;
}

.ux-app-loader-spinner::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, 0.1);
}

.ux-app-loader-spinner::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 3px solid transparent;
    border-top-color: var(--accent, #4a9eff);
    animation: uxSpinnerRotate 0.8s linear infinite;
}

@keyframes uxSpinnerRotate {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Variante avec message */
.ux-app-loader-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}

.ux-app-loader-message {
    color: var(--text-secondary, #a0aec0);
    font-size: 14px;
    font-weight: 500;
}

/* ============================
   TEXTAREA AUTO-RESIZE
   
   Textarea qui s'adapte automatiquement en hauteur
   selon son contenu, sans scroll interne tant que
   maxHeight n'est pas atteint.
   
   Usage:
     <textarea class="ux-textarea-auto" rows="1"></textarea>
   
   JavaScript (optionnel, pour contrôle manuel):
     UX.autoResize(document.getElementById('my-textarea'));
     UX.autoResizeAll('.ux-textarea-auto');
   
   Options par défaut:
     - minHeight: hauteur initiale (ou 60px)
     - maxHeight: 400px
     - resetOnEmpty: true (revient à minHeight si vide)
   ============================ */

.ux-textarea-auto {
    width: 100%;
    min-height: 60px;
    max-height: 400px;
    padding: 12px 16px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 10px;
    color: var(--text-primary, #f0f0f5);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.5;
    resize: none;
    overflow: hidden;
    box-sizing: border-box;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.ux-textarea-auto:focus {
    outline: none;
    border-color: var(--accent, #4a9eff);
    box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.15);
}

.ux-textarea-auto::placeholder {
    color: var(--text-secondary, #a0aec0);
    opacity: 0.7;
}

/* Variante compacte - pour les commentaires */
.ux-textarea-auto-sm {
    min-height: 44px;
    padding: 10px 14px;
    font-size: 13px;
    border-radius: 8px;
}

/* Variante grande - pour les descriptions longues */
.ux-textarea-auto-lg {
    min-height: 100px;
    max-height: 600px;
    padding: 16px 20px;
    font-size: 15px;
}

/* ============================
   MOBILE NAV (navigation panneau mobile)
   ============================ */

/**
 * Transition slide gauche/droite pour les apps avec sidebar + main panel.
 * 
 * Structure HTML attendue :
 *   <div class="app-container ux-mobile-nav">
 *     <div class="sidebar ux-panel ux-mobile-panel" id="sidebar">...</div>
 *     <div class="ux-resizer" id="sidebar-resizer"></div>
 *     <div class="main-content ux-panel-main ux-mobile-panel">...</div>
 *   </div>
 * 
 * Activation : UX.mobileNav({ containerId, breakpoint })
 */

@media (max-width: 768px) {
    .ux-mobile-nav {
        flex-direction: row;
        overflow: hidden;
        width: 200vw;
        transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .ux-mobile-nav.ux-mobile-show-main {
        transform: translateX(-100vw);
    }

    .ux-mobile-nav > .ux-panel,
    .ux-mobile-nav > .sidebar {
        width: 100vw !important;
        min-width: 100vw !important;
        flex-shrink: 0;
        height: 100vh;
        border-right: none !important;
    }

    .ux-mobile-nav > .ux-panel-main,
    .ux-mobile-nav > .main-content {
        width: 100vw;
        min-width: 100vw;
        flex-shrink: 0;
        height: 100vh;
    }

    .ux-mobile-nav > .ux-resizer {
        display: none !important;
    }

    .ux-mobile-back {
        display: flex !important;
    }
}

.ux-mobile-back {
    display: none;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    background: var(--bg-tertiary, rgba(30, 30, 45, 0.8));
    color: var(--text-primary, #f0f0f5);
    cursor: pointer;
    align-items: center;
    justify-content: center;
    transition: all 0.15s;
    flex-shrink: 0;
    user-select: none;
    padding: 0;
}

.ux-mobile-back:hover {
    background: var(--accent, #8b5cf6);
    border-color: var(--accent, #8b5cf6);
}

.ux-mobile-back svg {
    width: 20px;
    height: 20px;
    stroke: currentColor;
    stroke-width: 2;
    fill: none;
}

/* ProductivIA brand typography */
.productivia-brand {
    white-space: nowrap;
}
.productivia-brand .pia-ia {
    font-size: 0.72em;
    position: relative;
    top: -0.35em;
    font-weight: 700;
    letter-spacing: 0.02em;
}

/* ===== UX Player ===== */

.ux-player {
    background: var(--bg-card, rgba(18, 18, 26, 0.95));
    border: 1px solid var(--border, rgba(255, 255, 255, 0.06));
    border-radius: 10px;
    padding: 12px 16px;
    user-select: none;
}

.ux-player-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted, #a0aec0);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 10px;
}

.ux-player-controls {
    display: flex;
    align-items: center;
    gap: 12px;
}

.ux-player-play {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border: none;
    border-radius: 50%;
    background: var(--accent, #4a9eff);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    user-select: none;
    transition: transform 0.15s, background 0.15s;
}

.ux-player-play svg {
    width: 16px;
    height: 16px;
}

.ux-player-play:hover {
    transform: scale(1.08);
}

.ux-player-play:active {
    transform: scale(0.95);
}

.ux-player-wf-stack {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ux-player-wf-wrap {
    flex: 1;
    min-width: 0;
    height: 48px;
    cursor: pointer;
    position: relative;
    border-radius: 4px;
}

/* Dans la pile verticale (timeRuler), ne pas etirer la zone onde sur toute la hauteur */
.ux-player-wf-stack > .ux-player-wf-wrap {
    flex: 0 0 48px;
    height: 48px;
    max-height: 48px;
    align-self: stretch;
}

.ux-player-time-ruler {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 10px;
    line-height: 1.2;
    color: var(--text-muted, #6b7280);
    padding: 0 2px;
    user-select: none;
}

.ux-player-time-ruler-tick {
    flex: 0 0 auto;
}

.ux-player-wf-wrap canvas {
    width: 100%;
    height: 100%;
    display: block;
}

.ux-player-dl {
    width: 26px;
    height: 26px;
    border: none;
    background: transparent;
    color: var(--text-muted, #6b7280);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 5px;
    flex-shrink: 0;
    padding: 0;
    transition: color 0.15s, background 0.15s;
    user-select: none;
}
.ux-player-dl svg { width: 14px; height: 14px; }
.ux-player-dl:hover { color: var(--text-primary, #f0f0f5); background: rgba(255,255,255,0.06); }

.ux-player-time {
    font-size: 12px;
    font-family: var(--font-mono, monospace);
    color: var(--text-muted, #a0aec0);
    white-space: nowrap;
    min-width: 80px;
    text-align: right;
    flex-shrink: 0;
}

/* ===== Mode compact : controles empiles a droite (libere de la largeur pour
   la forme d'onde) -> L1 position courante, L2 duree totale, L3 son + dl ===== */
.ux-player-compact .ux-player-controls {
    gap: 10px;
}
.ux-player-side {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-width: 0;
}
.ux-player-compact .ux-player-time {
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1.15;
    min-width: 0;
    text-align: center;
    gap: 1px;
}
.ux-player-compact .ux-player-time-cur {
    color: var(--accent, #4a9eff);
    font-weight: 600;
}
.ux-player-compact .ux-player-time-dur {
    color: var(--text-muted, #6b7280);
    font-size: 11px;
}
.ux-player-side-icons {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
}

.ux-player-vol-wrap {
    position: relative;
    flex-shrink: 0;
}

.ux-player-vol-btn {
    width: 28px;
    height: 28px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted, #a0aec0);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    user-select: none;
    transition: color 0.15s, background 0.15s;
}

.ux-player-vol-btn svg {
    width: 16px;
    height: 16px;
}

.ux-player-vol-btn:hover {
    color: var(--text-primary, #f0f0f5);
    background: rgba(255, 255, 255, 0.06);
}

.ux-player-vol-popup {
    position: absolute;
    bottom: calc(100% + 8px);
    right: -10px;
    background: var(--bg-secondary, rgba(18, 18, 26, 0.98));
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 8px;
    padding: 10px 14px;
    width: 120px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(4px);
    transition: opacity 0.15s, transform 0.15s;
    z-index: var(--z-dropdown, 100);
    box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,0.4));
}

.ux-player-vol-popup.open {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0);
}

.ux-player-vol-track {
    width: 100%;
    height: 6px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 3px;
    position: relative;
    cursor: pointer;
}

.ux-player-vol-fill {
    height: 100%;
    background: var(--accent, #4a9eff);
    border-radius: 3px;
    width: 100%;
    pointer-events: none;
}

.ux-player-vol-thumb {
    width: 14px;
    height: 14px;
    background: #fff;
    border-radius: 50%;
    position: absolute;
    top: 50%;
    left: 100%;
    transform: translate(-50%, -50%);
    box-shadow: 0 1px 4px rgba(0,0,0,0.3);
    pointer-events: none;
}

/* =====================================================
   Lecteur video (UX.videoPlayer)
   ===================================================== */
.ux-video-player {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #000;
    border-radius: 10px;
    overflow: hidden;
    user-select: none;
    outline: none;
    font-family: inherit;
    cursor: default;
}

.ux-video-player:focus-visible {
    box-shadow: 0 0 0 2px var(--accent, #4a9eff);
}

.ux-video-player-el {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    background: #000;
    display: block;
    cursor: pointer;
}

.ux-video-player.is-fullscreen {
    border-radius: 0;
    aspect-ratio: auto;
    height: 100%;
}

/* Gros bouton play central (visible en pause uniquement) */
.ux-video-player-bigplay {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(1);
    width: 72px;
    height: 72px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s, transform 0.2s, background 0.2s;
    z-index: 3;
}

.ux-video-player.is-paused .ux-video-player-bigplay {
    backdrop-filter: blur(6px);
}

.ux-video-player-bigplay svg {
    width: 32px;
    height: 32px;
    margin-left: 4px;
}

.ux-video-player.is-paused .ux-video-player-bigplay {
    opacity: 1;
    pointer-events: auto;
}

/* Mode clavier : on masque le gros bouton PLAY central pour ne pas gener la visualisation.
   Reactive au prochain mouvement de souris > 15 px. */
.ux-video-player.keyboard-mode .ux-video-player-bigplay {
    opacity: 0;
    pointer-events: none;
}

/* Pendant un chargement (spinner visible) ou en cas d'erreur, on cache le gros
   bouton PLAY pour eviter de superposer play + spinner (UX confuse). */
.ux-video-player.is-loading .ux-video-player-bigplay,
.ux-video-player.has-error .ux-video-player-bigplay {
    opacity: 0 !important;
    pointer-events: none !important;
}

.ux-video-player-bigplay:hover {
    background: rgba(0, 0, 0, 0.75);
    transform: translate(-50%, -50%) scale(1.08);
}

/* Spinner de chargement */
.ux-video-player-loading {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
    z-index: 4;
}

.ux-video-player-loading.visible {
    opacity: 1;
}

.ux-video-player-spinner {
    width: 44px;
    height: 44px;
    border: 3px solid rgba(255, 255, 255, 0.18);
    border-top-color: #fff;
    border-radius: 50%;
    animation: ux-video-player-spin 0.8s linear infinite;
}

@keyframes ux-video-player-spin {
    to { transform: rotate(360deg); }
}

.ux-video-player-loading-msg {
    color: rgba(255, 255, 255, 0.85);
    font-size: 12px;
    text-align: center;
    margin-top: 10px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    min-height: 16px;
}

.ux-video-player-loading {
    flex-direction: column;
}

/* Overlay d'erreur */
.ux-video-player-error {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(4px);
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
    z-index: 7;
}

.ux-video-player-error.visible {
    opacity: 1;
    pointer-events: auto;
}

.ux-video-player-error-icon {
    width: 48px;
    height: 48px;
    color: #ef4444;
}

.ux-video-player-error-icon svg {
    width: 100%;
    height: 100%;
}

.ux-video-player-error-title {
    font-size: 16px;
    font-weight: 600;
    color: #fff;
    max-width: 80%;
}

.ux-video-player-error-desc {
    font-size: 13px;
    color: rgba(255, 255, 255, 0.7);
    max-width: 80%;
    line-height: 1.5;
}

.ux-video-player-error-retry {
    margin-top: 8px;
    padding: 8px 18px;
    background: var(--accent, #4a9eff);
    color: #fff;
    border: none;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, transform 0.15s;
    font-family: inherit;
}

.ux-video-player-error-retry:hover {
    transform: translateY(-1px);
    filter: brightness(1.15);
}

/* Thumbnail preview (popup sur hover timeline) */
.ux-video-player-thumb {
    position: absolute;
    left: 0;
    bottom: 80px;
    background: rgba(0, 0, 0, 0.85);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    padding: 4px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.1s;
    z-index: 5;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

.ux-video-player-thumb.visible {
    opacity: 1;
}

.ux-video-player-thumb canvas {
    display: block;
    border-radius: 4px;
    background: #111;
}

.ux-video-player-thumb-time {
    text-align: center;
    color: #fff;
    font-size: 11px;
    font-family: var(--font-mono, monospace);
    padding: 3px 0 1px;
    letter-spacing: 0.3px;
}

/* Barre de controles (bottom) */
.ux-video-player-controls {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 14px 12px;
    background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.35) 60%, rgba(0,0,0,0) 100%);
    color: #fff;
    opacity: 0;
    transform: translateY(4px);
    pointer-events: none;
    transition: opacity 0.2s, transform 0.2s;
    z-index: 6;
}

.ux-video-player.controls-visible .ux-video-player-controls,
.ux-video-player.is-paused .ux-video-player-controls {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.ux-video-player-play {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.15s, transform 0.15s;
}

.ux-video-player-play svg {
    width: 14px;
    height: 14px;
}

.ux-video-player-play:hover {
    background: rgba(255, 255, 255, 0.25);
}

.ux-video-player-play:active {
    transform: scale(0.95);
}

.ux-video-player-time {
    font-size: 12px;
    font-family: var(--font-mono, monospace);
    color: rgba(255, 255, 255, 0.9);
    white-space: nowrap;
    min-width: 80px;
    flex-shrink: 0;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.ux-video-player-wf-wrap {
    flex: 1;
    min-width: 0;
    height: 36px;
    cursor: pointer;
    position: relative;
    border-radius: 4px;
}

.ux-video-player-wf-wrap canvas {
    width: 100%;
    height: 100%;
    display: block;
}

.ux-video-player-fs {
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    color: rgba(255, 255, 255, 0.9);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    flex-shrink: 0;
    border-radius: 6px;
    transition: background 0.15s, color 0.15s;
}

.ux-video-player-fs svg {
    width: 18px;
    height: 18px;
}

.ux-video-player-fs .ux-fs-exit { display: none; }
.ux-video-player.is-fullscreen .ux-video-player-fs .ux-fs-enter { display: none; }
.ux-video-player.is-fullscreen .ux-video-player-fs .ux-fs-exit { display: block; }

.ux-video-player-fs:hover {
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
}

/* Volume (meme structure que UX.player) */
.ux-video-player-vol-wrap {
    position: relative;
    flex-shrink: 0;
}

.ux-video-player-vol-btn {
    width: 32px;
    height: 32px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: rgba(255, 255, 255, 0.9);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.15s, color 0.15s;
}

.ux-video-player-vol-btn svg {
    width: 18px;
    height: 18px;
}

.ux-video-player-vol-btn:hover {
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
}

.ux-video-player-vol-popup {
    position: absolute;
    bottom: calc(100% + 8px);
    right: -4px;
    background: rgba(18, 18, 26, 0.98);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
    padding: 10px 14px;
    width: 120px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(4px);
    transition: opacity 0.15s, transform 0.15s;
    z-index: var(--z-dropdown, 100);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

.ux-video-player-vol-popup.open {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0);
}

.ux-video-player-vol-track {
    width: 100%;
    height: 6px;
    background: rgba(255, 255, 255, 0.15);
    border-radius: 3px;
    position: relative;
    cursor: pointer;
}

.ux-video-player-vol-fill {
    height: 100%;
    background: var(--accent, #4a9eff);
    border-radius: 3px;
    width: 100%;
    pointer-events: none;
}

.ux-video-player-vol-thumb {
    width: 14px;
    height: 14px;
    background: #fff;
    border-radius: 50%;
    position: absolute;
    top: 50%;
    left: 100%;
    transform: translate(-50%, -50%);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    pointer-events: none;
}

/* Masquer le curseur natif quand les controles sont caches pendant la lecture */
.ux-video-player.is-playing:not(.controls-visible) {
    cursor: none;
}

/* Menu contextuel generique (UX.contextMenu) */
.ux-context-menu {
    position: fixed;
    /* Doit etre au-dessus de tous les overlays applicatifs : agent flottant
       (panel 200040, FAB 200050), modales, loaders. Reste sous le canal
       mentions OS (999999) qui est reserve aux notifications systeme
       critiques. */
    z-index: 999998;
    background: var(--bg-card, rgba(22, 22, 34, 0.98));
    backdrop-filter: blur(12px);
    border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    border-radius: 10px;
    padding: 0;
    min-width: 180px;
    box-shadow: var(--shadow-lg, 0 10px 40px rgba(0, 0, 0, 0.45));
    animation: uxCtxFadeIn 0.12s ease;
    overflow: hidden;
}

@keyframes uxCtxFadeIn {
    from { opacity: 0; transform: scale(0.96); }
    to { opacity: 1; transform: scale(1); }
}

.ux-context-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 14px;
    box-sizing: border-box;
    background: none;
    border: none;
    color: var(--text-primary, #f0f0f5);
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
    text-align: left;
}

.ux-context-menu-item:hover {
    background: rgba(255, 255, 255, 0.06);
}

.ux-context-menu-item svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.ux-context-menu-item.danger {
    color: #ef4444;
}

.ux-context-menu-item.danger:hover {
    background: rgba(239, 68, 68, 0.12);
    color: #ef4444;
}

.ux-context-menu-separator {
    display: block;
    height: 1px;
    margin: 0;
    padding: 0;
    border: none;
    background: rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

/* Visualiseur micro (UX.micLevelWaveform) — canvas block, dimensions fixees par l’app */
.ux-mic-level-waveform {
    display: block;
    vertical-align: middle;
    max-width: 100%;
}

/* ============================================================================
   SUPPRESSION D'ELEMENTS DE LISTE (.nav-item-deletable)

   Pattern UX standard pour rendre n'importe quel item de liste de sidebar
   supprimable. Voir documentation : app UX, section "Suppression dans la
   sidebar". Utilisable avec n'importe quel contenu (.nav-item, ou un item
   personnalise comme .history-item dans l'app Images).

   Structure DOM attendue :
     .nav-item-deletable                  // wrapper, overflow:hidden
       .nav-item-deletable-slide          // contenu glissant (translateX)
         <ITEM>                           // .nav-item ou tout autre conteneur
         .nav-item-delete-zone            // zone rouge a droite (souris)
           .nav-item-trash-btn            // bouton corbeille
           .nav-item-confirm-text         // « Confirmer ? »
       .nav-item-swipe-bg                 // zone rouge derriere (tactile)

   Etats CSS :
     hover-revealed     -> Hover 0.5s sur l'actif : zone rouge 48px
     confirm-revealed   -> Clic corbeille : zone 140px + texte
     swiping            -> Touch drag en cours
     swiped             -> Swipe valide (>30px) : slide a -60px
   ============================================================================ */

.nav-item-deletable {
    position: relative;
    overflow: hidden;
    flex-shrink: 0;
}

.nav-item-deletable-slide {
    display: flex;
    align-items: center;
    position: relative;
    z-index: 1;
    background: var(--bg-secondary, transparent);
    will-change: transform;
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Item interieur : on autorise n'importe quelle classe (pas seulement
   .nav-item) en visant le premier enfant du slide qui n'est pas la zone
   de suppression. Cela laisse les apps utiliser leur propre layout (ex:
   .history-item avec image + meta dans Images). */
.nav-item-deletable .nav-item-deletable-slide > :not(.nav-item-delete-zone) {
    flex: 1;
    min-width: 0;
}

.nav-item-deletable .nav-item {
    border-radius: 0;
}

.nav-item-deletable .nav-item span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
}

.nav-item-delete-zone {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    width: 0;
    min-width: 0;
    opacity: 0;
    overflow: hidden;
    background: rgba(239, 68, 68, 0.5);
    flex-shrink: 0;
    user-select: none;
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1),
                min-width 0.25s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.2s ease;
}

.nav-item-deletable.hover-revealed .nav-item-delete-zone {
    width: 48px;
    min-width: 48px;
    opacity: 1;
}

.nav-item-deletable.confirm-revealed .nav-item-delete-zone {
    width: 140px;
    min-width: 140px;
    opacity: 1;
    justify-content: flex-start;
}

.nav-item-trash-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    min-width: 48px;
    align-self: stretch;
    cursor: pointer;
    background: #ef4444;
}

.nav-item-trash-btn:active {
    background: #dc2626;
}

.nav-item-trash-btn svg {
    width: 18px;
    height: 18px;
    stroke: white;
    fill: none;
    stroke-width: 2;
    flex-shrink: 0;
}

.nav-item-confirm-text {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 3px;
    flex: 1;
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.85);
    cursor: default;
    letter-spacing: 0.02em;
    transition: max-width 0.25s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.2s ease 0.05s;
}

.nav-item-confirm-text svg {
    flex-shrink: 0;
    stroke: rgba(255, 255, 255, 0.6);
}

.nav-item-deletable.confirm-revealed .nav-item-confirm-text {
    max-width: 100px;
    opacity: 1;
}

.nav-item-swipe-bg {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #ef4444;
    z-index: 0;
    cursor: pointer;
    user-select: none;
    opacity: 0;
    transition: background 0.15s ease, opacity 0.15s ease;
}

.nav-item-deletable.swiping .nav-item-swipe-bg,
.nav-item-deletable.swiped .nav-item-swipe-bg {
    opacity: 1;
}

.nav-item-swipe-bg:active {
    background: #dc2626;
}

.nav-item-swipe-bg svg {
    width: 18px;
    height: 18px;
    stroke: white;
    fill: none;
    stroke-width: 2;
}

/* Fond actif rouge tres leger pendant suppression (souris ou tactile) */
.nav-item-deletable.swiped .nav-item-deletable-slide > :not(.nav-item-delete-zone),
.nav-item-deletable.swiping .nav-item-deletable-slide > :not(.nav-item-delete-zone),
.nav-item-deletable.confirm-revealed .nav-item-deletable-slide > :not(.nav-item-delete-zone) {
    background: rgba(239, 68, 68, 0.1);
}

.nav-item-deletable.swiped .nav-item-deletable-slide {
    transform: translateX(-60px);
}

/* ============================================================================
   ONGLETS DE PANNEAU (.sidebar-tabs-row + .sidebar-tab + .sidebar-tab-add-btn)

   Pattern UX standard pour la barre d'onglets en haut du panneau de gauche.
   Une SEULE ligne contenant :
     - N onglets (.sidebar-tab) qui se partagent la largeur restante
     - Un bouton "+" (.sidebar-tab-add-btn) optionnel, a droite, qui cree un
       nouvel item en fonction de l'onglet actif (l'app gere l'action via
       UX.sidebarTabs.attach({ onAdd })).

   Convention mio.land : ne PAS dupliquer le bouton "+ Nouveau X" sur une ligne
   separee sous les onglets. On integre le "+" dans la barre d'onglets pour
   garder une seule ligne et maximiser l'espace de la liste en dessous.

   Markup attendu :
     <div class="sidebar-tabs-row">
       <div class="sidebar-tabs">
         <button class="sidebar-tab active" data-tab="history"><span>Historique</span></button>
         <button class="sidebar-tab" data-tab="projects"><span>Projets</span></button>
       </div>
       <button class="sidebar-tab-add-btn" title="Nouvel element">
         <svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
       </button>
     </div>
     <div class="sidebar-tab-contents">
       <div class="sidebar-tab-content active" data-tab="history">...</div>
       <div class="sidebar-tab-content" data-tab="projects">...</div>
     </div>

   Voir : app UX, section "Onglets de panneau" (demo + API).
   ============================================================================ */

.sidebar-tabs-row {
    display: flex;
    align-items: stretch;
    gap: 6px;
    min-height: 40px;
    padding: 4px 8px 0;
    border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.08));
    flex-shrink: 0;
}

/* Variante : pas d'onglets, juste le bouton "+" - on l'aligne a droite
   pour rester coherent avec le pattern standard (onglets a gauche, "+" a droite). */
.sidebar-tabs-row-add-only {
    justify-content: flex-end;
}

.sidebar-tabs {
    display: flex;
    gap: 0;
    flex: 1;
    align-items: stretch;
    min-width: 0;
}

.sidebar-tab {
    flex: 1;
    padding: 0 8px;
    border: none;
    background: transparent;
    color: var(--text-secondary, rgba(255, 255, 255, 0.6));
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    border-bottom: 2px solid transparent;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Pas de transition : feedback :active instantane au pointerdown. */
}

.sidebar-tab:hover {
    color: var(--text-primary, #fff);
    background: rgba(255, 255, 255, 0.03);
}

.sidebar-tab:active {
    background: var(--accent, #8b5cf6) !important;
    color: #fff !important;
}

.sidebar-tab.active {
    color: var(--text-primary, #fff);
    border-bottom-color: var(--accent, #8b5cf6);
}

/* Compteur optionnel a droite du libelle (ex: "Historique 100"). Pattern
   utilise par Harmonie, Exercices, etc. Mis en sourdine au repos, pleine
   opacite quand l'onglet est actif. */
.sidebar-tab .tab-count {
    font-size: 10px;
    font-weight: 600;
    color: currentColor;
    opacity: 0.6;
    line-height: 1;
    margin-left: 4px;
}

.sidebar-tab.active .tab-count { opacity: 1; }

/* Bouton "+" a droite : compact, couleur accent, signale l'affordance de
   creation. L'app le wire via UX.sidebarTabs.attach({ onAdd }). */
.sidebar-tab-add-btn {
    flex-shrink: 0;
    align-self: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid rgba(74, 158, 255, 0.25);
    border-radius: 6px;
    background: linear-gradient(135deg, rgba(74, 158, 255, 0.10), rgba(74, 158, 255, 0.04));
    color: var(--accent, #4a9eff);
    cursor: pointer;
    user-select: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .15s, border-color .15s, transform .08s;
}

.sidebar-tab-add-btn:hover {
    background: linear-gradient(135deg, rgba(74, 158, 255, 0.18), rgba(74, 158, 255, 0.08));
    border-color: rgba(74, 158, 255, 0.45);
}

.sidebar-tab-add-btn:active {
    transform: scale(0.94);
}

.sidebar-tab-add-btn svg {
    width: 16px;
    height: 16px;
    stroke: currentColor;
    stroke-width: 2.2;
    fill: none;
    display: block;
}

/* Conteneurs de contenu d'onglet : panneaux superposes (position absolute)
   pour bascules instantanees. Toggle visibility plutot que display:none/block
   afin d'eviter un layout pass complet sur le panneau entrant. */
.sidebar-tab-contents {
    flex: 1;
    position: relative;
    min-height: 0;
    overflow: hidden;
}

.sidebar-tab-content {
    position: absolute;
    inset: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    visibility: hidden;
    pointer-events: none;
    z-index: 0;
    contain: layout paint style;
}

.sidebar-tab-content.active {
    visibility: visible;
    pointer-events: auto;
    z-index: 1;
}

/* ============================================================================
   UX.tooltip  -  Infobulle Material Design (survol)

   Remplace les infobulles natives du navigateur (attribut title) par une
   bulle solide, rectangulaire, avec une largeur maximale qui force le retour
   a la ligne du texte (au lieu d'une longue ligne horizontale illisible).

   - Surface inverse (sombre sur theme clair, claire sur theme sombre) facon
     Material Design 3.
   - Elevation (ombre), coins arrondis, petite fleche orientee vers la cible.
   - Apparition/disparition animees (fade + scale + leger glissement).

   S'utilise partout via l'attribut title (auto-converti) ou data-ux-tooltip.
   ============================================================================ */
.ux-tooltip {
    /* Une seule forme SVG (rectangle arrondi + fleche) dessine le fond complet.
       Le conteneur ne porte plus ni background ni border, pour eviter toute
       distinction entre le rectangle et la fleche. */
    /* Sombre, legerement translucide : combine au verre givre dessous (le fond
       de la page transparait, flou) tout en gardant une silhouette assez dense
       pour projeter un halo aurore franc a l'exterieur. */
    --ux-tip-bg: rgba(24, 24, 38, 0.80);
    --ux-tip-border: rgba(255, 255, 255, 0.16);
    --ux-arrow-h: 9px;
    --ux-tip-svg-w: 0px;
    --ux-tip-svg-h: 0px;
    /* Forme commune (rectangle + fleche) pour clipper l'aurore exactement sur
       le fond. Pilotee par JS (meme path que le SVG de fond). */
    --ux-tip-clip: none;
    /* Etats de l'animation "page de livre" : l'axe de rotation est la pointe de
       la fleche (transform-origin pose en px par JS). Pilotes par placement. */
    --ux-tip-flip-closed: perspective(720px) scale(0.94);
    --ux-tip-flip-open: perspective(720px) scale(1);
    position: fixed;
    top: 0;
    left: 0;
    /* Toujours au-dessus de tout (grille d'apps, fenetres, dialogues OS qui
       montent jusqu'a ~100000). Une infobulle est l'element transitoire le
       plus prioritaire visuellement. */
    z-index: 2147483600;
    width: max-content;
    max-width: min(220px, calc(100vw - 24px));
    box-sizing: border-box;
    padding: 8px 12px;
    border-radius: 8px;
    background: transparent;
    border: 0;
    color: #f4f4fb;
    font-family: var(--font-ui, 'Inter', -apple-system, BlinkMacSystemFont, sans-serif);
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.45;
    letter-spacing: 0.1px;
    text-align: left;
    overflow: visible;
    pointer-events: none;
    opacity: 0;
    /* DEFENSE EN PROFONDEUR : l'etat cache repose AUSSI sur visibility (propriete
       discrete, insensible aux animations d'opacite type uxAppFadeIn qui peuvent
       epingler opacity:1 via fill-mode:forwards). La bulle devient invisible au
       plus tard 0.24s apres le retrait de .visible, quoi qu'il arrive. */
    visibility: hidden;
    /* Repli initial : la "page" est legerement basculee autour de la pointe de
       la fleche. transform-origin est pose en px par JS sur cette pointe. */
    transform: var(--ux-tip-flip-closed, perspective(720px) scale(0.94));
    transition:
        opacity 0.16s cubic-bezier(0.4, 0, 0.2, 1),
        transform 0.24s cubic-bezier(0.33, 1.06, 0.6, 1),
        visibility 0s linear 0.24s;
    will-change: opacity, transform;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.ux-tooltip.visible {
    opacity: 1;
    visibility: visible;
    /* Apparition immediate : pas de delai sur le basculement de visibility. */
    transition-delay: 0s;
    /* Page deployee : retour a plat (rotation nulle). */
    transform: var(--ux-tip-flip-open, perspective(720px) scale(1));
}

/* La bulle et TOUTES ses couches (verre givre, fond SVG aurore, texte) sont
   totalement insensibles a la souris : aucune interception possible du
   pointeur, donc aucune perturbation de la detection du survol de l'element
   declencheur (mouseleave et :hover restent fiables, meme quand la bulle
   recouvre l'element ou ses voisins). NE JAMAIS retirer. */
.ux-tooltip,
.ux-tooltip * {
    pointer-events: none !important;
}

.ux-tooltip-text {
    /* Forme rectangulaire : on autorise le retour a la ligne et on limite a
       quelques lignes pour rester compact (le titre complet reste dans title). */
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
    word-break: normal;
    white-space: normal;
    position: relative;
    z-index: 2;
}

/* Verre givre : couche HTML clippee EXACTEMENT sur la forme (--ux-tip-clip,
   meme path que le SVG). Elle floute l'arriere-plan (backdrop-filter) pour un
   interieur sombre et givre. Posee SOUS le SVG : le fond SVG (legerement
   translucide) la laisse transparaitre. Aucune aurore a l'interieur. */
.ux-tooltip-glass {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--ux-tip-svg-w);
    height: var(--ux-tip-svg-h);
    pointer-events: none;
    z-index: 0;
    clip-path: var(--ux-tip-clip, none);
    -webkit-clip-path: var(--ux-tip-clip, none);
    background: rgba(14, 14, 22, 0.32);
    -webkit-backdrop-filter: blur(9px) saturate(1.15);
    backdrop-filter: blur(9px) saturate(1.15);
}
.ux-tooltip[data-placement="bottom"] .ux-tooltip-glass {
    top: calc(-1 * var(--ux-arrow-h));
}
.ux-tooltip[data-placement="right"] .ux-tooltip-glass {
    left: calc(-1 * var(--ux-arrow-h));
}

/* Fond SVG unique : un seul path ferme contient le rectangle arrondi ET la
   fleche. Le stroke suit donc le contour complet sans aucune jonction visible.
   C'est aussi cette forme qui PROJETTE l'aurore boreale a l'EXTERIEUR via des
   drop-shadow colores animes (vert / cyan / violet / rose) : la lueur emane du
   contour exact (fleche comprise) sans jamais empieter sur l'interieur. */
.ux-tooltip-bg {
    position: absolute;
    left: 0;
    top: 0;
    width: var(--ux-tip-svg-w);
    height: var(--ux-tip-svg-h);
    pointer-events: none;
    overflow: visible;
    z-index: 1;
    /* Etat statique (et repli reduced-motion) : auréole CENTREE (offset 0) et
       serree contre le contour exact (fleche comprise). Les lueurs colorees ne
       depassent que de ~4-6px, ce qui forme un anneau lumineux regulier (et
       non une bavure directionnelle). L'animation fait varier les teintes dans
       le temps pour retrouver la vie de l'aurore du rond de l'agent. */
    filter:
        drop-shadow(0 1px 3px rgba(0, 0, 0, 0.38))
        drop-shadow(0 0 3px rgba(52, 211, 153, 0.55))
        drop-shadow(0 0 6px rgba(167, 139, 250, 0.40));
    animation: uxTipAuroraGlow 8s ease-in-out infinite;
    will-change: filter;
}
.ux-tooltip[data-placement="bottom"] .ux-tooltip-bg {
    top: calc(-1 * var(--ux-arrow-h));
}
.ux-tooltip[data-placement="right"] .ux-tooltip-bg {
    left: calc(-1 * var(--ux-arrow-h));
}
.ux-tooltip-bg-shape {
    fill: var(--ux-tip-bg);
    stroke: var(--ux-tip-border);
    stroke-width: 1;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}


/* Aurore boreale EXTERIEURE, collee au contour : un anneau lumineux CENTRE
   (offset 0, flou 3-6px) dont les deux teintes evoluent dans le temps, sans
   jamais s'etendre loin de la forme. La lueur reste un halo regulier autour
   du contour exact (fleche comprise). */
@keyframes uxTipAuroraGlow {
    0%, 100% {
        filter:
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.38))
            drop-shadow(0 0 3px rgba(52, 211, 153, 0.55))
            drop-shadow(0 0 6px rgba(167, 139, 250, 0.40));
    }
    33% {
        filter:
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.38))
            drop-shadow(0 0 3px rgba(34, 211, 238, 0.55))
            drop-shadow(0 0 6px rgba(99, 102, 241, 0.40));
    }
    66% {
        filter:
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.38))
            drop-shadow(0 0 3px rgba(236, 72, 153, 0.50))
            drop-shadow(0 0 6px rgba(132, 204, 22, 0.38));
    }
}

/* Repli historique : sans backdrop-filter (verre givre indisponible), on rend
   le fond SVG quasi opaque pour garder un interieur sombre lisible. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .ux-tooltip { --ux-tip-bg: rgba(24, 24, 36, 0.97); }
}

@media (prefers-reduced-motion: reduce) {
    .ux-tooltip {
        transition: opacity 0.1s linear;
        transform: none;
    }
    .ux-tooltip.visible {
        transform: none;
    }
    /* On garde le halo aurore (statique, via le filtre par defaut du fond)
       pour l'identite visuelle, mais sans derive animee. */
    .ux-tooltip-bg {
        animation: none;
    }
}
