/* badges (pastilles) — content is flex-centred so the pill height is even and
   the label sits dead-centre, exactly like the sidebar alert pastille.
   Colour code: the six canonical kinds below; status names are aliases. */
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 20px; padding: 1px 10px; border-radius: 20px;
  font-size: var(--fs-12); font-weight: var(--fw-semibold); line-height: 1;
  background: var(--chip); color: var(--muted); white-space: nowrap; vertical-align: middle;
}
/* canonical kinds */
.badge-neutral { background: var(--chip); color: var(--muted); }
.badge-info    { background: var(--info-wash); color: var(--info); }
.badge-ok      { background: var(--ok-wash); color: var(--ok-d); }
.badge-warn    { background: var(--warn-wash); color: var(--warn-d); }
.badge-bad     { background: var(--bad-wash); color: var(--bad-d); }
.badge-accent  { background: var(--accent-wash); color: var(--accent); }
/* status aliases -> kinds */
.badge-draft     { background: var(--chip); color: var(--muted); border: 1px solid var(--line); }
.badge-sent      { background: var(--info-wash); color: var(--info); }
.badge-accepted  { background: var(--ok-wash); color: var(--ok-d); }
.badge-refused,
.badge-cancelled { background: var(--bad-wash); color: var(--bad-d); }
.badge-invoiced  { background: var(--accent-wash); color: var(--accent); }
/* Status dot (ui.badge {dot}) : pastille de la couleur du texte en tête d'un badge de
   STATUT (devis/facture/commande/retard) → l'état se repère d'un coup d'œil. Réservé
   aux statuts (jamais une catégorie Kit/Service/source), donc le point dit « état ». */
.badge-dot::before { content: ""; flex-shrink: 0; width: 6px; height: 6px; border-radius: 50%; background: currentColor; margin-right: 6px; }

/* author pastille — initials of who performed/created the row. Round, centred,
   deterministic colour. Permanent stamp on every line; tracing at a glance. */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
  font-size: var(--fs-11); font-weight: var(--fw-bold); line-height: 1; letter-spacing: .02em;
  color: var(--white); background: var(--muted); vertical-align: middle;
  user-select: none;
}
.avatar.av-empty { background: var(--chip); color: var(--muted); font-weight: var(--fw-semibold); }
.av-0 { background: var(--av-0); } .av-1 { background: var(--av-1); }
.av-2 { background: var(--av-2); } .av-3 { background: var(--av-3); }
.av-4 { background: var(--av-4); } .av-5 { background: var(--av-5); }
.av-6 { background: var(--av-6); } .av-7 { background: var(--av-7); }
/* pastille + full name, for detail pages ("Créé par"). */
.author { display: inline-flex; align-items: center; gap: 8px; }

/* toasts — transient success/info feedback, bottom-right, auto-dismissing.
   Errors stay as a top banner (.flash-error) so they're read in context. */
.toasts { position: fixed; right: 18px; bottom: 18px; z-index: 60;
  display: flex; flex-direction: column; gap: 10px; max-width: min(360px, calc(100vw - 36px)); }
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--muted);
  border-radius: 10px; padding: 12px 14px; box-shadow: var(--shadow-pop);
  font-size: var(--fs-14); font-weight: var(--fw-medium); color: var(--ink);
  animation: toast-in .22s ease-out;
}
.toast.leaving { animation: toast-out .18s ease-in forwards; }
.toast .toast-ic { flex-shrink: 0; font-size: var(--fs-15); line-height: 1.3; }
.toast .toast-msg { flex: 1; min-width: 0; }
.toast .toast-x { background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: var(--fs-18); line-height: 1; padding: 0 2px; flex-shrink: 0; }
.toast .toast-x:hover { color: var(--ink); }
.toast-success { border-left-color: var(--ok); }
.toast-success .toast-ic { color: var(--ok); }
.toast-info { border-left-color: var(--info); }
.toast-info .toast-ic { color: var(--info); }
@keyframes toast-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(12px); } }
@media (prefers-reduced-motion: reduce) { .toast, .toast.leaving { animation: none; } }

