/* ============================================================
   Pilot Médian — Feuille de style principale

   Tokens de marque Médian Events (indigo #2F2A76, fond crème, DM Sans,
   rayons variés intentionnellement) — même système que client-theme.css/
   admin.css côté custom-median, adapté ici à un cockpit d'admin dense
   (sidebar verticale conservée : 14 entrées de menu, une nav horizontale
   en pilules comme sur custom-median serait surchargée). Remplace le
   fond gris froid / rayons uniformes 18px-12px du premier jet de ce
   fichier.
   ============================================================ */

@import url("/assets/css/fonts.css");

/* ---- Variables ----------------------------------------------- */
:root {
    --bg:            #F4F1EA;
    --surface:       #FFFFFF;
    --surface-hover: #EFEBDE;
    --border:        #E3E0E6;
    --border-sub:    #EBE7DE;

    --text:          #2C2C2C;
    --text-muted:    #6B675F;
    /* Contrastes WCAG AA (chantier DD, 2026-10-18) — mesurés sur styles
       calculés à 375 et 1280 px, 27 écrans : chaque valeur ci-dessous tient
       4,5:1 sur le fond crème, le blanc et le survol (#EFEBDE). Avant :
       --text-dim 2,58:1, --warning 4,42:1 sur sa teinte, --danger-text
       4,31:1 dans une alerte, --success 4,31:1 sur blanc. */
    --text-dim:      #6F6A60;

    --accent:        #2F2A76;
    --accent-soft:   rgba(47, 42, 118, 0.06);
    --accent-mid:    #EDEBF9;

    --hover-border:  rgba(44, 44, 44, 0.14);
    /* Plein, plus en transparence : à 35 % l'anneau de focus faisait 2:1
       sur blanc (WCAG 1.4.11 demande 3:1 pour un état d'interface). */
    --focus-ring:    #2F2A76;
    /* Bordure des champs de saisie : --border (#E3E0E6, 1,3:1) reste pour
       les cartes et séparateurs, décoratifs ; un champ doit se voir à 3:1
       (3,25:1 sur le crème, 3,67:1 sur blanc). */
    --border-control: #8A857B;

    --danger:        #C0392B;
    --danger-soft:   rgba(192, 57, 43, 0.08);
    --danger-border: rgba(192, 57, 43, 0.25);
    --danger-text:   #A93226;

    --success:       #18744B;
    --success-tint:  #E7F6EF;

    --warning:       #8F5410;
    --warning-tint:  #F6ECDA;

    /* Rail permanent unique (plus de dépliage/repli, plus de tiroir mobile —
       voir la section "Sidebar" plus bas) : 68px à toutes les tailles
       d'écran pertinentes, réduit encore un peu en dessous de 480px pour
       laisser de la place au contenu sur téléphone. */
    --sidebar-w:      68px;
    --sidebar-w-xs:   56px;
    --topbar-h:      52px;
    --radius:        12px;
    --radius-sm:     8px;
    --shadow-sm:     0 1px 2px rgba(44, 34, 24, 0.06);
    --shadow-md:     0 4px 16px rgba(44, 34, 24, 0.10);
    --ease:          150ms ease;

    --font-body: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---- Reset --------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
    font-size: 14px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    font-family: var(--font-body);
    background: var(--bg);
    color: var(--text);
    line-height: 1.55;
    min-height: 100vh;
}

a    { color: inherit; text-decoration: none; }
button { cursor: pointer; border: none; background: none; font: inherit; color: inherit; }
input, select, textarea { font: inherit; color: inherit; }

code {
    font-family: 'Consolas', 'Monaco', 'Menlo', monospace;
    font-size: 0.875em;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 1px 5px;
}

/* ============================================================
   AUTH — Page de connexion
   ============================================================ */
.auth-body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 24px;
}

.auth-container {
    width: 100%;
    max-width: 380px;
}

/* Carte de connexion : même patron que .admin-login-card côté
   custom-median (bordure nette + shadow-md, jamais de transparence) --
   remplace l'ancien wordmark texte par le vrai logo, déjà utilisé dans
   la sidebar du cockpit. */
.auth-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    padding: 34px 30px;
}

.auth-logo {
    display: flex;
    justify-content: center;
    margin-bottom: 28px;
}
.auth-logo img { height: 30px; width: auto; display: block; }

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Alert */
.alert {
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    font-size: 0.8125rem;
    line-height: 1.45;
}
.alert-error {
    background: var(--danger-soft);
    border: 1px solid var(--danger-border);
    color: var(--danger-text);
}

/* Champ */
.field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.field label {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--text-muted);
    letter-spacing: 0.01em;
}

.field input {
    background: var(--bg);
    border: 1px solid var(--border-control);
    border-radius: var(--radius-sm);
    padding: 9px 11px;
    color: var(--text);
    transition: border-color var(--ease);
    outline: none;
}

.field input:focus {
    border-color: var(--focus-ring);
}

/* ============================================================
   CONTRASTES — champs de saisie (chantier DD, 2026-10-18)

   Bordure des champs à 3:1 : --border-control, posé directement dans
   les règles des champs (.field input, .filter-select/.filter-input,
   .add-user-form, .form-group textarea, .copy-field input, recherche de
   l'en-tête). Texte indicatif (placeholder) à 4,5:1 : celui des
   navigateurs (#757575) faisait 4,08:1 sur le fond crème des champs.
   ============================================================ */
input::placeholder,
textarea::placeholder { color: var(--text-dim); opacity: 1; }

/* Boutons */
.btn-primary {
    background: var(--accent);
    color: var(--bg);
    border-radius: var(--radius-sm);
    padding: 10px 16px;
    font-weight: 600;
    font-size: 0.875rem;
    transition: opacity var(--ease);
}
.btn-primary:hover { opacity: 0.86; }
.btn-block { width: 100%; }

.auth-notice {
    text-align: center;
    margin-top: 22px;
    color: var(--text-dim);
    font-size: 0.7375rem;
}

/* ============================================================
   COCKPIT — Mise en page principale

   height: 100dvh (avec repli 100vh pour les navigateurs qui ne le
   supportent pas — la seconde déclaration écrase la première seulement si
   dvh est reconnu, sinon elle est ignorée par la cascade) : sur Chrome/
   Safari iPad, 100vh statique correspond à la hauteur de viewport calculée
   à un instant donné (barre d'adresse visible ou non) et ne se recalcule
   PAS quand la barre se rétracte/réapparaît au défilement — laissant un
   trou visible en bas du rail une fois le viewport réel devenu plus grand.
   100dvh (dynamic viewport height) suit ces variations en temps réel.
   Appliqué à .cockpit-body/.layout/.sidebar ensemble : .sidebar a une
   hauteur explicite indépendante de ses parents, mais si .cockpit-body
   (overflow: hidden) restait figé à l'ancienne valeur plus petite, il
   rognerait quand même le bas du rail malgré son propre dvh — corrigé en
   contexte réel le 2026-09-07 (remontée iPad Damien), non reproductible
   par un navigateur automatisé (le rétrécissement dynamique de la barre
   d'adresse est un comportement de navigateur réel, pas simulable par
   Playwright/Chromium) : correctif juste par construction (dvh est la
   bonne pratique standard pour ce problème), à confirmer sur un vrai iPad.
   ============================================================ */
.cockpit-body {
    overflow: hidden;
    height: 100vh;
    height: 100dvh;
}

.layout {
    display: flex;
    height: 100vh;
    height: 100dvh;
}

/* ---- Sidebar --------------------------------------------- */
.sidebar-wrap {
    position: relative;
    flex-shrink: 0;
}

.sidebar {
    position: relative;
    width: var(--sidebar-w);
    flex-shrink: 0;
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    z-index: 100;
}

.sidebar-header {
    padding: 12px 8px 10px;
    border-bottom: 1px solid var(--border-sub);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.sidebar-logo-link { display: flex; align-items: center; justify-content: center; min-width: 0; }
/* Rail permanent, toujours étroit : seul le symbole (monogramme) tient à
   cette largeur. Le wordmark complet n'est plus rendu du tout (retiré du
   HTML) — il n'existait que pour le mode "déplié", qui disparaît avec le
   concept de repli/dépli lui-même. */
.sidebar-logo-symbol { height: 26px; width: auto; display: block; }

/* Navigation */
.sidebar-nav {
    flex: 1;
    padding: 6px 6px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    overflow-y: auto;
    scrollbar-width: none;
}
.sidebar-nav::-webkit-scrollbar { display: none; }

/* Indice de défilement du rail (audit R10, lot E, 2026-09-23) — la barre
   de défilement est masquée (ci-dessus) : quand les 13 entrées ne tiennent
   pas, rien ne disait qu'il en restait en dessous (« Réglages » invisible
   en iPad paysage). Ombre de contact discrète en bas (et en haut une fois
   défilé), sans flou ni halo : les deux dégradés « local » suivent le
   contenu et masquent l'ombre quand on est déjà au bout, les deux ombres
   « scroll » restent collées aux bords. Rien n'apparaît quand tout tient. */
.sidebar-nav {
    background:
        linear-gradient(var(--surface) 40%, rgba(255, 255, 255, 0)) center top / 100% 28px no-repeat local,
        linear-gradient(rgba(255, 255, 255, 0), var(--surface) 60%) center bottom / 100% 28px no-repeat local,
        linear-gradient(rgba(44, 34, 24, 0.14), rgba(44, 34, 24, 0)) center top / 100% 10px no-repeat scroll,
        linear-gradient(rgba(44, 34, 24, 0), rgba(44, 34, 24, 0.14)) center bottom / 100% 10px no-repeat scroll;
    background-color: var(--surface);
}


/* Item de rail : icône (pastille) empilée au-dessus d'un libellé court.
   L'état "actif" se lit uniquement sur la pastille de l'icône (fond
   indigo plein) — remplace l'ancienne barre d'accent à gauche, qui
   n'avait plus de sens sur un rail aussi étroit et permanent. */
.nav-item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 6px 3px;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    /* 0.6875rem, pas la valeur d'origine (0.625rem) : c'était, une fois
       html{font-size:14px} appliqué, plus petit (8,75px) que TOUT le reste
       du texte du produit, y compris les libellés les plus compacts déjà
       en place ailleurs (.upcoming-ref, .list-section-title, .badge —
       tous à 0.6875rem/9,625px). Or .nav-label est le texte de navigation
       le plus consulté du cockpit (lu en continu, pas un badge qu'on
       survole une fois) — il ne doit jamais être le plus petit texte du
       produit. Avant ce chantier (rail dépliable), ce même libellé était à
       0.875rem (14px, cf. historique git) ; 0.6875rem reste un compromis
       tenant compte de la largeur fixe de 68px du rail permanent, tout en
       revenant au plancher déjà utilisé partout ailleurs plutôt que
       d'inventer une échelle plus petite encore. */
    font-size: 0.6875rem;
    font-weight: 500;
    text-align: center;
    transition: background var(--ease), color var(--ease);
    user-select: none;
}
.nav-item:hover  { background: var(--accent-soft); color: var(--text); }
.nav-item.active { color: var(--accent); font-weight: 600; }

