/* =====================================================================
   LIBRARY-PAGE.CSS — Styles partagés entre library.html standalone et
   accueil.html (mode SPA-injecté). Couvre :
     - LibraryHub : tuiles style page album (Liked Songs + Playlists + +)
     - LibraryModal : overlay + form (cover picker + name + description)
     - LibraryContextMenu : menu flottant custom (clic droit)
     - Cross-tab toast

   ⚠ DEV NOTE : Si vous modifiez ce fichier, BUMPEZ le query string
     `?v=YYYYMMDD` aux deux endroits suivants :
     - NorexMusic/accueil.html     (link library-page.css + library-page.js)
     Note : NorexMusic/library.html n'est plus qu'un redirect-stub vers
     accueil.html?page=library.html ; il n'inclut plus ce fichier
     directement. Le bump doit donc uniquement toucher accueil.html
     aujourd'hui (cf. refactor library → sub-route SPA, commit
     courant "refonte-library-playlists").
     Sans ce bump, certains browsers garderont l'ancien CSS en cache
     et causeront des régressions du type "modal visible à l'ouverture"
     (cf. bug 2026-07-27). Version courante : 20260818.
   ===================================================================== */

/* =====================================================================
   BUG FIX (2026-07-27) : l'attribut `hidden` doit gagner contre
   `display: flex` sur .lib-modal-overlay.
   =====================================================================
   Cause du bug : `.lib-modal-overlay { display: flex }` (specificity
   0,0,1,0) entrait en cascade-tiebreak avec la UA rule `[hidden]
   { display: none }` (même specificity mais chargée avant). La règle
   auteur gagne → modal visible sur TOUTES les pages hébergeant le
   template, même avec attribut `hidden` présent. Setup
   `_overlay.hidden = true` (depuis close button) était silencieusement
   ignoré par CSS.

   Symptômes : (1) Modal "Create new playlist" spawnait à l'ouverture de
   accueil.html, (2) Close ✕ semblait ne rien faire.

   Fix : specificity + attribut = 0,0,2,0 qui bat .lib-modal-overlay
   (0,0,1,0). `!important` est conforme à la spec HTML : `[hidden]` est
   un indicateur binaire que author CSS ne devrait pas pouvoir casser.

   NB : #library-hub-root et .lib-ctx-menu n'ont PAS de règle author
   `display:` qui les concerne → UA `[hidden] { display: none }` les
   cache déjà correctement par défaut. Pas besoin de les traiter ici
   tant qu'on ne pose pas de `display:` sur eux. YAGNI.
   ⚠ GARDE-FOU : Si vous ajoutez display: sur #library-hub-root ou
      .lib-ctx-menu à l'avenir, étendez cette règle aux deux sélecteurs
      concernés (sinon le même bug réapparaît).
   ===================================================================== */
.lib-modal-overlay[hidden] {
    display: none !important;
}
/* =====================================================================
   BUG FIX (2026-07-31) : .lib-ctx-menu[id="lib-ctx-menu"] avait l'attribut
   `hidden` posé en HTML mais restait visible sur l'écran (le user le
   voyait comme « gros rectangle gris » flottant qui recouvrait la zone
   titre du player). Le garde-fou écrit en haut du fichier (YAGNI) avait
   été violé par l'ajout de `.lib-ctx-menu { display: flex; ... position:
   fixed; ... }` ligne ~432 : comme dans le bug modal-overlay de 2026-07-
   27, la règle author bat l'UA `[hidden] { display: none }` sur tiebreak
   de cascade.
   Fix : specificity + attribut = (0,1,1,0) bat .lib-ctx-menu (0,1,0,0)
   assure que le menu click-droit reste caché tant qu'on ne l'a pas
   `shown` via JS (LibraryContextMenu.show() retire l'attribut hidden).
   ⚠ Prévenir à l'avenir : tout ajout de display: sur .lib-ctx-menu,
   .lib-modal-overlay ou #library-hub-root doit s'accompagner de cette
   règle [hidden] !important derrière. Sans elle, le menu sera
   éternellement visible. YAGNI respecté maintenant.
   ===================================================================== */
.lib-ctx-menu[hidden] {
    display: none !important;
}
/* Symétrique : si on étend LibraryContextMenu à #library-hub-root plus
   tard (pas le cas aujourd'hui), ajouter
   #library-hub-root[hidden] { display: none !important; } pour le même
   motif. Pas ajouté pour respecter YAGNI — on ajoute seulement quand
   une display: sera posée sur ce sélacteur. */

/* =====================================================================
   LibraryHub : tuiles style "page album" + bouton ⋮ sur playlist cards
   ===================================================================== */
/* Container : on laisse l'élément prendre toute la place disponible dans
   .middle-column. Plus de section-wrapper, plus de background card — la
   grid .album-grid (déjà stylée par album.css) gère la présentation. */
.lib-hub-container {
    margin: 8px 0 24px;
}

/* Override léger sur .album-grid dans le contexte library : on veut que
   les tuiles gardent une taille FIXE, identique que la rangée en
   contienne 1, 2, 3 ou 4. Café Spotify-like : Liked Songs + N Playlists
   + Create playlist restent petits et alignés en haut, comme si la
   rangée de 4 était déjà réservée.

   Mécanisme : `repeat(4, minmax(220px, 1fr))` FORCE 4 colonnes,
   contrairement à `auto-fit` qui collapse les colonnes vides et
   étire les tuiles restantes pour fill la rangée. Avec exactement 4
   colonnes, chaque tuile reçoit 1fr — donc largeur identique
   indépendamment du nombre de tuiles réelles (la rangée garde
   l'empreinte visuelle de 4 colonnes même avec 1 seule tuile).
   minmax(220px, ?) garantit qu'on ne descend jamais sous 220px
   (sinon layout casse sur mobile); le `1fr` permet de grossir
   légèrement sur grands écrans sans jamais devenir énorme (le
   container library est de toute façon borné par .left-column +
   .main-container sur accueil.html).

   Les pages album (vocaloid, tadc, ros hidere, etc.) gardent leur
   auto-fit minmax(280px, 1fr) inchangé.

   NB : album.css est chargé dynamiquement APRÈS library-page.css (via
   ensureAlbumCss) → pour gagner le specificity-tiebreak, on monte d'un
   cran en préfixant par `.lib-hub-container`. */
