/* ==========================================================================
   DROPDOWN COMPONENTS
   ========================================================================== */

/* Entity Actions Dropdown */
.entity-actions { 
  position: relative;
}

/* MÊME pastille que `.kebab-btn` (tables.css), au pixel — 2026-08-02.
   Ce bouton est le dialecte HÉRITÉ du menu de ligne : son menu est
   `position: absolute` dans le cadre défilant (il se fait rogner dans un
   tableau large), là où `window.rowActions` détache le sien dans `<body>`.
   C'est une différence de MÉCANIQUE, qui justifie la migration page par page ;
   ce n'en était pas une d'APPARENCE, et pourtant il se dessinait avec une
   bordure grise permanente et un rayon de 8 px face au carré sans bordure du
   canon. Deux boutons visiblement différents pour la même action, à une page
   d'écart (/quotes vs /clients). Tant que la migration n'est pas finie, ils se
   ressemblent au moins. Ne PAS fusionner les deux sélecteurs : le jour où la
   dernière liste passe à `table_list()`, ce bloc se supprime d'un bloc. */
.entity-actions-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  min-height: 28px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.entity-actions-btn:hover {
  background: var(--gray-100);
  border-color: var(--gray-300);
  color: var(--text);
}

/* L'état ouvert du canon est `.kebab-btn[aria-expanded="true"]`. Le dialecte
   hérité, lui, ne touche PAS au bouton : `Dropdowns` (core.js) pose `.show` sur
   le `.entity-dropdown` FRÈRE. L'état ouvert se lit donc sur le frère — vérifié
   dans core.js, pas supposé. (`:has()` est déjà utilisé par tables.css.) */
.entity-actions-btn:has(+ .entity-dropdown.show) {
  background: var(--gray-200);
  border-color: var(--gray-300);
  color: var(--text);
}

.entity-actions-btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

.entity-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: var(--z-chrome);
  min-width: 160px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  overflow: visible;
  display: none !important;
}

.entity-dropdown.show {
  display: block !important;
}

.entity-dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-4);
  color: var(--text);
  font-size: 0.875rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
  border-radius: 0;
  white-space: nowrap;
}

.entity-dropdown-item:hover { 
  background: var(--gray-100); 
  color: var(--gray-900);
}

.entity-dropdown-item:first-child {
  border-radius: var(--radius) var(--radius) 0 0;
}

.entity-dropdown-item:last-child {
  border-radius: 0 0 var(--radius) var(--radius);
}

.entity-dropdown-item:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

.entity-dropdown-item.danger { 
  color: var(--error); 
}

.entity-dropdown-item.danger:hover {
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  background: #fef2f2;
  color: var(--error-hover);
}

/* Action indisponible : visible mais inerte, avec son motif sur une 2e ligne.
   Une action absente se lit comme un bug ; une action grisée qui dit pourquoi
   se lit comme une règle. Le hover ne réagit pas — rien à cliquer. */
.entity-dropdown-item.is-disabled,
.entity-dropdown-item[disabled] {
  color: var(--text-muted);
  cursor: not-allowed;
}

.entity-dropdown-item.is-disabled:hover,
.entity-dropdown-item[disabled]:hover {
  background: none;
  color: var(--text-muted);
}

.entity-dropdown-item.is-disabled svg { opacity: .5; }

.entity-dropdown-label {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.entity-dropdown-reason {
  font-size: 0.75rem;
  line-height: 1.2;
  color: var(--text-muted);
  white-space: normal;
}

.entity-dropdown-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}

/* ==========================================================================
   MENU KEBAB DÉTACHÉ — le menu de la colonne d'actions
   ==========================================================================
   Monté dans `<body>` et positionné en `position: fixed` par core.js
   (`RowActions.open`). C'est ce qui le distingue de `.entity-dropdown`, ancré
   en `absolute` dans la cellule : celui-là se fait rogner par le
   `overflow-x: auto` de `.table-container` dès qu'on fait défiler le tableau.

   Règles importées telles quelles depuis le `<style>` que `v2-core.js`
   injectait en JS (2026-08-01) — d'où les alias `.v2-*`, encore émis par 35
   fichiers du configurateur. Nom canonique : `.kebab-menu` / `.kebab-item`. */
