/* ============================================================
   components.css — Reusable UI components
   ============================================================ */

/* ── Avatar ───────────────────────────────────────────────── */
.ava {
  width: 34px; height: 34px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  /* e-Dent style: thin white ring + soft shadow that lifts the chip
     off the surface. Works on photos AND initials. */
  border: 2px solid #fff;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, .08),
    0 2px 6px rgba(0, 0, 0, .06);
  transition: box-shadow .15s ease, transform .15s ease;
}

/* Hover lift — subtle, only on cards that already have hover state */
.pc:hover .ava,
.pat-result:hover .ava {
  box-shadow:
    0 2px 4px rgba(0, 0, 0, .10),
    0 4px 12px rgba(0, 0, 0, .08);
  transform: translateY(-1px);
}

/* When the avatar has a real photo, hide the inline color background
   completely — the <img> already fills the disc. The fallback span
   inserted by onerror keeps the inline background style, so initials
   work as before in case of image error.
   `object-fit: contain` keeps the WHOLE photo visible (no cropping),
   even at small sizes — like the e-Dent avatar style. White background
   fills any empty space when the photo isn't square — most photos have
   a white/neutral background, so the empty bands blend in invisibly. */
.ava-photo { background-color: #ffffff !important; }
.ava-photo > img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}

/* ── Role badges ──────────────────────────────────────────── */
.role-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 20px;
  margin-top: 3px;
  font-weight: 500;
  text-transform: capitalize;
}
.role-dentiste   { background: var(--primary-soft); color: var(--primary-dark); }
.role-assistante { background: rgba(59, 130, 246, .15); color: var(--blue); }
.role-admin      { background: rgba(168, 85, 247, .15); color: var(--purple); }

/* ── Buttons ──────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: var(--r);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  border: none;
  transition: all .12s;
  white-space: nowrap;
}
.bp { background: var(--teal); color: #fff; }
.bp:hover { filter: brightness(1.08); }
/* Softer version of bp for less aggressive UI contexts (patient cards, tables, etc.) */
.bp-soft {
  background: var(--primary-soft);
  color: var(--primary-dark);
  border: 1px solid rgba(var(--primary-rgb, 255, 110, 22), .35);
  padding: 6px 12px;
  border-radius: var(--r);
  font-weight: 500;
  font-size: 12px;
  cursor: pointer;
  transition: all .12s;
  white-space: nowrap;
}
.bp-soft:hover {
  background: rgba(var(--primary-rgb, 255, 110, 22), .18);
  border-color: var(--primary);
  color: var(--primary-dark);
}
.bg { background: transparent; color: var(--text2); border: 1px solid var(--border2); }
.bg:hover { background: var(--surface); color: var(--text); }
.bd { background: rgba(239, 68, 68, .1); color: var(--red); border: 1px solid rgba(239, 68, 68, .25); }
.bi {
  padding: 7px;
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text2);
  border: 1px solid var(--border);
  cursor: pointer;
  font-size: 15px;
  transition: all .12s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.bi:hover { background: var(--surface2); color: var(--text); }

/* ── Forms ────────────────────────────────────────────────── */
.fg { margin-bottom: 13px; }
.fl {
  display: block;
  font-size: 11px;
  color: var(--text3);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .7px;
  margin-bottom: 4px;
}
.fi, .fs, .fta {
  width: 100%;
  background: var(--input);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 9px 12px;
  color: var(--text);
  outline: none;
  transition: border-color .12s;
}
.fi:focus, .fs:focus, .fta:focus { border-color: var(--teal); }
.fs option { background: var(--bg2); }
.fta { resize: vertical; min-height: 100px; }
.fr2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.fr3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }

/* Color picker swatches */
.crow { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 5px; }
.csw {
  width: 26px; height: 26px;
  border-radius: 7px;
  cursor: pointer;
  border: 2.5px solid transparent;
  transition: all .12s;
}
.csw.sel {
  border-color: var(--text);
  transform: scale(1.18);
  box-shadow: 0 0 0 3px rgba(45, 212, 191, .3);
}

/* Recurrence / choice options */
.rcopts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.ro {
  padding: 4px 11px;
  border-radius: 20px;
  font-size: 12px;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text2);
  transition: all .12s;
}
.ro.on {
  background: rgba(45, 212, 191, .15);
  border-color: var(--teal);
  color: var(--teal);
}

/* ── Toggle switch ────────────────────────────────────────── */
.tog {
  width: 38px; height: 21px;
  background: var(--border2);
  border-radius: 11px;
  cursor: pointer;
  position: relative;
  transition: background .18s;
  flex-shrink: 0;
}
.tog.on { background: var(--teal); }
.tog::after {
  content: '';
  position: absolute;
  width: 15px; height: 15px;
  background: white;
  border-radius: 50%;
  top: 3px; left: 3px;
  transition: transform .18s;
}
.tog.on::after { transform: translateX(17px); }

/* ── Tags / pills ─────────────────────────────────────────── */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 500;
}
.tag-green  { background: rgba(34, 197, 94, .13); color: #16a34a; }
.tag-blue   { background: rgba(59, 130, 246, .13); color: #2563eb; }
.tag-amber  { background: rgba(245, 158, 11, .13); color: #d97706; }
.tag-red    { background: rgba(239, 68, 68, .13); color: #dc2626; }
.tag-teal   { background: var(--primary-soft); color: var(--primary-dark); }
.tag-purple { background: rgba(168, 85, 247, .13); color: #9333ea; }

/* ── Patient / waiting cards ──────────────────────────────── */
.pgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 10px;
}
.pc {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r2);
  padding: 14px;
  cursor: pointer;
  transition: all .15s;
  position: relative;
}
.pc:hover {
  border-color: var(--teal);
  transform: translateY(-1px);
  box-shadow: var(--sh);
}
.pc.blacklisted {
  border-color: var(--red);
  opacity: .85;
}
.pc.blacklisted::after {
  content: '🚫 BLACKLISTÉ';
  position: absolute;
  top: 10px; right: 10px;
  background: var(--red);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 4px;
  letter-spacing: .5px;
}

/* ── Qualifiers (patient/appt icons) ──────────────────────── */
.qualifiers {
  display: inline-flex;
  gap: 3px;
  margin-left: 6px;
  vertical-align: middle;
}
.qual {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  font-size: 10px;
  cursor: help;
}

/* ── Search box ───────────────────────────────────────────── */
.sbox {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 9px 14px;
}
.sbox input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  color: var(--text);
  font-size: 14px;
}
.sbox input::placeholder { color: var(--text3); }

/* ── Empty state ──────────────────────────────────────────── */
.empty {
  text-align: center;
  padding: 50px 20px;
  color: var(--text3);
}
.eico { font-size: 42px; margin-bottom: 12px; opacity: .3; }
.empty h3 {
  font-family: var(--font-serif);
  font-size: 18px;
  font-style: italic;
  color: var(--text2);
}

/* ── Toasts ───────────────────────────────────────────────── */
.toasts {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 99999;
  display: flex;
  flex-direction: column;
  gap: 7px;
  pointer-events: none;
}
.toast {
  background: var(--surface2);
  border: 1px solid var(--border2);
  border-radius: var(--r2);
  padding: 11px 15px;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 8px;
  animation: slu .2s ease;
  box-shadow: var(--sh);
  max-width: 300px;
  font-weight: 500;
  pointer-events: auto;
}
.tok { border-left: 3px solid var(--green); }
.terr { border-left: 3px solid var(--red); }
.tinf { border-left: 3px solid var(--blue); }
.twrn { border-left: 3px solid var(--amber); }

@keyframes fdi { from { opacity: 0; } to { opacity: 1; } }
@keyframes slu {
  from { transform: translateY(10px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* ── Divider ──────────────────────────────────────────────── */
.div { height: 1px; background: var(--border); margin: 12px 0; }

/* ── Provider / filter chips ──────────────────────────────── */
.prov-filter {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
}
.pchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: 20px;
  font-size: 12px;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition: all .12s;
  user-select: none;
  background: var(--surface);
  font-weight: 500;
}
.pcdot { width: 7px; height: 7px; border-radius: 50%; }

/* ── Site filter (multi-site selector) ───────────────────────
 *
 * Sélecteur de site : visible uniquement si le cabinet a plusieurs
 * sites configurés. Apparaît sous le prov-filter, même style visuel
 * que les chips praticiens.
 */
.site-filter {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
}
.site-filter-label {
  font-size: 15px;
  margin-right: 2px;
  flex-shrink: 0;
}
.site-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: 20px;
  font-size: 12px;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition: all .12s;
  user-select: none;
  background: var(--surface);
  font-weight: 500;
  color: var(--text2);
}
.site-chip:hover {
  background: var(--surface2);
  border-color: var(--border2);
}
.site-chip-on {
  font-weight: 600;
}
.site-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ── Granularity strip ────────────────────────────────────── */
.gran-strip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
  flex-wrap: wrap;
}
.gran-opt {
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 11px;
  cursor: pointer;
  border: 1px solid var(--border2);
  background: var(--surface);
  color: var(--text2);
  transition: all .12s;
  user-select: none;
  font-weight: 500;
}
.gran-opt.on {
  background: rgba(45, 212, 191, .15);
  border-color: var(--teal);
  color: var(--teal);
  font-weight: 600;
}

/* ── Settings sections ────────────────────────────────────── */
.ss {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r2);
  margin-bottom: 14px;
  overflow: hidden;
  max-width: 620px;
}
.sh {
  padding: 13px 18px;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-serif);
  font-size: 16px;
  font-style: italic;
  color: var(--text);
}
.sr {
  padding: 12px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border);
  gap: 12px;
}
.sr:last-child { border: none; }
.sl3 { font-size: 13px; font-weight: 500; color: var(--text); }
.sd { font-size: 11px; color: var(--text3); margin-top: 2px; }