.lib-hub-container .album-grid.lib-hub-grid {
    grid-template-columns: repeat(4, minmax(220px, 1fr));
    gap: 18px;
    padding: 8px;
}

/* Justify-content : start pour que les tuiles soient collées à gauche
   (default justify-content: stretch fait pareil mais l'auto-rows le
   force à fill horizontalement). Avec justify-content: start, 1
   tuile seule s'aligne en haut-gauche, comportement Spotify-like. */
.lib-hub-container .album-grid.lib-hub-grid {
    justify-content: start;
}

/* Tuile générique : hérite de .album-card (album.css) pour le look
   page-album (border-radius, hover, etc.). On override juste la taille
   minimale du cover et la position du bouton ⋮. */
.lib-hub-tile {
    position: relative;
    text-decoration: none;
    color: inherit;
}
.lib-hub-tile-cover {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg,
        rgb(60, 60, 60),
        rgb(40, 40, 40));
}
.lib-hub-tile-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Icones au centre des covers Liked Songs et Create (♥ et +) */
.lib-hub-tile-icon {
    line-height: 1;
    user-select: none;
    pointer-events: none;
}

/* Liked Songs : cover avec icône cœur blanc centré (gris neutre) */
.lib-hub-tile-cover-liked {
    background: linear-gradient(135deg,
        rgb(70, 70, 70),
        rgb(45, 45, 45));
}
.lib-hub-tile-heart {
    font-size: 72px;
    color: white;
    filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.55));
    transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.lib-hub-tile-liked:hover .lib-hub-tile-heart,
.lib-hub-tile-liked:focus-visible .lib-hub-tile-heart {
    transform: scale(1.18) rotate(-6deg);
}

/* Create playlist : style aligned avec les tuples "Aller dans une
   playlist" (`.lib-hub-tile-playlist`). On garde le même gradient de
   cover que la classe parente `.lib-hub-tile-cover` pour que la tile
   ressemble à un slot de playlist vide — look uniforme dans la grille,
   Spotify-style.

   ⚠ Reset button natif (BUG FIX 2026-08-07) : ce tile est un <button>,
   pas un <a> comme les playlists. Sans reset explicite, Chromium/Firefox/
   Safari ajoutent une background gris/blanc (`background: ButtonFace`)
   + bordures 2px outset + petit padding interne 1-3px. Conséquence :
   cette tile a un fond blanc/gris "dégueulasse" derrière le cover
   gradient — complètement décalée du reste de la grille. On reset tout
   ça : background transparent, border:none, font/padding/color inherités,
   alignement identique aux <a> siblings. */
.lib-hub-tile-create {
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    line-height: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-align: inherit;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
}
.lib-hub-tile-create:focus-visible {
    /* Pas de ring navigateur (souvent blanc moche). À la place, on
       utilisera un léger outset transparent (cf. .lib-hub-tile:focus
       plus bas, ou inherité de .album-card:hover-transform). On doc
       ici pour que le futur dev ne soit pas surpris du focus invisible. */
    outline: none;
}

/* Cover : même gradient que les autres tiles (.lib-hub-tile-cover
   parent) — tuile ressemble à un slot de playlist vide, juste le "+"
   intérieur la différencie des autres (cf. .lib-hub-tile-plus). */
.lib-hub-tile-cover-create {
    background: linear-gradient(135deg,
        rgb(60, 60, 60),
        rgb(40, 40, 40));
}
.lib-hub-tile-plus {
    font-size: 48px;
    font-weight: 300;
    color: rgba(255, 255, 255, 0.94);
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
    transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.3, 1), color 0.3s ease;
}
.lib-hub-tile-create:hover .lib-hub-tile-plus,
.lib-hub-tile-create:focus-visible .lib-hub-tile-plus {
    transform: rotate(90deg) scale(1.18);
    color: white;
}

/* Fallback icon (music note) pour playlist sans cover */
.lib-hub-tile-fallback {
    width: 56px;
    height: 56px;
    color: rgba(255, 255, 255, 0.85);
}
.lib-hub-tile-fallback svg {
    width: 100%; height: 100%;
}