.kebab-menu,
.v2-kebab-menu {
  position: fixed;
  z-index: var(--z-float);
  min-width: 220px;
  padding: 4px 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}

/* ── Menu contextuel CHERCHABLE (`contextMenu({search})`, 2026-09-10) ────────
   Variante OPT-IN : tout est scopé sous `.has-search`, un menu d'actions
   ordinaire ne change pas d'un pixel.

   ⚠ LA RANGÉE DE RECHERCHE NE SE SURCHARGE PAS. Elle doit être EXACTEMENT
   celle des filtres d'en-tête de colonne (`.ms-search-wrap` / `.ms-search`,
   multiselect.css) — même balisage, rendu par la même fonction
   (`window.msSearchRow`), et donc mêmes styles calculés. Une première version
   lui posait `position: sticky`, un fond et un rayon : mesuré au navigateur,
   ça faisait TROIS écarts de style calculé avec le champ de l'en-tête, pour un
   composant censé être le même. Ils sont retirés.

   Ce qu'il fallait, ce n'était pas surcharger la rangée : c'est reproduire la
   MÉCANIQUE du panneau ms. Là-bas, `.ms-panel` porte `overflow: hidden` (il
   clippe ses enfants aux coins arrondis, d'où une rangée sans rayon à elle) et
   c'est `.ms-options` qui défile, pas le panneau — la recherche est donc déjà
   hors de la zone défilante, sans `sticky` ni fond. Même chose ici.

   ⚠ La hauteur bornée, elle, reste indispensable : `.kebab-menu` n'a jamais eu
   de `max-height`, et avec 43 responsables la liste sortait du bas de l'écran —
   le clamp du flottant repositionne le menu, il ne le raccourcit pas. */
.kebab-menu.has-search {
  padding-top: 0;
  overflow: hidden;   /* comme .ms-panel : clippe la rangée aux coins arrondis */
}

.kebab-menu.has-search .kebab-options {
  max-height: 220px;  /* la valeur de .ms-options : même famille, même mesure */
  overflow-y: auto;
  padding: 4px 0;
}

/* Surlignage de la navigation au clavier — MÊME teinte que le survol, pour
   qu'un déplacement à la flèche et un déplacement à la souris se lisent
   pareil. */
.kebab-menu.has-search .kebab-item.is-active { background: var(--color-surface-2); }

.kebab-menu.has-search .kebab-empty {
  padding: 12px 14px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  text-align: center;
}

.kebab-item,
.v2-kebab-item {
  display: block;
  width: 100%;
  padding: 8px 14px;
  background: none;
  border: none;
  text-align: left;
  font-size: 0.8125rem;
  font-family: inherit;
  color: var(--text);
  white-space: nowrap;
  cursor: pointer;
}
.kebab-item:hover,
.v2-kebab-item:hover {
  background: var(--primary-light);
  color: var(--primary);
}
.kebab-item.danger,
.v2-kebab-item.v2-kebab-danger {
  color: var(--state-red-fg);
}
.kebab-item.danger:hover,
.v2-kebab-item.v2-kebab-danger:hover {
  background: var(--state-red-bg);
  color: var(--state-red-fg);
}
/* Une action indisponible se montre grisée avec son motif — jamais retirée en
   silence : une absence se lit comme un bug. */
.kebab-item.is-disabled,
.kebab-item[disabled],
.v2-kebab-item.v2-kebab-disabled,
.v2-kebab-item[disabled] {
  color: var(--gray-400);
  cursor: not-allowed;
}
.kebab-item.is-disabled:hover,
.kebab-item[disabled]:hover,
.v2-kebab-item.v2-kebab-disabled:hover,
.v2-kebab-item[disabled]:hover {
  background: none;
  color: var(--gray-400);
}
.kebab-divider,
.v2-kebab-divider {
  height: 1px;
  margin: 4px 0;
  background: var(--border);
}
.kebab-icon,
.v2-kebab-icon {
  display: inline-block;
  width: 1.4em;
  text-align: center;
  margin-right: 2px;
}
/* Item porteur d'une icône SVG (window.icon) : aligne le glyphe sur le texte. */
.kebab-item:has(svg) { display: flex; align-items: center; gap: var(--space-2); }
.kebab-item > svg { flex-shrink: 0; }