/* 28px : même convention de taille que les autres badges/icônes ronds du
   produit (.sidebar-avatar, .cat-icon) plutôt qu'une valeur inventée. */
.nav-icon {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    transition: background var(--ease), color var(--ease);
}
.nav-item:hover .nav-icon  { background: var(--surface-hover); color: var(--text); }
.nav-item.active .nav-icon { background: var(--accent); color: #fff; }

.nav-label {
    max-width: 100%;
    line-height: 1.15;
    overflow-wrap: break-word;
    hyphens: auto;
}

/* Rail compact sur écran bas (audit R10) — mesuré avant (local) : 13
   entrées = 771px de rail pour 674px disponibles en 1024x768, 726px en
   1180x820 ; « Réglages » (bas à 814px) hors de l'écran dans les deux cas.
   Sous 900px de haut : pastille 26px, marges resserrées — tout tient en
   1024x768. En dessous (Safari avec barre d'onglets), l'indice ci-dessus
   prend le relais. Le téléphone (≤480px de large) garde son propre rail
   sans libellés, plus bas dans ce fichier. */
@media (max-height: 900px) and (min-width: 481px) {
    .sidebar-nav { padding: 4px 6px; gap: 0; }
    .nav-item { padding: 3px 3px; gap: 2px; }
    .nav-icon { width: 26px; height: 26px; }
}

/* Footer sidebar : plus de nom/rôle en clair (aucune largeur pour ça sur
   un rail à 68px) — l'identité reste consultable au survol via le
   `title` posé sur .sidebar-user (utilisateurs souris/trackpad), et
   l'avatar (initiale) reste visible en permanence pour un repère rapide.
   Avatar + déconnexion en ligne (et non plus empilés) : à cette taille,
   les deux tiennent côte à côte et ça libère de la hauteur pour la nav —
   un budget vertical serré à 68px de large sur un rail permanent avec
   11 entrées (cf. tests iPad paysage 1024x768, le point le plus contraint
   en hauteur des trois gabarits vérifiés). */
.sidebar-footer {
    padding: 8px 4px;
    border-top: 1px solid var(--border-sub);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    flex-shrink: 0;
}

.sidebar-user {
    display: flex;
    align-items: center;
    justify-content: center;
}

.sidebar-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--accent-mid);
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0;
    flex-shrink: 0;
}

.btn-logout {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    transition: background var(--ease), color var(--ease);
}
.btn-logout:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

/* ---- Main ------------------------------------------------ */
.main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
}

/* Topbar */
.topbar {
    height: var(--topbar-h);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    padding: 0 22px;
    gap: 14px;
    flex-shrink: 0;
}

.topbar-title {
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.015em;
}

/* Zone de contenu */
.content {
    flex: 1;
    /* min-height: 0 explicite, pas seulement déduit : la spec flexbox fait
       normalement retomber l'automatic minimum size à 0 dès qu'un axe a un
       overflow différent de "visible" (notre cas, overflow-y: auto),
       mais Safari/WebKit a un historique documenté ("flexbugs") de ne pas
       toujours appliquer cette déduction de façon fiable une fois plusieurs
       niveaux de flex imbriqués (cf. .content-area plus bas, ajouté par ce
       chantier, où le même symptôme a été identifié par revue de code).
       Non vérifié sur un moteur WebKit réel faute d'outil disponible dans
       cet environnement (Chromium seul, où le rendu était déjà correct
       avant ce correctif) — ajouté ici par défense en profondeur, au
       niveau le plus bas de la chaîne, en plus du correctif confirmé
       nécessaire sur .content-area > .content. Aucun changement visuel
       attendu dans tous les cas : ce n'est jamais qu'une valeur déjà nulle
       rendue explicite, pas une contrainte nouvelle. */
    min-height: 0;
    padding: 26px 28px;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.content::-webkit-scrollbar       { width: 5px; }
.content::-webkit-scrollbar-track { background: transparent; }
.content::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* ============================================================
   PANNEAU LATÉRAL DE DÉTAIL — Dashboard

   `.content-area` n'est ajouté par cockpit/index.php que sur la section
   dashboard (les 15 autres sections gardent leur ancien `.content` seul,
   directement enfant de `.main` — zéro changement de mise en page ailleurs).
   Sur desktop/tablette large, le panneau est un simple second enfant flex
   de `.content-area`, à largeur nulle au repos puis 380px en `.open` : il
   vient donc s'ajouter à droite de `.content`, jamais en position fixe qui
   chevaucherait le rail (cf. .sidebar, ~68px, toujours visible à gauche).
   En dessous de 768px (le rail passe déjà en `position: sticky` et
   `.content` repasse en flux document normal, cf. media query plus bas),
   le panneau bascule en recouvrement fixe ancré à droite de l'écran, avec
   un `left` égal à la largeur du rail à cette taille pour ne jamais le
   recouvrir.
   ============================================================ */
.content-area {
    flex: 1;
    display: flex;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}
/* min-width: 0 (empêche .content de forcer .content-area à s'élargir) a
   son symétrique vertical manquant ici : sans min-height: 0, `.content`
   n'est plus juste un enfant flex de `.main` (colonne, cf. règle `.content`
   de base plus haut) mais AUSSI un enfant flex de `.content-area` (ligne)
   — un niveau d'imbrication flex supplémentaire propre à ce chantier. Sur
   cet axe secondaire (cross-axis du conteneur ligne), le stretch qui borne
   normalement la hauteur de `.content` s'appuie, sur Safari/WebKit, sur un
   comportement moins fiable une fois plusieurs niveaux de flex empilés
   sans min-height:0 explicite à chaque niveau (catégorie de bug bien
   documentée, cf. "flexbugs") : `.content` peut alors grandir à la taille
   de son propre contenu au lieu d'être plafonné par `.content-area`,
   rendant son `overflow-y: auto` inopérant — rien à faire défiler puisque
   `scrollHeight` == `clientHeight`, tout le surplus étant simplement coupé
   par l'`overflow: hidden` de `.content-area`, cohérent avec le symptôme
   "Dashboard : défilement totalement impossible" remonté par test réel sur
   iPad le 2026-09-07. Diagnostic établi par revue de code + reproduction
   en Chromium (où le calcul de hauteur ne montrait pas le défaut avant ce
   correctif, cf. rapport de ce chantier) — pas de moteur WebKit disponible
   dans cet environnement pour confirmer le mécanisme exact sur le
   navigateur réellement utilisé (Safari iPadOS) ; ce correctif est donc
   une défense en profondeur ciblée sur la cause la plus probable, pas une
   reproduction pixel pour pixel du bug sur le moteur d'origine. */
.content-area > .content { flex: 1; min-width: 0; min-height: 0; }

.detail-panel {
    width: 0;
    flex-shrink: 0;
    overflow: hidden;
    background: var(--surface);
    border-left: 0 solid var(--border);
    transition: width 200ms ease;
}
.detail-panel.open { width: 380px; border-left-width: 1px; }

.detail-panel-inner {
    width: 380px;
    height: 100%;
    display: flex;
    flex-direction: column;
}

.detail-panel-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border-sub);
}
.detail-panel-title { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }

.detail-panel-close {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    transition: background var(--ease), color var(--ease);
}
.detail-panel-close:hover { background: var(--surface-hover); color: var(--text); }

.detail-panel-body {
    flex: 1;
    overflow-y: auto;
    padding: 14px 18px 20px;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.detail-panel-body::-webkit-scrollbar       { width: 5px; }
.detail-panel-body::-webkit-scrollbar-track { background: transparent; }
.detail-panel-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.detail-panel-loading,
.detail-panel-empty {
    color: var(--text-muted);
    font-size: 0.875rem;
    padding: 12px 2px;
}

/* Liste de lignes dans le panneau — variante compacte de .upcoming-item. */
.panel-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }

.panel-row {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    transition: background var(--ease), border-color var(--ease);
}
.panel-row:hover { background: var(--surface-hover); border-color: var(--hover-border); }
.panel-row-main  { min-width: 0; }
.panel-row-ref   { font-size: 0.6875rem; color: var(--text-muted); margin-bottom: 2px; font-family: 'Consolas','Monaco',monospace; }
/* 0.875rem, pas 0.8125rem d'origine : aligné sur .upcoming-name / .res-item-name
   / .search-hit-title, la même convention déjà en place partout ailleurs pour
   le nom/titre principal d'une ligne de liste — .panel-row-title est ce même
   rôle (nom client dans une ligne de panneau), il n'y a pas de raison qu'il
   soit plus petit que tous ses équivalents. */
.panel-row-title { font-size: 0.875rem; }
.panel-row-meta  { font-size: 0.75rem; color: var(--text-muted); margin-top: 2px; }
.panel-row-side  { flex-shrink: 0; }

.panel-footer-link {
    display: block;
    text-align: center;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    color: var(--accent);
    font-size: 0.8125rem;
    font-weight: 500;
    transition: background var(--ease), border-color var(--ease);
}
.panel-footer-link:hover { background: var(--accent-soft); border-color: var(--hover-border); }

.detail-panel-scrim { display: none; }

/* Le panneau et le fond assombri ("scrim") ne doivent JAMAIS occuper
   exactement le même rectangle : sinon le panneau (au-dessus, z-index 200)
   capte tous les clics et le scrim (z-index 199) devient totalement
   inatteignable, y compris pour "clic en dehors pour fermer" — bug constaté
   par test réel (Playwright), corrigé en ancrant le panneau à droite avec
   une largeur plafonnée plutôt qu'un simple `left` calé sur le rail : une
   bande du fond reste visible et cliquable entre le rail et le panneau. */