.lib-hub-tile-info {
    padding: 10px 4px 0;
}
.lib-hub-tile-info h4 {
    margin: 0;
    font-size: 0.875rem;  /* 14px */
    font-weight: 700;
    color: white;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lib-hub-tile-info p {
    margin: 4px 0 0;
    font-size: 0.78rem;
    opacity: 0.62;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ⋮ button (rename/delete) sur cards Playlists */
.lib-hub-tile-more {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 32px;
    height: 32px;
    border: none;
    background: rgba(0, 0, 0, 0.45);
    color: rgba(255, 255, 255, 0.95);
    border-radius: 999px;
    font-size: 1.15rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 0.2s ease, transform 0.2s ease, background 0.2s ease, color 0.2s ease;
    backdrop-filter: blur(4px);
    z-index: 2;
}
.lib-hub-tile:hover .lib-hub-tile-more,
.lib-hub-tile:focus-within .lib-hub-tile-more,
.lib-hub-tile-more:focus-visible {
    opacity: 1;
    transform: scale(1);
}
.lib-hub-tile-more:hover,
.lib-hub-tile-more:focus-visible {
    background: rgba(0, 0, 0, 0.75);
    color: var(--main-color);
    outline: none;
}

/* Cross-tab toast */
.lib-hub-cross-tab-toast {
    position: fixed;
    top: 18px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(20, 20, 20, 0.92);
    border: 1px solid var(--main-color);
    border-radius: 999px;
    padding: 10px 18px;
    font-size: 0.82rem;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: white;
    z-index: 9999;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55), 0 0 12px color-mix(in srgb, var(--main-color) 25%, transparent);
    opacity: 1;
    transition: opacity 0.4s ease;
}
.lib-hub-cross-tab-toast.is-fading {
    opacity: 0;
}
.lib-hub-cross-tab-icon {
    font-size: 1rem;
    color: var(--main-color);
}

/* =====================================================================
   LibraryModal : Create playlist
   ===================================================================== */
.lib-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.62);
    z-index: 9990;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    animation: lib-modal-overlay-in 0.18s ease;
}
@keyframes lib-modal-overlay-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.lib-modal {
    width: min(560px, 100%);
    background: #1c1c1c;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 16px;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.7),
                0 0 22px color-mix(in srgb, var(--main-color) 22%, transparent);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 48px);
    animation: lib-modal-in 0.20s cubic-bezier(0.2, 0.7, 0.3, 1);
}
@keyframes lib-modal-in {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.lib-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.lib-modal-header h2 {
    margin: 0;
    font-size: 1.15rem;
    letter-spacing: 0.2px;
}
.lib-modal-close {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.85);
    width: 32px;
    height: 32px;
    border-radius: 999px;
    cursor: pointer;
    font-size: 1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.18s ease, color 0.18s ease;
}
.lib-modal-close:hover,
.lib-modal-close:focus-visible {
    background: rgba(255, 91, 91, 0.15);
    color: #ff5b5b;
    outline: none;
}
.lib-modal-form {
    padding: 18px 20px 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.lib-modal-cover-row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 0;
}
.lib-modal-cover-preview {
    width: 96px;
    height: 96px;
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px dashed rgba(255, 255, 255, 0.25);
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255, 255, 255, 0.4);
    font-size: 1.6rem;
    overflow: hidden;
    flex-shrink: 0;
}
.lib-modal-cover-preview-loaded {
    background: rgba(0, 0, 0, 0.4);
    border: 1px solid rgba(255, 255, 255, 0.2);
}
.lib-modal-cover-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.lib-modal-cover-btn {
    display: inline-block;
    padding: 8px 14px;
    border: 1px solid var(--main-color);
    color: var(--main-color);
    border-radius: 999px;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
    transition: background 0.18s ease, color 0.18s ease;
    margin: 0; /* override default label margin */
}
.lib-modal-cover-btn:hover,
.lib-modal-cover-btn:focus-within {
    background: color-mix(in srgb, var(--main-color) 15%, transparent);
    outline: none;
}
.lib-modal-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.lib-modal-label {
    font-size: 0.82rem;
    opacity: 0.7;
    font-weight: 500;
}
.lib-modal-field input[type="text"],
.lib-modal-field textarea {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    padding: 10px 12px;
    color: white;
    font: inherit;
    font-size: 0.94rem;
    transition: border-color 0.18s ease, background 0.18s ease;
}
.lib-modal-field input[type="text"]:focus,
.lib-modal-field textarea:focus {
    outline: none;
    border-color: var(--main-color);
    background: rgba(255, 255, 255, 0.08);
}
.lib-modal-field textarea {
    resize: vertical;
    min-height: 72px;
    max-height: 200px;
}
.lib-modal-error {
    background: rgba(255, 91, 91, 0.12);
    border: 1px solid rgba(255, 91, 91, 0.45);
    color: #ff9191;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 0.85rem;
}
.lib-modal-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    margin-top: 4px;
}
.lib-modal-btn {
    padding: 9px 18px;
    border-radius: 999px;
    font-size: 0.9rem;
    font-weight: 600;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(255, 255, 255, 0.06);
    color: white;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.lib-modal-btn-cancel:hover,
.lib-modal-btn-cancel:focus-visible {
    background: rgba(255, 255, 255, 0.12);
    outline: none;
}
.lib-modal-btn-create {
    background: var(--main-color);
    border-color: var(--main-color);
    color: var(--bg-color-2, #111);
}
.lib-modal-btn-create:hover,
.lib-modal-btn-create:focus-visible {
    filter: brightness(1.12);
    outline: none;
}

/* =====================================================================
   LibraryContextMenu : menu flottant custom

   REWORK 2026-08-18 : refonte visuelle de la section "Manage playlists".
   Avant : checkbox Unicode `☑/☐` baked dans le label, header uppercase
   triste, items monochromatiques. Maintenant :
     - Container : entrée animée (fade + scale + translateY), shadow
       multi-couche (depth feel), backdrop-filter renforcé, min-width
       légèrement augmenté pour breathing room.
     - Items : padding plus généreux, hover state vibrant + transition
       unifiée sur background/border/transform, label en tabular-nums
       pour aligner le compte.      - Playlist checkbox : case dessinée en CSS (via pseudo-element
        ::before sur .lib-ctx-menu-icon) qui se colore avec
        var(--main-color) à l'état checked ; le label lui-même ne porte
        plus le prefix Unicode (cf. library-page.js#buildSongMenu pour
        le strip).
     - Section header : accent bar latéral 3px en var(--main-color),
       legère teinte de fond gradient pour démarquer la zone.
     - Empty state hint : passe d'un simple italic à une "soft card"
       centrée avec bordure pointillée + bg tint — feedback visuel
       EXPLICITE que la zone est vide vs cassée.
   ===================================================================== */
.lib-ctx-menu {
    position: fixed;
    z-index: 9995;
    background: rgba(18, 18, 18, 0.94);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 14px;
    box-shadow:
        0 22px 56px rgba(0, 0, 0, 0.72),
        0 4px 14px rgba(0, 0, 0, 0.45),
        0 0 18px color-mix(in srgb, var(--main-color) 16%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
    min-width: 280px;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    /* Animation d'entrée : fade + léger scale-up + translateY depuis le
       point d'origine. Très court (~ 140ms), cubic-bezier agressif pour
       un "snap" qui donne l'impression que le menu jaillit de la souris.
       `transform-origin` reste à défaut (center center) — le menu
       grandit depuis son centre, pas depuis le clic exact. Effet plus
       universellement joli sans calcul JS du `transform-origin`. */
    animation: lib-ctx-menu-in 0.14s cubic-bezier(0.2, 0.7, 0.3, 1);
    transform-origin: center top;
}
@keyframes lib-ctx-menu-in {
    from {
        opacity: 0;
        transform: scale(0.94) translateY(-6px);
    }
    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}
.lib-ctx-menu-item {
    position: relative;
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.92);
    padding: 10px 14px;
    border-radius: 10px;
    text-align: left;
    cursor: pointer;
    font: inherit;
    font-size: 0.92rem;
    line-height: 1.25;
    display: flex;
    align-items: center;
    gap: 10px;
    transition:
        background 0.14s ease,
        color 0.14s ease,
        box-shadow 0.14s ease;
}
/* L'item : hover / focus-visible déclenche un fond accentué + glow
   interne subtil qui donne une impression de "press". Le border-left
   crée un rail accent visible uniquement sur interaction — feedback
   sans polluer l'état idle. */
.lib-ctx-menu-item:hover,
.lib-ctx-menu-item:focus-visible {
    background: color-mix(in srgb, var(--main-color) 24%, transparent);
    color: white;
    outline: none;
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--main-color) 35%, transparent),
        inset 3px 0 0 var(--main-color);
}
.lib-ctx-menu-item-danger {
    color: rgba(255, 145, 145, 0.92);
}
.lib-ctx-menu-item-danger:hover,
.lib-ctx-menu-item-danger:focus-visible {
    background: rgba(255, 91, 91, 0.20);
    color: #ff7878;
    box-shadow:
        inset 0 0 0 1px rgba(255, 91, 91, 0.35),
        inset 3px 0 0 #ff5b5b;
}
.lib-ctx-menu-icon {
    width: 18px;
    flex-shrink: 0;
    text-align: center;
    opacity: 0.7;
}
/* === REWORK 2026-08-18 === L'icône span est incluse dans chaque
   `<button class="lib-ctx-menu-item">` par LibraryContextMenu#openAt.
   Elle est volontairement VIDE dans le HTML généré (text: '') pour
   servir de slot flexible : CSS peut y déposer une icône réelle
   (ex : checkbox dessin pour les playlists) sans modifier le JS.

   Cette règle cache la span pour TOUS les items qui ne sont PAS des
   playlists : sinon le span vide crée un gap visuel de 18px avant le
   label, ce qui décale les items standards (Liked Songs toggle,
   "Create new playlist…") inutilement à droite. La specificity
   (0,0,3,0) bat .lib-ctx-menu-icon ci-dessus (0,0,1,0), donc la
   règle s'applique sans race condition. */