/* Dropdown Divider (hr) */
.entity-dropdown hr.dropdown-divider,
.dropdown-divider {
  height: 1px;
  background: var(--border);
  border: none;
  margin: 4px 0;
}

/* Entity Dropdown Submenu */
.entity-dropdown-submenu {
  position: relative;
}

.entity-dropdown-submenu-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--space-2) var(--space-4);
  color: var(--text);
  font-size: 0.875rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
}

.entity-dropdown-submenu-trigger:hover {
  background: var(--gray-100);
}

.entity-dropdown-submenu-content {
  position: absolute;
  left: auto;
  right: 100%;
  top: 0;
  min-width: 200px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  display: none;
  z-index: 1001;
}

.entity-dropdown-submenu:hover .entity-dropdown-submenu-content,
.entity-dropdown-submenu.active .entity-dropdown-submenu-content {
  display: block;
}

/* Submenu position adjustment for right edge */
@media (max-width: 1200px) {
  .entity-dropdown-submenu-content {
    left: auto;
    right: 100%;
  }
}

/* Submenu items */
.entity-dropdown-submenu-content .entity-dropdown-item {
  border-radius: 0;
}

.entity-dropdown-submenu-content .entity-dropdown-item:first-child {
  border-radius: var(--radius) var(--radius) 0 0;
}

.entity-dropdown-submenu-content .entity-dropdown-item:last-child {
  border-radius: 0 0 var(--radius) var(--radius);
}

/* Dropdown Container */
.dropdown-container {
  position: relative;
}

.dropdown-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* Dropdown Menu - Fixed visibility */
.dropdown-menu {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  min-width: 180px;
  z-index: 100; /* ordre local : au-dessus du contenu, sous le chrome */
  display: none; /* Use display instead of opacity/visibility */
}

.dropdown-menu.show {
  display: block; /* Simple show/hide */
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.625rem 1rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
  font-size: 0.875rem;
}

.dropdown-item:hover {
  background: var(--gray-50);
}

/* Searchable Dropdown Components */
.searchable-dropdown {
  position: relative;
  width: 100%;
}

.searchable-dropdown .form-input {
  position: relative;
  width: 100%;
}

.searchable-dropdown .form-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.searchable-dropdown .form-input.error {
  border-color: var(--error);
}

.searchable-dropdown .form-input.error:focus {
  border-color: var(--error);
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}

/* EFFACER LA SÉLECTION — posé par SearchableDropdown, jamais par un gabarit.
   Voir `_buildClearAffordance()` dans core.js : sans lui, « aucune valeur »
   était inatteignable sur les 48 champs du parc.

   ⚠ LA RÉSERVE DE PLACE EST PORTÉE PAR `.has-clear`, pas par `.searchable-dropdown`.
   Un `padding-right` inconditionnel aurait rogné 1,75rem sur TOUS les champs, y
   compris les vides — intenable dans la colonne « Destination » d'une ligne de
   facture (~110 px). Le bouton n'existe que lorsqu'il sert, et la place avec. */
.searchable-dropdown.has-clear .form-input {
  padding-right: 1.75rem;
}

.searchable-dropdown-clear {
  position: absolute;
  top: 50%;
  right: var(--space-2);
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm, 4px);
  background: transparent;
  color: var(--text-muted, var(--gray-500));
  cursor: pointer;
  line-height: 0;
}

/* `hidden` est posé par le composant : sans cette règle, le `display: flex`
   ci-dessus l'emporterait sur la feuille du navigateur et le « × » resterait
   peint sur un champ qui n'a rien à effacer. */
.searchable-dropdown-clear[hidden] {
  display: none;
}

.searchable-dropdown-clear:hover {
  background: var(--gray-100);
  color: var(--text);
}

.searchable-dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 50;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 var(--radius) var(--radius);
  box-shadow: var(--shadow-lg);
  max-height: 200px;
  overflow-y: auto;
  /* Suppress the inner-scroll rubber-band that briefly reveals page content
     behind the menu when scrolling fast to the limit. `none` kills the
     bounce visual; padding-bottom is a failsafe for engines that ignore it
     on inner scrollers, so any residual overshoot repaints on white. */
  overscroll-behavior: none;
  padding-bottom: 4px;
}

