/* PharmaPlanni — feuille de style d'interface (thème « chaleureux »).
   Remplace le bloc <style> de templates/plannings/base.html ET celui de
   planning_hebdo.html. Aucune classe n'a été renommée : les gabarits et les
   tests existants continuent de fonctionner.
   Chargement : <link rel="stylesheet" href="{% static 'css/theme.css' %}">
   (fichier servi depuis le dépôt, aucun CDN — cf. CLAUDE.md § Conventions). */

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

/* ⚠️ `[hidden]` doit gagner. La feuille du navigateur pose `display: none`,
   mais toute règle d'auteur qui fixe un `display` (flex, grid…) l'écrase :
   l'élément reste visible alors que le JS le croit caché. */
[hidden] { display: none !important; }

/* ---------------- Thèmes ----------------
   Deux axes indépendants, posés par base.html sur <html> :
     data-theme  la palette   (sepia, ardoise, sauge, lavande)
     data-mode   jour ou nuit (clair, sombre, auto = réglage du système)

   Chaque palette déclare SES DEUX JEUX de valeurs — `--c-*` en clair, `--s-*`
   en sombre. Les blocs de mode, eux, ne contiennent aucune couleur : ils se
   contentent de brancher l'un des deux jeux sur les jetons que le reste de la
   feuille consomme. C'est ce qui permet d'ajouter une palette sans toucher au
   mode, et un mode sans retoucher les palettes.

   ⚠️ Toute couleur écrite en dur dans un gabarit échappe au thème et restera
   claire sur fond sombre. Un test (tests/test_theme.py) échoue si un gabarit
   non destiné à l'impression réintroduit un littéral hexadécimal. */

:root, [data-theme="sepia"] {
    /* Palette par défaut : Sépia, la teinte d'origine du projet. Le sélecteur
       porte les deux formes — `:root` seul sert de repli quand aucun thème
       n'est posé, `[data-theme="sepia"]` sert aux vignettes de l'écran
       Apparence, qui doivent rendre Sépia même sur une page d'une autre
       palette. */
    /* clair */
    --c-bg: #FBF7F2;
    --c-surface: #FFFDFB;
    --c-surface-sunk: #F6F0E8;
    --c-surface-track: #F4EFE7;
    --c-surface-form: #FBF3E4;
    --c-surface-hover: #F3EDE4;
    --c-border: #E7DED2;
    --c-border-strong: #DED4C6;
    --c-accent-border: #EEDDBE;
    --c-text: #2E2A26;
    --c-text-soft: #4C453E;
    --c-text-muted: #8A8177;
    --c-text-faint: #A79D92;
    --c-primary: #A4552E;
    --c-primary-hover: #8C4525;
    --c-primary-soft: #F6EEE1;
    --c-primary-soft-text: #7A5320;
    /* sombre */
    --s-bg: #191612;
    --s-surface: #221E19;
    --s-surface-sunk: #2B2620;
    --s-surface-track: #322C25;
    --s-surface-form: #2A241C;
    --s-surface-hover: #322C25;
    --s-border: #3A332B;
    --s-border-strong: #4A4237;
    --s-accent-border: #5A4B33;
    --s-text: #EFE8DE;
    --s-text-soft: #D6CDC0;
    --s-text-muted: #A69C8D;
    --s-text-faint: #857C6F;
    --s-primary: #D08A5C;
    --s-primary-hover: #E09C6E;
    --s-primary-soft: #3A2C1E;
    --s-primary-soft-text: #E8B57E;
}

/* Sélecteurs sans `:root` : une vignette de l'écran Apparence porte son
   propre `data-theme` et doit obtenir SES valeurs, pas celles de la page. */
[data-theme="ardoise"] {
    /* clair */
    --c-bg: #F6F7F9;
    --c-surface: #FFFFFF;
    --c-surface-sunk: #EDF0F4;
    --c-surface-track: #E9EDF2;
    --c-surface-form: #F2F6FB;
    --c-surface-hover: #E8ECF2;
    --c-border: #DCE1E8;
    --c-border-strong: #CBD2DC;
    --c-accent-border: #C9D6E6;
    --c-text: #1F2933;
    --c-text-soft: #3E4C59;
    --c-text-muted: #7B8794;
    --c-text-faint: #9AA5B1;
    --c-primary: #3E6B8C;
    --c-primary-hover: #2F5470;
    --c-primary-soft: #E7EFF6;
    --c-primary-soft-text: #2B4A6E;
    /* sombre */
    --s-bg: #12161B;
    --s-surface: #1A1F26;
    --s-surface-sunk: #232A33;
    --s-surface-track: #2A323C;
    --s-surface-form: #1E2630;
    --s-surface-hover: #2A323C;
    --s-border: #333C47;
    --s-border-strong: #45505E;
    --s-accent-border: #3E5670;
    --s-text: #E4E9EF;
    --s-text-soft: #C6CFD9;
    --s-text-muted: #94A0AE;
    --s-text-faint: #737E8B;
    --s-primary: #6FA8CE;
    --s-primary-hover: #86B9DC;
    --s-primary-soft: #1F303D;
    --s-primary-soft-text: #9CC6E2;
}

[data-theme="sauge"] {
    /* clair */
    --c-bg: #F5F8F4;
    --c-surface: #FFFFFF;
    --c-surface-sunk: #E9F0E7;
    --c-surface-track: #E4EDE1;
    --c-surface-form: #F0F6EE;
    --c-surface-hover: #E5EDE2;
    --c-border: #D8E2D4;
    --c-border-strong: #C6D3C1;
    --c-accent-border: #C3D8BD;
    --c-text: #222A22;
    --c-text-soft: #3D4A3B;
    --c-text-muted: #7C8A78;
    --c-text-faint: #9AA797;
    --c-primary: #4A7A52;
    --c-primary-hover: #3A6141;
    --c-primary-soft: #E6F0E4;
    --c-primary-soft-text: #2F5A3A;
    /* sombre */
    --s-bg: #131711;
    --s-surface: #1B211A;
    --s-surface-sunk: #242C22;
    --s-surface-track: #2B342A;
    --s-surface-form: #1F2A1D;
    --s-surface-hover: #2B342A;
    --s-border: #343E32;
    --s-border-strong: #465244;
    --s-accent-border: #40603F;
    --s-text: #E6EDE4;
    --s-text-soft: #C8D2C6;
    --s-text-muted: #93A090;
    --s-text-faint: #737F71;
    --s-primary: #7FB388;
    --s-primary-hover: #94C49C;
    --s-primary-soft: #1F3020;
    --s-primary-soft-text: #A8D2AE;
}

[data-theme="lavande"] {
    /* clair */
    --c-bg: #F8F6FB;
    --c-surface: #FFFFFF;
    --c-surface-sunk: #EFEBF6;
    --c-surface-track: #EAE5F2;
    --c-surface-form: #F4F0FA;
    --c-surface-hover: #EBE6F4;
    --c-border: #E0D9EC;
    --c-border-strong: #CFC5E0;
    --c-accent-border: #CFC2E6;
    --c-text: #262233;
    --c-text-soft: #453D57;
    --c-text-muted: #837A96;
    --c-text-faint: #A199B0;
    --c-primary: #6B4E9E;
    --c-primary-hover: #563E80;
    --c-primary-soft: #EDE7F7;
    --c-primary-soft-text: #4B3A66;
    /* sombre */
    --s-bg: #16131C;
    --s-surface: #1E1A26;
    --s-surface-sunk: #272231;
    --s-surface-track: #2E2939;
    --s-surface-form: #241E30;
    --s-surface-hover: #2E2939;
    --s-border: #383046;
    --s-border-strong: #4A4059;
    --s-accent-border: #524270;
    --s-text: #EAE6F2;
    --s-text-soft: #CDC6DB;
    --s-text-muted: #9A92AC;
    --s-text-faint: #7A7289;
    --s-primary: #A98BD8;
    --s-primary-hover: #BB9FE4;
    --s-primary-soft: #2A2038;
    --s-primary-soft-text: #C4AEE8;
}

/* Jetons de sens — statuts, types de créneau, ombres. Ils portent une
   signification (validé, refusé, comptoir…), pas une identité visuelle :
   ils ne changent donc pas d'une palette à l'autre, seulement d'un mode à
   l'autre. */
:root {
    --c-on-primary: #FFF8F2;
    --s-on-primary: #16120E;
    --c-danger: #9E3A32;
    --s-danger: #E08A82;
    --c-success: #2F5A3A;
    --s-success: #7FC08F;
    --c-warning: #C98A2B;
    --s-warning: #E0AC5E;
    --c-ok-bg: #E4EFE2;
    --s-ok-bg: #1F3324;
    --c-ok-fg: #2F5A3A;
    --s-ok-fg: #8ED3A0;
    --c-ok-bd: #CFE0CB;
    --s-ok-bd: #2F5A3A;
    --c-warn-bg: #F6EEE1;
    --s-warn-bg: #33280F;
    --c-warn-fg: #7A5320;
    --s-warn-fg: #E5BE7C;
    --c-danger-bg: #F7E5E3;
    --s-danger-bg: #3A1F1D;
    --c-danger-fg: #9E3A32;
    --s-danger-fg: #F0A9A2;
    --c-danger-bd: #EBCBC7;
    --s-danger-bd: #6E3A34;
    --c-info-bg: #EDF1F6;
    --s-info-bg: #1B2A3A;
    --c-info-fg: #2B4A6E;
    --s-info-fg: #9CC6E2;
    --c-anniv-bg: #FBF1F4;
    --s-anniv-bg: #33202A;
    --c-anniv-fg: #8C4059;
    --s-anniv-fg: #E7A5BC;
    --c-zebra: rgba(0, 0, 0, .022);
    --s-zebra: rgba(255, 255, 255, .035);
    --c-backdrop: rgba(46, 42, 38, .35);
    --s-backdrop: rgba(0, 0, 0, .6);
    --c-shadow-sm: 0 1px 2px rgba(46, 42, 38, .10);
    --s-shadow-sm: 0 1px 2px rgba(0, 0, 0, .45);
    --c-shadow-md: 0 6px 18px rgba(46, 42, 38, .08);
    --s-shadow-md: 0 6px 18px rgba(0, 0, 0, .40);
    --c-t-comptoir-bg: #DDEBD9;
    --s-t-comptoir-bg: #223527;
    --c-t-comptoir-fg: #2F5A3A;
    --s-t-comptoir-fg: #A8D8B4;
    --c-t-comptoir-bd: #79A183;
    --s-t-comptoir-bd: #3F6B4C;
    --c-t-back-bg: #DBE5F1;
    --s-t-back-bg: #1E2C3D;
    --c-t-back-fg: #2B4A6E;
    --s-t-back-fg: #A6C8E8;
    --c-t-back-bd: #7695BE;
    --s-t-back-bd: #3D5A7C;
    --c-t-garde-bg: #E5DEEE;
    --s-t-garde-bg: #2A2438;
    --c-t-garde-fg: #4B3A66;
    --s-t-garde-fg: #C3B4E0;
    --c-t-garde-bd: #9585B6;
    --s-t-garde-bd: #574A78;
    --c-t-livr-bg: #F3E5CE;
    --s-t-livr-bg: #33291A;
    --c-t-livr-fg: #6A4A1E;
    --s-t-livr-fg: #E0BC85;
    --c-t-livr-bd: #C79B5B;
    --s-t-livr-bd: #7A5F33;
}

/* Branchement du jeu selon le mode — aucune valeur ici, uniquement des
   renvois. La liste est répétée trois fois (défaut, sombre explicite, et
   sombre hérité du système) parce qu'une règle et une media query ne
   peuvent pas partager un même bloc de déclarations en CSS. */
/* Le branchement vaut aussi pour tout élément portant un `data-theme` :
   une propriété personnalisée se résout sur l'élément qui la déclare, donc
   sans ces sélecteurs une vignette de palette hériterait du `--bg` déjà
   calculé sur :root et afficherait la palette courante, pas la sienne. */
:root, :root[data-mode="clair"], :root:not([data-mode="sombre"]) [data-theme] {
    color-scheme: light;
    --bg: var(--c-bg);
    --surface: var(--c-surface);
    --surface-sunk: var(--c-surface-sunk);
    --surface-track: var(--c-surface-track);
    --surface-form: var(--c-surface-form);
    --surface-hover: var(--c-surface-hover);
    --border: var(--c-border);
    --border-strong: var(--c-border-strong);
    --accent-border: var(--c-accent-border);
    --text: var(--c-text);
    --text-soft: var(--c-text-soft);
    --text-muted: var(--c-text-muted);
    --text-faint: var(--c-text-faint);
    --primary: var(--c-primary);
    --primary-hover: var(--c-primary-hover);
    --primary-soft: var(--c-primary-soft);
    --primary-soft-text: var(--c-primary-soft-text);
    --on-primary: var(--c-on-primary);
    --danger: var(--c-danger);
    --success: var(--c-success);
    --warning: var(--c-warning);
    --ok-bg: var(--c-ok-bg);
    --ok-fg: var(--c-ok-fg);
    --ok-bd: var(--c-ok-bd);
    --warn-bg: var(--c-warn-bg);
    --warn-fg: var(--c-warn-fg);
    --danger-bg: var(--c-danger-bg);
    --danger-fg: var(--c-danger-fg);
    --danger-bd: var(--c-danger-bd);
    --info-bg: var(--c-info-bg);
    --info-fg: var(--c-info-fg);
    --anniv-bg: var(--c-anniv-bg);
    --anniv-fg: var(--c-anniv-fg);
    --zebra: var(--c-zebra);
    --backdrop: var(--c-backdrop);
    --shadow-sm: var(--c-shadow-sm);
    --shadow-md: var(--c-shadow-md);
    --t-comptoir-bg: var(--c-t-comptoir-bg);
    --t-comptoir-fg: var(--c-t-comptoir-fg);
    --t-comptoir-bd: var(--c-t-comptoir-bd);
    --t-back-bg: var(--c-t-back-bg);
    --t-back-fg: var(--c-t-back-fg);
    --t-back-bd: var(--c-t-back-bd);
    --t-garde-bg: var(--c-t-garde-bg);
    --t-garde-fg: var(--c-t-garde-fg);
    --t-garde-bd: var(--c-t-garde-bd);
    --t-livr-bg: var(--c-t-livr-bg);
    --t-livr-fg: var(--c-t-livr-fg);
    --t-livr-bd: var(--c-t-livr-bd);
}

:root[data-mode="sombre"], :root[data-mode="sombre"] [data-theme] {
    color-scheme: dark;
    --bg: var(--s-bg);
    --surface: var(--s-surface);
    --surface-sunk: var(--s-surface-sunk);
    --surface-track: var(--s-surface-track);
    --surface-form: var(--s-surface-form);
    --surface-hover: var(--s-surface-hover);
    --border: var(--s-border);
    --border-strong: var(--s-border-strong);
    --accent-border: var(--s-accent-border);
    --text: var(--s-text);
    --text-soft: var(--s-text-soft);
    --text-muted: var(--s-text-muted);
    --text-faint: var(--s-text-faint);
    --primary: var(--s-primary);
    --primary-hover: var(--s-primary-hover);
    --primary-soft: var(--s-primary-soft);
    --primary-soft-text: var(--s-primary-soft-text);
    --on-primary: var(--s-on-primary);
    --danger: var(--s-danger);
    --success: var(--s-success);
    --warning: var(--s-warning);
    --ok-bg: var(--s-ok-bg);
    --ok-fg: var(--s-ok-fg);
    --ok-bd: var(--s-ok-bd);
    --warn-bg: var(--s-warn-bg);
    --warn-fg: var(--s-warn-fg);
    --danger-bg: var(--s-danger-bg);
    --danger-fg: var(--s-danger-fg);
    --danger-bd: var(--s-danger-bd);
    --info-bg: var(--s-info-bg);
    --info-fg: var(--s-info-fg);
    --anniv-bg: var(--s-anniv-bg);
    --anniv-fg: var(--s-anniv-fg);
    --zebra: var(--s-zebra);
    --backdrop: var(--s-backdrop);
    --shadow-sm: var(--s-shadow-sm);
    --shadow-md: var(--s-shadow-md);
    --t-comptoir-bg: var(--s-t-comptoir-bg);
    --t-comptoir-fg: var(--s-t-comptoir-fg);
    --t-comptoir-bd: var(--s-t-comptoir-bd);
    --t-back-bg: var(--s-t-back-bg);
    --t-back-fg: var(--s-t-back-fg);
    --t-back-bd: var(--s-t-back-bd);
    --t-garde-bg: var(--s-t-garde-bg);
    --t-garde-fg: var(--s-t-garde-fg);
    --t-garde-bd: var(--s-t-garde-bd);
    --t-livr-bg: var(--s-t-livr-bg);
    --t-livr-fg: var(--s-t-livr-fg);
    --t-livr-bd: var(--s-t-livr-bd);
}