.lib-ctx-menu-item:not(.lib-ctx-menu-item-playlist) .lib-ctx-menu-icon {
    display: none;
}
.lib-ctx-menu-label {
    flex: 1 1 auto;
    font-variant-numeric: tabular-nums;
}
.lib-ctx-menu-divider {
    height: 1px;
    background: linear-gradient(to right,
        transparent,
        rgba(255, 255, 255, 0.08) 18%,
        rgba(255, 255, 255, 0.08) 82%,
        transparent);
    margin: 6px 4px;
}

/* === REWORK 2026-08-18 : .lib-ctx-menu-header =======================
   Avant : simple uppercase + tracked letter-spacing + accent color.
   Maintenant : on ajoute une vraie DÉMARCATION visuelle de la section
   "Manage playlists" pour qu'elle soit perçue comme un SOUS-menu :
     - Padding-top plus large (10px vs 6px) pour aérer visuellement.
     - Rail accent à gauche (3px de var(--main-color)) sur toute la
       hauteur — équivalent du "stick" visuel qu'on retrouve dans les
       sidebar de gestion (Spotify, Apple Music).
     - Léger bg gradient horizontal pour souligner la section sans
       concurrencer le hover state des items.
     - Petire marge bas (4px) pour décoller du premier item checkbox.
   Garde-fou semantic : role=presentation + pointer-events:none +
   user-select:none (déjà en place avant). Screen-readers continuent
   d'ignorer ce header ornemental. */
.lib-ctx-menu-header {
    position: relative;
    padding: 10px 14px 4px 17px; /* 17px pour réserver le rail accent à gauche */
    margin: 4px 0 0;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--main-color);
    pointer-events: none;
    user-select: none;
    background: linear-gradient(to right,
        color-mix(in srgb, var(--main-color) 14%, transparent) 0%,
        color-mix(in srgb, var(--main-color)  4%, transparent) 60%,
        transparent 100%);
}
/* Rail accent 3px à gauche : la barre verticale pleine-hauteur, dans
   la couleur principale du thème. C'est LE signal visuel qui dit
   "nouvelle section sémantique" — fort impact sans alourdir. */
.lib-ctx-menu-header::before {
    content: '';
    position: absolute;
    left: 6px;
    top: 12px;
    bottom: 8px;
    width: 3px;
    background: var(--main-color);
    border-radius: 2px;
    box-shadow: 0 0 8px color-mix(in srgb, var(--main-color) 60%, transparent);
}
/* Theme-light : on booste un peu le contraste qui sinon se confond
   avec le background clair. */
html.theme-light .lib-ctx-menu-header {
    background: linear-gradient(to right,
        color-mix(in srgb, var(--main-color) 18%, transparent) 0%,
        transparent 80%);
}