.searchable-dropdown-menu.hidden {
  display: none;
}

.searchable-dropdown-item {
  display: block;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  color: var(--text);
  background: var(--surface);
  border: none;
  text-align: left;
  cursor: pointer;
  font-size: 0.875rem;
  transition: background-color var(--transition);
  border-bottom: 1px solid var(--gray-100);
}

.searchable-dropdown-item:last-child {
  border-bottom: none;
}

.searchable-dropdown-item:hover,
.searchable-dropdown-item.highlighted {
  background: var(--gray-50);
}

.searchable-dropdown-item.selected {
  background: var(--primary-light);
  color: var(--primary);
  font-weight: 500;
}

/* Opt-in inline "create new" action (SearchableDropdown onCreate) — the last
   item in the menu, tinted primary to set it apart from the option list. */
.searchable-dropdown-item.searchable-dropdown-create {
  color: var(--primary);
  font-weight: 500;
}

.searchable-dropdown-item.searchable-dropdown-create:hover,
.searchable-dropdown-item.searchable-dropdown-create.highlighted {
  background: var(--primary-light);
}

.searchable-dropdown-empty,
.searchable-dropdown-no-results {
  padding: var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font-size: 0.875rem;
  font-style: italic;
}

.searchable-dropdown-loading {
  padding: 0.75rem;
  text-align: center;
  color: var(--text-muted);
}

.searchable-dropdown-icon {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}

/* Product Search Dropdown */
.product-search-container {
  position: relative;
}

.product-search-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  max-height: 200px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 var(--radius) var(--radius);
  box-shadow: var(--shadow-lg);
  z-index: 50;
}

.product-search-item {
  padding: 0.5rem;
  cursor: pointer;
  transition: background var(--transition);
}

.product-search-item:hover {
  background: var(--gray-50);
}

/* Component Search Dropdown */
.component-search-container {
  position: relative;
}

.component-search-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--gray-300);
  border-radius: 0.375rem;
  max-height: 200px;
  overflow-y: auto;
  z-index: var(--z-chrome);
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}

.component-search-item {
  padding: 0.75rem;
  cursor: pointer;
  border-bottom: 1px solid var(--gray-100);
  transition: background-color 0.15s;
}

.component-search-item:hover {
  background-color: var(--gray-50);
}

.component-search-item:last-child {
  border-bottom: none;
}

/* Condition Suggestions (for future autocomplete) */
.condition-suggestions {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  z-index: 50;
  max-height: 200px;
  overflow-y: auto;
}

.condition-suggestion-item {
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--gray-100);
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  font-size: 13px;
}

.condition-suggestion-item:hover {
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  background: #f8fafc;
}

.condition-suggestion-item:last-child {
  border-bottom: none;
}

/* Responsive dropdown positioning */
.entity-dropdown.position-left {
  right: 0;
  left: auto;
}

.entity-dropdown.position-right {
  left: 0;
  right: auto;
}

.dropdown-near-bottom {
  top: auto;
  bottom: 100%;
  transform: translateY(10px);
}

.dropdown-near-bottom.show {
  transform: translateY(0);
}

.dropdown-near-right {
  right: 0;
  left: auto;
}

.dropdown-near-left {
  left: 0;
  right: auto;
}

/* ==========================================================================
   STATUS POPOVER - Quick status change component
   ========================================================================== */

.status-popover {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-chrome);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  min-width: 280px;
  display: none;
}

.status-popover.show {
  display: block;
}

.status-popover-header {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.status-popover-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.status-popover-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  font-size: 0.8125rem;
  font-weight: 500;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: all var(--transition);
  white-space: nowrap;
}

.status-popover-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: var(--shadow);
}

.status-popover-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.status-popover-btn.current {
  cursor: default;
  box-shadow: inset 0 0 0 2px currentColor;
}

/* Status button variants */
.status-popover-btn.status-draft {
  background: var(--gray-100);
  color: var(--text-secondary);
  border-color: var(--gray-300);
}