@media (prefers-color-scheme: dark) {
    :root[data-mode="auto"], :root[data-mode="auto"] [data-theme] {
        color-scheme: dark;
        --bg: var(--s-bg);
        --surface: var(--s-surface);
        --surface-sunk: var(--s-surface-sunk);
        --surface-track: var(--s-surface-track);
        --surface-form: var(--s-surface-form);
        --surface-hover: var(--s-surface-hover);
        --border: var(--s-border);
        --border-strong: var(--s-border-strong);
        --accent-border: var(--s-accent-border);
        --text: var(--s-text);
        --text-soft: var(--s-text-soft);
        --text-muted: var(--s-text-muted);
        --text-faint: var(--s-text-faint);
        --primary: var(--s-primary);
        --primary-hover: var(--s-primary-hover);
        --primary-soft: var(--s-primary-soft);
        --primary-soft-text: var(--s-primary-soft-text);
        --on-primary: var(--s-on-primary);
        --danger: var(--s-danger);
        --success: var(--s-success);
        --warning: var(--s-warning);
        --ok-bg: var(--s-ok-bg);
        --ok-fg: var(--s-ok-fg);
        --ok-bd: var(--s-ok-bd);
        --warn-bg: var(--s-warn-bg);
        --warn-fg: var(--s-warn-fg);
        --danger-bg: var(--s-danger-bg);
        --danger-fg: var(--s-danger-fg);
        --danger-bd: var(--s-danger-bd);
        --info-bg: var(--s-info-bg);
        --info-fg: var(--s-info-fg);
        --anniv-bg: var(--s-anniv-bg);
        --anniv-fg: var(--s-anniv-fg);
        --zebra: var(--s-zebra);
        --backdrop: var(--s-backdrop);
        --shadow-sm: var(--s-shadow-sm);
        --shadow-md: var(--s-shadow-md);
        --t-comptoir-bg: var(--s-t-comptoir-bg);
        --t-comptoir-fg: var(--s-t-comptoir-fg);
        --t-comptoir-bd: var(--s-t-comptoir-bd);
        --t-back-bg: var(--s-t-back-bg);
        --t-back-fg: var(--s-t-back-fg);
        --t-back-bd: var(--s-t-back-bd);
        --t-garde-bg: var(--s-t-garde-bg);
        --t-garde-fg: var(--s-t-garde-fg);
        --t-garde-bd: var(--s-t-garde-bd);
        --t-livr-bg: var(--s-t-livr-bg);
        --t-livr-fg: var(--s-t-livr-fg);
        --t-livr-bd: var(--s-t-livr-bd);
    }
}

/* Géométrie — indépendante du thème. */
:root {
    /* Chasse fixe pour les horaires et les compteurs : « 09:00–12:00 » et
       « 31 » s'alignent d'une ligne à l'autre. Pile système — aucune police
       à télécharger, l'application ne charge rien depuis un tiers. */
    --police-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    --radius: 9px;
    --radius-lg: 13px;
    --radius-pill: 20px;

    /* ── Échelle typographique ────────────────────────────────────────────
       Huit barreaux. Il y en avait vingt valeurs distinctes réparties sur
       cinq cents déclarations — dont 12, 12,5 et 13 px côte à côte, que l'œil
       ne distingue pas mais qui donnent à une page l'air d'avoir été composée
       par morceaux. Les trois familles du bas portaient l'essentiel du
       défaut : ce sont les étiquettes et les aides, celles qu'on lit le plus
       souvent en petit.

       ⚠️ Aucune taille en dur hors de ce bloc : `tests/test_theme.py`
       refuse un `font-size: …px` dans une feuille ou un gabarit. Seuls les
       gabarits d'impression en sont exemptés — on compose pour le papier, pas
       pour un écran. */
    --t-2xs: 10px;    /* graduations d'axe, gouttières : la place manque */
    --t-xs: 11px;     /* données denses : pastilles, segments de frise */
    --t-sm: 13px;     /* étiquettes, aides, boutons, cellules de tableau */
    --t-base: 14px;   /* corps de texte */
    --t-md: 16px;     /* sous-titres, chiffres mis en avant */
    --t-lg: 20px;
    --t-xl: 24px;
    --t-2xl: 32px;
}

/* ⚠️ Les contrôles de formulaire n'héritent PAS de la police : le navigateur
   leur impose la sienne — Arial 13,33 px sur un moteur WebKit. Sur un écran
   comme Semaines types, le champ de recherche, les deux sélecteurs et les
   trois champs numériques sortaient donc dans une autre fonte et une autre
   taille que tout ce qui les entoure, sans qu'aucune règle du projet ne le
   dise. `font: inherit` serait trop brutal — il emporterait la graisse des
   boutons —, d'où les trois propriétés nommées. */
input, select, textarea, button {
    font-family: inherit;
    font-size: var(--t-sm);
    line-height: normal;
}

/* ⚠️ Style de base des champs, ici et pas dans chaque gabarit. `.form-select`
   et `.form-input` sont posés dans une trentaine de gabarits… et ne
   correspondaient à AUCUNE règle : ces contrôles n'avaient donc aucun style du
   tout, et sortaient en rectangle blanc du navigateur au milieu d'une
   interface qui a des coins arrondis partout ailleurs. Chaque écran qui en
   voulait un correct le redéclarait dans son propre `<style>`.

   Les types sont énumérés plutôt que `input` tout court : cases à cocher et
   boutons radio ont leur propre apparence, un rembourrage les déformerait. */
/* ⚠️ Tout est dans `:where()`, dont la spécificité vaut **zéro**. Sans lui,
   `input[type="time"]` (0,1,1) l'emporterait sur le `.hp-time` (0,1,0) d'un
   gabarit : c'est une couche de base, elle doit céder devant la moindre classe
   d'écran. Écrite en sélecteurs nus, elle imposait 9 px de rembourrage aux
   champs horaires de la grille Horaires ponctuels, dont la valeur se coupait
   en « 12:0( ».

   Les types sont énumérés plutôt que `input` tout court : cases à cocher et
   boutons radio ont leur propre apparence, un rembourrage les déformerait. */
:where(input[type="text"], input[type="email"], input[type="password"],
       input[type="number"], input[type="date"], input[type="time"],
       input[type="search"], input[type="url"], input[type="tel"], input:not([type]),
       select, textarea, .form-input, .form-select) {
    padding: 7px 9px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-form);
    color: var(--text);
    max-width: 100%;
}
/* Les champs horaire et date portent le composant natif du navigateur
   (horloge, calendrier) en plus de leur texte : il leur faut plus de place. */
:where(input[type="time"], input[type="date"]) { padding-inline: 5px; }

:where(input, select, textarea):focus {
    outline: none;
    border-color: var(--primary);
}
:where(input, textarea)::placeholder { color: var(--text-faint); }

/* ⚠️ Le fond est posé sur `html` ET sur `body`, ce qui paraît redondant.
   Il ne l'est pas : `html` étant transparent, le fond du `body` se propage au
   canevas — mais Safari peint la zone HORS viewport (sous la barre flottante
   d'iOS, dans l'encoche) avec le fond de l'élément RACINE, pas avec le fond
   propagé. Sans cette ligne, cette bande reste d'un gris neutre étranger au
   thème. Retirer l'une des deux déclarations ramène le défaut. */
html { background: var(--bg); }

body {
    font-family: Figtree, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    background: var(--bg);
    color: var(--text);
    font-size: var(--t-base);
    line-height: 1.5;
    font-feature-settings: "tnum" 1;   /* chiffres de même largeur : les heures s'alignent */
    -webkit-font-smoothing: antialiased;
}
a { color: var(--primary); }
a:hover { color: var(--primary-hover); }

/* ---------------- Coquille : barre latérale + zone principale ---------------- */
/* ⚠️ `100dvh` et non `100vh` : sur iOS, `100vh` vaut la hauteur SANS les
   barres du navigateur — une valeur qui ne correspond à aucun état réel de
   l'écran. `dvh` suit la hauteur visible, barres comprises. `100vh` reste en
   repli pour les navigateurs qui ignorent `dvh`. */
.app-shell { display: flex; min-height: 100vh; min-height: 100dvh; }

/* ⚠️ Marges de zone sûre : `viewport-fit=cover` fait passer la page SOUS les
   barres et l'encoche. Sans ces marges, le bas d'une page se retrouve derrière
   la barre flottante d'iOS 26 — le dernier bouton d'un écran devient
   inatteignable. `env()` vaut 0 partout ailleurs, la règle est donc sans effet
   sur un ordinateur. */
.app-main {
    /* Uniquement l'encoche réelle : `main` porte déjà son propre rembourrage,
       et une valeur plancher s'y ajouterait sur ordinateur, où aucune barre
       ne gêne. `env()` vaut 0 partout sauf sur un appareil à zone sûre. */
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}
.sidebar { padding-bottom: max(18px, env(safe-area-inset-bottom)); }

.sidebar {
    width: 244px; flex-shrink: 0;
    background: var(--surface); border-right: 1px solid var(--border);
    display: flex; flex-direction: column;
    padding: 18px 12px; gap: 4px;
    position: fixed; top: 0; left: 0; bottom: 0;
    /* ⚠️ `100lvh` en plus de `bottom: 0` : sur iOS, `position: fixed` se cale
       sur le viewport de MISE EN PAGE, qui s'arrête au-dessus de la barre
       flottante. La surcouche ne peignait donc pas la bande du bas, que Safari
       remplissait de sa propre couleur. `lvh` est la hauteur d'écran barres
       rétractées : elle couvre tout. Sans effet là où `bottom: 0` suffit. */
    min-height: 100lvh;
    overflow-y: auto; z-index: 200;
}
.sidebar .brand {
    font-weight: 700; font-size: var(--t-md); letter-spacing: -.01em;
    color: var(--text); text-decoration: none;
    padding: 0 6px 14px; display: flex; align-items: center; gap: 9px;
}
/* Le logo remplace la pastille « P » qui tenait lieu de marque. Il porte ses
   propres couleurs et ne suit donc ni la palette ni le mode — c'est une
   marque, pas un élément d'interface. */
/* Pas de `border-radius` ici : le dessin porte déjà ses coins arrondis,
   en ajouter rogne le bandeau rouge. */
.brand-logo { display: block; flex-shrink: 0; }
.sidebar .brand-logo { width: 26px; height: 26px; }
.sidebar-block { display: flex; flex-direction: column; gap: 1px; }
/* Le premier bloc (compte / officine / notifications) devient une carte. */
.sidebar-block:first-of-type {
    background: var(--surface-sunk); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 6px 5px; gap: 4px; margin-bottom: 12px;
}
.sidebar-sep { border: none; border-top: 1px solid var(--border); margin: 12px 6px; }

.sidebar-info { padding: 6px 8px; }
.sidebar-info .label {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    color: var(--text-faint); font-weight: 600;
}
.sidebar-info .value { font-size: var(--t-sm); font-weight: 600; word-break: break-word; }
.sidebar-label {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    color: var(--text-faint); font-weight: 600; padding: 4px 9px 2px;
}

.sidebar a, .sidebar .logout {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    color: var(--text-soft); text-decoration: none;
    font-size: var(--t-sm); padding: 7px 9px; border-radius: var(--radius);
    background: none; border: none; width: 100%; text-align: left;
    font-family: inherit; cursor: pointer;
}
.sidebar a:hover, .sidebar .logout:hover { background: var(--surface-hover); color: var(--text); }
.sidebar a.actif {
    background: var(--primary-soft); color: var(--primary-soft-text); font-weight: 600;
    box-shadow: inset 3px 0 0 var(--primary);
}
.sidebar .logout { color: var(--danger); }
.sidebar .badge {
    background: var(--primary); color: var(--on-primary); font-size: var(--t-2xs); font-weight: 700;
    min-width: 18px; height: 18px; border-radius: 9px; padding: 0 5px;
    display: inline-flex; align-items: center; justify-content: center;
}
.sidebar .badge.vide { background: var(--border); color: var(--text-muted); }
.sidebar .spacer { flex: 1 1 auto; min-height: 8px; }
.sidebar .cgu { font-size: var(--t-xs); color: var(--text-muted); }
.sidebar-pharmacy-select {
    width: 100%; padding: 5px 7px; border: 1px solid var(--border-strong);
    border-radius: 7px; font-size: var(--t-sm); font-family: inherit; font-weight: 600;
    background: var(--surface); color: var(--text);
}

.app-main { flex: 1 1 auto; min-width: 0; margin-left: 244px; }
.app-shell-anon .app-main { margin-left: 0; }
.mobile-topbar { display: none; }
.sidebar-backdrop { display: none; }

main { max-width: 1440px; margin: 0 auto; padding: 22px 26px 30px; }
.main-anon { max-width: 1440px; margin: 0 auto; padding: 22px 26px; }

/* ---------------- Mobile ---------------- */
@media (max-width: 767px) {
    .sidebar { transform: translateX(-100%); transition: transform .2s ease; box-shadow: 2px 0 18px rgba(46,42,38,.18); }
    #nav-toggle:checked ~ .app-shell .sidebar { transform: translateX(0); }
    #nav-toggle:checked ~ .app-shell .sidebar-backdrop {
        display: block; position: fixed; inset: 0; background: var(--backdrop); z-index: 150;
        /* Même raison que la barre latérale : sans cette hauteur, le voile
           s'arrête au-dessus de la barre flottante d'iOS. */
        min-height: 100lvh;
    }
    .app-main { margin-left: 0; }
    main { padding: 14px 12px 22px; }
    .mobile-topbar {
        display: flex; align-items: center; gap: 10px;
        height: 52px; padding: 0 12px;
        background: var(--surface); border-bottom: 1px solid var(--border);
        position: sticky; top: 0; z-index: 100;
    }
    .mobile-topbar .hamburger {
        font-size: var(--t-xl); line-height: 1; cursor: pointer; color: var(--text);
        background: none; border: none; padding: 4px 8px;
    }
    /* `inline-flex` : sans lui le logo et le nom s'empilent, la barre double
       de hauteur et pousse le contenu vers le bas. */
    .mobile-topbar .brand {
        font-weight: 700; color: var(--text); font-size: var(--t-md);
        display: inline-flex; align-items: center; gap: 8px;
    }
    .mobile-topbar .brand-logo { width: 22px; height: 22px; }

    /* Les actions passent sous le titre : le séparateur pendrait en bout de
       ligne, et la légende n'a plus de côté droit où se caler. */
    .planning-head-actions { padding-top: 4px; }
    .head-sep { display: none; }
    .week-bar .legende { margin-left: 0; }
}

/* ---------------- Titre de page ---------------- */
h1 {
    font-family: "Instrument Serif", Georgia, serif;
    font-size: var(--t-2xl); font-weight: 400; line-height: 1.12; letter-spacing: -.01em;
    margin-bottom: 14px;
}
h1 .h1-soft { color: var(--text-faint); }
h2 { font-size: var(--t-md); }

/* ---------------- En-tête de la page Planning ----------------
   Trois bandes : titre + actions, rail de semaines + utilitaires, ligne
   d'état. Le titre ne répète plus l'officine — elle passe en surtitre. */
.planning-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 16px; flex-wrap: wrap; margin-bottom: 12px;
}
.planning-head h1 { margin-bottom: 0; }
.page-eyebrow {
    font-size: var(--t-2xs); font-weight: 700; letter-spacing: .09em;
    text-transform: uppercase; color: var(--text-faint); margin-bottom: 2px;
}
.planning-head-actions {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    /* Aligné sur la ligne de base du titre, pas sur le surtitre. */
    padding-top: 14px;
}

/* Hauteur et icônes uniformes dans cette barre.

   Deux familles s'y côtoient et étaient réglées séparément : `btn-icone`
   (imprimer, CSV, calendrier) en 36 px avec des icônes de 19, et `btn-action`
   (crayon, alerte) en 44 px avec des icônes de 16 — soit des boutons de
   tailles différentes et 4 px de décalage vertical entre les deux.

   ⚠️ La règle est **bornée à `.planning-head-actions`** : `btn-action` sert
   aussi dans des lignes de tableau serrées, où une hauteur imposée de 36 px
   ferait doubler la hauteur des lignes. */
.planning-head-actions .btn {
    height: 36px; min-height: 36px; min-width: 36px;
}
.planning-head-actions .btn svg { width: 17px; height: 17px; }
/* ⚠️ `.btn.btn-icone-legende` (0,2,0) est déclaré plus loin dans la feuille et
   l'emporterait sur la règle ci-dessus à spécificité égale : la variante à
   légende gardait sa largeur minimale de 40 px au milieu de boutons de 44. */
.planning-head-actions .btn.btn-icone-legende { min-width: 36px; }

@media (max-width: 600px) {
    /* 44 px : le seuil tactile, appliqué à toute la barre plutôt qu'à une
       famille de boutons sur deux. */
    .planning-head-actions .btn {
        height: 44px; min-height: 44px; min-width: 44px;
    }
    .planning-head-actions .btn.btn-icone-legende { min-width: 44px; }
    /* Sans libellé, le rembourrage horizontal ne fait qu'élargir : les
       boutons d'action mesuraient 47 px contre 44 pour les autres. */
    .planning-head-actions .btn-action { padding-left: 0; padding-right: 0; }
}
/* Sépare les actions de lecture (imprimer, exporter) de celles qui écrivent. */
.head-sep { width: 1px; height: 22px; background: var(--border-strong); }

.week-bar {
    display: flex; align-items: center; gap: 10px;
    flex-wrap: wrap; margin-bottom: 12px;
}
/* ⚠️ `align-items: stretch` et une hauteur commune : le champ de date et le
   bouton avaient chacun leur rembourrage, et le bouton se retrouvait plus bas
   que le champ à sa gauche. Un `input[type=date]` ne se laisse pas dimensionner
   par son seul padding — les navigateurs y ajoutent l'icône de calendrier. */