@media (max-width: 768px) {
    .content-area { display: block; }

    /* `.detail-panel.open` répété explicitement : la règle de base
       `.detail-panel.open { width: 380px; }` (desktop, plus haut) a la même
       spécificité (deux classes) — sans ce doublon, elle continuerait à
       s'appliquer ici malgré cette media query, la largeur mobile calculée
       ci-dessous ne serait jamais utilisée une fois le panneau `.open`
       (constaté par test réel : le panneau s'ouvrait à 380px de large
       même en dessous de 768px, recouvrant entièrement la bande de scrim
       cliquable). */
    .detail-panel,
    .detail-panel.open {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: auto;
        width: min(420px, calc(100% - var(--sidebar-w) - 32px));
        transform: translateX(100%);
        transition: transform 200ms ease;
        border-left: 1px solid var(--border);
        box-shadow: var(--shadow-md);
        z-index: 200;
    }
    .detail-panel.open { transform: translateX(0); }
    .detail-panel-inner { width: 100%; }

    .detail-panel-scrim {
        display: block;
        position: fixed;
        top: 0; right: 0; bottom: 0;
        left: var(--sidebar-w);
        background: rgba(44, 34, 24, 0.32);
        opacity: 0;
        pointer-events: none;
        transition: opacity 200ms ease;
        z-index: 199;
    }
    .detail-panel-scrim.visible { opacity: 1; pointer-events: auto; }
}

@media (max-width: 480px) {
    /* Rail réduit à 56px (--sidebar-w-xs) à cette largeur (cf. règle
       .sidebar plus haut) : le calcul de largeur du panneau doit suivre,
       sinon la bande de scrim cliquable devient trop large/étroite selon
       le sens de l'écart entre --sidebar-w et --sidebar-w-xs. */
    .detail-panel,
    .detail-panel.open  { width: min(420px, calc(100% - var(--sidebar-w-xs) - 32px)); }
    .detail-panel-scrim { left: var(--sidebar-w-xs); }
}

/* ============================================================
   COMPOSANTS MODULES
   ============================================================ */

/* En-tête de module */
.module-header {
    margin-bottom: 24px;
}

.module-title {
    font-size: 1.3125rem;
    font-weight: 600;
    letter-spacing: -0.025em;
    margin-bottom: 4px;
}

.module-subtitle {
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/* ---- Bannière d'accueil — Dashboard ------------------------ */
.dashboard-banner {
    background: var(--accent);
    color: #fff;
    border-radius: var(--radius);
    padding: 24px 28px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 20px;
    margin-bottom: 24px;
    flex-wrap: wrap;
}
.dashboard-banner-greeting {
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -0.02em;
}
.dashboard-banner-date {
    font-size: 0.8125rem;
    color: rgba(255, 255, 255, 0.65);
    margin-top: 4px;
}
.dashboard-banner-summary {
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.85);
    margin-top: 10px;
}
.dashboard-banner-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.15);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 1rem;
    flex-shrink: 0;
}
@media (max-width: 480px) {
    .dashboard-banner { flex-direction: column; align-items: flex-start; }
}

/* Grille de statistiques — Dashboard */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 14px;
    margin-bottom: 24px;
}

.stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 18px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.stat-label {
    font-size: 0.725rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 500;
}

.stat-value {
    font-size: 1.875rem;
    font-weight: 600;
    letter-spacing: -0.04em;
    color: var(--text);
}

/* Cartes tactiles compactes — variantes alerte (caution/contrat/remboursement/
   personnalisation manquants, statut de préparation). Réutilise les tokens
   --warning/--danger déjà en place (field-warning, alert-warning) plutôt que
   d'inventer une nouvelle palette. Bordure + fond teinté pleins : pas de
   transparence/flou, contraste franc, même logique que .alert-warning. */
.stat-card-warning { background: var(--warning-tint); border-color: var(--warning); }
.stat-card-warning .stat-label,
.stat-card-warning .stat-value { color: var(--warning); }

.stat-card-danger { background: var(--danger-soft); border-color: var(--danger-border); }
.stat-card-danger .stat-label,
.stat-card-danger .stat-value { color: var(--danger-text); }

/* Carte cliquable ouvrant le panneau latéral (fetch + injection DOM, voir
   dashboard-panel.js) — <a> ou <button>, cible tactile normale (toute la
   carte), retour visuel au survol/focus clavier. */
.js-panel-card { cursor: pointer; transition: border-color var(--ease), background var(--ease), box-shadow var(--ease); text-align: left; }
.js-panel-card:hover { box-shadow: var(--shadow-sm); }
.js-panel-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* Section vide / placeholder */
.empty-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px 22px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.empty-badge {
    display: inline-flex;
    align-items: center;
    background: var(--accent-soft);
    border: 1px solid var(--border);
    border-radius: 100px;
    padding: 2px 10px;
    font-size: 0.6875rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    font-weight: 500;
    width: fit-content;
}

.empty-section p {
    font-size: 0.875rem;
    color: var(--text-muted);
    line-height: 1.5;
}

/* ============================================================
   RESPONSIVE — tablette & mobile

   Le rail (.sidebar) reste EN PERMANENCE visible et à côté du contenu,
   à toutes les largeurs — plus de tiroir hors-écran, plus de hamburger,
   plus d'overlay, plus de mode "replié" séparé : il n'y a plus qu'un
   seul état, déjà compact (68px). C'est justement l'appareil de test
   cible (iPad, y compris en dessous de 768px) qui a motivé ce choix.

   Ce que garde la mobile media query ci-dessous, sans rapport avec la
   sidebar : le passage à un défilement de document normal plutôt qu'un
   panneau de contenu à hauteur fixe (`.content` en scroll interne) —
   comportement déjà en place avant ce chantier, conservé tel quel pour
   éviter les soucis classiques de `100vh` sur mobile (barre d'adresse
   qui apparaît/disparaît). Pour que le rail reste visible pendant ce
   défilement de document, il passe en `position: sticky` au lieu d'un
   `height: 100vh` simple qui le ferait défiler avec le reste.
   ============================================================ */
@media (max-width: 768px) {
    .cockpit-body {
        overflow: auto;
        height: auto;
        min-height: 100vh;
    }

    .layout {
        height: auto;
        min-height: 100vh;
    }

    .sidebar {
        position: sticky;
        top: 0;
        height: 100vh;
        height: 100dvh;
    }

    .main {
        min-height: 100vh;
        overflow: visible;
    }

    .content {
        overflow: visible;
        padding: 18px 16px;
    }

    .stat-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 10px;
    }
}

/* Téléphone en portrait : pas l'appareil cible de ce chantier, mais on
   ne le laisse pas sans navigation pour autant — le rail rétrécit
   encore un peu (56px) et les libellés texte disparaissent (icônes +
   `title`/`aria-current` seuls) pour rendre le maximum de largeur au
   contenu, plutôt que de réintroduire un tiroir caché. */
@media (max-width: 480px) {
    .stat-grid { grid-template-columns: 1fr; }

    .sidebar { width: var(--sidebar-w-xs); }
    .nav-label { display: none; }
    .nav-item { padding: 8px 2px; }
    .nav-icon { width: 28px; height: 28px; }
}

/* ============================================================
   PHASE 2 — Composants données
   ============================================================ */