.status-popover-btn.status-submitted {
  background: var(--state-blue-bg);
  color: var(--state-blue-fg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #93c5fd;
}

.status-popover-btn.status-under_review {
  background: var(--state-amber-bg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #b45309;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #fcd34d;
}

.status-popover-btn.status-approved {
  background: var(--state-green-bg);
  color: var(--state-pine-dot);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #6ee7b7;
}

.status-popover-btn.status-rejected {
  background: var(--state-red-bg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #b91c1c;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #fca5a5;
}

.status-popover-btn.status-converted_to_po {
  background: var(--state-indigo-bg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #4338ca;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #a5b4fc;
}

.status-popover-btn.status-expired {
  background: var(--gray-100);
  color: var(--text-muted);
  border-color: var(--gray-300);
}

.status-popover-btn.status-converted_to_invoice {
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  background: #dcfce7;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #15803d;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #86efac;
}

.status-popover-btn.status-unknown {
  background: var(--gray-100);
  color: var(--text-secondary);
  border-color: var(--gray-300);
}

/* Purchase Order status button variants */
.status-popover-btn.status-sent {
  background: var(--state-indigo-bg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #4338ca;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #a5b4fc;
}

.status-popover-btn.status-confirmed {
  background: var(--state-blue-bg);
  color: var(--state-blue-fg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #93c5fd;
}

.status-popover-btn.status-partial {
  background: var(--state-amber-bg);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #b45309;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #fcd34d;
}

.status-popover-btn.status-delivered {
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  background: #dcfce7;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #15803d;
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #86efac;
}

.status-popover-btn.status-invoiced {
  background: var(--state-green-bg);
  color: var(--state-pine-dot);
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  border-color: #6ee7b7;
}

.status-popover-btn.status-cancelled {
  background: var(--gray-100);
  color: var(--text-muted);
  border-color: var(--gray-300);
}

/* Rejection reason section */
.status-popover-rejection {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  display: none;
}

.status-popover-rejection.show {
  display: block;
}

.status-popover-rejection textarea {
  width: 100%;
  min-height: 60px;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.875rem;
  resize: vertical;
  margin-bottom: var(--space-2);
}

.status-popover-rejection textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.status-popover-rejection-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* Loading state */
.status-popover-btn.loading {
  pointer-events: none;
  opacity: 0.7;
}

.status-popover-btn.loading::after {
  content: '';
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
  margin-left: var(--space-1);
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Popover arrow */
.status-popover::before {
  content: '';
  position: absolute;
  top: -6px;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 12px;
  height: 12px;
  background: var(--surface);
  border-left: 1px solid var(--border);
  border-top: 1px solid var(--border);
}

/* Position adjustments for table cells */
td .status-popover {
  left: 0;
  transform: none;
}

td .status-popover::before {
  left: 20px;
  transform: rotate(45deg);
}

/* Mobile Responsive */
@media (max-width: 768px) {
  /* Searchable Dropdown Mobile */
  .searchable-dropdown-menu {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    max-height: 50vh;
  }
  
  /* Dropdown Mobile */
  .entity-dropdown {
    right: auto;
    left: 0;
    min-width: 140px;
  }
  
  .entity-actions:nth-last-child(-n+2) .entity-dropdown {
    right: 0;
    left: auto;
  }
  
  .dropdown-menu {
    right: auto;
    left: 0;
  }
}

/* ==========================================================================
   COLLAPSE DRAWER — reusable smooth expand / collapse
   PHP:
     <?= collapse_toggle('myToggle', 'myDrawer', 'Filtres', 'filter', ['badge_id' => 'cnt']) ?>
     <?= collapse_start('myDrawer') ?>  …content…  <?= collapse_end() ?>
   JS:
     btn.addEventListener('click', function() {
       var open = document.getElementById('myDrawer').classList.toggle('is-open');
       btn.classList.toggle('is-open', open);                // icon rotation
       btn.setAttribute('aria-expanded', String(open));
     });
   ========================================================================== */

.collapse-drawer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s cubic-bezier(.4, 0, .2, 1);
}

.collapse-drawer.is-open {
  grid-template-rows: 1fr;
}

.collapse-drawer-body {
  overflow: hidden;
}

/* Fade content in after container starts opening */
.collapse-drawer-body > :first-child {
  opacity: 0;
  transition: opacity 0.2s 0s;
}

.collapse-drawer.is-open > .collapse-drawer-body > :first-child {
  opacity: 1;
  transition: opacity 0.2s 0.1s;
}

/* Toggle-trigger button */
.collapse-trigger {
  transition: background 0.25s, color 0.25s, border-color 0.25s;
}

/* Slide-switch track (rendered via ::after) */
.collapse-trigger-switch {
  position: relative;
  display: inline-block;
  width: 32px;
  height: 18px;
  background: var(--gray-300, var(--gray-300));
  border-radius: 100px;
  transition: background 0.3s cubic-bezier(.4, 0, .2, 1);
  flex-shrink: 0;
  vertical-align: middle;
}

/* Knob */
.collapse-trigger-switch::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  transition: transform 0.3s cubic-bezier(.4, 0, .2, 1);
}

/* Open state — track turns green, knob slides right */
.collapse-trigger.is-open .collapse-trigger-switch {
  background: var(--accent, var(--accent));
}

.collapse-trigger.is-open .collapse-trigger-switch::after {
  transform: translateX(14px);
}

/* Icon rotation on open */
.collapse-trigger .icon-cont,
.collapse-trigger > svg:first-child {
  transition: transform 0.3s cubic-bezier(.4, 0, .2, 1);
}

.collapse-trigger.is-open .icon-cont,
.collapse-trigger.is-open > svg:first-child {
  transform: rotate(180deg);
}

/* Badge on trigger button (filter count, etc.) */
.collapse-trigger-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  border-radius: 100px;
  font-size: 0.6875rem;
  font-weight: 700;
  background: var(--accent, var(--accent));
  /* css-color-ok: cran historique sans token (rampe status-popover / nuances 50-300) */
  color: #fff;
  padding: 0 0.3rem;
}

/* Default panel styling (optional — apps can override via extra class) */
.collapse-drawer-panel {
  background: var(--gray-50, var(--gray-50));
  border: 1px solid var(--border, var(--border));
  border-radius: var(--radius, 0.5rem);
  padding: 1rem;
}
/* ── Popover ancré canonique — window.anchoredPopover (core.js) ───────────
   Monté dans <body> en fixed : rien ne le rogne. Le contenu est à
   l'appelant ; la coquille (surface, bord, ombre, empilement) est ici. */
.anchored-popover {
  position: fixed;
  z-index: var(--z-float);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  min-width: 180px;
  max-width: 360px;
}

/* ==========================================================================
   L'ENTONNOIR — le menu d'actions à DEUX NIVEAUX (2026-09-15)
   ==========================================================================

   Demande d'Olivier : « il y a beaucoup d'actions pour le devis, pour les BC
   et pour les factures — on rentre en entonnoir, d'abord Facturer / Vendre /
   Acheter, ensuite les sous-actions. »

   Mesuré avant d'écrire, sur l'onglet d'ARRIVÉE de chaque page (celui qui
   porte le menu COMPLET, cf. `settings-shell.php` : « l'état par défaut garde
   le menu complet quand il n'en déclare pas ») :

       /jobs/N   Synthèse    18 entrées, 5 groupes
       /clients  Synthèse    11 entrées, 4 groupes
       /fournisseurs À faire  7 entrées, 3 groupes

   ── POURQUOI UN DÉPLIAGE SUR PLACE ET PAS UN SOUS-MENU AU SURVOL ──────────

   `.entity-dropdown-submenu` existe dans cette feuille depuis des mois et n'a
   **aucun consommateur** dans les 7 apps (vérifié par balayage). Ce n'est pas
   un hasard : il se déploie en `position: absolute; right: 100%`, DANS un
   `.dropdown-menu` que core.js passe en `position: fixed` à l'ouverture. Le
   volet sortirait de la fenêtre sur la moitié des largeurs, et un survol ne
   s'atteint ni au clavier ni au doigt.

   Les deux niveaux vivent donc dans le MÊME panneau, et le second REMPLACE le
   premier — la largeur ne bouge pas, le menu reste sous son bouton, et
   `positionDropdown()` du socle n'a qu'à recalculer la hauteur.

   ⚠ LE RANG 1 N'EST PAS UN `.dropdown-item`, ET C'EST STRUCTUREL. core.js
   ferme le menu sur tout clic d'un `.dropdown-menu .dropdown-item` (règle du
   13-09 : « un item de menu ferme son menu »). Un rang qui DESCEND d'un cran
   n'a pas fini son travail : il porte `.dropdown-drill`, que cette règle ne
   voit pas. Lui donner la classe de l'item aurait rendu l'entonnoir inerte —
   sans erreur, le menu se refermant à chaque tentative. */

/* Une largeur un cran au-dessus du menu ordinaire : le rang 1 porte un
   libellé, un compte et un chevron, et le rang 2 des libellés plus longs
   (« Émettre une facture d'avance forfaitaire »). Mesurée sur le plus long des
   trois menus, pas devinée. */
.dropdown-menu[data-drill] { min-width: 264px; }

/* Les DEUX niveaux sont rendus ensemble par le serveur ; on ne fait que
   montrer et cacher. Rebâtir le second niveau en JS perdrait les `hidden`
   posés par les modules sur des gestes individuels — c'est exactement ce que
   `jobActionsMenuSync` refuse de faire pour la même raison. */
.dropdown-menu[data-drill] > .dropdown-panneau { display: none; }
.dropdown-menu[data-drill] > .dropdown-panneau.est-ouvert { display: block; }
.dropdown-menu[data-drill].drill-ouvert > .dropdown-niveau1 { display: none; }

/* Un menu ne doit jamais dépasser la fenêtre : `positionDropdown()` sait
   basculer vers le haut, il ne sait pas raccourcir. ⚠ Le plafond est posé sur
   les PANNEAUX et non sur le menu : sur le menu, il rognerait aussi le rang 1,
   qui tient toujours (5 rangs au plus). Il ne mord donc que là où il sert. */
.dropdown-menu[data-drill] > .dropdown-panneau {
  max-height: min(60vh, 420px);
  overflow-y: auto;
}

/* ── RANG 1 : une intention ─────────────────────────────────────────────── */
.dropdown-drill {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.625rem 1rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
  font-size: 0.875rem;
  color: var(--text);
}

.dropdown-drill:hover { background: var(--gray-50); }

.dropdown-drill:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

.dropdown-drill > svg { flex-shrink: 0; }

/* Le libellé POUSSE le reste au bord : c'est lui qui prend la place restante,
   pas une marge automatique sur chacun des deux suivants (qui se battraient). */
.dropdown-drill-label { flex: 1 1 auto; }

/* LE COMPTE EST UN APPOINT, PAS UN BADGE. Il dit combien de gestes attendent
   derrière le rang — l'information qui décide si ça vaut le clic. En pastille
   colorée, il se lirait comme une alerte : trois pastilles ambre en haut d'un
   menu diraient qu'il y a un problème là où il n'y a qu'un dénombrement. */
.dropdown-drill-compte {
  flex: 0 0 auto;
  font-size: 0.75rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.dropdown-drill-chevron { flex: 0 0 auto; color: var(--text-muted); }

/* ── RANG 2 : le retour, puis les gestes ────────────────────────────────── */

/* Le retour est COLLANT : un panneau qui défile (Vendre en porte 6, et le
   plafond ci-dessus mord à partir de ~10) laisserait sinon la sortie hors de
   vue, dans un menu dont l'échappement au clavier ferme TOUT le menu et pas
   seulement le niveau. */
.dropdown-retour {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.5rem 1rem;
  background: var(--surface);
  border: none;
  border-bottom: 1px solid var(--border);
  text-align: left;
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
  transition: background var(--transition), color var(--transition);
  position: sticky;
  top: 0;
  z-index: 1;
}

.dropdown-retour:hover { background: var(--gray-50); color: var(--text); }

.dropdown-retour:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

.dropdown-retour > svg { flex-shrink: 0; }

/* ==========================================================================
   LE PANNEAU « QUE VOULEZ-VOUS FAIRE ? » (2026-09-15)
   ==========================================================================

   La troisième forme des gestes d'une page, après le bouton nommé (un geste)
   et le menu « Actions » (deux ou plus). Elle ne sert QUE sur l'onglet
   d'arrivée d'un écran qui porte les gestes de tous les autres — /jobs/N sur
   sa Synthèse en porte dix-huit.

   Il vit dans une `.modal-overlay` : tout ce qui suit est scopé sous
   `.actions-panneau`, rien ne fuit vers les autres modales. */

.actions-panneau {
  display: grid;
  /* `auto-fit` + `minmax` et PAS un nombre de colonnes : le panneau sert des
     pages à trois colonnes comme à deux, et une page qui n'en remplit qu'une ne
     doit pas peindre deux vides.

     ⚠ 260 px EST MESURÉ, ET MON PREMIER CHIFFRE ÉTAIT FAUX. À 280 px, les
     QUATRE colonnes de `/jobs/N` (Vendre · Facturer · Acheter · Tickets) ne
     tiennent pas dans les 1 200 px de `.modal-xl` : la quatrième bascule sous
     la première, et le panneau DÉFILE — à 1280, 1440 ET 1680 px, mesuré au
     navigateur. Un écran qui répond « que voulez-vous faire ? » en cachant un
     quart de ses réponses sous la ligne de flottaison manque son objet.

     Ce que ça coûte, et c'est assumé : à 274 px de colonne, le plus long
     libellé (« Émettre une facture d'avance forfaitaire ») se replie sur DEUX
     lignes — 18 px de haut deviennent 36. Dans un panneau, un libellé qui se
     replie est ordinaire (l'aide en dessous se replie aussi) ; dans un MENU
     c'était un défaut, parce que rien n'y annonce qu'une entrée fait deux
     lignes. Le seuil ne descend pas plus bas : en dessous de 260 rien ne
     change (les quatre colonnes se partagent déjà la largeur bornée du
     `.modal-xl`), et un plancher plus petit ne ferait que rapprocher le jour où
     une page à deux groupes peint deux colonnes maigres. */
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-6);
  align-items: start;
}

/* Une seule colonne au format téléphone : `auto-fit` y descendrait déjà tout
   seul, mais la gouttière de 24 px entre deux blocs EMPILÉS est trop lâche —
   elle sépare des colonnes, pas des sections. */
@media (max-width: 640px) {
  .actions-panneau { gap: var(--space-5); }
}

.actions-panneau-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

/* L'INTENTION — le premier étage de l'entonnoir, et il se lit en un balayage.
   Même graisse et même taille que `section_header()` : c'est le même rôle. */
.actions-panneau-tete {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text);
}

.actions-panneau-tete > svg { flex-shrink: 0; color: var(--text-muted); }

.actions-panneau-aide {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--text-muted);
}

/* LA PROVENANCE — le deuxième axe. Un intertitre discret : il RANGE, il
   n'annonce pas. En graisse pleine il se disputerait la lecture avec le nom de
   l'intention juste au-dessus, qui est ce qu'on cherche en premier. */
.actions-panneau-famille {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.actions-panneau-famille-titre {
  padding: 0 var(--space-2);
  margin-bottom: 2px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* LE GESTE — libellé, puis la PHRASE qui le distingue de son voisin. C'est
   cette phrase qui justifie le panneau ; sans elle un menu fait mieux. */
.actions-panneau-geste {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  text-align: left;
  cursor: pointer;
  color: var(--text);
  text-decoration: none;
  transition: background var(--transition), border-color var(--transition);
}

.actions-panneau-geste:hover {
  background: var(--gray-50);
  border-color: var(--border);
}

.actions-panneau-geste:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}

/* L'icône s'aligne sur la PREMIÈRE LIGNE du libellé, pas sur le centre du bloc :
   avec une aide de deux lignes, un centrage la ferait flotter au milieu du
   vide, en face de rien. */
.actions-panneau-geste > svg {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--text-muted);
}

.actions-panneau-geste:hover > svg { color: var(--text); }

.actions-panneau-geste-texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.actions-panneau-geste-label {
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.3;
}

.actions-panneau-geste-aide {
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--text-muted);
}