.week-goto { display: inline-flex; align-items: stretch; gap: 6px; }
.week-goto input[type="date"],
.week-goto .btn { height: 34px; }
@media (max-width: 600px) {
    /* ⚠️ Sur mobile, `.btn.btn-action` impose 44 px de cible tactile et
       l'emporte sur la hauteur ci-dessus : le champ doit suivre, sans quoi le
       bouton redevient plus haut que lui. Un champ de date est lui aussi une
       cible tactile, la même hauteur est donc la bonne des deux côtés. */
    .week-goto input[type="date"],
    .week-goto .btn { height: 44px; }
}
.week-goto input[type="date"] {
    padding: 0 10px; border: 1px solid var(--border-strong);
    border-radius: var(--radius); font-size: var(--t-sm); font-family: inherit;
    background: var(--surface); color: var(--text-soft);
}
/* La légende ferme la ligne, poussée à droite. */
.week-bar .legende { margin-bottom: 0; margin-left: auto; }

/* ---------------- Barre d'actions de semaine ---------------- */
.week-nav {
    display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap;
}
.week-nav .week-label { font-weight: 600; font-size: var(--t-md); white-space: nowrap; }

@media (max-width: 767px) {
    /* Le mois au centre, une flèche de chaque côté : les libellés entiers ne
       tiennent pas et le « Mois suivant » retombait seul sur une deuxième
       ligne. Masqués visuellement — une flèche seule ne dit rien à un lecteur
       d'écran. */
    .week-nav { flex-wrap: nowrap; justify-content: space-between; }
    .week-nav .nav-mot {
        position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }

    /* Formulaire de garde en grille régulière : `.form-inline` enroulait ses
       six groupes de largeurs inégales, et les libellés d'une même ligne se
       retrouvaient à des hauteurs différentes. */
    /* ⚠️ `.form-inline.form-garde` et non `.form-garde` seul : `.form-inline`
       pose `display: flex` et est déclaré PLUS LOIN dans la feuille — à
       spécificité égale il l'emportait, la grille ne s'appliquait pas et seul
       `grid-template-columns` restait, sans effet sur un conteneur flex. */
    .form-inline.form-garde {
        display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: end;
    }
    .form-garde .form-group-large,
    .form-garde > .btn { grid-column: 1 / -1; }
    .form-garde input,
    .form-garde select { width: 100%; min-width: 0; }
    .form-garde > .btn { justify-content: center; }
}
.week-nav a, .week-nav button {
    padding: 8px 14px; border: 1px solid var(--border-strong); border-radius: var(--radius);
    background: var(--surface); color: var(--text-soft); text-decoration: none; cursor: pointer;
    font-size: var(--t-sm); font-weight: 600; font-family: inherit;
}
.week-nav a:hover, .week-nav button:hover { background: var(--surface-hover); }

.pharmacy-selector { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.pharmacy-selector a {
    padding: 5px 12px; border-radius: var(--radius-pill); text-decoration: none; font-size: var(--t-sm);
    border: 1px solid var(--border); color: var(--text-soft); background: var(--surface);
}
.pharmacy-selector a.active { border-color: var(--primary); color: var(--primary); font-weight: 600; }

/* ---------------- Grille classique ---------------- */
.grid-wrapper {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
}
table.grid { width: 100%; border-collapse: collapse; background: var(--surface); }
table.grid th, table.grid td { border: 1px solid var(--border); padding: 7px 9px; vertical-align: top; min-width: 108px; }
table.grid th {
    background: var(--surface-sunk); font-weight: 700; font-size: var(--t-xs);
    text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted);
    text-align: center; white-space: nowrap;
}
table.grid th.employee-col { min-width: 152px; text-align: left; }
table.grid td { font-size: var(--t-sm); }
table.grid tbody tr:hover td { background: var(--surface-hover); }

.cell { min-height: 52px; cursor: pointer; position: relative; border-radius: 6px; }
.cell:hover { background: var(--primary-soft); }
.cell-consultation, .cell-consultation .creneau-pill { cursor: default; }
.cell-consultation:hover { background: transparent; }

/* Couleur par TYPE de créneau — garder en phase avec Creneau.TYPE_CHOICES,
   avec la légende de planning_hebdo.html et avec planning_print.html. */
.creneau-pill {
    /* Flex : l'icône de poste s'aligne avec les heures sur une seule ligne.
       Le libellé texte du type a disparu de la pastille — c'est l'icône qui le
       porte, et la cellule tient sur une ligne au lieu de deux. */
    display: flex; align-items: center; gap: 5px;
    padding: 4px 8px; margin: 3px 0; border-radius: 7px;
    background: var(--surface-track); color: var(--text);
    font-size: var(--t-xs); font-weight: 600; line-height: 1.3;
}
/* ⚠️ La couleur d'un créneau désigne la PERSONNE, plus le poste — c'est le
   poste qui a changé de support, il est porté par l'icône (`_type_icone.html`).
   La teinte du membre est posée en variable par le gabarit
   (`style="--couleur-membre: …"`), jamais en dur.

   `color-mix` mélange cette teinte au fond de la page : pâle en mode clair,
   sombre en mode nuit, sans que le membre ait deux couleurs à régler. Posée
   telle quelle, une pastille resterait vive et éblouissante sur fond sombre.
   La déclaration `background` neutre qui précède sert de repli aux navigateurs
   qui ignorent `color-mix` — ils gardent l'apparence d'avant. */
.creneau-membre {
    background: var(--surface-track);
    background: color-mix(in srgb, var(--couleur-membre, var(--border)) 22%, var(--surface));
    box-shadow: inset 3px 0 0 var(--couleur-membre, var(--border));
    color: var(--text);
}
/* Créneau maintenu à l'écran alors qu'une absence validée le recouvre : il dit
   quelles heures sont à remplacer. Trois marques cumulées, parce qu'aucune ne
   suffit seule — l'opacité seule se confond avec un thème pâle, le barré seul
   se perd dans une pastille de 60 px, et le tireté seul ne se voit pas à
   l'impression.

   ⚠️ La couleur de la personne est ramenée à ~8 % (contre 22 % pour un créneau
   tenu) et le liseré plein passe en tireté : c'est le contraste avec les
   créneaux voisins qui porte l'information, pas la teinte elle-même. Garder
   22 % ferait lire le créneau comme une journée travaillée sur un écran de
   loin, ce qui est exactement l'erreur qu'on cherche à éviter.

   ⚠️ Le texte reste au-dessus du seuil de lisibilité (`--text-muted`, pas une
   opacité globale de .4) : on l'affiche pour qu'il soit LU. Une pastille
   illisible n'apprendrait rien de plus qu'une pastille supprimée. */
.creneau-annule { color: var(--text-muted); }
/* ⚠️ Le fond et le liseré ne se posent que sur les éléments qui portent
   DÉJÀ la couleur du membre. La vue texte marque aussi son enveloppe
   `.texte-creneau` — pour que les horaires soient barrés avec le libellé, et
   pas seulement lui — mais cette enveloppe n'a pas de fond : lui en donner un
   dessinerait une seconde boîte autour de la pastille. */
.creneau-membre.creneau-annule {
    background: color-mix(in srgb, var(--couleur-membre, var(--border)) 8%, var(--surface));
    box-shadow: none;
    border-left: 3px dashed var(--couleur-membre, var(--border));
}
.creneau-annule .agenda-bloc-heures,
.creneau-annule .frise-seg-txt,
.creneau-annule .agenda-carte-titre,
.agenda-avant.creneau-annule { text-decoration: line-through; }
/* Dans la grille et l'impression, le texte du créneau est nu (pas d'élément
   interne à viser) : le barré se pose sur la pastille elle-même. */
.creneau-pill.creneau-annule, .creneau-item.creneau-annule,
.texte-creneau.creneau-annule { text-decoration: line-through; }
/* L'icône de poste n'est pas barrable ; on la fait reculer d'un cran. */
.creneau-annule .type-icone { opacity: .5; }
/* Vue texte : le libellé d'absence précède les créneaux barrés qu'elle
   recouvre. Il doit se distinguer d'eux — c'est lui qui dit POURQUOI ils sont
   barrés — sans crier plus fort qu'un horaire. */
.texte-absent { font-weight: 600; color: var(--warn-fg); }

/* Icône de poste : elle remplace le libellé texte dans la grille, ce qui fait
   tenir un créneau sur une ligne au lieu de deux. Le nom du type reste dans le
   `<title>` du SVG et l'attribut `title` de la pastille. */
.type-icone {
    width: 13px; height: 13px; flex: 0 0 13px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
    opacity: .75; vertical-align: -2px;
}
.type-icone circle { fill: currentColor; stroke: none; }
/* La croix de pharmacie et le croissant se lisent mieux pleins qu'en trait. */
.creneau-comptoir .type-icone path, .creneau-garde .type-icone path { fill: currentColor; stroke: none; }

.legende { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; font-size: var(--t-xs); color: var(--text-muted); }
.legende span { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; }
.legende .type-icone { opacity: 1; }
.legende i {
    width: 9px; height: 12px; border-radius: 3px; display: inline-block;
    border-left: 3px solid;
}
/* Badge discret de la synchro Google : les jours qui s'afficheront « Fermé »
   ou « Horaires réduits » sur la fiche. Sous la légende, donc près de la
   grille, et DANS `#planning-corps` pour suivre les écritures de la bulle. */
.google-badges {
    font-size: var(--t-xs); color: var(--text-muted);
    margin: -4px 0 12px;
}
.absence-pill {
    display: block; padding: 4px 8px; margin: 3px 0; border-radius: 7px;
    background: repeating-linear-gradient(45deg, var(--surface-track), var(--surface-track) 6px, var(--border) 6px, var(--border) 12px);
    color: var(--text-muted); font-size: var(--t-xs); font-style: italic;
}
.hs-pill {
    display: block; padding: 4px 8px; margin: 3px 0; border-radius: 7px;
    background: var(--primary-soft); color: var(--primary-soft-text); font-size: var(--t-xs); font-weight: 700;
}
.emp-emoji { font-style: normal; }

/* ---------------- Formulaires et boutons ---------------- */
/* Rangée de boutons. ⚠️ Deux `.btn` posés côte à côte dans un `<div>` nu sont
   des `inline-block` : leur seul écart est l'espace du balisage, qui disparaît
   quand ils passent à la ligne — sur téléphone, ils se touchaient. `gap` règle
   les deux axes d'un coup, et `flex-wrap` les fait descendre proprement. */
.btn-rangee { display: flex; flex-wrap: wrap; gap: 8px; }

.form-inline { display: flex; flex-wrap: wrap; gap: 8px; align-items: end; }
/* Variante pour un formulaire dont un groupe peut grandir (liste de personnes
   à cocher) : l'alignement par le bas ferait descendre tous les autres champs
   avec lui. Le bouton s'aligne alors via son groupe à étiquette vide. */
.form-inline.form-haut { align-items: flex-start; }
/* Saut de date replié : le résumé EST le bouton, le formulaire s'ouvre en
   dessous. Il occupait sinon une ligne entière au-dessus du planning, pour une
   action rare — le rail des semaines couvre le cas courant.

   ⚠️ `display: block` sur le summary retire son marqueur de dépliage, ce qui
   est ici voulu : c'est déjà un bouton, un chevron en plus le surchargerait.
   ⚠️ `position: relative` sur le details et `absolute` sur le formulaire : en
   flux, l'ouverture élargissait la barre d'actions et faisait sauter les
   boutons voisins d'une ligne à l'autre. */
.date-repli { position: relative; }
.date-repli > summary { list-style: none; }
.date-repli > summary::-webkit-details-marker { display: none; }
.date-repli[open] > summary { background: var(--surface-hover); }
/* ⚠️ Ancré à DROITE, à toutes les tailles : la barre d'actions est alignée
   sur le bord droit de l'en-tête, et un panneau qui s'ouvre vers la droite
   sort de l'écran — sur bureau il élargissait même la page. */
.date-repli > .week-goto {
    position: absolute; z-index: 20; top: calc(100% + 6px); right: 0;
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius); padding: 8px; box-shadow: var(--shadow-sm);
    white-space: nowrap;
}

/* Cale le bouton sur la ligne des saisies : il n'a pas d'étiquette, et
   alignés par le haut les autres champs commencent une hauteur de libellé plus
   bas. Un `<span>` vide plutôt qu'un `<label>` — une étiquette sans contrôle
   associé n'a pas de sens pour un lecteur d'écran — et la hauteur vient des
   mêmes valeurs que les vraies étiquettes, pas d'une marge recopiée à la main
   qui dériverait à la première retouche de typographie. */
.form-etiquette-vide {
    font-size: var(--t-2xs); font-weight: 700; text-transform: uppercase;
    letter-spacing: .06em;
}
.form-etiquette-vide::before { content: "\00a0"; }
.form-inline .form-group { display: flex; flex-direction: column; gap: 3px; }
.form-inline label {
    font-size: var(--t-2xs); font-weight: 700; color: var(--text-faint);
    text-transform: uppercase; letter-spacing: .06em;
}
/* ⚠️ Hauteur imposée : à rembourrage et police égaux, trois contrôles natifs
   donnent trois hauteurs différentes — un `select` 31 px, un `input[type=time]`
   34 (il porte une horloge), un `input[type=number]` 29. Alignés sur une même
   rangée, l'écart se voit immédiatement, et c'est le plus petit qu'on remarque.
   Les cases à cocher sont exclues : elles ont leur propre dessin. */
.form-inline input:not([type="checkbox"]), .form-inline select {
    height: 34px; padding: 0 9px;
    border: 1px solid var(--border-strong); border-radius: 7px;
    font-size: var(--t-sm); font-family: inherit; background: var(--surface); color: var(--text);
}
.form-inline input:focus, .form-inline select:focus { outline: none; border-color: var(--primary); }
/* Couple d'heures (début / fin) sur une seule ligne du formulaire en ligne.
   La disposition vit ici et non dans un attribut `style` : le script qui
   masque le bloc en mode « horaires d'ouverture » remettait le style à vide,
   ce qui emportait le `display` et empilait les deux champs. */
/* Ordre d'affichage des lignes du planning : une personne par ligne. C'était
   une suite de pastilles enroulées où le nom se trouvait ENTRE ses deux
   flèches — quatorze pastilles sur six rangées, et il fallait relire chaque
   étiquette pour savoir quelle flèche allait avec quel nom. */
.reorder-titre {
    display: block; font-size: var(--t-sm); font-weight: 600; margin-bottom: 4px;
}
.reorder-liste { list-style: none; }
.reorder-ligne {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; padding: 3px 0; font-size: var(--t-sm);
    border-bottom: 1px solid var(--border);
}
.reorder-ligne:last-child { border-bottom: 0; }
.reorder-nom { min-width: 0; overflow-wrap: anywhere; }
.reorder-actions { display: inline-flex; gap: 6px; flex: none; }
.reorder-btn {
    width: 30px; height: 30px; display: inline-flex;
    align-items: center; justify-content: center;
    background: none; border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text-soft); cursor: pointer; line-height: 1;
}
.reorder-btn:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
.reorder-btn:disabled { opacity: .35; cursor: default; }

@media (max-width: 600px) {
    /* Deux flèches voisines et identiques : sous le seuil tactile, on vise
       l'une pour l'autre. */
    .reorder-btn { width: 44px; height: 44px; }
}