/* ---- Badges statut --------------------------------------- */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 100px;
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    white-space: nowrap;
}
.badge-lg  { padding: 4px 12px; font-size: 0.8125rem; }
.badge-gray   { background: #F1F1F5; color: #5B5B6E; }
.badge-amber  { background: #FEF3C7; color: #92400E; }
.badge-blue   { background: #DBEAFE; color: #1D4ED8; }
.badge-green  { background: #DCFCE7; color: #15803D; }
.badge-purple { background: #F3E8FF; color: #7E22CE; }
.badge-teal   { background: #CCFBF1; color: #0F766E; }
.badge-red    { background: #FEE2E2; color: #B91C1C; }
.badge-orange { background: #FFEDD5; color: #C2410C; }

/* ---- Dashboard stat link --------------------------------- */
.stat-link { text-decoration: none; transition: border-color var(--ease), background var(--ease); }
.stat-link:hover { border-color: var(--hover-border); background: var(--surface-hover); }

/* ---- Dashboard upcoming ---------------------------------- */
.list-section-title {
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    margin: 24px 0 10px;
}

.upcoming-list { display: flex; flex-direction: column; gap: 6px; }

.upcoming-item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    transition: background var(--ease), border-color var(--ease);
}
.upcoming-item:hover { background: var(--surface-hover); border-color: var(--hover-border); }

.upcoming-ref  { font-size: 0.6875rem; color: var(--text-muted); margin-bottom: 2px; font-family: 'Consolas','Monaco',monospace; }
.upcoming-name { font-size: 0.875rem; }

.upcoming-right { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex-shrink: 0; }
.upcoming-date  { font-size: 0.875rem; font-weight: 600; }

/* ---- Dashboard suivi de préparation ----------------------- */
.upcoming-item-wrap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.upcoming-item-wrap .upcoming-item {
    background: none;
    border: none;
    border-radius: 0;
    border-bottom: 1px solid var(--border-sub);
}

.prep-status-group {
    display: flex;
    gap: 6px;
    padding: 8px;
}

.prep-btn {
    flex: 1;
    min-height: 48px;
    padding: 12px 10px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    font-size: 0.8125rem;
    font-weight: 600;
    text-align: center;
    transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.prep-btn:hover { border-color: var(--hover-border); color: var(--text); }

.prep-btn.active { border-width: 2px; box-shadow: 0 1px 3px rgba(44, 44, 44, 0.16); }
.prep-btn.active.status-non_prepare { background: #5B5B6E; border-color: #5B5B6E; color: #fff; }
.prep-btn.active.status-en_cours    { background: #B45309; border-color: #B45309; color: #fff; } /* 5,02:1, avant #D97706 3,19:1 */
.prep-btn.active.status-pret        { background: var(--success); border-color: var(--success); color: #fff; }

/* ---- Dashboard : swipe → force le statut de préparation ----
   Swipe droite = 'pret' (panneau ancré à gauche, démasqué quand le
   contenu glisse vers la droite) ; swipe gauche = 'non_prepare' (panneau
   ancré à droite). 'en_cours' reste volontairement inaccessible par ce
   geste — uniquement via les 3 boutons .prep-btn existants, inchangés.
   Même palette que .prep-btn.active.status-* ci-dessus, pour rester
   cohérent avec l'état déjà affiché sur les boutons. Le panneau est
   révélé purement par la géométrie (le contenu, opaque et empilé
   au-dessus via z-index, glisse et démasque le panneau fixe en dessous)
   — pas de fondu d'opacité nécessaire. */
.swipe-prep-row { position: relative; }
.swipe-prep-action {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8125rem;
    font-weight: 700;
    color: #fff;
}
.swipe-prep-action-pret        { left: 0;  background: var(--success); }
.swipe-prep-action-non_prepare { right: 0; background: #5B5B6E; }
/* Confirmation visuelle discrète une fois le seuil de déclenchement franchi. */
.swipe-prep-row.swipe-preview-pret        .swipe-prep-action-pret,
.swipe-prep-row.swipe-preview-non_prepare .swipe-prep-action-non_prepare {
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .55);
}
.swipe-prep-content {
    position: relative;
    z-index: 1;
    background: var(--surface);
    transition: transform .18s ease;
    will-change: transform;
}
.swipe-prep-row.swipe-dragging .swipe-prep-content { transition: none; }

/* ---- Fiche client : valeur client + CRM interne ----------- */
.stat-card-accent { border-color: var(--success); }

.add-user-form textarea {
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border-control);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    color: var(--text);
    font-size: .875rem;
    font-family: inherit;
    outline: none;
    resize: vertical;
    min-height: 90px;
    transition: border-color var(--ease);
}
.add-user-form textarea:focus { border-color: var(--focus-ring); }

.tag-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tag-chip {
    display: inline-flex;
    align-items: center;
    background: var(--accent-soft);
    border: 1px solid var(--border);
    border-radius: 100px;
    padding: 2px 10px;
    font-size: 0.75rem;
    color: var(--text-muted);
}

.satisfaction-select { display: flex; flex-wrap: wrap; gap: 10px; }
.satisfaction-option { cursor: pointer; }
.satisfaction-option input { position: absolute; opacity: 0; pointer-events: none; }
.satisfaction-option span {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    font-size: 0.8125rem;
    font-weight: 600;
    transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.satisfaction-option:hover span { border-color: var(--hover-border); }
.satisfaction-option input:checked + span { background: #B45309; border-color: #B45309; color: #fff; }

/* ---- Filter bar ------------------------------------------ */
.filter-bar {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: 12px;
    margin-bottom: 14px;
    flex-wrap: wrap;
}

.filter-form {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

.filter-select, .filter-input {
    background: var(--surface);
    border: 1px solid var(--border-control);
    border-radius: var(--radius-sm);
    padding: 7px 10px;
    color: var(--text);
    font-size: 0.8125rem;
    outline: none;
    transition: border-color var(--ease);
}
.filter-select:focus, .filter-input:focus { border-color: var(--focus-ring); }
.filter-input { min-width: 200px; }

.filter-count {
    font-size: 0.75rem;
    color: var(--text-muted);
    white-space: nowrap;
    flex-shrink: 0;
}

/* ---- Buttons (small) ------------------------------------- */
.btn-sm {
    min-height: 48px;
    padding: 12px 20px;
    border-radius: var(--radius-sm);
    font-size: 0.8125rem;
    font-weight: 500;
    transition: opacity var(--ease), background var(--ease), color var(--ease);
    cursor: pointer;
    border: none;
    display: inline-flex;
    align-items: center;
}
.btn-sm.btn-primary { background: var(--accent); color: var(--bg); }
.btn-sm.btn-primary:hover { opacity: .86; }
.btn-sm.btn-outline { background: none; border: 1px solid var(--border); color: var(--text-muted); }
.btn-sm.btn-outline:hover { border-color: var(--hover-border); color: var(--text); }
.btn-sm.btn-danger-hover:hover { border-color: var(--danger); color: var(--danger); }
.btn-sm.btn-danger { background: var(--danger); color: #fff; }
.btn-sm.btn-danger:hover { opacity: .86; }
.btn-sm.btn-danger-outline { background: none; border: 1px solid var(--danger); color: var(--danger); }
.btn-sm.btn-danger-outline:hover { background: var(--danger); color: #fff; }

.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-weight: 600; margin-bottom: 6px; font-size: 0.8125rem; }
.form-group textarea { border: 1px solid var(--border-control); border-radius: var(--radius-sm); padding: 10px 12px; font: inherit; }

/* ---- Table ----------------------------------------------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }

.data-table {
    width: 100%;
    border-collapse: collapse;
}
.data-table th {
    padding: 10px 14px;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-muted);
    text-align: left;
    border-bottom: 1px solid var(--border);
    font-weight: 500;
    white-space: nowrap;
    background: var(--surface);
}
.data-table td {
    padding: 11px 14px;
    font-size: 0.875rem;
    border-bottom: 1px solid var(--border-sub);
    vertical-align: middle;
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: var(--surface-hover); }
.data-table tbody tr.row-inactive td { opacity: .5; }
.data-table tbody tr[data-href] { cursor: pointer; }

/* ---- Réservations : swipe → action rapide "Appeler" --------
   Balayage vers la gauche : révèle un bouton tel: ancré sur le bord
   droit de la ligne (assets/js/reservations.js). Porté ici sur la carte
   .res-item de la vue fractionnée (à l'origine construit sur une table
   pleine largeur, cf. historique git) : chaque carte est déjà un bloc
   opaque avec son propre fond/bordure (.res-item), donc pas besoin du
   double mécanisme "cellule à largeur nulle + fond forcé" qu'imposait le
   contexte d'un <td> de tableau — .res-item porte directement position
   relative + z-index pour rester peint AU-DESSUS de .swipe-action-panel
   (frère dans .res-item-row, jamais enfant : un <a> dans un <a> serait
   invalide), qui lui reste ancré en place, jamais transformé. Le
   démasquage progressif est purement géométrique (aucun fondu d'opacité
   requis) : la carte glisse et cesse de couvrir la zone où le panneau se
   trouve déjà, immobile, en dessous.
*/
.res-item-row { position: relative; }
.res-item-row.swipe-row .res-item {
    position: relative;
    z-index: 1;
    transition: transform .18s ease;
    will-change: transform;
}
.res-item-row.swipe-row.swipe-dragging .res-item { transition: none; }
.swipe-action-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 96px;
}
.swipe-call-btn {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: var(--accent);
    color: #fff;
    font-size: 0.8125rem;
    font-weight: 600;
    text-decoration: none;
}
.swipe-call-btn:hover { opacity: .9; }
.detail-row[data-href] { cursor: pointer; }
.detail-row[data-href]:hover { background: var(--surface-hover); }

.text-right  { text-align: right; }
.text-muted  { color: var(--text-muted); }
.mono        { font-family: 'Consolas','Monaco',monospace; font-size: .8rem; }
.table-action { color: var(--text-muted); font-size: .8125rem; transition: color var(--ease); white-space: nowrap; }
.table-action:hover { color: var(--accent); }

/* ---- Pagination ------------------------------------------ */
.pagination {
    display: flex;
    gap: 3px;
    margin-top: 16px;
    align-items: center;
}
.page-btn {
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    font-size: 0.8125rem;
    color: var(--text-muted);
    transition: background var(--ease), color var(--ease);
    border: 1px solid transparent;
}
.page-btn:hover { background: var(--accent-soft); color: var(--text); }
.page-btn.active { background: var(--accent-mid); color: var(--accent); border-color: transparent; }

/* ---- Detail cards ---------------------------------------- */
.detail-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.detail-header-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.detail-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-bottom: 14px;
}
.detail-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 14px;
}
.detail-card-full { grid-column: 1 / -1; }

.detail-card-head {
    padding: 11px 16px;
    border-bottom: 1px solid var(--border-sub);
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    background: var(--bg);
}
.detail-card-body { padding: 14px 16px; }
.detail-card-body.p-0 { padding: 0; }

.detail-row {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 7px 0;
    border-bottom: 1px solid var(--border-sub);
    font-size: 0.875rem;
    align-items: baseline;
}
.detail-row:last-child { border-bottom: none; }
.dl { color: var(--text-muted); flex-shrink: 0; font-size: .8125rem; }
.dv { text-align: right; word-break: break-word; }
.dv a { color: var(--text); text-decoration: underline; text-decoration-color: var(--border); }
.dv a:hover { text-decoration-color: var(--text-muted); }

/* ---- Back link ------------------------------------------- */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--text-muted);
    font-size: 0.8125rem;
    margin-bottom: 18px;
    transition: color var(--ease);
}
.back-link:hover { color: var(--text); }

/* ---- Inspection / notes ---------------------------------- */
.inspection-block { padding: 10px 0; border-bottom: 1px solid var(--border-sub); }
.inspection-block:last-child { border-bottom: none; padding-bottom: 0; }
.inspection-header { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: .875rem; flex-wrap: wrap; }
.inspection-notes { font-size: .8125rem; color: var(--text-muted); line-height: 1.5; }

.notes-block { margin-bottom: 14px; }
.notes-block:last-child { margin-bottom: 0; }
.notes-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); font-weight: 600; margin-bottom: 6px; }
.notes-block p { font-size: .875rem; color: var(--text-muted); line-height: 1.55; }

/* ---- Alert messages -------------------------------------- */
.alert-success {
    background: var(--success-tint);
    border: 1px solid var(--success);
    color: var(--success);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    font-size: .875rem;
    margin-bottom: 16px;
}
.alert-warning {
    background: var(--warning-tint);
    border: 1px solid var(--warning);
    color: var(--warning);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    font-size: .875rem;
    margin-bottom: 16px;
}
.alert-link { display: block; text-decoration: none; font-weight: 500; transition: opacity var(--ease); }
.alert-link:hover { opacity: .8; }
/* Bandeau d'alerte au-dessus du bandeau d'accueil du tableau de bord (tâches
   planifiées arrêtées, commandes à examiner H22) : .alert-error n'a pas de marge. */
.alert + .dashboard-banner { margin-top: 16px; }
.field-warning {
    background: var(--warning-tint);
    border: 1px solid var(--warning);
    color: var(--warning);
    border-radius: var(--radius-sm);
    padding: 8px 12px;
    font-size: .8125rem;
    margin-top: 8px;
}

/* ---- Actions rapides (fiche équipement) ------------------- */
.quick-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }

/* ---- Settings form --------------------------------------- */
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
.add-user-form .field { margin-bottom: 12px; }
.add-user-form .field label { display: block; font-size: .8rem; color: var(--text-muted); font-weight: 500; margin-bottom: 6px; }
.add-user-form input { width: 100%; background: var(--bg); border: 1px solid var(--border-control); border-radius: var(--radius-sm); padding: 8px 10px; color: var(--text); font-size: .875rem; outline: none; transition: border-color var(--ease); }
.add-user-form input:focus { border-color: var(--focus-ring); }
.add-user-form input[type="checkbox"] { width: auto; flex: none; padding: 0; border: none; background: none; }
.add-user-form .field-checkbox { flex-direction: row; align-items: center; }
.add-user-form .field-checkbox label { display: flex; align-items: center; gap: 8px; margin-bottom: 0; font-weight: 400; color: var(--text); cursor: pointer; }

/* ---- Codes individuels catalogue (création / ajout de stock) --------- */
.unit-codes-grid { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.unit-codes-grid .field { flex: 0 1 160px; margin-bottom: 0; }

/* ---- Stock produit catalogue (compteur, détail repliable) ------------ */
.stock-counter { font-size: .95rem; font-weight: 600; margin: 0 0 8px; }
.link-toggle { display: inline-block; background: none; border: none; padding: 0; margin-bottom: 12px; font-size: .8rem; color: var(--text-muted); cursor: pointer; text-decoration: underline; }
.link-toggle:hover { color: var(--text); }
.stock-detail-table { margin-bottom: 12px; }
.stock-divider { border: none; border-top: 1px solid var(--border); margin: 16px 0; }

/* ---- FullCalendar dark overrides ------------------------- */
.fc-wrapper {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px;
}
.fc-wrapper .fc-toolbar-title     { font-size: 1rem; font-weight: 600; }
.fc-wrapper .fc-button             {
    background: var(--surface) !important;
    border-color: var(--border) !important;
    color: var(--text-muted) !important;
    font-size: .8rem !important;
    min-height: 40px !important;
    min-width: 40px !important;
    padding: 0 12px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
}
.fc-wrapper .fc-button:hover       { background: var(--surface-hover) !important; color: var(--text) !important; }
.fc-wrapper .fc-button-active,
.fc-wrapper .fc-button-primary:not(:disabled).fc-button-active { background: var(--accent-mid) !important; color: var(--accent) !important; border-color: transparent !important; }
.fc-wrapper .fc-toolbar-chunk      { display: flex; align-items: center; }
.fc-wrapper .fc-col-header-cell-cushion { color: var(--text-muted); font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 500; }
/* Lisibilité de la grille (2026-09-21) : numéros de jour en texte plein, jours
   hors du mois nettement estompés, week-ends légèrement teintés et aujourd'hui
   teinté — une grille presque vide doit rester lisible d'un coup d'œil. */
.fc-wrapper .fc-daygrid-day-number { color: var(--text); font-size: .875rem; font-weight: 500; }
.fc-wrapper .fc-day-other .fc-daygrid-day-number { color: var(--text-dim); font-weight: 400; }
/* FullCalendar pose opacity: .3 sur les jours hors mois (1,3:1) : la
   distinction passe par la couleur et la graisse ci-dessus, pas par un
   texte à peine visible. */
.fc-wrapper .fc-day-other .fc-daygrid-day-top { opacity: 1; }
.fc-wrapper .fc-daygrid-day.fc-day-sat,
.fc-wrapper .fc-daygrid-day.fc-day-sun { background: #FAF8F3; }
.fc-wrapper .fc-day-today          { background: var(--accent-soft) !important; }
.fc-wrapper .fc-day-today .fc-daygrid-day-number {
    background: var(--accent);
    color: #fff;
    border-radius: 50%;
    width: 22px;
    height: 22px;
    padding: 0 !important;
    margin: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
}
.fc-wrapper .fc-event              { font-size: .75rem; border-radius: 3px !important; padding: 1px 5px; cursor: pointer; }
/* L'heure ne doit jamais être coupée en plein milieu ("23:2" au lieu de
   "23:25") : elle reste toujours affichée en entier (flex-shrink: 0),
   seul le titre de l'événement s'abrège avec une ellipse si besoin. */
.fc-wrapper .fc-event-main-frame   { display: flex; flex-wrap: nowrap; overflow: hidden; }
.fc-wrapper .fc-event-time         { flex-shrink: 0; white-space: nowrap; }
.fc-wrapper .fc-event-title-container { min-width: 0; overflow: hidden; }
.fc-wrapper .fc-event-title        { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
.fc-wrapper td, .fc-wrapper th     { border-color: var(--border-sub) !important; }
.fc-wrapper .fc-list-event-title a { color: var(--text); }
.fc-wrapper .fc-list-day-cushion   { background: var(--bg); }

/* ---- Planning : onglets et liste « À venir » (modules/planning) ------- */
.planning-tabs { display: inline-flex; gap: 4px; padding: 4px; margin-bottom: 16px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; }
.planning-tab {
    display: inline-flex; align-items: center; min-height: 40px; padding: 0 16px;
    border-radius: 6px; color: var(--text-muted); font-size: .875rem; font-weight: 500; text-decoration: none;
}
.planning-tab:hover  { color: var(--text); background: var(--surface-hover); }
.planning-tab.active { background: var(--accent); color: #FFFFFF; }
.plan-up { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.plan-up-month {
    margin: 0; padding: 10px 20px; background: var(--bg); border-bottom: 1px solid var(--border-sub);
    font-size: .8125rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted);
}
.plan-up-item {
    display: grid; grid-template-columns: minmax(0, 17rem) minmax(0, 1fr) auto; align-items: center; gap: 16px;
    min-height: 56px; padding: 10px 20px; border-bottom: 1px solid var(--border-sub);
    color: var(--text); text-decoration: none;
}
.plan-up-item:last-child { border-bottom: none; }
.plan-up-item:hover { background: var(--accent-soft); }
.plan-up-dates { font-weight: 600; font-variant-numeric: tabular-nums; }
.plan-up-who   { font-weight: 500; min-width: 0; }
.plan-up-what  { display: block; color: var(--text-muted); font-size: .8125rem; font-weight: 400; }
.plan-up-item--block .plan-up-dates { color: var(--text-muted); }
@media (max-width: 700px) {
    .plan-up-item { grid-template-columns: 1fr auto; gap: 4px 12px; }
    .plan-up-dates { grid-column: 1 / -1; }
}

/* ---- Responsive Phase 2 ---------------------------------- */
@media (max-width: 768px) {
    .detail-grid { grid-template-columns: 1fr; }
    .form-row    { grid-template-columns: 1fr; }
    .filter-bar  { flex-direction: column; align-items: stretch; }
    .filter-form { flex-direction: column; }
    .filter-input { min-width: unset; }
}

/* ---- Icônes colorées par catégorie d'équipement ------------ */
.cat-chip { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; word-break: normal; }
.cat-chip-end { justify-content: flex-end; }
.cat-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}
.cat-icon-photobooth { background: #F3E8FF; color: #7E22CE; }
.cat-icon-son        { background: #E1F5EE; color: #0F6E56; }
.cat-icon-lumiere    { background: #FAEEDA; color: #854F0B; }
.cat-icon-video      { background: #DBEAFE; color: #1D4ED8; }
.cat-icon-jeux       { background: #FCE7F3; color: #BE185D; }
.cat-icon-autres     { background: #F1F1F5; color: #5B5B6E; }

/* ---- Équipements : photo & étiquette QR ------------------- */
.equip-photo {
    width: 100%;
    max-width: 240px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    display: block;
}
.equip-photo-placeholder {
    width: 100%;
    max-width: 240px;
    aspect-ratio: 4 / 3;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    border: 1px dashed var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: .75rem;
}
.qr-wrap { display: flex; justify-content: center; padding: 8px 0 14px; }
.qr-wrap svg { width: 160px; height: 160px; }

/* ---- Catalogue : galerie de photos supplémentaires --------- */
.catalog-gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 10px;
}
.catalog-gallery-item { text-align: center; }
.catalog-gallery-thumb {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    display: block;
    margin-bottom: 6px;
}

/* ---- Catalogue / Packs : grille de point de focus (9 positions) -- */
.focus-grid-form { display: flex; justify-content: center; margin-top: 6px; }
.focus-grid {
    display: grid;
    grid-template-columns: repeat(3, 18px);
    grid-template-rows: repeat(3, 18px);
    gap: 3px;
}
.focus-grid-cell {
    width: 18px;
    height: 18px;
    padding: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 4px;
    cursor: pointer;
    transition: background var(--ease), border-color var(--ease);
}
.focus-grid-cell:hover { background: var(--surface-hover); border-color: var(--hover-border); }
.focus-grid-cell.is-active { background: var(--accent); border-color: var(--accent); }
.focus-grid-label {
    font-size: .7rem;
    color: var(--text-muted);
    text-align: center;
    margin-top: 10px;
}

/* ---- Catalogue / Packs : pellicule réordonnable (couverture + galerie) --
   Réordonnancement en Pointer Events côté JS (pas de drag-and-drop HTML5
   natif : ne répond pas au tactile sur Safari iPadOS). touch-action: none
   empêche le scroll de page de "voler" le geste pendant un glisser tactile. */
.photo-filmstrip {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 4px 2px 10px;
}
.filmstrip-item {
    position: relative;
    flex: 0 0 84px;
    width: 84px;
    height: 84px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    overflow: hidden;
    cursor: grab;
    touch-action: none;
    background: var(--surface);
}
.filmstrip-item.is-cover { border-color: var(--accent); border-width: 2px; }
.filmstrip-item.is-dragging {
    cursor: grabbing;
    opacity: .55;
    border-color: var(--accent);
}
.filmstrip-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    pointer-events: none;
    user-select: none;
    -webkit-user-drag: none;
}
.filmstrip-cover-badge {
    position: absolute;
    top: 4px;
    left: 4px;
    background: var(--accent);
    color: #fff;
    font-size: .62rem;
    font-weight: 600;
    line-height: 1;
    padding: 3px 7px;
    border-radius: 999px;
    pointer-events: none;
    z-index: 1;
}

/* ---- Catalogue / Packs : viseur caméra (getUserMedia + canvas) -------
   Capture via <canvas> (jamais <input capture>) pour éviter la rotation
   EXIF classique : l'image capturée est déjà dans l'orientation vue à
   l'écran. Bouton déclencheur rond en indigo plein, contraste franc, sans
   flou ni halo (charte Médian Events). */
.camera-panel {
    margin-top: 10px;
    padding: 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
}
.camera-video {
    width: 100%;
    max-width: 360px;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    display: block;
    background: #000;
}
.camera-controls {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 10px;
}
.camera-shutter-btn {
    width: 56px;
    height: 56px;
    border-radius: 999px;
    background: var(--accent);
    border: 3px solid var(--surface);
    box-shadow: 0 0 0 2px var(--accent), var(--shadow-sm);
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
}
.camera-shutter-btn:hover { background: #262260; }
.camera-shutter-btn:active { background: #1f1b4f; }

/* ---- Packs : ligne "produit + quantité" du component picker ---------- */
.pack-component-row {
    display: grid;
    grid-template-columns: 1fr 100px auto;
    gap: 10px;
    align-items: end;
    margin-bottom: 10px;
}
.pack-component-row .field { margin-bottom: 0; }
@media (max-width: 640px) {
    .pack-component-row { grid-template-columns: 1fr; }
}

/* Étiquette imprimable — masquée à l'écran, isolée à l'impression */
.print-label { display: none; }

/* ---- Bon de livraison (page dédiée, visible à l'écran) ----- */
.dn-toolbar { margin-bottom: 16px; }
.delivery-note {
    max-width: 800px;
    margin: 0 auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 32px;
}
.dn-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
    border-bottom: 2px solid var(--text);
    padding-bottom: 16px;
    margin-bottom: 20px;
}
.dn-brand { display: flex; align-items: center; gap: 14px; }
.dn-logo { height: 42px; width: auto; }
.dn-company-name { font-weight: 600; font-size: .9rem; }
.dn-company-meta { font-size: .75rem; color: var(--text-muted); }
.dn-doc-info { text-align: right; }
.dn-doc-title { font-size: 1.1rem; font-weight: 700; letter-spacing: .05em; }
.dn-doc-ref { font-size: .8125rem; margin-top: 4px; }
.dn-doc-date { font-size: .75rem; color: var(--text-muted); }
.dn-meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 24px; }
.dn-block-head { font-size: .7rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); margin-bottom: 6px; }
.dn-block-body { font-size: .875rem; line-height: 1.5; }
.dn-table { margin-bottom: 24px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.dn-check { font-size: .875rem; margin-bottom: 28px; }
.dn-checkbox {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 1px solid var(--text);
    margin-right: 8px;
    vertical-align: middle;
}
.dn-signatures { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.dn-sign-label { font-size: .8125rem; color: var(--text-muted); margin-bottom: 8px; }
.dn-sign-area { height: 90px; border: 1px solid var(--border); border-radius: var(--radius-sm); }

/* Bon de livraison sur téléphone (audit R9, lot E, 2026-09-23) — mesuré
   avant (local, 375 et 390px) : la page débordait de 110 et 95px à droite
   (en-tête « BON DE LIVRAISON » et tableau à 5 colonnes plus larges que
   l'écran). En-tête et blocs client/événement empilés ; chaque ligne du
   tableau devient une carte : désignation et créneau en pleine largeur,
   puis Qté / HT / TTC côte à côte, chacun sous son libellé (data-label,
   posé dans modules/bon_livraison/index.php). Écran seulement :
   l'impression A4 garde le tableau. */
@media screen and (max-width: 640px) {
    .delivery-note { padding: 18px 16px; }
    .dn-header { flex-direction: column; gap: 12px; }
    .dn-doc-info { text-align: left; }
    .dn-meta-grid { grid-template-columns: 1fr; gap: 16px; }

    .dn-table thead { display: none; }
    .dn-table,
    .dn-table tbody,
    .dn-table tfoot { display: block; }
    .dn-table tr {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 6px 12px;
        padding: 12px 14px;
        border-bottom: 1px solid var(--border-sub);
    }
    .dn-table tbody tr:last-child { border-bottom: none; }
    .dn-table tfoot tr { border-top: 1px solid var(--border); border-bottom: none; }
    .dn-table td {
        display: block;
        padding: 0;
        border: none;
        text-align: left;
    }
    .dn-table td:not([data-label]),
    .dn-table td[data-label="Créneau"] { grid-column: 1 / -1; }
    /* Totaux alignés sous les colonnes HT et TTC des lignes. */
    .dn-table td[data-label="Total HT"] { grid-column: 2; }
    .dn-table td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: .75rem;
        text-transform: uppercase;
        letter-spacing: .05em;
        color: var(--text-muted);
    }
    .dn-table tbody tr:hover td { background: none; }
    .dn-signatures { gap: 12px; }
}

@page { size: A4; margin: 15mm; }

@media print {
    body * { visibility: hidden; }
    .print-label, .print-label *,
    .delivery-note, .delivery-note * { visibility: visible; }
    .print-label {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        text-align: center;
        padding: 24px;
    }
    .print-label-name { font-size: 1.1rem; font-weight: 600; margin-bottom: 4px; }
    .print-label-code { font-family: 'Consolas','Monaco',monospace; font-size: .9rem; margin-bottom: 12px; }
    .print-label-qr svg { width: 200px; height: 200px; }
    .delivery-note {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        max-width: none;
        margin: 0;
        border: none;
        border-radius: 0;
        padding: 0;
    }
    .dn-sign-area { border-color: #000; }
    .dn-checkbox { border-color: #000; }
}

/* ============================================================
   PHASE 3 — Recherche globale, vignette équipements, planning
   ============================================================ */

/* ---- Recherche globale (topbar) --------------------------- */
.topbar-search {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--bg);
    border: 1px solid var(--border-control);
    border-radius: 100px;
    padding: 6px 6px 6px 12px;
    width: 260px;
    max-width: 40vw;
    transition: border-color var(--ease);
}
.topbar-search:focus-within { border-color: var(--focus-ring); }
.topbar-search-icon { display: flex; align-items: center; color: var(--text-muted); flex-shrink: 0; }
.topbar-search input[type="search"] {
    flex: 1;
    min-width: 0;
    background: none;
    border: none;
    outline: none;
    color: var(--text);
    font-size: 0.8125rem;
}
.topbar-search input[type="search"]::placeholder { color: var(--text-dim); }
.topbar-search-submit {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    color: var(--text-muted);
    background: none;
    flex-shrink: 0;
    transition: background var(--ease), color var(--ease);
}
.topbar-search-submit:hover { background: var(--accent-mid); color: var(--accent); }

@media (max-width: 768px) {
    .topbar-search { width: auto; max-width: none; flex: 1; }
}

/* ---- Résultats de recherche -------------------------------- */
.search-query { color: var(--text); font-weight: 600; }
.search-group { margin-bottom: 22px; }
.search-group:last-child { margin-bottom: 0; }
.search-group-list { display: flex; flex-direction: column; gap: 6px; }
.search-hit {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    transition: background var(--ease), border-color var(--ease);
}
.search-hit:hover { background: var(--surface-hover); border-color: var(--hover-border); }
.search-hit-title { font-size: 0.875rem; }
.search-hit-sub   { font-size: 0.75rem; color: var(--text-muted); margin-top: 2px; }

/* ---- Équipements : vignette liste --------------------------- */
.equip-thumb, .equip-thumb-placeholder {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
    display: block;
}
.equip-thumb { object-fit: cover; border: 1px solid var(--border); }
.equip-thumb-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    border: 1px dashed var(--border);
    color: var(--text-dim);
}

/* ---- Planning : légende statuts + info-bulle événement ------ */
.planning-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin-bottom: 14px;
    padding: 12px 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.planning-legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.75rem;
    color: var(--text-muted);
}
.planning-legend-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
}

.event-tooltip {
    position: fixed;
    z-index: 300;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 16px rgba(44, 44, 44, 0.14);
    padding: 10px 12px;
    font-size: 0.8125rem;
    max-width: 260px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 120ms ease;
}
.event-tooltip.visible { opacity: 1; }
.event-tooltip-title { font-weight: 600; margin-bottom: 4px; }
.event-tooltip-row { color: var(--text-muted); font-size: 0.75rem; margin-top: 2px; }

/* ============================================================
   VAGUE 2 — Grilles de cartes (catalogue, équipements, packs)
   ============================================================ */
.item-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 16px;
    margin-bottom: 16px;
}
@media (max-width: 768px) {
    .item-card-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
}
@media (max-width: 480px) {
    .item-card-grid { grid-template-columns: 1fr; }
}

.item-card {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    transition: border-color var(--ease), box-shadow var(--ease);
}
.item-card:hover { border-color: var(--hover-border); box-shadow: 0 2px 10px rgba(44, 44, 44, 0.08); }

.item-card-photo {
    width: 100%;
    aspect-ratio: 4 / 3;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}
.item-card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.item-card-photo-placeholder { display: flex; align-items: center; justify-content: center; color: var(--text-dim); }

.item-card-body {
    padding: 14px 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    flex: 1;
}
.item-card-title {
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.item-card-meta { display: flex; flex-wrap: wrap; gap: 6px; }
.item-card-footer {
    margin-top: auto;
    padding-top: 8px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
}
.item-card-price { font-size: 0.8125rem; font-weight: 600; color: var(--text); }

/* Icônes de catégorie, version compacte (cartes packs : plusieurs composants) */
.cat-icon-sm { width: 20px; height: 20px; }
.cat-icon-sm svg { width: 12px; height: 12px; }
.cat-chip-sm { font-size: 0.75rem; }
.pack-card-components { flex-direction: column; align-items: flex-start; gap: 4px; }

/* ============================================================
   VAGUE 3 — Réservations : vue fractionnée (liste compacte + détail)

   Remplace la table pleine largeur par une colonne compacte à gauche
   (nom client / date / statut uniquement — le reste des colonnes de
   l'ancienne table n'a plus sa place ici) et un panneau détail à droite,
   mis à jour par assets/js/reservations.js sans recharger la page tout en
   restant fonctionnel sans JS (chaque item est un vrai <a href>, cf.
   modules/reservations/index.php). Respecte le rail de navigation
   permanent (--sidebar-w, 68px) : cette grille vit entièrement dans
   .content, à droite du rail, sans le concerner.
   ============================================================ */
.res-split {
    display: flex;
    align-items: flex-start;
    gap: 20px;
}

.res-split-list {
    flex: 0 0 320px;
    max-width: 320px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.res-split-detail {
    flex: 1;
    min-width: 0;
    transition: opacity var(--ease);
}
.res-split-detail.is-loading { opacity: .5; pointer-events: none; }

.res-detail-placeholder {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.res-detail-placeholder p { font-size: .875rem; color: var(--text-muted); line-height: 1.5; }

/* Filtres réductibles : la colonne compacte (320px) ne permet plus le
   filter-bar horizontal existant (select/select/input alignés côte à
   côte) — empilé verticalement et rangé dans un <details>, ouvert par
   défaut uniquement si un filtre est déjà actif (cf. $anyFilterActive
   côté PHP) pour rendre de la hauteur à la liste le reste du temps. */
.res-filters-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.res-filters-summary {
    cursor: pointer;
    padding: 11px 14px;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--text);
    user-select: none;
}
.res-filters-summary::-webkit-details-marker { display: none; }
.res-filters-summary::before {
    content: '';
    width: 0;
    height: 0;
    border-left: 4px solid var(--text-muted);
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transition: transform var(--ease);
    flex-shrink: 0;
}
.res-filters-panel[open] .res-filters-summary::before { transform: rotate(90deg); }
.res-filters-panel .filter-bar { padding: 0 14px 14px; margin-bottom: 0; }

.res-split-list .filter-bar   { flex-direction: column; align-items: stretch; }
.res-split-list .filter-form  { flex-direction: column; align-items: stretch; }
.res-split-list .filter-select,
.res-split-list .filter-input { width: 100%; min-width: 0; }
.res-split-list .filter-count { white-space: normal; }

.res-list-count { font-size: .75rem; color: var(--text-muted); padding: 0 2px; }

.res-compact-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* Item de liste compacte : ancre réelle (fonctionne sans JS), interceptée
   par assets/js/reservations.js pour la mise à jour fetch()/pushState.
   data-id/data-phone : points d'ancrage volontairement laissés pour un
   futur portage du geste swipe "Appeler" construit séparément sur la
   branche feature/app-like-swipe-camera-reorder (ancienne table pleine
   largeur, pas encore porté ici) — cf. rapport de ce chantier. */
.res-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    min-width: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    transition: background var(--ease), border-color var(--ease);
}
.res-item:hover { background: var(--surface-hover); border-color: var(--hover-border); }
/* État "sélectionné" : même logique que .nav-item.active/.page-btn.active
   déjà en place dans ce cockpit (fond accent-mid + bordure accent) —
   pas le badge cercle+coche (convention réservée à un choix parmi un
   ensemble à valider, ex. sélection de produit côté client, différent
   sémantiquement d'un item de navigation actif). */
.res-item.is-active { background: var(--accent-mid); border-color: var(--accent); }

.res-item-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.res-item-name {
    font-size: .875rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.res-item-date { font-size: .75rem; color: var(--text-muted); }

.res-pagination { justify-content: center; margin-top: 4px; }

@media (max-width: 900px) {
    .res-split { flex-direction: column; }
    .res-split-list  { flex: 1 1 auto; max-width: none; width: 100%; }
    .res-split-detail { width: 100%; }
}

/* ---- Blocages de dates (modules/blocages) ---------------------------- */
/* Champs de date selon le motif — bascule en CSS pur, sans JS : les champs
   masqués ne portent pas de `required`, la validation est faite côté hub. */
.blk-form:has(input[name="reason"][value="unavailable"]:checked) .blk-fields-phone { display: none; }
.blk-form:has(input[name="reason"][value="phone_booking"]:checked) .blk-fields-free { display: none; }
.blk-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.blk-choice {
    display: inline-flex; align-items: center; gap: 8px;
    min-height: 44px; padding: 0 14px;
    border: 1px solid #D9D9E3; border-radius: 8px; background: #FFFFFF;
    font-weight: 500; cursor: pointer;
}
.blk-choice:has(input:checked) { border-color: #2F2A76; color: #2F2A76; }
/* .add-user-form .field label/input (display:block, width:100%) l'emportent
   sinon : la case s'empilait au-dessus de son libellé. */
.add-user-form .field .blk-choice { display: inline-flex; margin-bottom: 0; font-size: .875rem; color: #2C2C2C; }
.add-user-form .field .blk-choice:has(input:checked) { color: #2F2A76; }
.add-user-form .blk-choice input { width: auto; flex: none; margin: 0; padding: 0; border: none; background: none; accent-color: #2F2A76; }
.blk-preview-window {
    margin: 0 0 8px; padding: 10px 14px;
    border-left: 3px solid #2F2A76; background: #F4F3FA;
    font-size: 15px; font-weight: 600; color: #2C2C2C;
}
.blk-preview--conflict .blk-preview-window { border-left-color: #B91C1C; background: #FEF2F2; }
.blk-preview-actions { display: flex; gap: 8px; margin-top: 14px; }
.blk-preview-actions button[disabled] { opacity: .5; cursor: not-allowed; }
.blk-delete { display: inline; }

/* ============================================================
   Écran tactile et téléphone (audit R11, lot E, 2026-09-23)
   Mesuré avant (local, 390px et iPad 1024x768) : champs de connexion en
   14px sur 42px (Safari iOS zoome au focus sous 16px), bouton « Se
   connecter » 39px ; déconnexion 28x28, loupe de recherche 24x24, croix
   du panneau latéral 28x28 ; en-tête de fiche réservation écrasé à 390px
   (référence HUB-2026-… coupée sur 3 lignes dans une colonne de 75px, à
   côté des boutons).
   ============================================================ */
@media (hover: none) and (pointer: coarse) {
    /* 16px partout où l'on saisit : plus aucun zoom automatique au focus. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
    select,
    textarea {
        font-size: 16px;
    }

    .field input { min-height: 44px; }
    .auth-form .btn-primary { min-height: 44px; }

    /* Rail : la déconnexion passe à 44x44. À 68px de large, avatar (28) +
       bouton (44) ne tiennent plus côte à côte : l'avatar, simple
       initiale non cliquable dont le nom n'est lisible qu'au survol
       (inexistant au doigt), est masqué sur écran tactile. */
    .sidebar-user { display: none; }
    .btn-logout { width: 44px; height: 44px; }

    /* min-width/width 0 : à 16px, la largeur intrinsèque du champ de
       recherche poussait la barre 21px hors de l'écran à 375px. */
    .topbar-search { padding: 0 0 0 12px; min-width: 0; }
    .topbar-search input[type="search"] { width: 0; }
    .topbar-search-submit { width: 44px; height: 44px; }

    .detail-panel-close { width: 44px; height: 44px; }
}

@media (max-width: 768px) {
    /* Fiche réservation : titre et actions l'un sous l'autre au lieu de
       se disputer la largeur. */
    .detail-header { flex-direction: column; align-items: stretch; gap: 12px; }
}

/* ---- Champ à copier (fiche partenaire, audit H35) ---------- */
.copy-field { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; }
.copy-field input {
    flex: 1 1 18rem;
    min-width: 0;
    min-height: 48px;
    padding: 0 12px;
    border: 1px solid var(--border-control);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
}
.copy-field input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.copy-field .btn-sm { justify-content: center; min-width: 96px; }
.copy-field .btn-sm.is-copied { background: var(--success); }

/* Texte réservé aux lecteurs d'écran (zones aria-live). */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- Lien et QR code d'un code promo (2026-10-20) ------------
   QR noir sur blanc dans sa propre zone blanche (jamais teinté : la
   lecture en dépend). Code inactif : tout grisé, boutons désactivés. */
.promo-share-intro { margin-bottom: 14px; }
.promo-share-grid { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; }
.promo-qr {
    flex: 0 0 auto;
    width: 200px;
    max-width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--surface);
}
.promo-qr svg { display: block; width: 100%; height: auto; }
.promo-share-actions { flex: 1 1 18rem; min-width: 0; }
.copy-field .promo-link-field {
    flex: 1 1 18rem;
    min-width: 0;
    min-height: 48px;
    padding: 12px;
    border: 1px solid var(--border-control);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
    font-size: .875rem;
    line-height: 1.45;
    resize: none;
    overflow: hidden;
    overflow-wrap: anywhere;
    field-sizing: content;
}
.copy-field .promo-link-field:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.promo-share-label { display: block; margin-bottom: 6px; font-size: .875rem; font-weight: 600; color: var(--text); }
.promo-share-downloads { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.promo-share-downloads .btn-sm { justify-content: center; font-size: .875rem; color: var(--text); border-color: var(--border-control); }
.promo-share-hint { margin-top: 8px; font-size: .875rem; }
.promo-share-inactive {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
    margin-bottom: 14px;
    color: var(--text);
}
.promo-share.is-inactive .promo-qr { opacity: .3; }
.promo-share.is-inactive .copy-field .promo-link-field { color: var(--text-muted); background: var(--surface-hover); text-decoration: line-through; }
.promo-share .btn-sm:disabled { opacity: .45; cursor: not-allowed; }
@media (max-width: 480px) {
    .promo-qr { width: 220px; margin: 0 auto; }
    .promo-share-downloads .btn-sm { flex: 1 1 100%; }
}

/* ============================================================
   STATISTIQUES DU SITE (modules/statistiques, Matomo)

   Mobile d'abord, aucune police sous 12px (0.875rem = 12,25px au
   html{font-size:14px} de ce fichier) : les classes partagées trop
   petites (.stat-label, .list-section-title, .empty-badge, .btn-sm à
   0.8125rem, code à 0.875em) ne sont pas réutilisées ici, ou sont
   remontées dans la portée .stx seulement. Segmenté = .planning-tabs.
   Chiffres en tabular-nums. Aucune couleur hors des variables du haut.
   ============================================================ */
.stx { max-width: 1200px; }
.stx .btn-sm { font-size: .875rem; gap: 8px; }
.stx code { font-size: .875rem; }
.stx .module-subtitle { font-size: .875rem; }
.stx-muted { color: var(--text-muted); }
.stx-alert { margin-bottom: 16px; }

.stx-header { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px 20px; flex-wrap: wrap; }
.stx-header-text { min-width: 0; }
.stx-header-side { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; }
.stx-stamp { font-size: .875rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

.stx-section-title { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 12px; }

/* ---- Visites : trois cartes ------------------------------------ */
.stx-kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.stx-kpi {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.stx-kpi-label { font-size: .9375rem; font-weight: 600; }
.stx-kpi-dates { font-size: .875rem; color: var(--text-muted); margin-top: -4px; font-variant-numeric: tabular-nums; }
.stx-kpi-value { font-size: 1.875rem; font-weight: 600; letter-spacing: -0.03em; line-height: 1.15; font-variant-numeric: tabular-nums; margin-top: 4px; }
.stx-kpi-unit { font-size: .875rem; font-weight: 500; letter-spacing: 0; color: var(--text-muted); }
.stx-kpi-line { font-size: .875rem; color: var(--text-muted); border-top: 1px solid var(--border-sub); padding-top: 8px; margin-top: auto; }
.stx-kpi-line strong { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

.stx-trend { display: flex; align-items: center; flex-wrap: wrap; gap: 2px 6px; font-size: .875rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.stx-trend svg { flex-shrink: 0; }
.stx-trend.is-up   { color: var(--success); }
.stx-trend.is-down { color: var(--danger); }
.stx-trend.is-flat { color: var(--text-muted); }
.stx-trend-ref { color: var(--text-muted); font-weight: 400; }

.stx-note { font-size: .875rem; color: var(--text-muted); margin-top: 10px; max-width: 70ch; line-height: 1.5; }

/* ---- Période et blocs de détail -------------------------------- */
.stx-period-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px 16px; flex-wrap: wrap; margin: 28px 0 14px; }
.stx-period-bar .stx-section-title { margin: 0; font-variant-numeric: tabular-nums; }
.stx-segmented { margin-bottom: 0; }
.stx-segmented .planning-tab { min-height: 44px; justify-content: center; }

.stx-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }
@media (min-width: 1100px) {
    .stx-card--pages { grid-column: 2; grid-row: 1 / span 3; }
}

.stx-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px 8px;
    min-width: 0;
}
.stx-card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }
.stx-empty { font-size: .875rem; color: var(--text-muted); padding: 8px 0 12px; }

.stx-conv-list, .stx-rows, .stx-pages { list-style: none; }
.stx-conv {
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
    min-height: 52px; border-bottom: 1px solid var(--border-sub);
}
.stx-conv:last-child, .stx-row:last-child, .stx-page:last-child { border-bottom: none; }
.stx-conv-label { font-size: .9375rem; min-width: 0; }
.stx-conv-value { font-size: 1.375rem; font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

.stx-row { padding: 10px 0; border-bottom: 1px solid var(--border-sub); }
.stx-row-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.stx-row-label { font-size: .9375rem; min-width: 0; }
.stx-row-value { font-size: .9375rem; font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.stx-row-value .stx-muted { font-weight: 400; }
.stx-bar { display: block; height: 6px; border-radius: 999px; background: var(--accent-mid); overflow: hidden; }
.stx-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--accent); }

.stx-page {
    display: grid; grid-template-columns: 1.75rem minmax(0, 1fr) auto; align-items: center; gap: 8px;
    min-height: 44px; border-bottom: 1px solid var(--border-sub);
}
.stx-page-rank { font-size: .875rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.stx-page-label { font-size: .9375rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stx-page-value { font-size: .9375rem; font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.stx-page-value .stx-muted { font-weight: 400; }

/* ---- États : non configuré, indisponible ------------------------ */
.stx-state {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px;
    max-width: 640px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.stx-state--error { border-color: var(--danger-border); }
.stx-state-title { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em; }
.stx-state--error .stx-state-title { color: var(--danger-text); }
.stx-state p, .stx-state li { font-size: .9375rem; color: var(--text-muted); line-height: 1.55; }
.stx-state-list { padding-left: 1.2em; display: flex; flex-direction: column; gap: 4px; }

@media (max-width: 768px) {
    .stx-grid { grid-template-columns: minmax(0, 1fr); }
    .stx-segmented .planning-tab { min-height: 48px; flex: 1; }
    .stx-period-bar .stx-segmented { width: 100%; }
}

/* Téléphone : les trois cartes défilent de côté dans leur propre rangée
   (la page, elle, ne défile jamais horizontalement). */
@media (max-width: 600px) {
    .stx-kpis {
        display: flex;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        gap: 10px;
        margin: 0 -16px;
        padding: 0 16px 6px;
        scroll-padding: 0 16px;
        scrollbar-width: none;
    }
    .stx-kpis::-webkit-scrollbar { display: none; }
    .stx-kpi { flex: 0 0 82%; scroll-snap-align: start; }
    .stx-header-side { width: 100%; justify-content: space-between; }
}

@media (hover: none) and (pointer: coarse) {
    .stx-segmented .planning-tab { min-height: 48px; }
}

/* ============================================================
   BARRE D'ONGLETS DU TÉLÉPHONE (≤ 480 px, chantier DD, 2026-10-18)

   Mesuré avant (local, 375x667) : rail de 56 px sans libellés, 14
   entrées, « Réglages » sous la ligne de flottaison ; « Bornes »
   (Équipements) absent du menu, atteint en 2 touches + défilement.
   En dessous de 480 px, le rail disparaît au profit d'une barre de
   5 onglets en bas (4 écrans du terrain + « Plus » qui ouvre une feuille
   avec le reste du menu), cibles de 56 px de haut, libellés à 12 px.
   Au-dessus de 480 px (iPad compris), rien ne change : le rail reste.
   ============================================================ */
.tabbar,
.tabbar-sheet,
.tabbar-scrim { display: none; }
/* Règle [hidden] explicite : .tabbar-sheet/.tabbar-scrim reçoivent un
   display par classe sous 480 px, qui l'emporterait sur le [hidden] du
   navigateur (piège déjà rencontré deux fois, cf. custom-median). */
.tabbar-sheet[hidden],
.tabbar-scrim[hidden] { display: none !important; }

@media (max-width: 480px) {
    .sidebar-wrap { display: none; }

    .content { padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }

    .tabbar {
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 150;
        background: var(--surface);
        border-top: 1px solid var(--border);
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
    .tabbar-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        min-height: 56px;
        padding: 6px 2px;
        color: var(--text-muted);
        font-size: 0.8571rem; /* 12 px */
        font-weight: 500;
        line-height: 1.1;
        text-align: center;
    }
    .tabbar-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--radius-sm); }
    .tabbar-icon {
        width: 28px; height: 28px;
        display: flex; align-items: center; justify-content: center;
        border-radius: 999px;
        flex-shrink: 0;
    }
    .tabbar-icon svg { width: 18px; height: 18px; }
    .tabbar-item.active { color: var(--accent); font-weight: 600; }
    .tabbar-item.active .tabbar-icon { background: var(--accent); color: #fff; width: 44px; }
    .tabbar-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .tabbar-scrim {
        display: block;
        position: fixed; inset: 0;
        z-index: 160;
        background: rgba(44, 34, 24, 0.32);
    }
    .tabbar-sheet {
        display: block;
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 170;
        max-height: 80dvh;
        overflow-y: auto;
        background: var(--surface);
        border-top: 1px solid var(--border);
        border-radius: var(--radius) var(--radius) 0 0;
        box-shadow: var(--shadow-md);
        padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    }
    .tabbar-sheet-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
    .tabbar-sheet-title { font-size: 1.0714rem; font-weight: 600; }
    .tabbar-sheet-user { font-size: 0.8571rem; color: var(--text-muted); }
    .tabbar-sheet-list { list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
    .tabbar-sheet-link {
        display: flex; align-items: center; gap: 10px;
        min-height: 48px; padding: 0 10px;
        border-radius: var(--radius-sm);
        color: var(--text);
        font-size: 1rem;
        font-weight: 500;
    }
    .tabbar-sheet-link .tabbar-icon { color: var(--text-muted); }
    .tabbar-sheet-link.active { background: var(--accent-mid); color: var(--accent); }
    .tabbar-sheet-link.active .tabbar-icon { color: var(--accent); }
    .tabbar-sheet-logout { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-sub); }
    .tabbar-logout-btn {
        width: 100%; min-height: 48px;
        border: 1px solid var(--border);
        border-radius: var(--radius-sm);
        color: var(--danger-text);
        font-weight: 600;
    }

    /* Plus de rail à cette largeur : le panneau latéral du tableau de
       bord et son fond assombri ne réservent plus ses 56 px. */
    .detail-panel,
    .detail-panel.open  { width: min(420px, calc(100% - 32px)); }
    .detail-panel-scrim { left: 0; }
}

/* Accès « Réservations du jour » (chantier DD) : lien du bandeau d'accueil
   et contrôle segmenté de la liste. Bouton contour blanc sur l'indigo du
   bandeau (contraste 12,25:1), jamais un second bouton plein. */
.dashboard-banner-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-top: 14px;
    padding: 0 16px;
    border: 1px solid rgba(255, 255, 255, 0.7);
    border-radius: var(--radius-sm);
    color: #fff;
    font-weight: 600;
    font-size: 0.9286rem;
}
.dashboard-banner-link:hover { background: rgba(255, 255, 255, 0.12); }
.dashboard-banner-link:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.res-day-tabs { display: flex; margin-bottom: 12px; }
.res-day-tabs .planning-tab { flex: 1; justify-content: center; }
@media (max-width: 480px) {
    .dashboard-banner-link { min-height: 48px; }
    .res-day-tabs .planning-tab { min-height: 48px; font-size: 1rem; }
}