/* ── Status indicator (FTP, sync) ─────────────────────────── */
.st-ind {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 20px;
}
.st-ok { background: rgba(34, 197, 94, .12); color: var(--green); }
.st-idle { background: var(--surface); color: var(--text3); }
.st-err { background: rgba(239, 68, 68, .12); color: var(--red); }

/* ── Stats ────────────────────────────────────────────────── */
.sc {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r2);
  padding: 16px;
}
.snum {
  font-family: var(--font-serif);
  font-size: 38px;
  font-style: italic;
  line-height: 1;
}

/* Responsive */
@media (max-width: 768px) {
  .fr2, .fr3 { grid-template-columns: 1fr; }
  .pgrid { grid-template-columns: 1fr; }
}

/* Settings anchor chips — quick jump menu sticking at top of settings page */
.anchor-chip {
  display: inline-block;
  padding: 5px 11px;
  font-size: 12px;
  color: var(--text2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  text-decoration: none;
  transition: all .15s;
  white-space: nowrap;
}
.anchor-chip:hover {
  background: var(--bg2);
  color: var(--primary);
  border-color: var(--primary);
}
/* Give scroll-anchored sections some top padding so the sticky
   menu doesn't cover them when jumped to */
.ss[id^="s-"] {
  scroll-margin-top: 60px;
}

/* Stats page grid: force 4 columns on desktop, 2 on tablet, 1 on mobile */
.stats-grid-main {
  grid-template-columns: repeat(4, minmax(220px, 1fr)) !important;
}
@media (max-width: 1100px) {
  .stats-grid-main {
    grid-template-columns: repeat(2, minmax(220px, 1fr)) !important;
  }
}
@media (max-width: 640px) {
  .stats-grid-main {
    grid-template-columns: 1fr !important;
  }
}

/* Settings anchor chips — stronger styles so browsers never fall
   back to default <a> blue underline (which we saw slipping through
   when the CSS file wasn't fully loaded yet). */
#settingsAnchorMenu a.anchor-chip,
.anchor-chip {
  display: inline-block !important;
  padding: 6px 14px !important;
  font-size: 12px !important;
  color: var(--text2) !important;
  background: var(--card) !important;
  border: 1px solid var(--border) !important;
  border-radius: 20px !important;
  text-decoration: none !important;
  transition: all .15s !important;
  white-space: nowrap !important;
  font-weight: 500 !important;
  box-shadow: 0 1px 2px rgba(0,0,0,.04);
}
#settingsAnchorMenu a.anchor-chip:hover,
.anchor-chip:hover {
  background: var(--bg2) !important;
  color: var(--primary) !important;
  border-color: var(--primary) !important;
  transform: translateY(-1px);
}

/* Provider chips: give them a subtle but visible background so they
   don't look crammed together on the desktop toolbar */
.prov-filter {
  gap: 8px !important;
  padding: 10px 16px !important;
}
.pchip {
  padding: 5px 12px !important;
  background: var(--card) !important;
  border: 1px solid var(--border) !important;
  box-shadow: 0 1px 2px rgba(0,0,0,.03);
}
.pchip[data-pc="clear"] {
  background: var(--surface) !important;
  color: var(--text3);
}

/* Mobile safety: the sidebar overlay must ONLY be visible when the
   sidebar is explicitly open (.ovl.show). This overrides any stale
   CSS that may leave it covering the screen. */
@media (max-width: 768px) {
  .ovl:not(.show) {
    display: none !important;
    pointer-events: none !important;
    visibility: hidden !important;
    opacity: 0 !important;
  }
}

/* When .gran-strip is used inline with margin/padding overrides (settings
   page for the SMS reminder day picker), kill the bottom border + bg
   color which only make sense on the calendar toolbar. */
.gran-strip[style*="padding:0"],
.gran-strip[style*="padding: 0"] {
  border-bottom: none !important;
  background: transparent !important;
}

/* Desktop default: provider toggle button is hidden (only appears on mobile
   via the @media block below). */
.prov-toggle { display: none; }

/* Mobile: show the providers toggle pill and hide the legend by default.
   Tap the pill to expand the legend (.mobile-open class added by JS).
   These rules must live here (not just in responsive.css) because
   responsive.css isn't currently linked from index.html. */
@media (max-width: 768px) {
  .prov-toggle.show-mobile {
    display: inline-flex !important;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    margin: 4px 0 6px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 12px;
    color: var(--text2);
    cursor: pointer;
    min-height: 32px;
    flex-shrink: 0;
    align-self: flex-start;
  }
  .prov-toggle.active {
    background: var(--bg2);
    color: var(--primary);
    border-color: var(--primary);
  }
  .prov-toggle-count {
    background: var(--primary);
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 10px;
    min-width: 18px;
    text-align: center;
  }
  /* Providers legend collapsed by default on mobile; only shown when
     .mobile-open is added by the toggle handler. */
  .prov-filter {
    display: none !important;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 10px;
    margin: 0 0 6px;
    background: var(--surface);
    border-radius: 12px;
    max-height: 40vh;
    overflow-y: auto;
  }
  .prov-filter.mobile-open {
    display: flex !important;
  }
  .prov-filter .pchip {
    font-size: 12px;
    padding: 5px 10px;
  }
}

/* Responsive layout additions for mobile — spacing between view buttons
   (Jour/3j/Semaine/Liste) on small screens. FullCalendar groups them
   tight by default. */
@media (max-width: 768px) {
  .fc .fc-header-toolbar .fc-button-group .fc-button {
    margin-right: 4px !important;
    border-radius: 18px !important;
  }
  .fc .fc-header-toolbar .fc-button-group .fc-button:last-child {
    margin-right: 0 !important;
  }
}

/* ── Waiting list Kanban responsive ─────────────────────────
   On mobile the 3-column grid is too cramped. Switch to a
   horizontally scrollable row of fixed-width columns so each
   card still has readable space. */
@media (max-width: 768px) {
  .wait-kanban {
    display: flex !important;
    grid-template-columns: none !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 6px !important;
    padding-bottom: 8px;
    /* Proximity (not mandatory): snap only when user is close to an
       anchor, allowing them to stop freely on the middle column. */
    scroll-snap-type: x proximity;
    scroll-padding-left: 10px;
  }
  .wait-col {
    flex: 0 0 98vw;
    max-width: 400px;
    scroll-snap-align: center;
    min-height: 200px !important;
  }
  /* Block native mobile behaviors on cards:
     - touch-callout: no system menu on long-press (iOS)
     - user-select: no text selection (causes "zoom" on long-press)
     - tap-highlight: no blue/gray flash on tap (iOS/Android)
     - touch-action: pan-x pan-y → allow swipe in BOTH directions
       (we need pan-x so the kanban scrolls horizontally over cards) */
  .wait-card,
  .wait-card * {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  .wait-card {
    touch-action: pan-x pan-y;
  }
}

/* ── Kanban select-and-tap UI (mobile) ──────────────────────
   Two-step move: long-press a card → it becomes "selected", then
   tap any column → card is moved. Much more forgiving than drag.

   .selected-for-move          → the card currently waiting to be placed
   .move-target-candidate      → all columns during selection mode
   #wait-move-hint             → floating toast at the bottom */
.wait-card {
  transition: box-shadow .15s ease-out, border-color .15s ease-out, transform .15s ease-out;
}
.wait-card.selected-for-move {
  border: 2px solid var(--primary) !important;
  box-shadow: 0 4px 18px rgba(var(--primary-rgb, 255, 110, 22), .4);
  position: relative;
  z-index: 2;
}
/* When the card is in "selected-for-move" mode, disable pointer
   interactions on its children. This prevents iOS from detecting
   long-press on the inner text and triggering its native zoom /
   text-selection menu while the user keeps their finger on the
   selected card. */
.wait-card.selected-for-move * {
  pointer-events: none !important;
  -webkit-user-select: none !important;
  user-select: none !important;
}
.wait-card.selected-for-move::after {
  content: "✓";
  position: absolute;
  top: -6px; right: -6px;
  width: 22px; height: 22px;
  background: var(--primary);
  color: #fff;
  border-radius: 50%;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0,0,0,.2);
}
.wait-col {
  transition: background-color .2s, border-color .2s, outline .2s;
}
.wait-col.move-target-candidate {
  outline: 2px dashed rgba(var(--primary-rgb, 255, 110, 22), .45);
  outline-offset: -4px;
  background: rgba(var(--primary-rgb, 255, 110, 22), .04);
  cursor: pointer;
}
.wait-col.move-target-candidate:hover,
.wait-col.move-target-candidate:active {
  outline-color: var(--primary);
  background: rgba(var(--primary-rgb, 255, 110, 22), .1);
}

#wait-move-hint {
  position: fixed;
  left: 50%;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  background: var(--text);
  color: #fff;
  padding: 10px 14px 10px 18px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,.28);
  z-index: 9998;
  animation: smilo-hint-appear .2s ease-out;
}
#wait-move-hint button {
  background: rgba(255,255,255,.15);
  border: none;
  color: #fff;
  width: 24px; height: 24px;
  border-radius: 50%;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