/* Démonstration publique. */
.demo-tete { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.demo-sous-titre { font-size: var(--t-sm); color: var(--text-muted); margin-top: 2px; }
.demo-bandeau { margin-bottom: 16px; }
.demo-officine { font-size: var(--t-md); margin-bottom: 8px; }
.demo-suite { margin-top: 24px; max-width: 640px; }
.demo-suite h2 { font-size: var(--t-md); margin-bottom: 8px; }
.demo-suite ul { margin: 0 0 16px 18px; font-size: var(--t-sm); line-height: 1.7; }
.demo-retour { margin-top: 4px; }
.demo-glisser { display: none; font-size: var(--t-sm); color: var(--text-muted); margin-top: 6px; }

/* Fonctionnalités : une carte par sujet, avec un aperçu RENDU (pas une
   capture). Une image figée jurerait sur une page qui propose quatre palettes
   et deux modes, et vieillirait en silence à la première retouche. */
.demo-fonctions {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px; margin-bottom: 20px;
}
.demo-fonction {
    display: flex; flex-direction: column; gap: 8px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 14px 16px 16px;
}
.demo-fonction h3 { font-size: var(--t-base); }
.demo-fonction p { font-size: var(--t-sm); color: var(--text-muted); line-height: 1.5; margin: 0; }
/* La vignette est posée sur le fond « creusé » : elle se lit comme un extrait
   d'écran, pas comme un élément de la page. */
.demo-vignette {
    margin-top: auto; display: flex; align-items: center; gap: 8px;
    background: var(--surface-sunk); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 10px 12px; overflow: hidden;
}
.demo-vignette-pile { flex-direction: column; align-items: stretch; gap: 6px; font-size: var(--t-sm); }
.demo-vignette .agenda-carte { padding: 5px 9px; }
.demo-vignette .trame-identite { gap: 1px; }
.demo-boutons { display: flex; gap: 6px; }
.demo-boutons .btn { padding: 3px 10px; }
.demo-alerte { margin: 0; font-size: var(--t-sm); }
.demo-chg { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.demo-compteur { display: flex; justify-content: space-between; gap: 12px; }
.demo-compteur b { font-family: var(--police-mono); }
/* Vignettes de palette : chaque pastille porte son propre `data-theme`, donc
   ses propres jetons — c'est le mécanisme de l'écran Apparence. */
.demo-palettes { gap: 10px; }
.demo-palettes span {
    width: 34px; height: 34px; border-radius: 50%;
    border: 1px solid var(--border-strong); background: var(--primary);
    box-shadow: inset 0 0 0 4px var(--surface);
}

.demo-confidentialite {
    font-size: var(--t-sm); line-height: 1.6; max-width: 640px;
    padding: 12px 14px; border-radius: var(--radius);
    background: var(--surface-sunk); border: 1px solid var(--border);
}

@media (max-width: 600px) {
    /* Colonnes resserrées : la colonne des noms prenait près de la moitié de
       l'écran, pour ne laisser voir que deux jours. */
    .demo-grille table.grid th.employee-col,
    .demo-grille table.grid td.employee-col { min-width: 104px; }
    .demo-grille table.grid th,
    .demo-grille table.grid td { min-width: 84px; }
    .demo-glisser { display: block; }
    .demo-tete h1 { font-size: var(--t-xl); }
}

.heures-plage { display: inline-flex; gap: 8px; align-items: end; }

/* Interrupteur en toutes lettres, à droite de sa case : une étiquette en
   capitales au-dessus d'une case isolée lui donnait le poids d'un champ, et
   laissait un trou dans la rangée. */
.form-inline .form-case {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--t-sm); color: var(--text); cursor: pointer;
    text-transform: none; font-weight: 500; letter-spacing: normal;
}
.form-inline .form-case input { height: auto; }

@media (max-width: 600px) {
    /* Six contrôles sur une rangée de 349 px : ils s'enroulent au hasard, et
       la ligne des heures se retrouvait coupée entre « Début » et « Fin ».
       Deux par ligne, la plage d'heures d'un seul tenant. */
    .form-effectif { align-items: stretch; }
    .form-effectif > .form-group { flex: 1 1 calc(50% - 4px); min-width: 0; }
    .form-effectif .heures-plage { flex: 1 1 100%; }
    .form-effectif .heures-plage .form-group { flex: 1 1 0; }
    /* Le minimum tient en un chiffre : il partage sa ligne avec « Actif »
       plutôt que d'occuper une demi-rangée pour rien. */
    .form-effectif > .form-group-court { flex: 0 1 auto; }
    .form-effectif .form-case { flex: 0 1 auto; margin-bottom: 6px; }
    .form-effectif .btn { flex: 1 1 100%; }
}
.heures-plage[hidden] { display: none; }
input[type="checkbox"] { accent-color: var(--primary); }

.btn {
    padding: 8px 14px; border: 1px solid transparent; border-radius: var(--radius); cursor: pointer;
    font-size: var(--t-sm); font-family: inherit; font-weight: 600;
}
.btn-primary { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-danger { background: var(--danger); color: var(--on-primary); }
.btn-secondary { background: var(--surface); color: var(--text-soft); border: 1px solid var(--border-strong); }
.btn-secondary:hover { background: var(--surface-hover); }
a.btn { text-decoration: none; display: inline-block; }

/* Bouton réduit à une icône. Le carré garde la hauteur des boutons voisins —
   sans quoi la barre d'actions se désaligne dès qu'on retire un libellé.
   Les tracés héritent de `currentColor` : une couleur en dur resterait
   sombre sur fond sombre, comme n'importe quelle couleur de gabarit.

   ⚠️ Sélecteur `.btn.btn-icone` et non `.btn-icone` : ces boutons sont des
   liens, et `a.btn { display: inline-block }` (0,1,1) l'emporte sur une
   classe seule (0,1,0). Le `display: inline-flex` était donc ignoré et
   l'icône se calait en haut à gauche au lieu d'être centrée. */
.btn.btn-icone {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; padding: 0;
    line-height: 1;
}
.btn-icone svg {
    width: 19px; height: 19px; display: block;
    fill: none; stroke: currentColor;
    stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.btn-icone svg .icone-plein { fill: currentColor; stroke: none; }

/* Variante avec légende sous l'icône : « CSV » ne se devine pas d'un
   pictogramme de tableur — l'icône dit « feuille de calcul », pas le format
   du fichier. La hauteur reste celle du bouton carré pour ne pas décaler la
   barre ; c'est l'icône qui rétrécit, pas le bouton qui grandit. */
.btn.btn-icone-legende {
    flex-direction: column; gap: 2px;
    width: auto; min-width: 40px; padding: 0 7px;
}
.btn-icone-legende svg { width: 16px; height: 16px; }
.icone-legende {
    font-size: var(--t-2xs); font-weight: 700; letter-spacing: .07em; line-height: 1;
}

/* Bouton « icône + libellé » : le mot sur ordinateur, l'icône seule sur
   téléphone. C'est ce qui permet d'apprendre le pictogramme là où il y a la
   place, puis de le reconnaître là où il n'y en a plus — une infobulle
   n'existe pas sur un écran tactile, l'icône seule y serait muette.

   ⚠️ Le libellé est masqué VISUELLEMENT (`.sr-only`), jamais par
   `display: none` : ce dernier le retire aussi de l'arbre d'accessibilité, et
   le bouton perdrait son nom. Le nom accessible reste donc identique aux deux
   tailles — et les tests qui cherchent le texte continuent de le trouver.

   ⚠️ Bascule en CSS et non en JS : un basculement en JS arriverait après le
   premier rendu, et htmx réexécute les scripts du contenu qu'il remplace. */
/* ⚠️ `.btn.btn-action` et non `.btn-action` : ces boutons sont souvent des
   liens, et `a.btn { display: inline-block }` (0,1,1) l'emporte sur une classe
   seule (0,1,0). Le `inline-flex` était ignoré et l'icône se calait AU-DESSUS
   du libellé au lieu d'être à côté. Même piège que `.btn.btn-icone`. */
.btn.btn-action { display: inline-flex; align-items: center; gap: 6px; }
/* ⚠️ Tout bouton qui inclut `_icone.html` doit passer par ces règles : sans
   elles le SVG retombe sur le défaut du format — `fill: black`, pas de stroke —
   et l'icône s'affiche en carré noir plein. Ajouter la classe ici plutôt que
   de recopier les cinq déclarations. */
.btn-action svg,
.btn.agenda-btn-calendrier svg {
    width: 16px; height: 16px; flex: none; display: block;
    fill: none; stroke: currentColor;
    stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.btn-action svg .icone-plein,
.btn.agenda-btn-calendrier svg .icone-plein { fill: currentColor; stroke: none; }
/* Teintes de sens, par jeton : une couleur en dur resterait sombre sur fond
   sombre, et `tests/test_theme.py` la refuse. */
.btn-action.action-danger  { color: var(--danger); }
.btn-action.action-ok      { color: var(--success); }

/* Valider / Refuser d'une demande : le style en ligne du gabarit est remonté
   ici pour pouvoir écarter les deux boutons sur mobile. */
.decision { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

@media (max-width: 600px) {
    /* ⚠️ Écart délibérément large entre Valider et Refuser. Réduits à une
       coche et une croix, ils deviennent deux carrés voisins de même taille —
       et valider une absence RETIRE les créneaux qu'elle couvre, que la
       réactivation ne recrée pas. Un pouce qui glisse coûte un planning. */
    .decision { gap: 14px; }

    .btn-action .btn-libelle {
        position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }
    .btn.btn-action { gap: 0; }
    /* 44 px : le seuil tactile. En dessous, une poubelle rouge voisine d'un
       crayon se manque, et une suppression ne se rattrape pas. */
    .btn.btn-action { min-width: 44px; min-height: 44px; justify-content: center; }
}

.alert { padding: 10px 14px; border-radius: var(--radius); font-size: var(--t-sm); margin: 10px 0; font-weight: 500; }
.alert-success { background: var(--ok-bg); color: var(--success); }
.alert-error { background: var(--danger-bg); color: var(--danger); }
.alert-warning { background: var(--warn-bg); color: var(--primary-soft-text); }
.alert-info { background: var(--info-bg); color: var(--info-fg); }

.duplicate-panel {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: 14px; margin-bottom: 14px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.duplicate-panel label { font-size: var(--t-sm); font-weight: 600; }
.duplicate-panel input { padding: 6px 9px; border: 1px solid var(--border-strong); border-radius: 7px; font-size: var(--t-sm); }
.duplicate-panel input[type="number"] { width: 56px; }

.employee-grid { display: flex; flex-direction: column; gap: 10px; }
.employee-day {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px;
}
.employee-day h3 { font-size: var(--t-base); margin-bottom: 6px; color: var(--text); }
.empty-state { text-align: center; padding: 26px; color: var(--text-muted); font-size: var(--t-sm); }

/* ---------------- Planning hebdo : en-tête, semaines, mémo ---------------- */
.hours-col { min-width: 66px; font-size: var(--t-sm); }

.planning-status { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
/* Pastille d'état. ⚠️ Elle était redéfinie dans les `<style>` de NEUF
   gabarits, à l'identique — même arrondi, même graisse, mêmes teintes. Neuf
   copies d'une même règle divergent à la première retouche, et une page de
   démonstration ne pouvait pas s'en servir du tout : la classe n'existait
   nulle part en commun.

   Deux tailles coexistent et c'est délibéré : la grande sur les écrans de
   demandes, où la pastille porte l'information principale d'une ligne ; la
   petite sur les écrans denses, où elle se répète à chaque ligne d'un tableau.
   Les seconds surchargent localement `padding` et `font-size`, rien d'autre. */
.badge {
    display: inline-block; padding: 4px 12px; border-radius: var(--radius-pill);
    font-size: var(--t-sm); font-weight: 600; white-space: nowrap;
}
.badge-demandee { background: var(--warn-bg); color: var(--warn-fg); }
.badge-validee  { background: var(--ok-bg); color: var(--ok-fg); }
.badge-refusee  { background: var(--danger-bg); color: var(--danger-fg); }
/* Fin de mois : un mois est clôturé ou ouvert — mêmes teintes que validée et
   en attente, le vocabulaire seul change. */
.badge-valide { background: var(--ok-bg); color: var(--ok-fg); }
.badge-ouvert { background: var(--warn-bg); color: var(--warn-fg); }
/* Écran Équipe. */
.badge-role    { background: var(--t-back-bg); color: var(--info-fg); }
.badge-actif   { background: var(--ok-bg); color: var(--ok-fg); }
.badge-inactif { background: var(--danger-bg); color: var(--danger-fg); }
.badge-bloque  { background: var(--warn-bg); color: var(--warn-fg); border: 1px solid var(--accent-border); }

.badge-pharmacien {
    display: inline-flex; align-items: center; gap: 9px;
    padding: 8px 12px; border-radius: var(--radius); font-size: var(--t-sm); font-weight: 600;
}
.badge-pharmacien::before { content: ""; width: 7px; height: 7px; border-radius: 4px; background: currentColor; flex-shrink: 0; }
.badge-ph-ok      { background: var(--ok-bg); color: var(--success); border: 1px solid var(--ok-bd); }
.badge-ph-partiel { background: var(--warn-bg); color: var(--primary-soft-text); border: 1px solid var(--accent-border); }

/* Tête du corps du planning : « Semaine N » à gauche, l'état de présence
   pharmacien à droite quand il est bon.

   Il occupait auparavant une rangée pleine au-dessus du planning, pour dire
   que tout allait bien — alors que c'est l'absence d'alerte qui porte cette
   information. Sur cette ligne, il ne coûte rien. */
.planning-corps-tete {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 10px; flex-wrap: wrap; margin: 4px 0 12px;
}
.planning-corps-tete h2 { margin: 0; font-size: var(--t-md); }
.planning-corps-tete .badge-ph-ok { flex: none; }

@media (max-width: 600px) {
    /* L'année : « 20 septembre 2026 » fait passer le titre sur deux lignes,
       et personne n'ignore l'année en cours. Masquée visuellement et non
       retirée — un lecteur d'écran continue de l'annoncer. */
    .h1-annee {
        position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }
}
.badge-ph-aucun   { background: var(--danger-bg); color: var(--danger); border: 1px solid var(--danger-bd); }

.week-pills-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.week-pills-bar {
    display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
    background: var(--surface-sunk); border: 1px solid var(--border); border-radius: 11px; padding: 4px;
}
.week-pill {
    padding: 5px 11px; border-radius: 8px; font-size: var(--t-sm); font-weight: 600;
    text-decoration: none; color: var(--text-muted); background: transparent; border: none;
    /* Sans `nowrap`, « S34 · en cours » se casse en deux et double la hauteur
       du rail — pour une seule pastille sur neuf. */
    white-space: nowrap;
}
.week-pill:hover { background: var(--surface); color: var(--text); }
.week-pill.active { background: var(--primary); color: var(--on-primary); box-shadow: var(--shadow-sm); }
.week-pill.current:not(.active) { color: var(--primary); background: var(--surface); }
/* Flèches semaine précédente / suivante intégrées au même rail. */
.week-step {
    width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px; text-decoration: none; color: var(--text-muted); font-size: var(--t-base); font-weight: 700;
}
.week-step:hover { background: var(--surface); color: var(--text); }

/* ⚠️ Le rail s'enroulait sur trois lignes sur un téléphone, flèches échouées
   au milieu. Plutôt que de le faire défiler — un défilement horizontal que
   rien n'annonce et que personne ne tente —, on montre moins de semaines :
   ±2 au lieu de ±4 sous 700 px, ±1 sous 420 px. Les flèches précédent/suivant
   restent, donc aucune semaine ne devient inatteignable. */
@media (max-width: 700px) {
    /* Le rail prend toute la largeur et répartit ses pastilles : centré sur
       309 px dans un écran de 390, il laissait deux marges vides de part et
       d'autre alors que la place manquait à l'intérieur. `flex-basis: 100%`
       et non `width` : `.week-bar` est un conteneur flex enroulant, un
       élément y garde sinon sa largeur de contenu. */
    .week-pills-bar {
        flex-wrap: nowrap; flex: 1 1 100%;
        justify-content: space-between;
    }
    .week-pill-loin { display: none; }
    /* Le champ de date et sa flèche remplissent la ligne suivante, plutôt que
       de laisser un vide à leur droite. */
    .week-goto { flex: 1 1 100%; }
    .week-goto input[type="date"] { flex: 1 1 auto; min-width: 0; }
}
@media (max-width: 480px) {
    /* « · en cours » vaut deux pastilles de large. La semaine du jour reste
       reconnaissable à sa couleur, et son numéro suffit à la situer. */
    .week-pill-mention { display: none; }
    .week-pills-bar { gap: 2px; padding: 3px; }
}

.memo-epingle {
    flex: 1; min-width: 300px;
    background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--primary);
    border-radius: var(--radius); padding: 8px 12px; font-size: var(--t-sm);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.memo-epingle-titre { font-weight: 700; }
.memo-epingle-texte { color: var(--text-muted); margin-left: 6px; }

/* Mémo long : aperçu d'une ligne, texte entier au dépliage. La coupure seule
   rendait illisible le message que le gérant a justement épinglé pour qu'on
   le lise. */
.memo-depliable { white-space: normal; padding: 0; }
.memo-depliable > summary {
    padding: 8px 12px; cursor: pointer;
    display: flex; align-items: baseline; gap: 6px;
    /* ⚠️ `display: flex` supprime le marqueur de dépliage : le chevron est
       dessiné à la main, comme sur les alertes d'effectif. */
    list-style: none;
}
.memo-depliable > summary::-webkit-details-marker { display: none; }
.memo-depliable > summary::after {
    content: ""; margin-left: auto; align-self: center;
    width: 6px; height: 6px; flex: none;
    border-right: 2px solid var(--text-faint); border-bottom: 2px solid var(--text-faint);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform .15s ease;
}
.memo-depliable[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
/* L'aperçu garde la coupure d'origine ; `min-width: 0` sans quoi un élément
   flex refuse de rétrécir sous son contenu et la coupure n'opère jamais. */
/* ⚠️ Le titre ne rétrécit jamais : en flex il cédait la place à l'aperçu et
   se cassait mot par mot — « Beta / Test / ! » sur trois lignes. */
.memo-depliable > summary .memo-epingle-titre { flex: none; white-space: nowrap; }
/* L'aperçu garde la coupure d'origine ; `min-width: 0` sans quoi un élément
   flex refuse de rétrécir sous son contenu et la coupure n'opère jamais. */
.memo-epingle-apercu {
    color: var(--text-muted); flex: 1 1 auto; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 600px) {
    /* Aucune place pour un aperçu : il ne montrerait que des points de
       suspension. Le titre suffit à identifier le mémo, le texte est à un
       geste. */
    .memo-epingle-apercu { display: none; }
}
.memo-depliable[open] .memo-epingle-apercu { display: none; }
.memo-epingle-complet {
    margin: 0; padding: 0 12px 10px; color: var(--text-muted);
    max-width: 70ch;
}
@media (prefers-reduced-motion: reduce) {
    .memo-depliable > summary::after { transition: none; }
}

.btn-shortcut-ponctuel {
    background: var(--primary-soft); color: var(--primary-soft-text); border: 1px solid var(--accent-border);
}
.btn-shortcut-ponctuel:hover { background: var(--accent-border); }
.btn-shortcut-absence {
    background: var(--primary); color: var(--on-primary); border: 1px solid var(--primary);
}
.btn-shortcut-absence:hover { background: var(--primary-hover); }

.options-panel {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 12px 16px; margin-top: 16px;
}
.options-panel > summary { list-style: none; color: var(--primary); }
.options-panel > summary::-webkit-details-marker { display: none; }
/* Bulle d'édition d'un créneau — `<dialog>` natif, une seule par page,
   partagée par les trois modes d'affichage. Le fond et la fermeture par Échap
   viennent du navigateur : pas de bibliothèque, pas de piège au clavier à
   écrire soi-même. */
.bulle {
    /* `margin: auto` explicite : le reset global `* { margin: 0 }` écrase la
       marge automatique que le navigateur pose sur un dialog modal, et la
       bulle se colle alors en haut à gauche. `position: relative` ancre la
       croix de fermeture. */
    margin: auto; position: relative;
    width: min(560px, calc(100vw - 32px));
    padding: 20px; border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--surface); color: var(--text);
    box-shadow: var(--shadow-md);
}
.bulle::backdrop { background: var(--backdrop); }
.bulle-fermer { position: absolute; top: 8px; right: 10px; margin: 0; }
.bulle-fermer button {
    border: none; background: none; cursor: pointer; line-height: 1;
    font-size: var(--t-xl); color: var(--text-muted); padding: 2px 6px;
    border-radius: var(--radius);
}
.bulle-fermer button:hover { background: var(--surface-hover); color: var(--text); }
.bulle-titre { font-size: var(--t-md); margin: 0 24px 2px 0; }
.bulle-sous-titre {
    font-size: var(--t-sm); color: var(--text-muted); margin: 0 0 14px;
    text-transform: capitalize;
}
.bulle-texte {
    font-size: var(--t-sm); line-height: 1.6; margin: 0 0 14px;
    padding: 10px 12px; background: var(--surface-sunk);
    border-radius: var(--radius); color: var(--text);
}
.bulle-form {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: end;
}
.bulle-form .form-group { flex: 1 1 150px; min-width: 0; }
/* « Qui est disponible ? » — la liste des remplaçants possibles, rendue dans la
   bulle à la place du formulaire. Une ligne par personne : pastille de couleur,
   identité et fonction, marge d'heures, bouton d'attribution. */
.bulle-absent {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin-bottom: 12px;
}
.bulle-absent > span { flex: 1 1 12rem; }

.remplacants { list-style: none; display: flex; flex-direction: column; gap: 6px; margin: 12px 0; }
.remplacant {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius);
}
/* La pastille reprend la couleur de la personne, comme au planning : c'est la
   clé de lecture de toute l'application, elle doit servir ici aussi. */
.remplacant-pastille {
    flex: 0 0 30px; height: 30px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: var(--t-xs); font-weight: 700;
}
.remplacant-identite { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.remplacant-meta { font-size: var(--t-xs); color: var(--text-muted); }
.remplacant-marge { font-size: var(--t-sm); white-space: nowrap; }
.remplacant-ok { color: var(--success); font-weight: 600; }
.remplacant-alerte { color: var(--warn-fg); font-weight: 600; }
.remplacant-inconnu { color: var(--text-faint); }
/* Dépassement de contrat : signalé, jamais masqué — la personne est libre, et
   l'arbitrage revient au gérant. */
.remplacant-depasse { border-style: dashed; }
.remplacant-choisir { flex: 0 0 auto; }
.remplacants-reste { font-size: var(--t-sm); color: var(--text-muted); margin-top: 4px; }

@media (max-width: 600px) {
    /* La marge passe sous l'identité : quatre colonnes ne tiennent pas dans
       340 px, et c'est le bouton qu'on écraserait en premier. */
    .remplacant { flex-wrap: wrap; }
    .remplacant-identite { flex: 1 1 8rem; }
    .remplacant-marge { flex: 1 1 100%; order: 3; padding-left: 40px; }
    .remplacant-choisir { order: 2; }
}

.bulle-actions-secondaires {
    margin-top: 14px; padding-top: 12px;
    border-top: 1px solid var(--border);
}

/* Zones cliquables de la frise : même affordance que les pastilles de la
   grille — le curseur et un liseré au survol, rien de plus. */
.frise-track-editable { cursor: pointer; }
.frise-track-editable:hover { background: var(--surface-hover); }
.frise-seg-editable { cursor: pointer; }
.frise-seg-editable:hover { outline: 2px solid var(--primary); outline-offset: 1px; }

/* Absence en mode frise : bande sur toute la piste, hachurée pour ne pas se
   confondre avec un créneau plein. La grille et la vue texte l'affichaient
   déjà — la frise était la seule à la taire, et on y lisait une journée de
   repos là où quelqu'un était en congés. */
.frise-absence {
    position: absolute; left: 0; right: 0; top: 0; bottom: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm);
    /* Mêmes hachures que `.absence-pill` de la grille : une absence se
       reconnaît d'une vue à l'autre. */
    background: repeating-linear-gradient(
        45deg,
        var(--surface-track), var(--surface-track) 6px,
        var(--border) 6px, var(--border) 12px
    );
    border: 1px dashed var(--border-strong);
    color: var(--text-muted); font-size: var(--t-xs); font-style: italic;
}

/* Visibilité d'un événement : la liste des personnes est repliée tant qu'on
   n'a pas choisi la portée restreinte — le cas courant reste « tout le
   monde », il ne doit pas coûter un écran de cases à cocher. */
.evt-visibilite { display: flex; flex-direction: column; gap: 4px; }
.evt-visibilite-titre {
    font-size: var(--t-sm); font-weight: 600; color: var(--text-muted);
}
.evt-visibilite ul { list-style: none; display: flex; flex-wrap: wrap; gap: 4px 14px; }
.evt-visibilite label { font-weight: 400; font-size: var(--t-sm); }
.evt-personnes {
    margin-top: 4px; padding: 8px 10px;
    background: var(--surface-sunk); border: 1px solid var(--border);
    border-radius: var(--radius); max-width: 520px;
}
.evt-personnes ul { max-height: 160px; overflow-y: auto; }

/* Zone d'action destructrice, en pied du panneau d'options du planning.
   Séparée des réglages par un filet et teintée `danger` : on ne clique pas
   « Vider la semaine » en cherchant à masquer le dimanche. */
.zone-danger {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
    margin-top: 12px; padding: 12px 14px;
    background: var(--danger-bg); border: 1px solid var(--danger-bd);
    border-radius: var(--radius);
}
.zone-danger strong { font-size: var(--t-sm); color: var(--danger-fg); }
.zone-danger p {
    margin: 4px 0 0; font-size: var(--t-sm); line-height: 1.5;
    color: var(--text-soft); max-width: 62ch;
}
.zone-danger form { margin: 0; flex-shrink: 0; }

.emp-toggle {
    transition: background .15s, color .15s;
    background: var(--surface); color: var(--text-soft);
    border: 1px solid var(--border-strong) !important; border-radius: var(--radius-pill);
    font-family: inherit; font-weight: 600;
}
.emp-toggle:hover { background: var(--surface-hover); }
.emp-toggle.masked { background: var(--surface-sunk); color: var(--text-faint); text-decoration: line-through; }
/* Bascule de mode : un seul groupe segmenté. */
.mode-toggle {
    padding: 5px 13px; border-radius: 7px; font-size: var(--t-sm); font-weight: 600;
    border: none; cursor: pointer; background: transparent; color: var(--text-muted);
    font-family: inherit;
}
.mode-toggle.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

/* ---------------- Mode frise : une carte par jour ---------------- */
.frise-day {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    font-size: var(--t-md); font-weight: 700; letter-spacing: -.01em; color: var(--text);
    margin: 0; padding: 11px 16px; border-bottom: 1px solid var(--border);
}
.frise-day .frise-date { font-size: var(--t-sm); color: var(--text-faint); font-weight: 600; }
.frise-day.today { background: var(--surface); }
.frise-tag {
    font-size: var(--t-xs); font-weight: 600; padding: 2px 9px; border-radius: var(--radius-pill);
    background: var(--surface-sunk); color: var(--text-muted);
}
.frise-tag-ferie { background: var(--primary-soft); color: var(--primary-soft-text); }
.frise-tag-vac { background: var(--info-bg); color: var(--info-fg); }
.frise-tag-today { background: var(--primary); color: var(--on-primary); text-transform: uppercase; letter-spacing: .06em; font-size: var(--t-2xs); }
.frise-resume { margin-left: auto; font-size: var(--t-xs); color: var(--text-muted); font-weight: 600; }

.frise-card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 14px;
}
.frise-card.today { background: var(--surface); border-color: var(--border-strong); }
.frise-day-block { position: relative; padding: 10px 16px 13px; }
.frise-axis, .frise-row { display: flex; align-items: center; }
.frise-gutter, .frise-label { width: 150px; flex-shrink: 0; }
.frise-label {
    display: flex; align-items: center; justify-content: flex-end; gap: 7px;
    padding-right: 12px; overflow: hidden; text-align: right;
}
.frise-label .frise-nom {
    display: flex; flex-direction: column; align-items: flex-end; min-width: 0;
}
.frise-label .frise-nom b {
    font-size: var(--t-sm); font-weight: 600; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; max-width: 108px;
}
/* Nom entier sur grand écran, « Moreau M. » sur téléphone. La bascule est en
   CSS et non en JS : le libellé doit être juste au premier rendu. */
.frise-nom-court { display: none; }
.frise-label .frise-nom small { font-size: var(--t-2xs); color: var(--text-faint); font-weight: 600; white-space: nowrap; }
.frise-label .frise-avatar {
    width: 22px; height: 22px; border-radius: 7px; background: var(--surface-hover);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: var(--t-sm); flex-shrink: 0;
}
.frise-axis-track { position: relative; flex: 1; height: 15px; }
.frise-axis-mark {
    position: absolute; top: 0;
    font-size: var(--t-2xs); font-weight: 600; color: var(--text-faint); white-space: nowrap;
}

.frise-row { padding: 3px 0; border-radius: 8px; position: relative; z-index: 1; }
.frise-row:hover { background: var(--surface-hover); }
.frise-track { position: relative; flex: 1; height: 26px; background: var(--surface-track); border-radius: 7px; }
/* Ligne d'un membre sans créneau ce jour-là : la ligne reste affichée pour que
   les employés restent alignés d'un jour à l'autre. */
.frise-jour-vide {
    margin: 8px 0 0; padding: 10px 12px; font-size: var(--t-sm);
    color: var(--text-faint); background: var(--surface-sunk);
    border-radius: var(--radius);
}
.frise-track.repos::before {
    content: "Repos";
    position: absolute; inset: 0; display: flex; align-items: center; padding-left: 10px;
    font-size: var(--t-2xs); font-weight: 600; color: var(--text-faint); letter-spacing: .04em;
}
.frise-track.ferme {
    background: repeating-linear-gradient(45deg, var(--surface-track), var(--surface-track) 6px, var(--border) 6px, var(--border) 12px);
}
.frise-seg {
    position: absolute; top: 0; height: 26px; min-width: 3px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 7px; font-size: var(--t-xs); font-weight: 600; overflow: hidden;
}
.frise-seg-txt {
    display: flex; align-items: center; gap: 4px;
    padding: 0 8px; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; letter-spacing: -.01em;
}
/* Sur une barre étroite, l'icône passe avant les heures : mieux vaut
   perdre la fin de « 09h00–12h00 » que le type du poste. */
.frise-seg-txt .type-icone { opacity: .85; }
/* Les segments réutilisent .creneau-<type> : le liseré passe par box-shadow
   (le border-left décalerait le positionnement en %). */
.frise-seg.creneau-comptoir, .frise-seg.creneau-back_office,
.frise-seg.creneau-garde, .frise-seg.creneau-livraison { border-left: none; }
/* ⚠️ Sur la frise, le liseré est posé aux DEUX bouts. Ailleurs, un créneau est
   une pastille dans une case et ses horaires sont écrits : le liseré ne fait
   qu'y porter la couleur de la personne. Ici, la **position et la largeur de
   la barre SONT l'information** — marquée d'un seul côté, l'œil trouve le
   début et cherche la fin, d'autant que deux segments voisins se touchent
   presque et que le texte se coupe sur une barre étroite. Le second bout se
   pose en `inset -3px` et non en `border-right`, pour la même raison que la
   gauche : un bord décalerait le positionnement en pourcentage.

   `:not(.creneau-annule)` parce qu'un créneau neutralisé par une absence ne
   porte aucun liseré — c'est justement ce qui le distingue. Cette règle étant
   déclarée APRÈS `.creneau-membre.creneau-annule`, elle l'emporterait à
   spécificité égale et lui rendrait ses deux barres pleines. */
.frise-seg.creneau-membre:not(.creneau-annule) {
    box-shadow: inset 3px 0 0 var(--couleur-membre, var(--border)),
                inset -3px 0 0 var(--couleur-membre, var(--border));
}

@media (max-width: 600px) {
    .week-nav .week-label { white-space: normal; font-size: var(--t-sm); }
    h1 { font-size: var(--t-xl); }
    table.grid th, table.grid td { padding: 5px 6px; min-width: 84px; }
    /* ⚠️ Pas de `max-width` sur le nom : il était plafonné à 62 px dans une
       colonne de 96, ce qui tronquait « De Leeuw C. » en laissant un tiers de
       la colonne vide. C'est la colonne qui borne, pas un chiffre en dur. */
    .frise-gutter, .frise-label { width: 92px; }
    .frise-label { padding-right: 8px; }
    .frise-label .frise-nom b { max-width: none; font-size: var(--t-xs); }
    .frise-nom-long { display: none; }
    .frise-nom-court { display: block; }
    .frise-label .frise-avatar { display: none; }
    /* Le segment doit loger l'icône de poste ET les heures : sur un créneau
       de trois heures, la piste n'accorde qu'une soixantaine de pixels. */
    .frise-seg { font-size: var(--t-2xs); }
    .frise-seg-txt { padding: 0 5px; gap: 3px; }
    .frise-seg-txt .type-icone { width: 11px; height: 11px; flex-basis: 11px; }
    .frise-day-block { padding: 8px 10px 10px; }
    .frise-day { padding: 9px 10px; }
}

/* ---------------- Historique des changements ---------------- */
/* Trois colonnes : catégorie (motif), détail, actions. La catégorie sert de
   colonne de balayage — on lit la page en la parcourant, sans lire chaque
   ligne en entier. */
/* Une entrée = une ligne. Un mois chargé dépasse les 150 entrées : tout ce qui
   ajoute une deuxième ligne (traçabilité, note) double la hauteur de la page.
   La note passe en fin de ligne, la traçabilité dans une pastille. */
.changements-table {
    /* `fixed` est indispensable : il y a une table par employé, et sans lui
       chacune se dimensionne sur son propre contenu — les colonnes ne
       s'alignent plus d'un membre à l'autre et la pastille part hors écran. */
    table-layout: fixed;
    width: 100%; border-collapse: collapse; font-size: var(--t-sm);
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); overflow: hidden;
}
.changements-table td {
    padding: 6px 12px; border-bottom: 1px solid var(--border);
    vertical-align: middle; white-space: normal; overflow-wrap: anywhere;
}
.changements-table tr:last-child td { border-bottom: none; }
/* Zebrures : sur des lignes courtes et nombreuses, elles guident l'œil bien
   mieux qu'un filet clair sur fond clair. */
.changements-table tr:nth-child(even) td { background: var(--zebra); }

.chg-categorie { width: 190px; font-weight: 600; }
.chg-detail { width: auto; }
.chg-trace-cell { width: 36px; text-align: center; }
.chg-actions { width: 168px; text-align: right; }
/* Sans `nowrap`, la colonne étroite du mobile coupe « Supprimer » lettre par
   lettre, en colonne verticale. */
.chg-actions .btn { white-space: nowrap; }
/* Une journée « 09:00-12:00 14:00-18:00 » doit pouvoir passer à la ligne
   ENTRE deux plages, jamais au milieu d'un horaire. La cellule autorise la
   coupure n'importe où (overflow-wrap: anywhere, pour les vieux résumés) : on
   la remet à `normal` ici, seuls les espaces restent des points de coupure.
   En `nowrap`, la plage débordait sous la pastille de traçabilité. */
.chg-avant, .chg-apres { overflow-wrap: normal; word-break: keep-all; }
.chg-detail .chg-jour { white-space: nowrap; }

/* L'ancien état en rouge, le nouveau en vert : la nature du changement se voit
   avant d'être lue. */
.chg-avant { color: var(--danger); }
.chg-apres { color: var(--success); }
.chg-fleche { color: var(--text-muted); }
.chg-note { color: var(--text-muted); font-size: var(--t-sm); }
/* Écart d'heures de la journée : grisé quand le motif ne l'alimente pas le
   compteur mensuel (formation, correction…) — le solde le lit ou non. */
.chg-delta { color: var(--text-soft); font-size: var(--t-xs); font-weight: 600; }
.chg-delta-muet { color: var(--text-faint); font-weight: 400; }

/* Pastille de traçabilité : « Modifié le … par … » au survol. */
.chg-trace {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--border); color: var(--text-muted);
    font-size: var(--t-xs); font-style: italic; font-weight: 700;
    cursor: help; user-select: none;
}
.chg-trace:hover, .chg-trace:focus { background: var(--text-muted); color: var(--surface); }
/* ⚠️ `display: none` en dur, révélé par `:popover-open`. Un navigateur qui
   ignore l'API laisse tomber la seconde règle — le bloc reste caché — au lieu
   d'afficher la trace en clair sous chacune des 150 lignes du mois, ce que la
   pastille existe précisément pour éviter. */
.chg-trace-info { display: none; }
.chg-trace-info:popover-open {
    display: block;
    margin: auto;                       /* le reset global écrase la marge
                                           automatique de la couche supérieure */
    max-width: min(300px, calc(100vw - 32px));
    padding: 12px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--surface); color: var(--text);
    box-shadow: var(--shadow-md);
    font-size: var(--t-sm); line-height: 1.5;
}
.chg-trace-info::backdrop { background: var(--backdrop); }
/* ⚠️ 44 px de cible tactile sans grossir la pastille : la zone cliquable est
   étendue par un pseudo-élément, le point reste à 18 px. */
@media (max-width: 600px) {
    .chg-trace { position: relative; }
    .chg-trace::after { content: ""; position: absolute; inset: -13px; }
}

@media (max-width: 600px) {
    .changements-table td { padding: 6px 8px; }
    .chg-categorie { width: 104px; font-size: var(--t-sm); }
    .chg-trace-cell { width: 28px; }
    /* Les deux boutons s'empilent : côte à côte, ils ne tiennent pas. */
    .chg-actions { width: 86px; }
    .chg-actions > div { flex-direction: column; align-items: flex-end; }
}

/* ---------------- Impression ---------------- */
@media print {
    .sidebar, .mobile-topbar, .week-nav, .week-pills-row, .options-panel { display: none !important; }
    .app-main { margin-left: 0; }
    body { background: var(--on-primary); }
    main { max-width: none; padding: 0; }
    .frise-card, .grid-wrapper { break-inside: avoid; box-shadow: none; }
}

/* ---------------- Écran Apparence ---------------- */
.apparence-modes { display: flex; gap: 8px; flex-wrap: wrap; }
.apparence-mode {
    display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
    padding: 9px 14px; font-size: var(--t-sm);
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius);
}
.apparence-mode.choisi, .apparence-theme.choisi {
    border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary);
}