/* confirm modal — one reusable dialog for every guarded/irreversible action
   (see app.js confirmModal + ui.confirmAttrs). Centred over a dimmed backdrop. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(20,25,40,.42); animation: modal-fade .14s ease-out;
}
.modal-overlay[hidden] { display: none; }
.modal {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-card);
  padding: 22px 24px; width: 100%; max-width: 420px; box-shadow: var(--shadow-pop);
  animation: modal-pop .16s ease-out;
}
.modal-title { margin: 0 0 8px; font-size: var(--fs-18); }
.modal-msg { margin: 0; color: var(--ink-2); font-size: var(--fs-14); line-height: 1.55; white-space: pre-line; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 22px; }
body.modal-open { overflow: hidden; }
@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-pop { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .modal-overlay, .modal { animation: none; } }

/* Right-click context menu (ui.contextMenu + ui.table row `ctx`; client
   public/app.js initContextMenu). A transparent full-viewport layer hosts ONE pop-up
   positioned at the cursor — the client clones the row's <template> into it. Reuses
   the modal pop shadow + radius and the brand-wash hover of the nav/menu surfaces;
   no backdrop (the page stays visible), closes on click-outside / Esc / scroll. */
.ctx-overlay { position: fixed; inset: 0; z-index: 90; }
.ctx-overlay[hidden] { display: none; }
.ctx-menu { position: fixed; min-width: 188px; max-width: 280px; padding: 5px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-pop); animation: ctx-pop .12s ease-out; }
.ctx-menu form { margin: 0; }
.ctx-item { display: flex; align-items: center; gap: 9px; width: 100%; box-sizing: border-box;
  padding: 7px 10px; border: 0; border-radius: 7px; background: transparent; cursor: pointer;
  font: inherit; font-size: var(--fs-14); color: var(--ink); text-align: left;
  text-decoration: none; white-space: nowrap; }
.ctx-item:hover, .ctx-item:focus-visible { background: var(--brand-wash); color: var(--brand-d); outline: 0; }
.ctx-danger { color: var(--bad); }
.ctx-danger:hover, .ctx-danger:focus-visible { background: var(--bad-wash); color: var(--bad); }
@keyframes ctx-pop { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ctx-menu { animation: none; } }

/* content dialog (ui.dialog + app.js initDialog) — reuses .modal-overlay/.modal,
   wider for rich content (forms), with a head row (title + close ✕) and a body. */
.modal-dialog { max-width: 560px; }
/* wide variant (data table content, e.g. the catalogue picker): near-viewport,
   own body scroll so the head/footer stay put while the result list scrolls. */
.modal-wide { max-width: min(1080px, 96vw); }
.modal-wide .modal-body { max-height: calc(90vh - 64px); overflow-y: auto; }
.modal-loading { color: var(--muted); font-size: var(--fs-14); padding: 24px 4px; text-align: center; }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.modal-head .modal-title { margin: 0; }
.modal-head-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.modal-x { background: transparent; border: 0; cursor: pointer; color: var(--muted);
  font-size: var(--fs-18); line-height: 1; padding: 2px 4px; border-radius: var(--radius); }
.modal-x:hover { background: var(--brand-wash); color: var(--brand-d); }

/* catalogue picker (ui.catalogPickerDialog + GET /catalog/picker + app.js
   initCatalogPicker): a wide dialog browsing the catalogue to multi-select +
   import. The modal keeps a FIXED height (it never shrinks when the table empties);
   the filter bar + tray stay put and ONLY the results table scrolls, so the table
   fills all remaining space. Submit + hint live in the dialog head (.modal-head-right). */
.modal-picker { height: min(86vh, 760px); display: flex; flex-direction: column; }
.modal-picker .modal-body { flex: 1 1 auto; min-height: 0; max-height: none; overflow: hidden; }
.catpick { display: flex; flex-direction: column; gap: 10px; height: 100%; }
.catpick-bar { flex: 0 0 auto; display: flex; flex-wrap: wrap;
  align-items: center; gap: 8px; padding-bottom: 10px; background: var(--panel);
  border-bottom: 1px solid var(--line); }
.catpick-q { flex: 1 1 220px; min-width: 200px; height: var(--control-h);
  padding: 0 10px; border: 1px solid var(--line); border-radius: var(--radius); }
.catpick-tray { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 6px; }
.catpick-tray[hidden] { display: none; }
.catpick-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 6px 2px 8px;
  font-size: var(--fs-12); background: var(--brand-wash); color: var(--brand-d);
  border: 1px solid var(--line); border-radius: 999px; }
.catpick-chip-x { background: transparent; border: 0; cursor: pointer; color: inherit;
  font-size: var(--fs-14); line-height: 1; padding: 0 2px; }
.catpick-chip-x:hover { color: var(--bad); }
.catpick-results { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.catpick-results.catpick-loading { opacity: .55; }
.catpick-hint { font-size: var(--fs-13); }
/* the picker reuses ui.table opts.select for its checkbox column, but that column
   is display:none until bulk-mode (.bulk-on) — the picker is always in selection
   mode, so reveal it unconditionally inside .catpick. */
.catpick .bulk-cell { display: table-cell; }
.catpick tbody tr:has(.bulk-cb:checked) { background: var(--brand-wash); }