#wait-move-hint button:hover {
  background: rgba(255,255,255,.28);
}
@keyframes smilo-hint-appear {
  from { transform: translateX(-50%) translateY(20px); opacity: 0; }
  to   { transform: translateX(-50%) translateY(0);     opacity: 1; }
}

/* Stats reorder: hide the ↑↓ buttons on mobile (long-press drag is
   the gesture there). Desktop keeps them visible for accessibility. */
@media (max-width: 768px) {
  .stat-move-btns { display: none !important; }
  .stat-card-wrap { touch-action: pan-y; }
}

/* ── Users table responsive ─────────────────────────────────
   On mobile the 6-column table overflows and gets cropped. Switch
   to a stacked card layout where each row becomes a block. */
@media (max-width: 768px) {
  .datatable { display: block; }
  .datatable thead { display: none; }
  .datatable tbody { display: block; }
  .datatable tr {
    display: block;
    border: 1px solid var(--border);
    border-radius: var(--r2);
    margin-bottom: 10px;
    padding: 10px 12px;
    background: var(--card);
  }
  .datatable td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 4px 0 !important;
    border-bottom: 1px dashed var(--border) !important;
    gap: 10px;
  }
  .datatable td:last-child { border-bottom: none !important; }
  /* Add label via data-label attribute if available, otherwise just show the value */
  .datatable td::before {
    content: attr(data-label);
    font-size: 10px;
    color: var(--text3);
    text-transform: uppercase;
    letter-spacing: .5px;
    font-weight: 600;
    flex-shrink: 0;
  }
}

/* ── Settings tabs: dropdown style always ──────────────────────
 *
 * With 13+ sections (Apparence, Agenda, Booking, Praticiens, Soins,
 * Jours fériés, SMS, Email, Backup, iCal, Sécurité, Permissions, RGPD…),
 * the horizontal tab bar overflows on most screen widths and forces
 * horizontal scroll — which users never notice. We now use the
 * "title + dropdown" pattern at all widths: cleaner, scales to any
 * number of sections, and keeps the active section visible at all times.
 *
 * The desktop element stays in the DOM as a fallback in case JS is
 * partially loaded, but is hidden by default.
 */
.settings-tabs-desktop  { display: none; }
.settings-tabs-mobile   { display: flex; }

/* Stats KPI row: 8 columns on wide screens, degrading gracefully */
.stats-kpi-row {
  grid-template-columns: repeat(8, minmax(0, 1fr)) !important;
}
@media (max-width: 1200px) {
  .stats-kpi-row { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; }
}
@media (max-width: 640px) {
  .stats-kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}

/* ── FullCalendar mobile layout ─────────────────────────────
   Single source of truth for FC toolbar on mobile.
   responsive.css is NOT loaded in index.html (yet), so everything
   lives here for now. */