.apparence-themes {
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
/* La vignette porte son propre `data-theme`, donc ses pastilles rendent la
   palette réelle : aucune couleur n'est recopiée dans le gabarit. */
.apparence-theme {
    display: flex; flex-direction: column; gap: 8px; cursor: pointer;
    padding: 12px; background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.apparence-theme input, .apparence-mode input { accent-color: var(--primary); }
.apparence-theme input { align-self: flex-start; }
.apparence-pastilles { display: flex; gap: 5px; }
.apparence-pastilles .pastille {
    width: 26px; height: 26px; border-radius: 7px;
    border: 1px solid var(--border-strong);
}
.apparence-nom { font-size: var(--t-sm); font-weight: 600; }

/* ---------------- Écran Semaines types ----------------
   Cartes dépliables. L'ancien tableau déversait une ligne par créneau : avec
   quatorze collaborateurs et quatre-vingt-dix créneaux, une trame faisait
   plusieurs écrans de haut. Ici chaque trame est une carte refermée qui
   annonce sa charge, et le corps regroupe les créneaux par horaire. */

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.trames-entete {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 24px; flex-wrap: wrap; margin-bottom: 18px;
}
.trames-titre { margin: 0 0 5px; font-size: var(--t-xl); font-weight: 600; letter-spacing: -.02em; }
.trames-contexte { margin: 0; font-size: var(--t-sm); color: var(--text-muted); }
.trames-creation { display: flex; align-items: center; gap: 8px; }
.trames-creation input { height: 34px; }

/* Deux panneaux côte à côte : ce que l'officine ouvre, et où appliquer. */
/* Un seul panneau depuis le retrait du rappel d'horaires : plus de grille à
   deux colonnes, mais une largeur bornée — étiré sur un écran large, un
   formulaire de six champs se dilue. */
.trames-panneaux { max-width: 560px; margin-bottom: 16px; }
.trames-panneau {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 14px 16px;
    display: flex; flex-direction: column; gap: 10px; min-width: 0;
}
.trames-panneau-titre {
    font-size: var(--t-xs); letter-spacing: .07em; text-transform: uppercase;
    color: var(--text-faint); font-weight: 600;
}

.trames-appliquer {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: var(--t-sm);
}
.trames-appliquer label { color: var(--text-muted); }
.trames-champ-court { width: 62px; text-align: center; }
.trames-date { font-family: var(--police-mono); font-size: var(--t-sm); color: var(--text-muted); white-space: nowrap; }
.trames-appliquer-bouton { margin-left: auto; }

.trames-filtres {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px;
}
.trames-filtres form { display: contents; }
.trames-filtres input[type="search"] { width: 266px; max-width: 100%; }
.trames-seuil {
    display: flex; align-items: center; gap: 5px; height: 34px; padding: 0 10px;
    border: 1px solid var(--border-strong); border-radius: var(--radius);
    background: var(--surface); font-size: var(--t-sm); color: var(--text-muted);
}
.trames-seuil input { width: 44px; border: 0; padding: 0; height: auto; text-align: center; background: transparent; }
.trames-effacer { font-size: var(--t-sm); color: var(--danger); text-decoration: none; }
#trames-bascule { margin-left: auto; }

.trames-liste { display: flex; flex-direction: column; gap: 10px; }

/* ----- La carte ----- */
.trame-carte {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); overflow: hidden;
}
.trame-carte-danger { border-color: var(--danger-bd); background: var(--danger-bg); }
.trame-confirm { padding: 13px 16px; display: flex; flex-direction: column; gap: 10px; }
.trame-confirm p { margin: 0; font-size: var(--t-sm); }
.trame-confirm form { display: flex; gap: 6px; }

/* L'en-tête est le `summary` : tout le bandeau déplie la carte, et le clavier
   fonctionne sans une ligne de JS. */
.trame-entete {
    display: flex; align-items: center; gap: 14px; row-gap: 10px; flex-wrap: wrap;
    padding: 13px 16px; cursor: pointer; list-style: none;
}
.trame-entete::-webkit-details-marker { display: none; }
.trame-entete:hover { background: var(--surface-hover); }

/* Le chevron est la seule marque du dépliage : à 11 px il ne se voyait pas,
   et rien n'indiquait qu'une carte s'ouvre. */
.trame-chevron { flex: 0 0 22px; font-size: var(--t-lg); color: var(--text-muted); line-height: 1; }
.trame-chevron::before { content: "\25B8"; }
.trame-details[open] .trame-chevron::before { content: "\25BE"; }

.trame-pastille {
    flex: 0 0 30px; width: 30px; height: 30px; border-radius: var(--radius);
    background: var(--primary-soft); color: var(--primary-soft-text);
    display: grid; place-items: center; font-size: var(--t-sm); font-weight: 700;
}
.trame-identite { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 150px; }
.trame-nom { font-size: var(--t-base); font-weight: 600; letter-spacing: -.01em; }
.trame-meta { font-size: var(--t-sm); color: var(--text-muted); }

.trame-frise { display: flex; align-items: center; gap: 5px; min-width: 0; flex: 0 1 auto; }
.trame-frise-jour {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    width: 34px; flex: 0 1 34px; min-width: 20px;
}
.trame-frise-libelle { font-size: var(--t-2xs); letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.trame-frise-piste { height: 5px; width: 100%; border-radius: 3px; background: var(--surface-track); overflow: hidden; }
.trame-frise-barre { display: block; height: 100%; border-radius: 3px; background: var(--primary); }
.trame-frise-nombre { font-family: var(--police-mono); font-size: var(--t-xs); color: var(--text-muted); }

/* ⚠️ `margin-left: auto` + `flex-wrap` sur l'en-tête : sans le retour à la
   ligne, les actions sont coupées par l'`overflow: hidden` de la carte en
   dessous de ~950px de large. */
.trame-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; flex: 0 0 auto; }
.trame-action {
    height: 30px; padding: 0 12px; display: inline-flex; align-items: center;
    border-radius: var(--radius); font-size: var(--t-sm); font-weight: 500;
    text-decoration: none; cursor: pointer;
}
.trame-action-danger {
    border: 1px solid transparent; background: transparent; color: var(--danger);
    font-family: inherit;
}
.trame-action-danger:hover { background: var(--danger-bg); }

/* ----- Le corps déplié ----- */
.trame-corps {
    border-top: 1px solid var(--border); background: var(--surface-sunk);
    padding: 14px 16px 18px; overflow-x: auto;
}
.trame-jours {
    display: grid; grid-template-columns: repeat(6, minmax(168px, 1fr));
    gap: 12px; align-items: start;
}
.trame-jour {
    display: flex; flex-direction: column; gap: 9px; min-width: 0;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 11px 11px 13px; min-height: 80px;
}
.trame-jour-entete {
    display: flex; align-items: baseline; justify-content: space-between; gap: 6px;
    padding-bottom: 7px; border-bottom: 1px solid var(--border);
}
.trame-jour-nom { font-size: var(--t-sm); font-weight: 600; letter-spacing: -.01em; }
.trame-jour-compte { font-family: var(--police-mono); font-size: var(--t-xs); color: var(--text-muted); white-space: nowrap; }
.trame-jour-vide { margin: 0; padding: 6px 0; font-size: var(--t-sm); color: var(--text-faint); }

.trame-groupe { display: flex; flex-direction: column; gap: 6px; }
.trame-groupe-entete { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.trame-horaire { font-family: var(--police-mono); font-size: var(--t-xs); letter-spacing: -.02em; color: var(--text); }
.trame-effectif { font-size: var(--t-2xs); color: var(--text-muted); }
.trame-point {
    width: 5px; height: 5px; border-radius: 50%; background: var(--warn-fg);
    display: inline-block; flex-shrink: 0;
}

.trame-noms { display: flex; flex-wrap: wrap; gap: 4px; }
/* La pastille reprend la couleur du type de créneau (`.creneau-comptoir` &c.)
   pour qu'une trame mélangeant les types se lise sans légende. */
.trame-nom-pastille {
    font-size: var(--t-xs); line-height: 1; padding: 5px 7px; border-radius: 6px;
    border: 1px solid var(--border); white-space: nowrap; color: var(--text);
}
.trame-nom-vide { background: var(--surface-track); color: var(--text-faint); font-style: italic; }

.trames-pied {
    display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    margin-top: 16px; font-size: var(--t-sm); color: var(--text-muted);
}
.trames-legende { display: flex; align-items: center; gap: 6px; }

/* Un libellé et son champ ne se séparent jamais : c'est le groupe qui
   s'enroule, pas ses morceaux. */
.trames-appliquer-groupe { display: inline-flex; align-items: center; gap: 6px; }

@media (max-width: 767px) {
    /* Le nom de la nouvelle trame prend la ligne, le bouton la suivante :
       « Créer une semaine type » ne tient pas à côté d'un champ de saisie. */
    .trames-creation { flex-wrap: wrap; }
    .trames-creation input { flex: 1 1 100%; }
    .trames-creation .btn { flex: 1 1 100%; justify-content: center; }

    /* Le sélecteur de trame et le bouton prennent la largeur ; les deux
       groupes de champs se partagent la ligne du milieu. */
    .trames-appliquer > select { flex: 1 1 100%; }
    .trames-appliquer-bouton { flex: 1 1 100%; margin-left: 0; justify-content: center; }

    /* Recherche sur sa ligne, filtres partagés sur la suivante. */
    .trames-filtres input[type="search"] { flex: 1 1 100%; width: auto; }
    .trames-filtres select { flex: 1 1 45%; min-width: 0; }
}

@media (max-width: 900px) {
    /* La frise est un confort de survol : sur petit écran elle vole la place
       du nom et des actions, qui sont, eux, indispensables. */
    .trame-frise { display: none; }
    /* ⚠️ `margin-left: auto` colle les actions à droite. Tant qu'elles sont sur
       la même ligne que le nom, c'est ce qu'on veut ; une fois qu'elles passent
       à la ligne — ce qui arrive toujours ici, la frise partie —, elles se
       retrouvent seules, alignées sur un bord qui ne porte plus rien. Elles
       reprennent le bord gauche, celui du chevron et du nom. */
    .trame-actions { margin-left: 0; }
}

/* ---------------- Édition d'une semaine type ----------------
   Une ligne = un horaire, pas un créneau. L'écran précédent donnait un
   formulaire de six contrôles par créneau : environ sept cents contrôles pour
   une trame de quatre-vingt-dix créneaux, alors que la plupart des lignes ne
   différaient que par l'employé. */

.ct-entete {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 20px; flex-wrap: wrap; margin-bottom: 16px;
}
.ct-entete-titre { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ct-fil { margin: 0; font-size: var(--t-sm); color: var(--text-muted); }
.ct-fil a { color: var(--primary); text-decoration: none; }
.ct-entete-actions { display: flex; align-items: center; gap: 8px; }

/* Le titre EST le champ de renommage : bordure invisible au repos, elle
   apparaît au survol pour dire que c'est éditable. `margin-left: -8px`
   compense le padding, pour que le texte reste aligné sur le fil d'ariane. */
.ct-titre-form { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
/* Le `<h1>` n'est qu'un conteneur sémantique : c'est l'input qui porte
   l'apparence du titre. */
.ct-titre-h1 { margin: 0; font-size: inherit; font-weight: inherit; line-height: 1; }
.ct-titre {
    font-family: inherit; font-size: var(--t-xl); font-weight: 600; letter-spacing: -.02em;
    color: var(--text); background: transparent;
    border: 1px solid transparent; border-radius: var(--radius);
    padding: 3px 8px; margin-left: -8px; width: 320px; max-width: 100%;
}
.ct-titre:hover { border-color: var(--border-strong); background: var(--surface); }
.ct-titre:focus { border-color: var(--primary); background: var(--surface); outline: none; }
.ct-meta { margin: 0; font-size: var(--t-sm); color: var(--text-muted); }
.ct-note { font-size: var(--t-sm); color: var(--text-muted); }
.ct-statut-ok { font-size: var(--t-sm); color: var(--success); }
.ct-statut-ko { font-size: var(--t-sm); color: var(--danger); }

.ct-panneau {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 14px 16px;
    display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px;
}

/* ----- Outils repliés ----- */
.ct-outils-tete { padding: 11px 16px; gap: 10px; }
.ct-outils-titre { font-size: var(--t-sm); font-weight: 600; }
.ct-outils-soustitre { font-size: var(--t-sm); color: var(--text-muted); }
.ct-outils-corps {
    border-top: 1px solid var(--border); background: var(--surface-sunk);
    padding: 14px 16px;
    display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 14px; align-items: start;
}
.ct-outil { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.trame-add-form, .ct-copier-form {
    display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: var(--t-sm);
}
.ct-copier-form label { color: var(--text-muted); }
.trame-add-form input[type="time"] { width: 96px; }
.trame-add-form input[type="text"] { width: 150px; }
.trame-add-form select { min-width: 120px; }
/* Cases des jours de destination : compactes, elles tiennent sur la ligne de
   l'outil au lieu de la faire déborder. Un jour désactivé (le jour source)
   reste lisible mais visiblement hors jeu. */
.ct-jours-cibles { display: inline-flex; flex-wrap: wrap; gap: 2px 10px; align-items: center; }
.ct-jours-cibles label {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: var(--t-sm); color: var(--text-soft); white-space: nowrap;
}
.ct-jours-cibles input { width: auto; margin: 0; }
.ct-jours-cibles input:disabled + * , .ct-jours-cibles label:has(input:disabled) {
    color: var(--text-faint);
}

.ct-plus1 {
    display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
    font-size: var(--t-sm); color: var(--text-muted);
}
.ct-plus1 input[type="checkbox"] { width: auto; margin: 0; }
.ct-tiret { color: var(--text-faint); }

/* ----- Barre de vue ----- */
.ct-barre {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 14px 0 12px;
}
.ct-segmenteur {
    display: flex; gap: 2px; padding: 2px;
    background: var(--surface-sunk); border-radius: var(--radius);
}
.ct-segment {
    height: 28px; padding: 0 12px; border: 0; border-radius: 7px;
    font-family: inherit; font-size: var(--t-sm); cursor: pointer;
    background: transparent; color: var(--text-muted);
}
.ct-segment.actif { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
.ct-barre input[type="search"] { width: 210px; }
.ct-legende { display: flex; align-items: center; gap: 6px; margin-left: auto; font-size: var(--t-sm); color: var(--text-muted); }

.ct-vue { display: flex; flex-direction: column; gap: 10px; }

/* ----- Carte-jour et lignes de groupe ----- */
.ct-carte {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); overflow: hidden;
}
.ct-carte-tete {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 11px 16px; border-bottom: 1px solid var(--border);
}
.ct-jour-nom { font-size: var(--t-base); font-weight: 600; letter-spacing: -.01em; min-width: 78px; }
.ct-jour-meta { font-family: var(--police-mono); font-size: var(--t-sm); color: var(--text-muted); }
.ct-jour-vide { margin: 0; padding: 16px; font-size: var(--t-sm); color: var(--text-faint); }
.ct-initiales {
    flex: 0 0 28px; width: 28px; height: 28px; border-radius: var(--radius);
    background: var(--primary-soft); color: var(--primary-soft-text);
    display: grid; place-items: center; font-size: var(--t-xs); font-weight: 700;
}

/* Bordure pointillée : l'ajout se distingue ainsi des actions destructives ou
   de navigation, qui sont toutes en trait plein. */
.ct-ajout-horaire {
    margin-left: auto; height: 28px; padding: 0 11px;
    border: 1px dashed var(--border-strong); background: var(--surface);
    border-radius: var(--radius); font-family: inherit; font-size: var(--t-sm);
    color: var(--text-soft); cursor: pointer;
}
.ct-ajout-horaire:hover { border-color: var(--primary); color: var(--primary); }

.ct-ligne {
    display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap;
    padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.ct-ligne:last-child { border-bottom: 0; }
.ct-ligne:hover { background: var(--surface-hover); }
.ct-bloc-horaire { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; flex-wrap: wrap; }
.ct-bloc-horaire input[type="time"] {
    height: 30px; width: 96px; font-family: var(--police-mono); font-size: var(--t-sm); text-align: center;
}
.ct-bloc-horaire select { height: 30px; font-size: var(--t-sm); }

.ct-personnes {
    display: flex; flex-wrap: wrap; gap: 5px; align-items: center;
    flex: 1 1 340px; min-width: 0;
}
.ct-pastille {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: var(--t-sm); line-height: 1; padding: 6px 5px 6px 8px;
    border-radius: 7px; border: 1px solid var(--border); color: var(--text);
    white-space: nowrap;
}
.ct-pastille-vide { background: var(--surface-track); color: var(--text-faint); font-style: italic; }
.ct-retirer {
    border: 0; background: transparent; cursor: pointer; font-family: inherit;
    font-size: var(--t-xs); color: var(--text-muted); padding: 0 1px; line-height: 1;
}
.ct-retirer:hover { color: var(--danger); }
.ct-ajout-personne {
    height: 28px; padding: 0 6px; font-size: var(--t-sm);
    border: 1px dashed var(--border-strong); border-radius: 7px;
    background: var(--surface); color: var(--text-muted);
}
.ct-ajout-personne:hover { border-color: var(--primary); color: var(--primary); }

.ct-fin-ligne { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.ct-fin-ligne input[type="text"] { height: 30px; width: 130px; font-size: var(--t-sm); }
.ct-supprimer-groupe {
    height: 30px; width: 30px; border: 1px solid transparent; background: transparent;
    border-radius: var(--radius); font-family: inherit; font-size: var(--t-sm);
    color: var(--danger); cursor: pointer;
}
.ct-supprimer-groupe:hover { background: var(--danger-bg); }

/* ----- Vue par personne ----- */
.ct-fiche-jours {
    display: grid; grid-template-columns: repeat(6, minmax(140px, 1fr));
    gap: 10px; padding: 12px 16px 14px; overflow-x: auto;
}
.ct-fiche-jour { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ct-fiche-jour-nom {
    font-size: var(--t-xs); letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint);
}
.ct-creneau-mono {
    display: flex; align-items: center; gap: 5px;
    font-family: var(--police-mono); font-size: var(--t-sm);
    padding: 5px 7px; border: 1px solid var(--border); border-radius: 7px;
}
.ct-creneau-mono .ct-retirer { margin-left: auto; }
.ct-fiche-vide { font-size: var(--t-sm); color: var(--text-faint); }
/* Ajout d'un créneau depuis la vue Par personne. Reprend le **tireté** de
   `.ct-ajout-horaire` : c'est la marque des affordances d'ajout de cet écran,
   qui les distingue des actions destructives ou de navigation. Il reste
   discret (pas de `margin-left: auto`, il suit les créneaux du jour) et prend
   toute la largeur de sa colonne — la colonne d'un jour fait une centaine de
   pixels, un « + » de 28 px y flotterait sans qu'on sache à quel jour il se
   rattache. */
/* Heures de la trame face au contrat, dans la carte « Par personne ». La
   chasse fixe aligne les chiffres d'une carte à l'autre — on lit une colonne
   de valeurs, pas des phrases. */
/* Fin de mois : le détail semaine par semaine de la comparaison au contrat.
   Déplié d'office, contrairement aux « Changements du mois » — c'est lui qui
   fait le solde quand le mode hebdomadaire est actif, et un chiffre de paie
   doit pouvoir se décomposer. */
/* Bandeau de panne d'envoi. Il tient sur toute la largeur et n'est pas
   refermable : tant que l'email est cassé, l'admin doit le voir à chaque
   écran — c'est le seul canal qui fonctionne encore. */
/* Délégation du titulaire, dans le panneau d'un membre (écran Équipe). Styles
   ici et non dans le partiel : htmx remplace le panneau à chaque ouverture, et
   un <style> local serait réinjecté à chaque fois. */
.delegation {
    margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border);
    display: flex; flex-direction: column; gap: 10px;
}
.delegation-titre { font-size: var(--t-md); }
.delegation-aide, .delegation-trace { font-size: var(--t-sm); color: var(--text-muted); }
.delegation-form { display: flex; flex-direction: column; gap: 8px; }
/* Case + libellé sur deux lignes : le nom du pouvoir, puis ce qu'il couvre.
   L'étiquette entière est la cible du clic — 44 px de hauteur utile sur
   téléphone, pas seulement la case de 16. */
.delegation-choix {
    display: flex; align-items: flex-start; gap: 10px; min-height: 44px;
    padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius);
    cursor: pointer;
}
.delegation-choix input { margin-top: 3px; flex: 0 0 auto; }
.delegation-choix span { display: flex; flex-direction: column; gap: 2px; }
.delegation-choix small { font-size: var(--t-xs); color: var(--text-muted); }

.alerte-smtp { display: block; margin-bottom: 14px; }
.alerte-smtp-detail, .alerte-smtp-quand {
    display: block; margin-top: 3px; font-size: var(--t-sm); opacity: .85;
}

.hebdo-detail { margin: 6px 0 10px; font-size: var(--t-sm); }
.hebdo-titre { color: var(--text-soft); font-weight: 600; }
.hebdo-semaines {
    list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 5px 0 0;
}
.hebdo-semaine {
    display: inline-flex; align-items: baseline; gap: 6px;
    padding: 2px 8px; border: 1px solid var(--border); border-radius: var(--radius);
    font-family: var(--police-mono); font-size: var(--t-xs);
}
.hebdo-lundi { color: var(--text-muted); }
.hebdo-ecart { font-weight: 700; }
.hebdo-plus { color: var(--warn-fg); }
.hebdo-moins { color: var(--danger); }
/* Une semaine non planifiée est DITE, pas tue : muette et « zéro » ne veulent
   pas dire la même chose, et c'est la distinction qui protège la paie. Le
   tireté la range visuellement du côté de « pas de donnée ». */
.hebdo-ignoree { border-style: dashed; opacity: .75; }
.hebdo-muette { color: var(--text-faint); font-style: italic; }

/* Liste des successions qui empêchent de supprimer une trame. Nommées et non
   comptées : « utilisée par 2 successions » dit qu'on ne peut pas, pas ce
   qu'il faut défaire. */
.trame-bloquants { list-style: none; margin: 8px 0; display: flex; flex-direction: column; gap: 4px; }
.trame-bloquants li {
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
    font-size: var(--t-sm);
}
.trame-bloquant-periode { color: var(--text-muted); font-size: var(--t-xs); }
.trame-bloquants-aide { font-size: var(--t-sm); color: var(--text-soft); margin-bottom: 10px; }

.ct-fiche-heures { font-family: var(--police-mono); }
.ct-fiche-ecart { font-family: var(--police-mono); color: var(--text-muted); }
/* Seul le DÉPASSEMENT est mis en couleur. Une trame qui ne remplit pas le
   contrat est utile à voir, elle n'a pas à alerter : deux signaux de même
   force ne se distinguent plus. */
.ct-fiche-depasse { color: var(--warn-fg); font-weight: 700; }
.ct-depassements { color: var(--warn-fg); font-weight: 600; }

.ct-fiche-ajout {
    height: 26px; padding: 0 8px;
    border: 1px dashed var(--border-strong); background: transparent;
    border-radius: 7px; font-family: inherit; font-size: var(--t-sm);
    color: var(--text-faint); cursor: pointer;
}
.ct-fiche-ajout:hover { border-color: var(--primary); color: var(--primary); }

@media (max-width: 900px) {
    .ct-outils-corps { grid-template-columns: 1fr; }
    .ct-fin-ligne { margin-left: 0; }
}

/* --- « Ajouter à mon calendrier » (Mon planning) ------------------------- */
/* Replié par défaut : c'est un réglage qu'on fait une fois, il n'a pas à
   occuper l'écran d'un salarié qui vient consulter ses horaires. */
.carte-calendrier {
    margin-top: 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.carte-calendrier > summary {
    padding: 12px 16px;
    cursor: pointer;
    list-style-position: inside;
}
.calendrier-corps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
    padding: 0 16px 16px;
    border-top: 1px solid var(--border);
    padding-top: 14px;
}
.calendrier-corps h3 {
    font-size: var(--t-sm); letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-faint); margin-bottom: 8px;
}
.calendrier-aide {
    font-size: var(--t-sm); color: var(--text-muted);
    margin-bottom: 10px; line-height: 1.5;
}
/* Chasse fixe et sélection au clic : le lien se copie à la main, sans API
   presse-papier — celle-ci exige un contexte sécurisé et se tait en cas de
   refus, ce qui donnerait un bouton qui ne dit pas qu'il n'a rien fait. */
.calendrier-lien {
    width: 100%;
    font-family: var(--police-mono);
    font-size: var(--t-sm);
    padding: 7px 9px;
    margin-bottom: 10px;
    background: var(--surface-form);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 7px;
}

/* Mise en forme des cellules, sortie des attributs `style` en ligne : ceux-ci
   l'emportent sur toute feuille et figeaient la table sur mobile — le passage
   en carte ne pouvait ni changer les marges internes ni retirer les filets. */
.table-cartes th,
.table-cartes td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
.table-cartes tbody tr:last-child td { border-bottom: 0; }
.table-cartes .cellule-actions { white-space: nowrap; }
/* ⚠️ Les boutons d'une cellule d'actions s'étirent à la hauteur du plus grand
   (le conteneur est un flex en `stretch`). Un `a.btn` est en `inline-block` :
   son texte se collait alors en HAUT d'une boîte de 44 px, à côté d'un bouton
   tactile de même hauteur dont le contenu, lui, est centré. Deux classes
   (0,2,0) pour passer devant `a.btn` (0,1,1) — sans toucher à cette règle-là,
   dont la spécificité est load-bearing ailleurs. */
.cellule-actions .btn {
    display: inline-flex; align-items: center; justify-content: center;
}
@media (max-width: 600px) {
    /* En carte, ces contrôles se touchent au doigt : 44 px, comme partout
       ailleurs sous ce point de rupture. « Réactiver » annule une décision de
       validation — il ne doit pas se manquer. ⚠️ Le sélecteur est de la partie :
       posé au milieu de boutons de 44 px, un menu de 24 px paraît décroché,
       et il se vise aussi mal. */
    .cellule-actions .btn,
    .cellule-actions select { min-height: 44px; }
    .cellule-actions .btn { padding-inline: 12px; }
}

/* Effectifs minimums — le pied d'une règle porte deux choses de nature
   différente : ce qui agit sur LA règle, et l'outil qui la recopie ailleurs.
   Mêlés dans une seule rangée, le sélecteur de jour se lisait comme un
   troisième bouton. */
.effectif-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.effectif-copie { display: inline-flex; align-items: center; gap: 6px; }
.effectif-copie-libelle { font-size: var(--t-xs); color: var(--text-muted); white-space: nowrap; }

@media (max-width: 600px) {
    /* L'outil de copie prend sa propre ligne : trois contrôles et un libellé
       ne tiennent pas à côté de deux boutons tactiles. */
    .effectif-copie { flex: 1 1 100%; margin-top: 2px; }
    .effectif-copie select { flex: 1 1 auto; min-width: 0; }
}

/* --- Tables en cartes empilées sous 600 px ------------------------------- */
/*
   Une table de cinq colonnes ne rentre pas sur un téléphone : la colonne
   Actions sort de l'écran et rien n'annonce qu'il faut faire défiler
   horizontalement pour l'atteindre. Sous 600 px, chaque ligne devient donc une
   carte — même geste que l'écran Équipe avec ses vues Puces et Colonnes.

   Le libellé de colonne est repris depuis `data-libelle` sur chaque cellule :
   une seule source de balisage, donc aucun risque que la table et les cartes
   divergent. C'est ce qui a fait préférer cette approche à un second gabarit
   par écran.

   ⚠️ Les sélecteurs sont qualifiés `table.table-cartes` : plusieurs de ces
   tables portent aussi `class="grid"`, dont les règles `table.grid td`
   (0,1,2) l'emportent sur une classe seule (0,1,1). Sans cette qualification,
   filets et largeur minimale des cellules survivaient au passage en carte.

   ⚠️ **`display: block` sur un `<table>` détruit sa sémantique** : les
   navigateurs retirent alors les rôles de l'arbre d'accessibilité, et un
   lecteur d'écran n'annonce plus ni lignes ni cellules. D'où les `role=`
   explicites exigés sur le balisage (`tests/test_tables_cartes.py`) — ils sont
   redondants tant que la table est une table, et indispensables dès qu'elle
   cesse de l'être.
*/
@media (max-width: 600px) {
    table.table-cartes,
    table.table-cartes tbody,
    table.table-cartes tr,
    table.table-cartes td { display: block; width: 100%; }

    /* L'en-tête n'a plus de sens en pile — le libellé descend dans chaque
       cellule. Masqué visuellement et non retiré : `display: none` le
       sortirait aussi de l'arbre d'accessibilité. */
    table.table-cartes thead {
        position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }

    table.table-cartes tr {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: 9px;
        padding: 11px 13px;
        margin-bottom: 9px;
    }
    table.table-cartes tr:last-child { margin-bottom: 0; }

    /* ⚠️ Le conteneur et la table gardaient leur fond, leur bordure et leurs
       coins arrondis : une **carte vide se dessinait derrière** la pile de
       cartes. Le `.grid-wrapper` ne sert qu'au défilement horizontal, dont une
       pile n'a pas besoin. `:has()` évite de toucher aux gabarits — et le jour
       où une table passe en cartes, elle est traitée d'office. */
    .grid-wrapper:has(table.table-cartes) {
        border: 0; background: none; border-radius: 0; overflow: visible;
    }
    table.grid.table-cartes { background: none; }

    table.table-cartes td {
        border: 0; padding: 4px 0; min-width: 0;
        /* ⚠️ `flex-wrap` indispensable : une valeur longue — « du 31/08/2026 au
           04/09/2026 » — ne peut pas revenir à la ligne sans lui et sort de la
           carte. `row-gap: 0` pour que l'enroulement ne creuse pas la ligne. */
        display: flex; flex-wrap: wrap; column-gap: 12px; row-gap: 0;
        align-items: baseline; justify-content: space-between;
        text-align: right;
    }
    table.table-cartes td::before {
        content: attr(data-libelle);
        flex: none; text-align: left;
        font-size: var(--t-2xs); font-weight: 700; letter-spacing: .06em;
        text-transform: uppercase; color: var(--text-faint);
    }
    /* Cellule sans libellé (colonne d'actions, colonne vide) : pas d'étiquette
       fantôme, et le contenu reprend toute la largeur. */
    table.table-cartes td:not([data-libelle])::before,
    table.table-cartes td[data-libelle=""]::before { content: none; }

    /* La première cellule fait le titre de la carte : pas d'étiquette, plus
       grande, alignée à gauche.

       ⚠️ `display: block` et non `flex` : le `space-between` des autres
       cellules y disperserait son contenu — la pastille de couleur à gauche,
       le nom au centre, la ville à droite. Le titre est une phrase, il doit
       couler. */
    table.table-cartes td.cellule-titre {
        display: block; text-align: left;
        font-size: var(--t-base); padding-bottom: 7px;
    }
    table.table-cartes td.cellule-titre::before { content: none; }

    /* Les actions se posent en pied de carte, séparées du contenu. */
    table.table-cartes td.cellule-actions {
        justify-content: flex-start; text-align: left;
        margin-top: 7px; padding-top: 9px;
        border-top: 1px solid var(--border);
    }

    /* Une ligne de section (« Pharmaciens titulaires ») n'est pas une carte. */
    table.table-cartes tr.ligne-section {
        background: none; border: 0; border-radius: 0;
        padding: 12px 0 4px; margin: 0;
    }
    table.table-cartes tr.ligne-section td { text-align: left; }

    /* Une ligne « aucun résultat » non plus. */
    table.table-cartes tr.ligne-vide { text-align: center; }

    /* Fin de mois : chaque collaborateur tient sur DEUX lignes — ses chiffres,
       puis le détail dépliable des changements. En carte, la seconde doit
       prolonger la première au lieu de former une carte orpheline sous elle.
       `:has(+ …)` ferme le bas de la carte des chiffres, le détail en reprend
       le haut. */
    table.table-cartes tr:has(+ tr.compteur-detail-row) {
        margin-bottom: 0;
        border-bottom: 0;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        padding-bottom: 4px;
    }
    table.table-cartes tr.compteur-detail-row {
        border-top: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        padding-top: 0;
    }
    table.table-cartes tr.compteur-detail-row td { padding: 0; }

    /* L'allocation porte deux saisies et un bouton : sur la largeur d'une
       carte ils tiennent en ligne, mais doivent pouvoir s'enrouler. */
    table.table-cartes td[data-libelle^="Allocation"] form { flex-wrap: wrap; }
}

/* --- Alertes d'effectif : jours en résumé, heures au dépliage ------------ */
/*
   Ces deux alertes déversaient une phrase de plusieurs centaines de caractères
   sur le planning — « lundi 17 : 6 membres attendus, 4 présents (14h00-18h00),
   lundi 17 : 5 membres… » — que personne ne lisait jusqu'au bout. Le jour est
   la clé de lecture : le résumé nomme les jours en cause, le détail horaire
   n'apparaît qu'au dépliage.

   `<details>` natif : accessibilité clavier gratuite, aucun script — et htmx
   réexécute les scripts du contenu qu'il remplace, ce qu'un dépliage maison
   supporterait mal.
*/
.alerte-jours {
    margin-bottom: 12px;
    border-radius: var(--radius);
    background: var(--warn-bg);
    color: var(--primary-soft-text);
    border: 1px solid var(--accent-border);
    font-size: var(--t-sm);
}
.alerte-jours > summary {
    padding: 9px 13px;
    cursor: pointer;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
/* ⚠️ `display: flex` sur un `<summary>` supprime son marqueur de dépliage :
   plus rien n'indiquait qu'on pouvait ouvrir le bloc. Le chevron est donc
   dessiné à la main — et `list-style: none` retire le marqueur résiduel des
   navigateurs qui le conservent, sinon on en aurait deux. */
.alerte-jours > summary { list-style: none; }
.alerte-jours > summary::-webkit-details-marker { display: none; }
.alerte-jours > summary::after {
    content: ""; margin-left: auto; align-self: center;
    width: 7px; height: 7px; flex: none;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform .15s ease;
}
.alerte-jours[open] > summary::after {
    transform: rotate(-135deg) translate(-2px, -2px);
}
@media (prefers-reduced-motion: reduce) {
    .alerte-jours > summary::after { transition: none; }
}
.alerte-jours > summary b { font-weight: 600; }
/* Les jours, en abrégé : c'est la première question qu'on se pose devant
   l'alerte, et cinq noms entiers reprendraient deux lignes sur un téléphone. */
.alerte-quand {
    font-family: var(--police-mono); font-size: var(--t-sm);
    opacity: .85; font-variant-numeric: tabular-nums;
}
.alerte-jours ul {
    list-style: none; margin: 0;
    padding: 2px 13px 12px;
    display: flex; flex-direction: column; gap: 7px;
}
.alerte-jours li {
    display: grid; grid-template-columns: 7.5em 1fr; gap: 10px;
    align-items: baseline;
}
.alerte-jour { font-weight: 600; }
.alerte-detail { display: flex; flex-direction: column; gap: 2px; }
.alerte-plage {
    font-family: var(--police-mono); font-size: var(--t-sm);
    font-variant-numeric: tabular-nums;
}
.alerte-plage b { font-weight: 700; }

@media (max-width: 600px) {
    /* Le nom du jour passe au-dessus : en colonne de 7,5 em il ne resterait
       qu'une centaine de pixels aux horaires, qui reviendraient à la ligne. */
    .alerte-jours li { grid-template-columns: 1fr; gap: 1px; }
}

/* ── Mon planning : grille façon agenda ──────────────────────────────────
   Une colonne par jour d'ouverture, les créneaux placés à l'heure. Les
   colonnes viennent de `--agenda-colonnes`, posé par le gabarit : cinq ou six
   selon que l'officine ouvre le samedi, et non sept — dans les ~340 px utiles
   d'un téléphone, sept colonnes en font 48, où « 09h00 » ne tient plus. */
.agenda-tete {
    display: flex; flex-wrap: wrap; gap: 12px;
    align-items: flex-start; justify-content: space-between;
    margin-bottom: 14px;
}
.agenda-titre h1 { margin-bottom: 2px; }
.agenda-identite { font-size: var(--t-sm); color: var(--text-muted); }
.agenda-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* `.btn.` et non `.agenda-btn-calendrier` seul : `a.btn`/`button.btn` posent
   `display: inline-block` à spécificité (0,1,1), qui l'emporterait sur une
   classe nue et recollerait l'icône au-dessus du libellé. */
.btn.agenda-btn-calendrier { display: inline-flex; align-items: center; gap: 7px; }

.agenda-barre {
    display: flex; flex-wrap: wrap; gap: 10px;
    align-items: center; justify-content: space-between;
    margin-bottom: 10px;
}
.agenda-nav { display: flex; align-items: center; gap: 6px; }
.agenda-nav .btn.btn-icone { font-size: var(--t-lg); line-height: 1; }
.agenda-periode { display: flex; align-items: baseline; gap: 8px; }
.agenda-dates { font-size: var(--t-sm); color: var(--text-muted); }
.agenda-total {
    font-family: var(--police-mono); font-size: var(--t-md); font-weight: 600;
    padding: 4px 10px; border-radius: var(--radius);
    background: var(--surface-sunk); color: var(--text);
}
.agenda-total small { font-weight: 400; color: var(--text-muted); }
.agenda-total.depasse { color: var(--danger-fg); background: var(--danger-bg); }

.agenda {
    --agenda-gouttiere: 42px;
    /* Hauteur de la grille horaire. Fixe et non proportionnelle à l'amplitude :
       une officine ouverte 10 h et une autre ouverte 12 h doivent se lire à la
       même échelle de lecture, seuls les blocs changent de proportion. */
    --agenda-hauteur: 620px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.agenda-entete,
.agenda-journee,
.agenda-corps {
    display: grid;
    grid-template-columns: var(--agenda-gouttiere) repeat(var(--agenda-colonnes), minmax(0, 1fr));
}
.agenda-entete { border-bottom: 1px solid var(--border); }
.agenda-jour-tete {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    padding: 7px 2px; border-left: 1px solid var(--border);
}
.agenda-jour-nom {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .05em;
    color: var(--text-faint);
}
.agenda-jour-num { font-size: var(--t-md); font-weight: 600; line-height: 1.1; }
.agenda-jour-tete.aujourdhui .agenda-jour-num {
    /* `--on-primary` et non `--primary-soft-text` : ce dernier est le texte
       posé sur le fond *pâle* de la primaire ; sur la primaire pleine il
       disparaît, et la pastille du jour s'affichait vide. */
    background: var(--primary); color: var(--on-primary);
    border-radius: 999px; width: 26px; height: 26px;
    display: flex; align-items: center; justify-content: center;
}
.agenda-jour-tete.ferme .agenda-jour-nom,
.agenda-jour-tete.ferme .agenda-jour-num { color: var(--text-faint); }

.agenda-gouttiere { border-right: 1px solid var(--border); }
.agenda-gouttiere-titre {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-faint); padding: 5px 4px; text-align: right;
}
.agenda-journee { border-bottom: 1px solid var(--border); background: var(--surface-sunk); }
.agenda-journee-cell {
    display: flex; flex-direction: column; gap: 3px;
    padding: 5px 3px; min-height: 26px;
    border-left: 1px solid var(--border);
}
.agenda-journee-cell.aujourdhui { background: var(--surface-hover); }
/* `white-space: normal` : les pastilles sont taillées pour une cellule de
   grille de planning, plus large. Ici « Congés payés (après-midi) » doit
   pouvoir revenir à la ligne au lieu d'être coupé. */
.agenda-journee-cell .absence-pill,
.agenda-journee-cell .hs-pill {
    font-size: var(--t-2xs); padding: 2px 5px; line-height: 1.25;
    white-space: normal; overflow-wrap: anywhere;
}

.agenda-corps { position: relative; height: var(--agenda-hauteur); }
.agenda-heures { position: relative; }
.agenda-heure {
    position: absolute; right: 5px; transform: translateY(-50%);
    font-family: var(--police-mono); font-size: var(--t-2xs); color: var(--text-faint);
}
/* Première et dernière graduation : centrées sur leur trait, elles sortiraient
   à moitié de la grille, que `.agenda` rogne. On les rentre du bon côté plutôt
   que d'ouvrir le débordement — l'écrêtage sert aussi aux blocs. */
.agenda-heure:first-child { transform: none; }
.agenda-heure:last-child { transform: translateY(-100%); }
.agenda-colonne { position: relative; border-left: 1px solid var(--border); }
.agenda-colonne.aujourdhui { background: var(--surface-hover); }
/* Un jour fermé sur lequel il y a tout de même quelque chose : hachuré, comme
   partout ailleurs pour « hors ouverture ». */
.agenda-colonne.ferme {
    background: repeating-linear-gradient(
        45deg, transparent, transparent 5px,
        var(--surface-sunk) 5px, var(--surface-sunk) 10px);
}
/* Bandeau « la garde de cette nuit déborde ici », épinglé au sommet de la
   colonne : l'information est hors échelle, elle ne prétend donc pas occuper
   une plage horaire. */
.agenda-avant {
    position: absolute; top: 0; left: 2px; right: 2px; z-index: 2;
    padding: 1px 4px; border-radius: var(--radius);
    background: var(--warn-bg); color: var(--warn-fg);
    font-size: var(--t-2xs); line-height: 1.3; white-space: nowrap; overflow: hidden;
}
.agenda-ligne {
    position: absolute; left: 0; right: 0; height: 0;
    border-top: 1px solid var(--border);
}
.agenda-maintenant {
    position: absolute; left: 0; right: 0; height: 0; z-index: 3;
    border-top: 2px solid var(--danger);
}
.agenda-bloc {
    position: absolute; left: 2px; right: 2px; z-index: 2;
    overflow: hidden;
    display: flex; flex-direction: column; gap: 1px;
    padding: 3px 5px;
    border-radius: var(--radius);
    font-size: var(--t-xs); line-height: 1.25;
}
.agenda-bloc-heures { font-family: var(--police-mono); font-weight: 600; }
.agenda-bloc-type { display: flex; align-items: center; gap: 3px; color: var(--text-muted); }
.agenda-bloc-type svg { width: 12px; height: 12px; flex: none; }
.agenda-bloc-note { color: var(--text-muted); font-style: italic; }
.agenda-bloc abbr { text-decoration: none; }

.agenda-vide {
    margin-top: 10px; font-size: var(--t-sm); color: var(--text-muted); text-align: center;
}
.agenda-totaux {
    display: flex; flex-wrap: wrap; gap: 20px;
    margin-top: 12px; padding: 10px 14px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); font-size: var(--t-sm);
}
.agenda-totaux small { color: var(--text-muted); }

/* La bulle d'export porte deux colonnes de texte : 560 px les tasse. */
.bulle-large { width: min(720px, calc(100vw - 32px)); }
.calendrier-corps-nu { border-top: none; padding: 0; }

@media (max-width: 767px) {
    /* Écran étroit : la grille se resserre, mais garde toutes ses colonnes —
       un défilement horizontal que rien n'annonce ne serait pas tenté. */
    .agenda { --agenda-gouttiere: 30px; --agenda-hauteur: 520px; }
    .agenda-jour-num { font-size: var(--t-md); }
    .agenda-bloc { font-size: var(--t-2xs); padding: 2px 3px; left: 1px; right: 1px; }
    /* Le nom du poste ne tient pas dans une colonne de 60 px : l'icône le dit,
       et le libellé reste dans l'arbre d'accessibilité. */
    .agenda-bloc-libelle {
        position: absolute; width: 1px; height: 1px;
        clip-path: inset(50%); overflow: hidden; white-space: nowrap;
    }
    .agenda-bloc-note { display: none; }
    .agenda-barre { gap: 8px; }
    .agenda-periode { order: -1; flex: 1 1 100%; }
    .agenda-annee { display: none; }
}

/* ── Mon planning, mode liste (« Planning » des agendas mobiles) ───────── */
.agenda-modes {
    display: inline-flex; border: 1px solid var(--border-strong);
    border-radius: var(--radius); overflow: hidden;
}
.agenda-modes a {
    padding: 5px 12px; font-size: var(--t-sm); text-decoration: none;
    color: var(--text-soft); background: var(--surface);
}
.agenda-modes a + a { border-left: 1px solid var(--border); }
.agenda-modes a.actif { background: var(--primary); color: var(--on-primary); }

.agenda-choix-employe { max-width: 100%; }

.agenda-liste {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); overflow: hidden;
}
.agenda-jour {
    display: grid; grid-template-columns: 52px minmax(0, 1fr);
    gap: 10px; padding: 10px 12px 10px 6px;
    border-top: 1px solid var(--border);
}
.agenda-jour:first-child { border-top: none; }
.agenda-jour.aujourdhui { background: var(--surface-hover); }
.agenda-jour-date {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    padding-top: 2px;
}
.agenda-jour.aujourdhui .agenda-jour-num {
    background: var(--primary); color: var(--on-primary);
    border-radius: 999px; width: 28px; height: 28px;
    display: flex; align-items: center; justify-content: center;
}
.agenda-jour-entrees { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.agenda-carte {
    display: flex; flex-direction: column; gap: 1px;
    padding: 7px 10px; border-radius: var(--radius);
    font-size: var(--t-sm); line-height: 1.3;
}
.agenda-carte-titre {
    display: flex; align-items: center; gap: 5px; font-weight: 600;
}
.agenda-carte-titre svg { width: 13px; height: 13px; flex: none; }
.agenda-carte-detail { font-size: var(--t-sm); color: var(--text-muted); }
.agenda-carte abbr { text-decoration: none; }
.agenda-carte-repos {
    color: var(--text-faint); font-style: italic; font-weight: 400;
    padding: 4px 10px;
}
.agenda-carte-garde { background: var(--warn-bg); color: var(--warn-fg); }
/* Trait de l'heure courante, glissé entre deux cartes : le point à gauche est
   le repère de Google Agenda, il évite de confondre le trait avec une bordure
   de carte. */
.agenda-liste-maintenant {
    position: relative; display: block; height: 0;
    border-top: 2px solid var(--danger); margin: 2px 0;
}
.agenda-liste-maintenant::before {
    content: ""; position: absolute; left: -4px; top: -4px;
    width: 7px; height: 7px; border-radius: 999px; background: var(--danger);
}

/* Libellé court, réservé au téléphone. Un pictogramme seul ne dit pas de quoi
   il retourne, et une infobulle n'existe pas sur un écran tactile : quand
   « Demander une absence » ne tient plus, « Absence » vaut mieux qu'un
   triangle nu. Le libellé long reste dans le DOM, masqué visuellement — c'est
   lui qui nomme le bouton pour un lecteur d'écran, et le court porte
   `aria-hidden` pour ne pas être annoncé deux fois. */
.btn-libelle-court { display: none; }

@media (max-width: 600px) {
    .btn-action .btn-libelle-court { display: inline; }
    /* `gap: 0` est posé sur `.btn.btn-action` en mobile pour l'icône seule :
       dès qu'un mot l'accompagne, il faut le rendre. */
    .btn.btn-action:has(.btn-libelle-court) { gap: 6px; }
}

@media (max-width: 767px) {
    .agenda-jour { grid-template-columns: 44px minmax(0, 1fr); gap: 8px; }
    .agenda-choix-employe { flex: 1 1 100%; }
}

/* ── Parcours de démarrage d'une officine ────────────────────────────────── */
.demarrage-intro { font-size: var(--t-sm); color: var(--text-muted); margin-bottom: 14px; max-width: 640px; line-height: 1.6; }
.demarrage-tete { margin-bottom: 16px; }
.demarrage-rang {
    font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
    color: var(--text-faint); font-weight: 700; margin-bottom: 6px;
}
.demarrage-fil { list-style: none; display: flex; flex-wrap: wrap; gap: 8px 16px; }
.demarrage-fil li {
    display: flex; align-items: center; gap: 7px;
    font-size: var(--t-sm); color: var(--text-faint);
}
.demarrage-fil li.actuelle { color: var(--text); font-weight: 600; }
.demarrage-fil li.faite { color: var(--text-muted); }
.demarrage-puce {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%; flex: none;
    border: 1px solid var(--border-strong); font-size: var(--t-xs); font-weight: 700;
}
.demarrage-fil li.actuelle .demarrage-puce { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.demarrage-fil li.faite .demarrage-puce { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }

.demarrage-carte {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 16px; margin-bottom: 14px;
}
.demarrage-champ {
    display: grid; grid-template-columns: 180px minmax(0, 1fr);
    gap: 10px; align-items: center; padding: 7px 0;
}
.demarrage-champ label { font-weight: 600; font-size: var(--t-sm); }
.demarrage-aide { font-size: var(--t-sm); color: var(--text-muted); line-height: 1.5; }
.demarrage-erreur { font-size: var(--t-sm); color: var(--danger); margin-top: 3px; }
.demarrage-actions { margin-top: 12px; }
.demarrage-soustitre { font-size: var(--t-md); margin: 16px 0 8px; }
.demarrage-membres { list-style: none; display: flex; flex-direction: column; }
.demarrage-membres li {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 8px; padding: 7px 0; font-size: var(--t-sm);
    border-bottom: 1px solid var(--border);
}
.demarrage-membres li:last-child { border-bottom: 0; }

/* Rappel tant que la mise en service n'est pas terminée. */
.demarrage-rappel {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 10px; margin-bottom: 14px;
}

@media (max-width: 600px) {
    .demarrage-champ { grid-template-columns: 1fr; gap: 4px; }
    /* Sept contrôles sur une rangée de 349 px : ils s'enroulent au hasard.
       Deux par ligne, l'email et le bouton sur toute la largeur. */
    .form-demarrage > .form-group { flex: 1 1 calc(50% - 4px); min-width: 0; }
    .form-demarrage > .form-groupe-email { flex: 1 1 100%; }
    .form-demarrage .form-case { flex: 1 1 100%; margin-bottom: 2px; }
    .form-demarrage .btn { flex: 1 1 100%; }
}
/* Pastille de couleur d'une catégorie importée : la teinte vient du PDF, elle
   permet de reconnaître les couleurs de l'ancien logiciel. */
.import-couleur {
    display: inline-block; width: 22px; height: 22px; flex: none;
    border-radius: 50%; border: 1px solid var(--border-strong);
}
.trames-import { font-size: var(--t-sm); margin-bottom: 12px; }