/* === REWORK 2026-08-18 : .lib-ctx-menu-item-playlist + checkbox ====
   Avant : lib-ctx-menu-item-playlist avait juste un padding-left
   légèrement accru (14px) + un changement de couleur sur la version
   `.checked` (teinte verte). Le caractère ☑/☐ Unicode était baked
   dans le label, ce qui posait deux problèmes :
     1. Rendu incohérent selon la font Unicode du système (les
        caractères ballot-box sont souvent moches sur Windows/Safari).
     2. Impossible d'animer la transition unchecked → checked.

   Maintenant : on dessine la checkbox en CSS via :
     - .lib-ctx-menu-icon en position:absolute (gauche 12px, centré v.)
     - ::before : la boîte qui se Color-fill avec var(--main-color)
       à l'état `.checked` (border + bg + box-shadow accent).
   La case a un border-radius doux (5px) et un border 1.5px pour
   rester lisible sur font-rendering petite taille. L'état checked
   est un simple Color-fill avec transition courte (perçu comme
   feedback de toggle, pas comme "mouvement").
   Le label ne contient plus le prefix Unicode ☑/☐ (cf. JS strip).

   Note : initialement la case affichait aussi un V (checkmark via
   ::after, deux bords du pseudo rotated 45deg). Retiré — l'effet
   visuel de remplissage seul est plus simple et suffit à marquer
   la différence d'état, surtout vu la taille 16×16 du contrôle. */
.lib-ctx-menu-item-playlist {
    padding-left: 40px; /* 40px pour laisser 18px-icône + 8px espace + label à 14px */
}
.lib-ctx-menu-item-playlist .lib-ctx-menu-icon {
    /* Override les styles par défaut (.lib-ctx-menu-icon width:18px)
       en passant en position:absolute pour ne pas pousser le label
       vers la droite — le label commence direct après le rail
       gauche, l'icône flotte par-dessus. */
    position: absolute;
    left: 12px;
    top: 50%;
    width: 18px;
    height: 18px;
    margin: 0;
    transform: translateY(-50%);
    opacity: 1;
    pointer-events: none;
}
/* La boîte unchecked : carrée 16x16, border 1.5px subtile mais
   visible, fond à peine teinté pour suggérer un "slot cliquable".
   Border-radius 5px = compromis entre carré strict (8px) et rond
   complet — donne ce look moderne de checkbox d'app native. */
.lib-ctx-menu-item-playlist .lib-ctx-menu-icon::before {
    content: '';
    display: block;
    width: 16px;
    height: 16px;
    border: 1.5px solid rgba(255, 255, 255, 0.4);
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.04);
    box-sizing: border-box;
    transition:
        border-color 0.16s ease,
        background 0.16s ease,
        box-shadow 0.16s ease;
}
/* État CHECKED : la boîte se remplit avec var(--main-color) + bordure
   accent. Border reste accent pour cohérence du pair unchecked/checked
   (pas un "disappear-then-replace" qui serait distrayant). */
.lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked .lib-ctx-menu-icon::before {
    border-color: var(--main-color);
    background: var(--main-color);
    box-shadow: 0 0 8px color-mix(in srgb, var(--main-color) 50%, transparent);
}
/* Theme-light : teinte unchecked plus contrastée pour rester visible
   sur le fond clair (~245,245,245) — fond très légèrement grisé + bord
   noir à 32% d'opacité. Le main-color reste lisible quel que soit le
   thème pour l'état CHECKED (couleur saturée = bon contraste). */
   (souvent saturé, lumineux) → on reste sur white qui reste lisible
   quel que soit le thème. La boîte unchecked garde son border subtil
   mais boosté en alpha (rgba black 0.35) pour rester visible sur
   fond clair (~245,245,245). */
html.theme-light .lib-ctx-menu-item-playlist .lib-ctx-menu-icon::before {
    border-color: rgba(0, 0, 0, 0.32);
    background: rgba(0, 0, 0, 0.04);
}
html.theme-light .lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked .lib-ctx-menu-icon::before {
    border-color: var(--main-color);
    background: var(--main-color);
}

/* Subtle visual reward pour les playlists qui CONTIENNENT la chanson :
   on conserve le tint vert-soft de l'ancienne version, mais on le
   complète avec une légère emphase font-weight et un padding-right
   suffisant pour que les (count) soient lisibles. La couleur verte
   est volontairement adoucie (30% mix vers blanc) pour éviter le
   "trop voyant" — l'idée est "présence confirmée" ≠ "alerte". */
.lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked {
    color: color-mix(in srgb, #4ade80 32%, rgba(255, 255, 255, 0.92));
}
html.theme-light .lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked {
    color: color-mix(in srgb, #1a8754 42%, #1a1a1a);
}
/* Sur les items CHECKED, le hover garde le rail accent gauche
   vert/mint pour confirmer visuellement que la ligne reste une
   action (toggle off). Sans ce hover spécial, on retomberait sur le
   rail accent général (main-color) qui pourrait suggérer
   "déjà-cliqué = inactif". */
.lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked:hover,
.lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked:focus-visible {
    background: color-mix(in srgb, #4ade80 18%, transparent);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, #4ade80 35%, transparent),
        inset 3px 0 0 #4ade80;
    color: white;
}
html.theme-light .lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked:hover,
html.theme-light .lib-ctx-menu-item-playlist.lib-ctx-menu-item-checked:focus-visible {
    background: color-mix(in srgb, #1a8754 18%, transparent);
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, #1a8754 35%, transparent),
        inset 3px 0 0 #1a8754;
    color: #1a1a1a;
}

/* === REWORK 2026-08-18 : .lib-ctx-menu-hint ==========================
   Avant : simple paragraphe italic + opacity basse.
   Maintenant : passage à un look "soft card" qui DÉMARQUE clairement
   la zone comme étant un état particulier (= "vide / à compléter"),
   pas un texte d'aide oublié. Trois éléments visuels :
     1. Background tint léger pour faire ressortir du menu (vs item).
     2. Border-radius + dashed border pour évoquer un slot vide.
     3. Centrage du texte pour donner ce "call to action" feel.
   Padding-left:14px (aligné sur les items clickable) pour rester dans
   la grille mentale du user. Le `line-height: 1.4` aide le wrap
   éventuel. Pointer-events:none + user-select:none conservés (info
   pure, pas un widget). */
.lib-ctx-menu-hint {
    margin: 2px 6px 4px;
    padding: 10px 14px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px dashed rgba(255, 255, 255, 0.18);
    border-radius: 10px;
    font-size: 0.80rem;
    font-style: italic;
    text-align: center;
    /* Couleur tertiaire basse-opacité pour bien distinguer d'un bouton ;
       on garde la couleur du thème via --main-color mais avec mix vers
       blanc à 50% pour un look "gris pâle" légèrement teinté. */
    color: color-mix(in srgb, var(--main-color) 50%, rgba(255, 255, 255, 0.55));
    pointer-events: none;
    user-select: none;
    line-height: 1.4;
}
/* Theme-light : contraste boosté + border-color plus foncé sinon le
   gris pâle devient illisible sur fond clair. */
html.theme-light .lib-ctx-menu-hint {
    background: rgba(0, 0, 0, 0.035);
    border: 1px dashed rgba(0, 0, 0, 0.22);
    color: color-mix(in srgb, var(--main-color) 32%, #1a1a1a);
}

/* =====================================================================
   Light theme overrides
   ===================================================================== */
html.theme-light .lib-hub-tile-info h4 { color: #1a1a1a; }
html.theme-light .lib-hub-tile-info p { color: #3a3a3a; opacity: 0.85; }
html.theme-light .lib-modal {
    background: #f4f4f4;
    color: #1a1a1a;
}
html.theme-light .lib-modal-header h2 { color: #1a1a1a; }
html.theme-light .lib-modal-close { color: #1a1a1a; }
html.theme-light .lib-modal-field input[type="text"]:focus,
html.theme-light .lib-modal-field textarea:focus {
    background: rgba(255, 255, 255, 0.95);
}
html.theme-light .lib-modal-cover-preview {
    /* Sur theme-light le preview blanc-sur-blanc était quasi invisible.
     On force un background gris-pale + bordure gris-foncé + signe "?"
     en gris foncé pour rester lisible. */
    background: rgba(0, 0, 0, 0.04);
    border: 1px dashed rgba(0, 0, 0, 0.22);
    color: #5a5a5a;
}
html.theme-light .lib-modal-cover-preview-loaded { background: rgba(255,255,255,0.9); border-color: rgba(0,0,0,0.18); }
html.theme-light .lib-modal-field input[type="text"],
html.theme-light .lib-modal-field textarea {
    background: rgba(255, 255, 255, 0.85);
    border-color: rgba(0, 0, 0, 0.12);
    color: #1a1a1a;
}
html.theme-light .lib-ctx-menu {
    background: rgba(245, 245, 245, 0.95);
    border-color: rgba(0, 0, 0, 0.12);
    box-shadow:
        0 22px 56px rgba(0, 0, 0, 0.18),
        0 4px 14px rgba(0, 0, 0, 0.10),
        0 0 18px color-mix(in srgb, var(--main-color) 12%, transparent),
        inset 0 1px 0 rgba(255, 255, 255, 0.6);
    backdrop-filter: blur(14px) saturate(125%);
    -webkit-backdrop-filter: blur(14px) saturate(125%);
}
html.theme-light .lib-ctx-menu-item { color: #1a1a1a; }
html.theme-light .lib-ctx-menu-item:hover,
html.theme-light .lib-ctx-menu-item:focus-visible {
    background: color-mix(in srgb, var(--main-color) 22%, rgba(0,0,0,0.03));
    color: #1a1a1a;
}
html.theme-light .lib-ctx-menu-item-danger {
    color: rgba(190, 30, 30, 0.95);
}
html.theme-light .lib-ctx-menu-item-danger:hover,
html.theme-light .lib-ctx-menu-item-danger:focus-visible {
    background: rgba(255, 91, 91, 0.18);
    color: #b8181e;
}
html.theme-light .lib-ctx-menu-divider {
    background: linear-gradient(to right,
        transparent,
        rgba(0, 0, 0, 0.10) 18%,
        rgba(0, 0, 0, 0.10) 82%,
        transparent);
}

/* =====================================================================
   Responsive mobile
   ===================================================================== */
/* === MOBILE FIX v52 (2026-08-14) : BUMP LIB-HUB GAP + MODAL TOP ======
   User : « boutons de library trop collés tailles des covers full
   buggées (certaines petites, d'autres énormes) et je voudrais que la
   toolbar avec les boutons soit déplacée au dessus de l'écran ».

   3 sous-fixes :
     1. lib-hub-grid gap 14 → 20px : « boutons trop collés ».
     2. album-cover-wrapper + .album-card img → MOBILE OVERRIDE explicite
        (width:100% + aspect-ratio:1/1 + height:auto !important). Force
        toutes les covers à être carrées et de même taille sur standalone
        ET SPA. User : « certaines petites, d'autres énormes » venait
        du fait que sans override mobile, certaines cards n'avaient pas
        aspect-ratio appliqué (cover-wrappers ou img directs).
     3. lib-modal-actions : déplacée AU-DESSUS de la form (= top de
        modal après le header). Toolbar = button row. Order:-1 sur le
        flex column de .lib-modal pousse .lib-modal-actions juste sous
        .lib-modal-header. Position:sticky pour qu'elle reste visible
        même quand l'user scroll la longue form.

   Note : les sizes des lib-hub-tile ICÔNES (heart 44, plus 52, h4 0.95,
   p 0.80) introduites en v51 sont CONSERVÉES ici, parce que v52
   remplace l'ancien @media (max-width: 600px) qui les contenait.
   =============================================================== */
@media (max-width: 1024px) {
    /* === Fix #1 : lib-hub-grid gap bump ===========================
       v52 : 14 → 20px. v53 : 20 → 28px. User « toujours trop collés ».
       +50% sur le gap horizontal entre tuiles en v53. */
    .lib-hub-grid {
        grid-template-columns: repeat(3, 1fr) !important;
        gap: 28px !important;
        padding: 0 12px !important;
    }
    .lib-hub-tile-cover {
        border-radius: 10px !important;
    }
    /* Icônes : v51 sweet spot conservées. */
    .lib-hub-tile-heart { font-size: 44px !important; }
    .lib-hub-tile-plus  { font-size: 52px !important; }
    .lib-hub-tile-fallback {
        width: 44px !important;
        height: 44px !important;
    }
    .lib-hub-tile-more {
        width: 28px !important;
        height: 28px !important;
        font-size: 0.95rem !important;
        top: 6px !important;
        right: 6px !important;
        opacity: 1 !important;
        background: rgba(0, 0, 0, 0.55) !important;
    }
    /* Info : h4/p v51 sweet spot conservées. */
    .lib-hub-tile-info {
        padding: 10px 2px 0 !important;
    }
    .lib-hub-tile-info h4 {
        font-size: 0.85rem !important;
        font-weight: 700 !important;
        line-height: 1.25 !important;
    }
    .lib-hub-tile-info p {
        font-size: 0.8rem !important;
        margin: 2px 0 0 !important;
        line-height: 1.3 !important;
    }
    /* Modal : responsive global + actions au top + buttons gros. */
    .lib-modal {
        max-height: calc(100vh - 24px) !important;
        border-radius: 12px !important;
    }
    .lib-modal-cover-row {
        flex-direction: column !important;
        align-items: flex-start !important;
        gap: 10px !important;
    }
    .lib-modal-cover-preview {
        width: 84px !important;
        height: 84px !important;
    }
    /* === Fix #3 : lib-modal-actions AU-DESSUS (au top de l'écran) ==
       v52 a tenté `order: -1` + `position: sticky`. Sur le device du
       user ça n'a PAS marché : la toolbar est restée en bas. Cause :
       `.lib-modal-actions` est dans `.lib-modal-form` ; le form est
       `overflow-y:auto` ; sticky `top:0` est relatif au FORM pas au
       MODAL, mais `order:-1` n'a pas l'effet attendu probablement à
       cause de specificity ou du flex-direction. v53 : on utilise
       `position: absolute` sur actions + on rend `.lib-modal-form`
       `position: relative` (anchor). Actions overlay le top du form
       (entre header et cover-row). Padding-top sur form réserve la
       place pour ne pas cacher le cover-row derrière les actions. */
    .lib-modal-form {
        position: relative !important;
        padding-top: 76px !important;  /* reserve 60 (actions h) + 16 (gap) */
    }
    .lib-modal-actions {
        position: absolute !important;
        top: 0 !important;
        left: 0 !important;
        right: 0 !important;
        z-index: 5 !important;
        padding: 12px 16px !important;
        background: rgba(28, 28, 28, 0.96) !important;
        backdrop-filter: blur(8px) !important;
        -webkit-backdrop-filter: blur(8px) !important;
        border-bottom: 1px solid rgba(255, 255, 255, 0.10) !important;
        justify-content: space-between !important;
        gap: 12px !important;
        margin: 0 !important;
        order: 0 !important;
        transform: none !important;
    }
    .lib-modal-btn {
        padding: 14px 22px !important;
        font-size: 1rem !important;
        flex: 1 !important;
        max-width: 50% !important;
        min-height: 44px !important;
    }
}
/* === Fix #2 v54 (2026-08-14) : COVERS ASYMÉTRIQUES ROOT CAUSE FIX ====
   User : « les covers sont encore toutes asymétriques ». v52/v53
   forçaient aspect-ratio + width:100% sur le wrapper, mais les IMG
   gardaient leurs DIMENSIONS INTRINSÈQUES (chaque image source a un
   ratio différent : certaines 1000×1000, d'autres 800×600, etc.). Sans
   position:absolute sur l'img, le navigateur RÉCONCILIE width:100% +
   aspect-ratio du wrapper ET height:100% de l'img → résultat : tailles
   finales inconsistantes selon le ratio de l'image source.

   Fix root : position:absolute + inset:0 sur chaque img de cover.
   = l'img est forcée de remplir EXACTEMENT le wrapper, peu importe son
   ratio intrinsèque. object-fit:cover fait le crop final.

   Couvre :
     • .album-card .album-cover-wrapper img (renderGrid, vocaloid.html)
     • .lib-hub-tile-img (renderFlat, library.html, accueil.html SPA)
     • .lib-hub-tile-cover (Liked Songs / Create placeholders)
     • .album-cover-wrapper img direct (sans descendant)
   Toutes ces img passent en absolute-inset pour FORCER le fill exact.
   =============================================================== */
@media (max-width: 1024px) {
    /* === album-card covers (vocaloid.html / tadc.html standalone + SPA) */
    .album-card .album-cover-wrapper,
    .middle-column .album-card .album-cover-wrapper,
    .album-grid .album-card .album-cover-wrapper {
        position: relative !important;
        width: 100% !important;
        height: auto !important;
        aspect-ratio: 1 / 1 !important;
        min-height: 0 !important;
        max-height: none !important;
        overflow: hidden !important;
    }
    /* >>> La ROOT cause fix <<< : absolute-position sur l'img pour
       overrides intrinsèque dimensions. AVANT : width/height 100% sur
       img sans position:absolute → dépend du flow + ratio source. MAINT :
       position:absolute + inset:0 = fill exact du wrapper. */
    .album-card .album-cover-wrapper img,
    .middle-column .album-card .album-cover-wrapper img,
    .album-grid .album-card .album-cover-wrapper img {
        position: absolute !important;
        top: 0 !important;
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;
        width: 100% !important;
        height: 100% !important;
        object-fit: cover !important;
        display: block !important;
        margin: 0 !important;
        padding: 0 !important;
    }
    /* === lib-hub-tile covers (Library Hub : Liked Songs / Playlists / Create) */
    .lib-hub-tile-cover {
        position: relative !important;
        aspect-ratio: 1 / 1 !important;
        width: 100% !important;
        max-width: none !important;
        min-height: 0 !important;
        overflow: hidden !important;
    }
    .lib-hub-tile-img {
        position: absolute !important;
        top: 0 !important;
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;
        width: 100% !important;
        height: 100% !important;
        object-fit: cover !important;
        display: block !important;
        margin: 0 !important;
        padding: 0 !important;
    }
    /* === Pour Liked Songs/Create (qui n'ont pas d'img, juste une icône
           centrée), on s'assure que l'icône reste centrée même si le
           cover devient absolute-positioned parent. */
    .lib-hub-tile-cover > .lib-hub-tile-icon {
        position: relative !important;
        z-index: 1 !important;
    }
}

/* =====================================================================
   No-animations : coupe tout proprement (cohérence accueil.css)
   ===================================================================== */
html.no-animations .lib-hub-tile,
html.no-animations .lib-hub-tile-heart,
html.no-animations .lib-hub-tile-plus,
html.no-animations .lib-modal,
html.no-animations .lib-modal-overlay,
html.no-animations .lib-ctx-menu,
html.no-animations .lib-ctx-menu-item,
html.no-animations .lib-modal-btn {
    transition: none !important;
    animation: none !important;
}

/* =====================================================================
   prefers-reduced-motion : respecte la préférence système d'accessibilité
   pour les users qui désactivent les animations au niveau OS (ex : macOS
   Reduce Motion, Windows Ease of Access). Coupe UNIQUEMENT l'animation
   d'entrée du menu — les transitions courtes sur hover (couleur,
   background) restent car elles ne sont pas perçues comme "mouvement".
   Pattern suivi : un user qui active cette préférence veut généralement
   voir les transformations INSTANTanées (pas de scale-in fade), mais
   garde un feedback visuel rapide sur hover (= transitions < 200ms).
   Cohérent avec l'usage qu'on fait ailleurs dans le projet (cf.
   accueil.css qui désactive les transitions sur tuiles albums).
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
    .lib-ctx-menu {
        animation: none !important;
    }
    .lib-ctx-menu-item-playlist .lib-ctx-menu-icon::before {
        /* La transition background/border reste (courte, perçue comme
           feedback de toggle pas comme "mouvement"). On ne touche pas
           à la boîte unchecked seule — son hover reste son hover. */
        transition: none !important;
    }
}

/* === MOBILE FIX v55 (2026-08-14) : REPART DE LA VERSION PC ===========
   User (répété) : « trop collé et disproportionné, repars de la version
   PC ». On RETIRE les sur-couches mobiles custom (v51→v54 gap bump,
   heart icon shrink, modal top, etc.) et on RÉAPPLIQUE les valeurs PC
   via un block v55 final qui gagne contre les overlayers.

   Tableau (PC default → v55 mobile surchargé) :
     • .lib-hub-grid : repeat(auto-fit, minmax(180px, 1fr)) → PC-like,
       le browser fit autant de cols que possible sur mobile.
     • .lib-hub-tile-heart : 60px (PC 72 → un peu plus petit)
     • .lib-hub-tile-plus : 50px (PC 48 → un peu plus gros)
     • .lib-hub-tile-fallback : 56×56 (= mobile-spotify)
     • .lib-hub-tile-info h4 : 1rem (16px = PC default)
     • .lib-hub-tile-info p : 0.85rem (13.6px = PC default)
     • .lib-hub-tile-more ⋮ : 32×32 (PC default)
     • .lib-modal-actions : retour à position normale (static) dans
       form, pas d'absolute top.
   =============================================================== */
@media (max-width: 1024px) {
    /* lib-hub-grid : PC-like auto-fit */
    /* vXX : tuiles alignées sur les covers d'album (3 colonnes) au lieu
       d'une seule énorme colonne auto-fit minmax(180px). */
    .lib-hub-grid,
    .lib-hub-container .album-grid.lib-hub-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
        gap: 14px !important;
        padding: 8px 4px !important;
    }
    /* tiles : retour aux proportions PC. */
    .lib-hub-tile-cover {
        aspect-ratio: 1 / 1 !important;
        width: 100% !important;
        border-radius: 12px !important;
    }
    .lib-hub-tile-heart { font-size: 52px !important; }
    .lib-hub-tile-plus  { font-size: 44px !important; }
    .lib-hub-tile-fallback {
        width: 48px !important;
        height: 48px !important;
    }
    .lib-hub-tile-more {
        width: 32px !important;
        height: 32px !important;
        font-size: 1rem !important;
        opacity: 0 !important;  /* reste hover-only comme PC */
    }
    /* info : retour à la taille PC. */
    .lib-hub-tile-info {
        padding: 10px 4px 0 !important;
    }
    .lib-hub-tile-info h4 {
        font-size: 0.875rem !important;  /* 14px */
        font-weight: 700 !important;
        line-height: 1.2 !important;
    }
    .lib-hub-tile-info p {
        font-size: 0.85rem !important;
        margin: 4px 0 0 !important;
        line-height: 1.3 !important;
    }
    /* modal : retour au comportement PC. Pas d'absolute position. */
    .lib-modal-form {
        position: static !important;
        padding: 18px 20px 20px !important;
    }
    .lib-modal-actions {
        position: static !important;
        top: auto !important;
        left: auto !important;
        right: auto !important;
        padding: 0 !important;
        margin: 0 !important;
        background: transparent !important;
        backdrop-filter: none !important;
        border-bottom: none !important;
        justify-content: flex-end !important;
        gap: 10px !important;
    }
    .lib-modal-btn {
        padding: 9px 18px !important;
        font-size: 0.9rem !important;
        min-height: auto !important;
        flex: none !important;
        max-width: none !important;
    }
}