@media (max-width: 768px) {

  /* HEADER toolbar: [< >]  title  [Aujourd'hui] on ONE line */
  .fc .fc-header-toolbar.fc-toolbar {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: 6px !important;
    margin: 0 0 4px !important;
    padding: 4px 6px !important;
    min-height: 0 !important;
  }
  .fc .fc-header-toolbar .fc-toolbar-chunk {
    display: flex !important;
    flex: 0 0 auto !important;
    width: auto !important;
    align-items: center !important;
    gap: 4px !important;
    margin: 0 !important;
    order: initial !important;
  }
  /* Center chunk (title) takes remaining space */
  .fc .fc-header-toolbar .fc-toolbar-chunk:nth-child(2) {
    flex: 1 1 auto !important;
    justify-content: center !important;
    min-width: 0 !important;
  }
  .fc .fc-toolbar-title {
    font-size: 13px !important;
    font-style: italic !important;
    font-family: var(--font-serif) !important;
    font-weight: 500 !important;
    text-align: center !important;
    width: auto !important;
    margin: 0 !important;
    padding: 0 4px !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    line-height: 1.2 !important;
  }
  .fc .fc-button {
    padding: 4px 8px !important;
    font-size: 11.5px !important;
    min-height: 30px !important;
    min-width: 30px !important;
    border-radius: 8px !important;
  }
  .fc .fc-prev-button,
  .fc .fc-next-button {
    min-width: 30px !important;
    padding: 4px 6px !important;
  }
  /* Shorten "Aujourd'hui" → "Auj." to save space on tight mobile */
  .fc .fc-today-button {
    font-size: 11.5px !important;
    padding: 4px 10px !important;
    white-space: nowrap !important;
  }

  /* ── FOOTER toolbar: pills Jour · 3j · Semaine · Liste ── */
  .fc .fc-footer-toolbar.fc-toolbar {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 0 !important;
    margin: 8px 0 0 !important;
    padding: 8px 4px !important;
    background: transparent !important;
    border-top: none !important;
    flex-shrink: 0;
    width: 100%;
    box-sizing: border-box;
  }
  .fc .fc-footer-toolbar .fc-toolbar-chunk {
    display: flex !important;
    justify-content: center !important;
    gap: 6px !important;
  }
  .fc .fc-footer-toolbar .fc-button-group {
    display: flex !important;
    gap: 6px !important;
    justify-content: center;
    background: transparent !important;
    border: none !important;
  }
  .fc .fc-footer-toolbar .fc-button {
    background: var(--surface) !important;
    border: 1px solid var(--border2) !important;
    color: var(--text2) !important;
    font-weight: 500 !important;
    min-height: 34px !important;
    font-size: 12px !important;
    border-radius: 999px !important;
    padding: 6px 14px !important;
    margin: 0 !important;
    box-shadow: none !important;
    white-space: nowrap !important;
  }
  .fc .fc-footer-toolbar .fc-button:hover {
    background: var(--surface2) !important;
    color: var(--text) !important;
  }
  .fc .fc-footer-toolbar .fc-button.fc-button-active {
    background: var(--primary) !important;
    color: #fff !important;
    border-color: var(--primary) !important;
    font-weight: 600 !important;
  }

  /* Reduce calendar-wrap side padding on mobile so footer fits,
     AND tell the browser to only handle vertical pan + pinch natively —
     horizontal swipes are ours. This prevents iOS/Android edge-swipe
     system gestures from stealing the touch at the left/right edges
     of the screen. overscroll-behavior kills pull-to-refresh too. */
  #calendar-wrap {
    padding: 0 6px 8px !important;
    touch-action: pan-y pinch-zoom;
    overscroll-behavior: contain;
    transition: opacity .45s ease-out;
  }
  /* During the zoom-end rebuild (destroy+init), fade slightly so the
     "flash" of the calendar being rebuilt is hidden visually. */
  #calendar-wrap.smilo-zoom-rebuilding {
    opacity: 0.25;
    transition: opacity .35s ease-in;
  }

  /* Ensure the grid has enough height */
  .fc-view-harness {
    padding-bottom: 4px;
  }

  /* Day-header cells: center text and shrink slightly on mobile.
     "LUN. 20/04" was left-aligned and a bit big for narrow columns. */
  .fc .fc-col-header-cell {
    text-align: center !important;
    padding: 6px 2px !important;
  }
  .fc .fc-col-header-cell-cushion {
    font-size: 10.5px !important;
    letter-spacing: 0 !important;
    display: block !important;
    width: 100%;
    text-align: center !important;
    padding: 0 !important;
  }

  /* Week number in the top-left axis cell (e.g. "S17") */
  .fc .fc-timegrid-axis-cushion,
  .fc .fc-week-number {
    font-size: 11px !important;
    color: var(--text2) !important;
    font-weight: 700 !important;
    text-decoration: none !important;
    padding: 2px !important;
    text-align: center !important;
    display: block !important;
    width: 100%;
  }
  .fc .fc-timegrid-axis {
    text-align: center !important;
  }
  .fc .fc-timegrid-axis-frame {
    justify-content: center !important;
  }

  /* Timegrid slot height driven by JS (pinch-zoom).
     Calendar._bindPinchZoom() writes --agenda-slot-h to :root.
     Spécificité renforcée (.fc .fc-timegrid-slot) pour battre calendar.css
     qui force height:auto avec !important. */
  .fc .fc-timegrid-slot,
  .fc .fc-timegrid-slot-lane,
  .fc .fc-timegrid-slot-label {
    height: var(--agenda-slot-h, 2.6em) !important;
  }

  /* While a horizontal swipe is in progress, disable pointer-events on
     events so FC's drag handlers can't grab them even if they armed
     their long-press timer before we stole the gesture.
     Class is added/removed by Calendar._bindPinchZoom(). */
  body.smilo-swiping .fc-event,
  body.smilo-swiping .fc-event * {
    pointer-events: none !important;
  }
  body.smilo-swiping .fc-timegrid-col,
  body.smilo-swiping .fc-daygrid-day {
    pointer-events: none !important;
  }

  /* ── Granularity strip: compact mobile layout ───────────────
     - Hide the "Granularité:" label (just keep the pills)
     - Hide the "Rechercher disponibilité" button text (keep 🔍 only)
     - Move the 🔍 button to the end of the last row, next to "Fin" */
  .gran-strip {
    padding: 6px 8px !important;
    gap: 5px !important;
  }
  .gran-strip .gran-label {
    display: none !important;
  }
  .gran-strip .gran-spacer {
    display: none !important;
  }
  .gran-strip .find-slot-text {
    display: none !important;
  }
  /* Force a line break BEFORE the Début label so pills stay on line 1
     and Début/Fin/selects go to line 2. We use an invisible pseudo
     sibling with width:100% + order matching to insert the break. */
  .gran-strip .time-start-label {
    order: 50 !important;
    margin-left: 0 !important;
  }
  .gran-strip .time-end-label {
    order: 52 !important;
    margin-left: 4px !important;
  }
  .gran-strip select.fs:nth-of-type(1) {
    order: 51 !important;
  }
  .gran-strip select.fs:nth-of-type(2) {
    order: 53 !important;
  }
  .gran-strip .find-slot-btn {
    order: 99 !important;
    margin-left: auto !important;
    padding: 3px 8px !important;
    min-width: 32px;
  }
  /* Invisible line-break: insert before "Début" (order 50) to push
     all time controls onto line 2. */
  .gran-strip::before {
    content: "";
    order: 49;
    flex-basis: 100%;
    height: 0;
  }

  /* ════════════════════════════════════════════════════════
     MODALS — fullscreen bottom-sheet style on mobile
     Safe-area on top so iOS/Android status bar doesn't clip
     the modal header (title / close button).
     ════════════════════════════════════════════════════════ */
  .mbg {
    align-items: flex-end !important;
    padding: 0 !important;
  }
  .modal {
    box-sizing: border-box;
    width: 100% !important;
    max-width: 100% !important;
    max-height: 95dvh !important;
    height: auto !important;
    border-radius: 16px 16px 0 0;
    display: flex;
    flex-direction: column;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden;
  }
  .modal .mh {
    flex-shrink: 0;
    /* Extra top padding = system status bar safe area.
       Without this, the title/close button hide behind the status bar. */
    padding: calc(14px + env(safe-area-inset-top)) 16px 14px;
    border-bottom: 1px solid var(--border);
  }
  .modal .mb {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding: 14px 16px;
  }
  .modal .mf {
    flex-shrink: 0;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
    background: var(--bg);
  }

  /* ════════════════════════════════════════════════════════
     SETTINGS ROWS — smart layout on mobile
     Default: keep row layout with wrap, so short controls stay
     next to their label. When a row has many pills (granularity,
     language chips), pills wrap to a 2nd line but still fit.
     ════════════════════════════════════════════════════════ */
  .ss {
    max-width: 100% !important;
  }
  .sh {
    padding: 12px 14px !important;
    font-size: 15px !important;
  }
  .sr {
    padding: 12px 14px !important;
    gap: 10px !important;
    align-items: center !important;
    flex-wrap: wrap;
  }
  /* Label side: take auto width so it doesn't hog the row */
  .sr > div:first-child {
    flex: 0 1 auto;
    min-width: 0;
  }
  /* Control side: allow flex-wrap for pills, grow to fill when possible.
     EXCLUDE .tog (toggle switches must keep their fixed 38px width). */
  .sr > div:last-child:not(.tog) {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: flex-end;
    flex: 1 1 auto;
    min-width: 0;
  }
  /* Toggles: always stay at their fixed size, align right */
  .sr > .tog:last-child {
    flex: 0 0 auto !important;
    margin-left: auto;
  }
  /* Also force selects/inputs on the control side not to stretch full width */
  .sr > select.fs,
  .sr > input.fi {
    flex: 0 0 auto;
    margin-left: auto;
  }
  /* If the row contains only a select (simple "Début/Fin"-like rows),
     keep it compact on one line. Selects already have fixed width. */

  /* Language chip rows / any horizontally-scrollable chip strip */
  .sr .lang-chips,
  .sr > div:last-child:has(> .chip) {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .sr .lang-chips::-webkit-scrollbar { display: none; }

  /* Granularity pills: compact on mobile to fit 6 on one line */
  .gran-opt {
    padding: 2px 7px !important;
    font-size: 10.5px !important;
    border-radius: 14px !important;
  }
  .sr .gran-strip {
    gap: 4px !important;
  }

  /* Grouped rows where multiple label+control pairs share one row */
  .sr-hours {
    flex-wrap: wrap;
    gap: 8px !important;
  }
  .sr-hours > div:not(:has(select)) {
    flex: 0 0 auto;
  }
  .sr-hours select.fs {
    flex: 0 0 auto;
    width: 90px !important;
  }
}

/* ──────────────────────────────────────────────────────────
   Patient search (modal RDV) — autocomplete with rich cards
   ────────────────────────────────────────────────────────── */
.pat-search-wrap { position: relative; }

.pat-search-input {
  width: 100%;
  padding: 10px 36px 10px 36px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  font-size: 14px;
  background: var(--bg);
  color: var(--text);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pat-search-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
.pat-search-icon {
  position: absolute;
  left: 12px; top: 50%; transform: translateY(-50%);
  color: var(--text3);
  pointer-events: none;
  font-size: 16px;
}
.pat-search-clear {
  position: absolute;
  right: 8px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer;
  color: var(--text3);
  font-size: 18px;
  padding: 4px 8px;
  border-radius: 4px;
  line-height: 1;
}
.pat-search-clear:hover { color: var(--text); background: var(--surface); }

/* Results dropdown */
.pat-search-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0; right: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: 0 4px 16px rgba(0,0,0,.12), 0 1px 3px rgba(0,0,0,.06);
  max-height: 320px;
  overflow-y: auto;
  z-index: 100;
}

.pat-result {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background-color .1s ease;
}
.pat-result:last-child { border-bottom: none; }
.pat-result:hover,
.pat-result.is-active { background: var(--surface); }

.pat-result-info { flex: 1; min-width: 0; }
.pat-result-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pat-result-meta {
  font-size: 11px;
  color: var(--text3);
  margin-top: 2px;
}

/* Highlight matched substring (light yellow) */
.pat-result mark {
  background: #fef08a;
  color: var(--text);
  padding: 0 2px;
  border-radius: 2px;
}

/* "Create new patient" button at the bottom of the results */
.pat-result-new {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px;
  cursor: pointer;
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-weight: 600;
  font-size: 13px;
  border-top: 2px dashed var(--primary);
  transition: background-color .1s ease;
}
.pat-result-new:hover { background: var(--primary); color: #fff; }
.pat-result-new .plus {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  flex-shrink: 0;
}
.pat-result-new:hover .plus { background: #fff; color: var(--primary); }

/* Empty / loading state */
.pat-result-empty {
  padding: 16px 12px;
  text-align: center;
  color: var(--text3);
  font-size: 12px;
  font-style: italic;
}

/* Selected patient banner (above search bar) */
.pat-selected {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  margin-bottom: 12px;
}
.pat-selected-info { flex: 1; min-width: 0; }
.pat-selected-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
}
.pat-selected-meta {
  font-size: 11px;
  color: var(--text3);
  margin-top: 2px;
}
.pat-selected-change {
  background: none;
  border: 1px solid var(--border);
  color: var(--text2);
  padding: 4px 10px;
  border-radius: var(--r);
  cursor: pointer;
  font-size: 12px;
  transition: all .1s ease;
}
.pat-selected-change:hover {
  border-color: var(--primary);
  color: var(--primary);
}

/* ── Holiday days in the calendar ─────────────────────────────
 *
 * Class .is-holiday is added to FullCalendar day cells that match
 * a cabinet-wide holiday (Calendar._dayCellClasses). We layer a
 * warm stripe pattern + tint over the existing background so the
 * day is clearly "different" without being aggressive.
 *
 * Combined with the custom dayHeaderContent (which shows the
 * holiday name under the day number) and the background-event
 * tint (already done via buildFcEvents), this makes the closure
 * unmistakable in all views (month / week / 3-day / day).
 */
.fc .is-holiday {
  background-color: rgba(180, 110, 60, 0.10);
  background-image: repeating-linear-gradient(
    45deg,
    rgba(180, 110, 60, 0.18) 0,
    rgba(180, 110, 60, 0.18) 8px,
    transparent 8px,
    transparent 16px
  );
}
.fc .is-holiday .fc-daygrid-day-number,
.fc .is-holiday .fc-col-header-cell-cushion {
  color: #b15a17;
  font-weight: 600;
}


/* ── Custom HORIZONTAL Timeline view ───────────────────────────
 *
 * Vraie timeline : heures HORIZONTALES en X, praticiens en LIGNES.
 * Layout (2D) :
 *
 *   ┌─ tl-corner ┬── tl-header-hours ─────────────────────────┐
 *   │ (sticky)   │ 08:00 │ 09:00 │ 10:00 │ 11:00 │ 12:00 ...  │
 *   ├────────────┼─────────────────────────────────────────────┤
 *   │ tl-row-    │ tl-row (lane → contient tl-appt blocks)     │
 *   │  head      │                                             │
 *   │ (sticky)   │                                             │
 *   ├────────────┼─────────────────────────────────────────────┤
 *   │ Dr. Y      │  ← chaque ligne fait `rowHeight` px         │
 *   ├────────────┼─────────────────────────────────────────────┤
 *   │ Dr. Z      │                                             │
 *   └────────────┴─────────────────────────────────────────────┘
 *
 * Scroll : X (heures dépassent l'écran) + Y (si beaucoup de prov).
 * La colonne de gauche (noms praticiens) est sticky-left.
 * La ligne du haut (heures) est sticky-top.
 */

.tl-wrap {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;     /* allow children to shrink so flex+overflow works */
  background: var(--bg);
  overflow: hidden;
  font-size: 12px;
}

/* Toolbar (prev/today/next + date) — pixel-matches FC's headerToolbar */
.tl-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  margin-bottom: 10px;
  gap: 12px;
  flex-shrink: 0;
}
.tl-toolbar-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Flèches ‹ › : min-width 64px pour matcher la largeur des flèches FC,
 * mais on hérite du font-size standard .tl-fc-btn (12px) pour garder
 * la MÊME HAUTEUR que les view-switchers et "Aujourd'hui". */
.tl-toolbar-nav > [data-tl-nav="prev"],
.tl-toolbar-nav > [data-tl-nav="next"] {
  min-width: 64px;
  text-align: center;
}
/* Button style: copy of .fc-button from calendar.css, with the same
 * "pill" border-radius (20px) used for view-switcher buttons in FC.
 * Taille réduite ~20% par rapport au défaut FC pour un look compact
 * et cohérent partout (Timeline, DayView, FC standard). */
.tl-fc-btn {
  background: var(--surface);
  border: 1px solid var(--border2);
  color: var(--text2);
  border-radius: 20px;
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  padding: 5px 10px;
  box-shadow: none;
  transition: all .12s;
  cursor: pointer;
  line-height: 1.4;
}
.tl-fc-btn:hover {
  background: var(--surface2);
  color: var(--text);
  border-color: var(--border2);
}
.tl-fc-btn-active {
  background: var(--primary) !important;
  color: #fff !important;
  border-color: var(--primary) !important;
  font-weight: 600 !important;
}
/* Title: 20px, serif, italic — same as .fc-toolbar-title */
.tl-toolbar-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  flex: 1;
  text-align: center;
}
.tl-toolbar-right {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Desktop : view-switchers tous à la MÊME largeur. Sans ça, "3j" (2 chars)
 * apparaît comme un mini-badge à côté de "Timeline" (8 chars) → incohérent.
 * min-width: 80px garantit que même les boutons courts ont la même
 * apparence que les boutons longs. */
.tl-toolbar-right > [data-tl-switch] {
  min-width: 64px;
  text-align: center;
}

/* Mobile (≤768px) : toolbar haut compacte (nav + titre + today)
 * + footer toolbar avec view-switchers — exact pattern de
 * FullCalendar.footerToolbar (cf. calendar.js init()). */
.tl-toolbar-mobile-top {
  padding: 8px 8px;
  gap: 6px;
}
.tl-toolbar-mobile-top .tl-toolbar-title {
  font-size: 16px;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tl-toolbar-mobile-top .tl-fc-btn {
  padding: 6px 8px;
}
.tl-toolbar-mobile-bottom {
  margin-top: 8px;
  margin-bottom: 0;
  padding: 8px 8px;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  border-top: 1px solid var(--border, #e5e7eb);
  background: var(--card, #fff);
  flex-shrink: 0;
}
.tl-toolbar-mobile-bottom .tl-fc-btn {
  /* flex: 1 1 0 (au lieu de juste flex: 1) → tous les boutons ont la
   * MÊME largeur peu importe la longueur du contenu. Sans flex-basis: 0,
   * un bouton court comme "3j" prend moins de place qu'un bouton long
   * comme "Timeline" → tailles inégales. */
  flex: 1 1 0;
  min-width: 0;
  padding: 8px 4px;
  font-size: 12px;
  text-align: center;
}

/* Holiday banner above the grid */
.tl-holiday-banner {
  background: linear-gradient(90deg, rgba(180, 110, 60, 0.18), rgba(180, 110, 60, 0.08));
  color: #b15a17;
  padding: 6px 12px;
  font-weight: 600;
  text-align: center;
  font-size: 12px;
  border-bottom: 1px solid rgba(180, 110, 60, 0.25);
  flex-shrink: 0;
}

/* The big scrollable area. Both X and Y scroll happen here.
 *
 * IMPORTANT: we put the closing borders here on the OUTER container
 * (which has a fixed viewport size) instead of trying to put them on
 * the inner sticky/wide elements. This is reliable because:
 *   - .tl-scroller has the actual viewport dimensions (no overflow)
 *   - Its border is drawn at the viewport edges, ALWAYS visible
 *   - Inner elements with width:max-content + sticky have unreliable
 *     border behaviour because they extend past the visible area.
 *
 * The visual effect is a clean rectangle around the entire timeline
 * grid (top, right, bottom, left). The hour ticks and row dividers
 * inside the scroller draw the inner gridlines.
 */
.tl-scroller {
  flex: 1;
  min-height: 0;
  width: 100%;
  /* overflow-x: scroll → scrollable, MAIS on cache la barre native via
   * ::-webkit-scrollbar:horizontal { display:none } et scrollbar-width
   * sur Firefox, parce qu'on a notre propre scrollbar custom (.tl-hscroll)
   * qui est toujours visible et utilisable à la souris. La barre native
   * en mode overlay sur Mac/Chrome moderne est invisible avec une souris
   * classique → inutilisable. */
  overflow-x: scroll;
  overflow-y: auto;
  position: relative;
  background: var(--bg);
}
/* Cacher la scrollbar horizontale native de .tl-scroller (la verticale
 * reste, elle, utile et fonctionnelle avec la molette). */
.tl-scroller::-webkit-scrollbar:horizontal {
  height: 0;
  display: none;
}
.tl-scroller::-webkit-scrollbar:vertical {
  width: 8px;
}
.tl-scroller::-webkit-scrollbar-thumb {
  background: #b0a89c;
  border-radius: 4px;
}

/* Inner wrapper: sized to the EXACT pixel-width of the content
 * (set inline by the JS as leftColW + totalW). Holds the cells which
 * each draw their own borders — see "cell-border approach" below. */
.tl-inner {
  background: var(--card, #fff);
}

/* Header strip: empty corner (left, sticky) + hour ticks (scrolls X) */
.tl-header-strip {
  display: flex;
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--card, #fff);
  border-bottom: 1px solid var(--border, #e5e7eb);
  width: max-content;
}
.tl-corner {
  position: sticky;
  left: 0;
  z-index: 30;
  background: var(--bg2);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Cadre 3 côtés (top, left, bottom) autour du n° de semaine.
   * Pas de border-right car .tl-header-hours fournit déjà sa border-left
   * au point de jonction. */
  border-top:    1px solid var(--border, #e5e7eb);
  border-left:   1px solid var(--border, #e5e7eb);
  border-bottom: 1px solid var(--border, #e5e7eb);
}
/* Week number inside the corner — same style as FC's "S19" in the
 * timeGrid row header. Bold, ~14px, dark grey. */
.tl-week-num {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.02em;
}
.tl-header-hours {
  position: relative;
  flex-shrink: 0;
  /* Pas de border-left du tout : .tl-corner fournit la jonction.
   * Border 3 côtés (top, right, bottom) pour fermer la cellule. */
  border-top:    1px solid var(--border, #e5e7eb);
  border-right:  1px solid var(--border, #e5e7eb);
  border-bottom: 1px solid var(--border, #e5e7eb);
  border-left:   0;
}
.tl-hour-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--border, #e5e7eb);
}
.tl-hour-label {
  position: absolute;
  top: 12px;
  left: 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text2);
  font-family: 'Menlo', 'Courier New', monospace;
  white-space: nowrap;
}

/* Body: column of practitioner rows */
.tl-body {
  width: max-content;
}
.tl-row-wrap {
  display: flex;
  /* CRITIQUE : sans ça, le flex container se laisse compresser à
   * la largeur du parent (.tl-body) au lieu de mesurer par contenu.
   * Résultat : pas de débordement → pas de scrollbar horizontale.
   * width:max-content force la mesure sur la somme des enfants. */
  width: max-content;
}

/* Left cell: provider name (sticky on horizontal scroll) */
.tl-row-head {
  position: sticky;
  left: 0;
  z-index: 10;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 4px;
  background: var(--card, #fff);
  border-left:   1px solid var(--border, #e5e7eb);
  border-bottom: 1px solid var(--border, #e5e7eb);
}
.tl-row-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 12px;
  flex-shrink: 0;
}
.tl-row-name {
  flex: 1;
  min-width: 0;
}
.tl-row-last {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tl-row-first {
  font-size: 11px;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Lane: where appointments live, scrolls with X.
 * Each lane carries border-right (closes the grid at dayEnd) and
 * border-bottom (row divider). Combined with .tl-inner's border-top
 * and border-left, this produces a fully enclosed grid. */
.tl-row {
  position: relative;
  flex-shrink: 0;
  background: var(--card, #fff);
  cursor: crosshair;
  border-left:   1px solid var(--border, #e5e7eb);
  border-right:  1px solid var(--border, #e5e7eb);
  border-bottom: 1px solid var(--border, #e5e7eb);
}
.tl-row:hover {
  background: rgba(var(--primary-rgb, 255, 110, 22), 0.02);
}

/* Vertical hour grid lines inside the lane */
.tl-grid-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(0, 0, 0, 0.05);
  pointer-events: none;
}

/* Out-of-hours stripes */
.tl-non-work {
  position: absolute;
  top: 0;
  bottom: 0;
  background: repeating-linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.04) 0,
    rgba(0, 0, 0, 0.04) 4px,
    transparent 4px,
    transparent 8px
  );
  pointer-events: none;
}

/* Lunch break stripes (orange) */
.tl-lunch {
  position: absolute;
  top: 0;
  bottom: 0;
  background: repeating-linear-gradient(
    -45deg,
    rgba(255, 200, 100, 0.18) 0,
    rgba(255, 200, 100, 0.18) 4px,
    transparent 4px,
    transparent 8px
  );
  pointer-events: none;
}

/* Appointment block: horizontal pill */
.tl-appt {
  position: absolute;
  border-radius: 4px;
  padding: 3px 6px;
  font-size: 11px;
  cursor: grab;
  overflow: hidden;
  z-index: 2;
  transition: box-shadow 0.1s ease;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.tl-appt:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.tl-appt:active { cursor: grabbing; }

.tl-appt-time {
  font-weight: 600;
  font-size: 10px;
  color: var(--text2);
  font-family: 'Menlo', 'Courier New', monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tl-appt-patient {
  font-weight: 600;
  font-size: 12px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tl-appt-type {
  font-size: 10px;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tl-appt-phone {
  font-size: 10px;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: 'Menlo', 'Courier New', monospace;
}

/* ── Custom horizontal scrollbar ─────────────────────────────
 *
 * Indépendante des scrollbars natives qui sont en mode "overlay"
 * sur Chrome moderne / Mac et invisibles au repos. Avec une souris
 * classique sans scroll horizontal, c'est inutilisable. On redessine.
 *
 * Style identique à la scrollbar verticale native que FullCalendar
 * affiche en vue Semaine/Jour : très fine, gris clair, discret.
 *
 * Le thumb est draggable à la souris ; un clic sur la track scrolle
 * d'une page. La logique JS est dans Timeline._bindHScroll().
 */
.tl-wrap .tl-hscroll {
  flex-shrink: 0;
  height: 8px;
  padding: 0;
  background: transparent;
  position: relative;
}
.tl-wrap .tl-hscroll-track {
  position: relative;
  height: 8px;
  background: transparent;
  cursor: pointer;
}
.tl-wrap .tl-hscroll-thumb {
  position: absolute;
  top: 1px;
  left: 0;
  height: 6px;
  min-width: 30px;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 3px;
  cursor: grab;
  transition: background 0.15s;
  /* width et transform sont gérés en JS */
}
.tl-wrap .tl-hscroll-thumb:hover {
  background: rgba(0, 0, 0, 0.35);
}
.tl-wrap .tl-hscroll-thumb.is-dragging,
.tl-wrap .tl-hscroll-thumb:active {
  cursor: grabbing;
  background: rgba(0, 0, 0, 0.5);
}

/* ── Custom DAY view (multi-praticiens) ────────────────────────
 *
 * Vraie vue Jour multi-colonnes : heures verticales à gauche,
 * 1 colonne par praticien, RDV en blocs verticaux dans leur colonne.
 *
 * Reproduit le comportement de FC.resourceTimeGridDay (Premium).
 * Layout (2D) :
 *
 *   ┌─ dv-corner ┬─ dv-prov-cell ─┬─ dv-prov-cell ─┐
 *   │  (sticky)  │ (sticky-top)   │ (sticky-top)   │
 *   ├────────────┼────────────────┼────────────────┤
 *   │ 08:00      │ dv-col         │ dv-col         │
 *   │ (sticky-L) │  + dv-appts    │  + dv-appts    │
 *   │ 09:00      │                │                │
 *   ├────────────┼────────────────┼────────────────┤
 *   │ ...        │                │                │
 *   └────────────┴────────────────┴────────────────┘
 *
 * Largeur des colonnes : (wrapW − leftColW) / nbProviders.
 * → Adapté plein écran, pas de scroll horizontal en cas normal.
 */

.dv-wrap {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: var(--bg);
  overflow: hidden;
  font-size: 12px;
}

/* Toolbar (mêmes styles que .tl-toolbar/Timeline pour cohérence visuelle) */
.dv-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  margin-bottom: 10px;
  gap: 12px;
  flex-shrink: 0;
}
.dv-toolbar-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Flèches ‹ › : min-width 64px, hérite du font standard pour
 * garder la même hauteur que les autres boutons. */
.dv-toolbar-nav > [data-dv-nav="prev"],
.dv-toolbar-nav > [data-dv-nav="next"] {
  min-width: 64px;
  text-align: center;
}
.dv-fc-btn {
  background: var(--surface);
  border: 1px solid var(--border2);
  color: var(--text2);
  border-radius: 20px;
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  padding: 5px 10px;
  box-shadow: none;
  transition: all .12s;
  cursor: pointer;
  line-height: 1.4;
}
.dv-fc-btn:hover {
  background: var(--surface2);
  color: var(--text);
  border-color: var(--border2);
}
.dv-fc-btn-active {
  background: var(--primary) !important;
  color: #fff !important;
  border-color: var(--primary) !important;
  font-weight: 600 !important;
}
.dv-toolbar-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
  flex: 1;
  text-align: center;
}
.dv-toolbar-right {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Desktop : view-switchers tous à la MÊME largeur (cf. .tl-toolbar-right). */
.dv-toolbar-right > [data-dv-switch] {
  min-width: 64px;
  text-align: center;
}
.dv-toolbar-mobile-top {
  padding: 8px 8px;
  gap: 6px;
}
.dv-toolbar-mobile-top .dv-toolbar-title {
  font-size: 16px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dv-toolbar-mobile-top .dv-fc-btn {
  padding: 6px 8px;
}
.dv-toolbar-mobile-bottom {
  margin-top: 8px;
  margin-bottom: 0;
  padding: 8px 8px;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  border-top: 1px solid var(--border, #e5e7eb);
  background: var(--card, #fff);
  flex-shrink: 0;
}
.dv-toolbar-mobile-bottom .dv-fc-btn {
  /* flex: 1 1 0 → tous les boutons ont la MÊME largeur peu importe
   * la longueur du contenu (3j court, Timeline long, etc.). */
  flex: 1 1 0;
  min-width: 0;
  padding: 8px 4px;
  font-size: 12px;
  text-align: center;
}

/* Holiday banner */
.dv-holiday-banner {
  background: linear-gradient(90deg, rgba(180, 110, 60, 0.18), rgba(180, 110, 60, 0.08));
  color: #b15a17;
  padding: 6px 12px;
  font-weight: 600;
  text-align: center;
  font-size: 12px;
  border-bottom: 1px solid rgba(180, 110, 60, 0.25);
  flex-shrink: 0;
}

/* Scrollable area (vertical scroll only ; horizontal géré par layout adaptatif) */
.dv-scroller {
  flex: 1;
  min-height: 0;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
  background: var(--bg);
}
.dv-inner {
  background: var(--card, #fff);
}

/* Header strip: corner + provider names (sticky top) */
.dv-header-strip {
  display: flex;
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--card, #fff);
  border-bottom: 1px solid var(--border, #e5e7eb);
}
.dv-corner {
  position: sticky;
  left: 0;
  z-index: 30;
  background: var(--bg2);
  flex-shrink: 0;
  border-right:  1px solid var(--border, #e5e7eb);
  border-bottom: 1px solid var(--border, #e5e7eb);
}
.dv-prov-headers {
  display: flex;
  flex-shrink: 0;
}
.dv-prov-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  flex-shrink: 0;
  border-right: 1px solid var(--border, #e5e7eb);
  background: var(--card, #fff);
}
.dv-prov-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 12px;
  flex-shrink: 0;
}
.dv-prov-name {
  flex: 1;
  min-width: 0;
}
.dv-prov-last {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dv-prov-first {
  font-size: 11px;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Body: hour axis + columns */
.dv-body {
  display: flex;
}

/* Time axis: sticky left, hour labels at fixed Y positions */
.dv-time-axis {
  position: sticky;
  left: 0;
  z-index: 10;
  flex-shrink: 0;
  background: var(--bg2);
  border-right: 1px solid var(--border, #e5e7eb);
}
.dv-hour-label {
  position: absolute;
  left: 0;
  right: 0;
  padding: 2px 4px 0 0;
  text-align: right;
  font-size: 11px;
  font-weight: 600;
  color: var(--text2);
  font-family: 'Menlo', 'Courier New', monospace;
  /* Décale le label de quelques px vers le haut pour que le "08:00"
   * apparaisse aligné avec la ligne horaire 08:00 et pas en dessous. */
  transform: translateY(-2px);
}

/* Columns wrapper */
.dv-cols-wrap {
  display: flex;
  flex-shrink: 0;
}
.dv-col {
  position: relative;
  flex-shrink: 0;
  background: var(--card, #fff);
  cursor: crosshair;
  border-right: 1px solid var(--border, #e5e7eb);
}
.dv-col:hover {
  background: rgba(var(--primary-rgb, 255, 110, 22), 0.02);
}

/* Horizontal grid lines (one per hour, lighter ones every granMin) */
.dv-grid-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  pointer-events: none;
}
.dv-grid-hour {
  background: var(--border, #e5e7eb);
}
.dv-grid-gran {
  background: var(--border, #e5e7eb);
  opacity: 0.4;
}

/* Out-of-hours overlay (before startHour, after endHour) */
.dv-non-work {
  position: absolute;
  left: 0;
  right: 0;
  background: repeating-linear-gradient(
    -45deg,
    rgba(0, 0, 0, 0.02),
    rgba(0, 0, 0, 0.02) 6px,
    rgba(0, 0, 0, 0.05) 6px,
    rgba(0, 0, 0, 0.05) 12px
  );
  pointer-events: none;
}

/* Lunch break overlay */
.dv-lunch {
  position: absolute;
  left: 0;
  right: 0;
  background: repeating-linear-gradient(
    -45deg,
    rgba(245, 211, 122, 0.25),
    rgba(245, 211, 122, 0.25) 6px,
    rgba(245, 211, 122, 0.4) 6px,
    rgba(245, 211, 122, 0.4) 12px
  );
  pointer-events: none;
}

/* Appointment block (vertical, positioned absolute in its column) */
.dv-appt {
  position: absolute;
  border-radius: 4px;
  padding: 3px 5px;
  cursor: grab;
  font-size: 11px;
  line-height: 1.25;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 1px;
  user-select: none;
  transition: box-shadow 0.12s, transform 0.12s;
}
.dv-appt:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 5;
}
.dv-appt:active { cursor: grabbing; }

.dv-appt-time {
  font-weight: 600;
  font-size: 10px;
  color: var(--text2);
  font-family: 'Menlo', 'Courier New', monospace;
  white-space: nowrap;
}
.dv-appt-patient {
  font-weight: 600;
  font-size: 11px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dv-appt-type {
  font-size: 10px;
  color: var(--text2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dv-appt-phone {
  font-size: 10px;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: 'Menlo', 'Courier New', monospace;
}

/* Mobile: shrink left col, hide first name */
@media (max-width: 600px) {
  .tl-row-head { padding: 4px 6px; gap: 6px; }
  .tl-row-avatar { width: 24px; height: 24px; font-size: 10px; }
  .tl-row-last { font-size: 12px; }
  .tl-row-first { display: none; }
}

/* ── Dashboard ────────────────────────────────────────────────*/
.dash-wrap {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
.dash-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 10px;
}
.dash-greeting {
  font-size: 22px;
  font-weight: 700;
  font-family: var(--font-serif, 'Instrument Serif', serif);
  color: var(--text);
}
.dash-date {
  font-size: 13px;
  color: var(--text3);
  text-transform: capitalize;
  margin-top: 2px;
}
.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}
.dash-widget {
  background: var(--card, var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
}
.dash-widget-header {
  padding: 12px 16px;
  font-weight: 600;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
  background: var(--bg2);
  color: var(--text);
}
.dash-widget-body {
  padding: 14px 16px;
  min-height: 80px;
}
.dash-loading {
  color: var(--text3);
  font-size: 12px;
  text-align: center;
  padding: 20px 0;
}
.dash-empty {
  color: var(--text3);
  font-size: 12px;
  text-align: center;
  padding: 16px 0;
}
/* RDV list */
.dash-appt-list { display: flex; flex-direction: column; gap: 6px; }
.dash-appt-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg2);
  cursor: pointer;
  transition: background .1s;
}
.dash-appt-row:hover { background: var(--surface2); }
.dash-appt-time { font-size: 12px; font-weight: 600; color: var(--text2); min-width: 36px; }
.dash-appt-dot  { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dash-appt-info { flex: 1; min-width: 0; }
.dash-appt-name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dash-appt-sub  { font-size: 11px; color: var(--text3); }
.dash-appt-status { font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 10px; white-space: nowrap; }
.dash-status-confirmed { background: rgba(34,197,94,.15); color: #16a34a; }
.dash-status-pending   { background: rgba(245,158,11,.15); color: #d97706; }
.dash-status-arrived   { background: rgba(59,130,246,.15); color: #2563eb; }
.dash-status-late      { background: rgba(249,115,22,.15); color: #ea580c; }
.dash-status-no_show   { background: rgba(239,68,68,.15);  color: #dc2626; }
.dash-status-completed { background: rgba(107,114,128,.15);color: var(--text3); }
/* KPIs */
.dash-kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px,1fr)); gap: 12px; }
.dash-kpi { text-align: center; padding: 12px 8px; background: var(--bg2); border-radius: 8px; }
.dash-kpi-val { font-size: 24px; font-weight: 700; color: var(--primary); }
.dash-kpi-lbl { font-size: 11px; color: var(--text3); margin-top: 2px; }
/* Week grid */
/* v1.2.49 — flex-wrap permet d'afficher 5-7 jours selon les workDays
   des prestataires. Min-width assure une lisibilité même quand on a
   7 jours dans peu de place (responsive : wrap à la 2è ligne). */
.dash-week-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.dash-week-day { flex: 1 1 80px; text-align: center; padding: 10px 4px; border-radius: 8px; background: var(--bg2); }
.dash-week-day.today { background: var(--primary); }
.dash-week-day.today .dash-week-day-label { color: #fff; }
.dash-week-day.today .dash-week-count { color: #fff; }
.dash-week-day-label { font-size: 11px; color: var(--text3); text-transform: capitalize; }
.dash-week-count { font-size: 20px; font-weight: 700; color: var(--text); margin-top: 4px; }
.dash-week-count.empty { color: var(--text3); font-size: 14px; }
/* Alerts */
.dash-alerts { display: flex; flex-direction: column; gap: 10px; }
.dash-alert-row { display: flex; align-items: center; gap: 10px; padding: 10px; background: var(--bg2); border-radius: 8px; }
/* Weather */
.dash-weather { display: flex; flex-direction: column; gap: 12px; }
.dash-weather-current { display: flex; align-items: center; gap: 14px; }
.dash-weather-forecast { display: flex; gap: 8px; }
.dash-weather-day { flex: 1; text-align: center; padding: 8px 4px; background: var(--bg2); border-radius: 8px; }

/* ── Dashboard drag & drop ───────────────────────────────────*/
.dash-drag-handle {
  font-size: 16px;
  color: var(--text3);
  cursor: grab;
  padding: 0 4px;
  line-height: 1;
  flex-shrink: 0;
}
.dash-drag-handle:active { cursor: grabbing; }
.dash-widget[draggable="true"] { cursor: default; }
.dash-widget.dash-drag-over {
  border-color: var(--primary);
  background: var(--primary)08;
  transform: scale(1.01);
  transition: all .1s;
}
.dash-widget-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* ═══════════════════════════════════════════════════════════════
   Bandeau de statut tenant (read_only / suspended)
   Affiché entre topbar et content. Persistant tant que le mode
   n'est pas réactivé. Couleurs distinctes pour bien différencier.
   ═══════════════════════════════════════════════════════════════ */
.tenant-status-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 11px 20px;
  flex-shrink: 0;
  border-bottom: 1px solid;
  font-size: 13px;
  line-height: 1.5;
}
.tenant-status-banner .tsb-icon {
  font-size: 18px;
  flex-shrink: 0;
  line-height: 1.2;
}
.tenant-status-banner .tsb-text {
  flex: 1;
  min-width: 0;
}
.tenant-status-banner.tenant-status-readonly {
  background: rgba(245, 158, 11, 0.12);
  color: #92400e;
  border-bottom-color: #fcd34d;
}
.tenant-status-banner.tenant-status-suspended {
  background: rgba(220, 38, 38, 0.12);
  color: #991b1b;
  border-bottom-color: #fca5a5;
}

/* En mode sombre, garder une lisibilité correcte */
[data-theme="dark"] .tenant-status-banner.tenant-status-readonly {
  background: rgba(245, 158, 11, 0.18);
  color: #fde68a;
}
[data-theme="dark"] .tenant-status-banner.tenant-status-suspended {
  background: rgba(220, 38, 38, 0.22);
  color: #fecaca;
}

/* ─────────────────────────────────────────────────────────────────
   v1.3.72 — Pastille save-status pour le panneau Settings cabinet
   (Jacob 23/05/2026)
   
   Avant : pas d'indicateur visuel de l'état de persistance des settings.
   L'utilisateur ne savait pas si ses modifs étaient en DB ou pas.
   Combiné au bug de debounce 600ms (perdu sur F5), ça causait des pertes
   silencieuses de configuration.
   
   Maintenant : pastille sticky en haut à droite du panneau Settings.
   3 états : 🟢 sauvé · 🟠 en cours · ❌ erreur (cliquable pour retry).
   Même look-and-feel que le save-status côté SA (cohérence visuelle).
   ───────────────────────────────────────────────────────────────── */
.save-status {
  /* v1.3.79 — Hérite TOUT de .bi (border-radius var(--r), padding 7px,
     background var(--surface), border 1px solid var(--border)) → forme
     identique aux boutons 🔄 et 🌙 voisins (Jacob 23/05/2026).
     
     Le seul rôle de .save-status est de définir la transition opacity
     pour le fade-out 2s. Les variantes -ok/-saving/-error ne changent
     QUE la couleur du texte et la couleur de bordure (subtile) pour
     signaler l'état, SANS toucher au background → cohérence visuelle
     parfaite avec les boutons voisins. */
  transition: color 0.2s, border-color 0.2s, opacity 0.4s ease-out;
  user-select: none;
}
/* Variantes : on change uniquement la COULEUR du texte (et un léger
   indice de bordure colorée). Le fond reste le var(--surface) gris
   de .bi pour rester cohérent avec les autres boutons du topbar. */
.save-status-ok {
  color: #047857;
  border-color: rgba(16, 185, 129, 0.45);
}
.save-status-saving {
  color: #b45309;
  border-color: rgba(245, 158, 11, 0.50);
}
.save-status-error {
  color: #b91c1c;
  border-color: rgba(239, 68, 68, 0.55);
  cursor: pointer;
  /* Indicateur d'interactivité : "clique-moi pour retry" */
  animation: save-status-error-pulse 2s ease-in-out infinite;
}
.save-status-error:hover {
  background: rgba(239, 68, 68, 0.10);
  animation: none;
}
@keyframes save-status-error-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
  50%      { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0.0); }
}

/* Dark mode : adapter les couleurs de texte pour rester lisibles
   sur le fond sombre hérité de .bi (var(--surface) en dark). */
[data-theme="dark"] .save-status-ok {
  color: #34d399;
  border-color: rgba(16, 185, 129, 0.50);
}
[data-theme="dark"] .save-status-saving {
  color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.55);
}
[data-theme="dark"] .save-status-error {
  color: #fca5a5;
  border-color: rgba(239, 68, 68, 0.55);
}
