/* ============================================================================
   Beauty Store Dispatch — design system (§13)
   ============================================================================

   One stylesheet, one set of components, used by every screen.

   What changed and why:

   - The palette is neutral. The old one was the storefront's beige and soft
     pink, which is right for a shop and wrong for a tool people stare at for
     eight hours: it tinted every surface, so nothing could be emphasised by
     colour alone. Surfaces are now white on light grey, text is near-black,
     and there is exactly ONE accent — used for primary actions and the current
     page, and for nothing else. That is what makes "Dispatcher la sélection"
     findable at a glance.

   - The accent is deliberately not any status colour. Status badges are pale
     tints with dark text (see the block near the end); the accent is solid and
     saturated. They cannot be confused even at a glance.

   - Class names are unchanged from the previous stylesheet (.card, .order-card,
     .item-row, .badge, .empty-state, .actions, button.primary…). Every screen
     therefore picks up the new system without its markup being rewritten,
     which is what made a change this size safe to do in one pass.

   - Density is a modifier, not a separate theme. Aya's picking list and Bilal's
     dispatch board are worked through fast on a phone and stay list-first and
     tight; Commandes, Caisse and the daily summary are read and thought about,
     so they get more air. Same tokens, same components, different spacing.
   ============================================================================ */

:root {
  /* --- surfaces & text ---------------------------------------------------
     A light grey page with white cards, so a card reads as a distinct object
     without needing a border or a shadow heavy enough to be noticed. */
  --bg:            #f4f5f7;
  --surface:       #ffffff;
  --surface-2:     #fafbfc;   /* table headers, inset panels */
  --border:        #e3e6ea;
  --border-strong: #d2d7dd;

  --text:          #1d2430;
  --text-2:        #5a6472;   /* secondary copy */
  --text-3:        #8a93a0;   /* timestamps, hints */

  /* --- the one accent ---------------------------------------------------- */
  --accent:        #3b5bd9;
  --accent-hover:  #2f49b3;
  --accent-soft:   #eef1fd;   /* active nav background */

  /* --- chart ink ----------------------------------------------------------
     Two series colours, and only ever two. They were measured against the white
     card the charts sit on rather than picked by eye: worst colour-blind
     separation ΔE 28.8, normal-vision ΔE 37.0, both clearing 3:1 contrast on
     white. Slot 1 is the accent itself, so a chart reads as part of the app;
     slot 2 is the one hue that passed every check beside it. A third series
     would have to be re-measured — until then, the answer past two is a table.
     Deliberately NOT the status colours: a bar must never look like a badge. */
  --chart-1:       #3b5bd9;
  --chart-2:       #eb6834;
  --chart-grid:    #e8ebef;   /* one step off white — a hairline, never dashed */

  /* --- semantic ----------------------------------------------------------
     Used for feedback, not decoration. */
  --danger:        #c2413c;
  --danger-soft:   #fbe9e8;
  --success:       #2e7d55;
  --success-soft:  #e7f4ed;
  --warning:       #9a6a15;
  --warning-soft:  #fdf3dc;

  /* --- scale -------------------------------------------------------------
     A small fixed set. Anything not on it is a one-off, and one-offs are what
     made every screen look like it was built separately. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  --radius:    10px;
  --radius-sm: 7px;
  --radius-lg: 14px;

  --shadow:    0 1px 2px rgba(16, 24, 40, 0.05);
  --shadow-md: 0 4px 12px rgba(16, 24, 40, 0.08);
  --shadow-lg: 0 12px 32px rgba(16, 24, 40, 0.14);

  --sidebar-w: 224px;
  --topbar-h:  56px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  /* --- compatibility aliases ---------------------------------------------
     The previous stylesheet's token names, pointed at the new values.
     Page-local styles still refer to them in places; without these, those
     declarations would resolve to nothing and silently drop — a screen losing
     its colours is much worse than one that is briefly inconsistent. They also
     mean the old pink resolves to the new accent, so anything not yet cleaned
     up still looks like the rest of the app rather than like the old one.

     These are a migration aid. New code uses the real tokens above. */
  --white:     var(--surface);
  --beige:     var(--bg);
  --muted:     var(--text-2);
  --pink:      var(--accent);
  --pink-dark: var(--accent);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;

  /*
   * The sidebar is fixed, so the page is inset by its width here rather than
   * by wrapping every screen in a container div. That matters practically:
   * each page writes its own <main> straight after including the shell, and a
   * wrapper would have to be closed by a footer include on all sixteen of them
   * — sixteen chances to leave one unclosed. Padding on the body offsets the
   * top bar and the content together, and `main`'s own max-width still centres
   * it inside the remaining space.
   */
  padding-left: var(--sidebar-w);
}

a { color: var(--accent); }

/* ============================================================================
   Layout shell — sidebar + top bar + content
   ============================================================================
   The sidebar is fixed and always present on desktop. Below 900px it becomes a
   drawer that slides in over the content, because a permanent 224px column on a
   phone leaves nothing for the actual work — and Bilal and Aya are on phones.
   ============================================================================ */

.app { min-height: 100vh; }

.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w);
  background: var(--surface); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; z-index: 40;
  transition: transform 0.18s ease;
}

.sidebar-brand {
  display: flex; align-items: center; gap: var(--sp-2);
  height: var(--topbar-h); padding: 0 var(--sp-4);
  border-bottom: 1px solid var(--border);
  font-weight: 700; font-size: 0.95rem; letter-spacing: -0.01em;
  color: var(--text); text-decoration: none; flex-shrink: 0;
}
.sidebar-brand .dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex-shrink: 0;
}

.sidebar-nav { padding: var(--sp-3) var(--sp-2); overflow-y: auto; flex: 1; }

/* Grouped, mirroring how the work actually splits up. */
.nav-group { margin-bottom: var(--sp-4); }
.nav-group-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--text-3); font-weight: 700;
  padding: 0 var(--sp-3); margin-bottom: var(--sp-1);
}

.nav-link {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 8px var(--sp-3); margin-bottom: 1px;
  border-radius: var(--radius-sm); text-decoration: none;
  color: var(--text-2); font-size: 0.89rem; font-weight: 500;
}
.nav-link:hover { background: var(--surface-2); color: var(--text); }
.nav-link.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.nav-link .ico { width: 17px; text-align: center; font-size: 0.95rem; flex-shrink: 0; }
.nav-link .count {
  margin-left: auto; font-size: 0.72rem; font-weight: 700;
  background: var(--danger); color: #fff; border-radius: 20px; padding: 1px 6px;
}

.sidebar-foot {
  border-top: 1px solid var(--border); padding: var(--sp-3);
  font-size: 0.8rem; color: var(--text-3); flex-shrink: 0;
}
.sidebar-foot a { color: var(--text-2); text-decoration: none; display: block; padding: 3px 0; }
.sidebar-foot a:hover { color: var(--accent); }

.topbar {
  position: sticky; top: 0; z-index: 30;
  height: var(--topbar-h); background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 0 var(--sp-5);
}
.topbar h1 { font-size: 1rem; font-weight: 650; margin: 0; letter-spacing: -0.01em; }
.topbar .spacer { flex: 1; }

.burger {
  display: none; background: none; border: none; font-size: 1.25rem;
  cursor: pointer; padding: 4px 8px; width: auto; margin: 0; color: var(--text);
}

main { padding: var(--sp-5); max-width: 1180px; margin: 0 auto; }

/* Working screens: tighter, list-first, less scrolling per order. */
main.dense { max-width: 960px; padding: var(--sp-4); }

.scrim {
  display: none; position: fixed; inset: 0; background: rgba(16,24,40,0.4); z-index: 35;
}

/* The responsive overrides live at the END of this file, not here — they and
   the base rules have the same specificity, so whichever comes last wins, and
   a media query placed above the rule it overrides simply does nothing. */

/* ============================================================================
   Typography
   ============================================================================ */

.page-title { font-size: 1.25rem; font-weight: 650; margin: 0 0 var(--sp-1); letter-spacing: -0.015em; }
.page-sub   { color: var(--text-2); font-size: 0.88rem; margin: 0 0 var(--sp-5); }

h2 { font-size: 1.05rem; font-weight: 650; margin: var(--sp-5) 0 var(--sp-3); letter-spacing: -0.01em; }
h3 { font-size: 0.93rem; font-weight: 650; margin: 0 0 var(--sp-2); }

.section-label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-3); font-weight: 700; margin-bottom: var(--sp-2);
}
.muted { color: var(--text-2); font-size: 0.85rem; }
.note  { font-size: 0.85rem; color: var(--text-2); line-height: 1.5; }

/* ============================================================================
   Cards
   ============================================================================ */

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--sp-5);
  box-shadow: var(--shadow); margin-bottom: var(--sp-4);
}
.card-tight { padding: var(--sp-4); }

/* The per-order card used by the working boards. */
.order-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--sp-4);
  margin-bottom: var(--sp-3); box-shadow: var(--shadow);
}
.order-card:hover { border-color: var(--border-strong); }

.order-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-2);
}
.order-header .num { font-weight: 700; font-size: 0.95rem; }
.order-header .num a { color: var(--text); text-decoration: none; }
.order-header .num a:hover { color: var(--accent); }

.order-meta { font-size: 0.85rem; color: var(--text-2); line-height: 1.55; margin-bottom: var(--sp-3); }

.item-row {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) 0; border-top: 1px solid var(--border);
}
.item-row img {
  width: 40px; height: 40px; object-fit: cover; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border); cursor: pointer;
}
.item-row img:hover { border-color: var(--accent); }
.item-row .name { flex: 1; font-size: 0.89rem; }
.item-row .qty { color: var(--text-2); font-size: 0.86rem; font-variant-numeric: tabular-nums; }
.item-row.unavailable .name { text-decoration: line-through; color: var(--danger); }

/* Stat tiles for the overview screens. */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.stat-box {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--sp-4); box-shadow: var(--shadow);
}
.stat-box .label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-3); font-weight: 700; }
.stat-box .val { font-size: 1.5rem; font-weight: 700; margin-top: var(--sp-1); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat-box .sub { font-size: 0.78rem; color: var(--text-2); margin-top: 2px; line-height: 1.4; }

/* ============================================================================
   Forms & buttons
   ============================================================================
   The old sheet set `input, select, button { width: 100% }`, which is why
   nearly every control in the app carried style="width:auto" inline. Controls
   now size to their content by default; full width is asked for with .w-full.
   ============================================================================ */

input, select, textarea {
  font-family: inherit; font-size: 0.9rem; color: var(--text);
  padding: 8px 11px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--surface);
  max-width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input[type=checkbox], input[type=radio] { width: auto; accent-color: var(--accent); }
.w-full { width: 100%; }
label { font-size: 0.87rem; }

button, .btn {
  font-family: inherit; font-size: 0.88rem; font-weight: 600;
  padding: 8px 15px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  text-decoration: none; white-space: nowrap;
}
button:hover, .btn:hover { background: var(--surface-2); border-color: var(--text-3); }

/* The primary action on a screen. One per context — that is the whole point. */
button.primary, .btn.primary {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
button.primary:hover, .btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

button.secondary, .btn.secondary { background: var(--surface); color: var(--text-2); }
button.secondary:hover { color: var(--text); }

button.danger { color: var(--danger); border-color: #e8c4c2; }
button.danger:hover { background: var(--danger-soft); }

button.small, .btn.small { padding: 5px 10px; font-size: 0.82rem; }
button.icon-btn, .icon-btn {
  padding: 5px 9px; font-size: 0.82rem; color: var(--text-2); background: var(--surface);
}
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:disabled:hover { background: var(--surface); border-color: var(--border-strong); }

.actions { margin-top: var(--sp-3); display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }

/* A toolbar of filters above a table. */
.toolbar {
  display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
  margin-bottom: var(--sp-3); padding: var(--sp-3);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}

/* Sticky bar for bulk actions on the working boards. */
.bulk-bar {
  position: sticky; top: var(--topbar-h); z-index: 20;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-3); box-shadow: var(--shadow-md);
  display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
}
.bulk-count { font-weight: 650; font-variant-numeric: tabular-nums; }

/* ============================================================================
   Tables
   ============================================================================ */

table { width: 100%; border-collapse: collapse; }

/*
 * overflow-x: auto, not hidden.
 *
 * A seven-column table cannot shrink below its content, so on a phone it was
 * pushing the whole page wider than the screen: the stat tiles and the banners
 * above it were being clipped too, and the right-hand columns were simply gone
 * with no way to reach them. Scrolling the table inside its own box keeps the
 * rest of the page at the width of the screen.
 */
.table-wrap {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}

th {
  text-align: left; font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-3); font-weight: 700;
  padding: 10px var(--sp-3); background: var(--surface-2);
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
td {
  padding: 10px var(--sp-3); font-size: 0.87rem;
  border-bottom: 1px solid var(--border); vertical-align: middle;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

th a.sort { color: var(--text-3); text-decoration: none; font-weight: 700; }
th a.sort:hover, th a.sort.on { color: var(--accent); }

/* ============================================================================
   Badges
   ============================================================================
   Pale tint plus a darkened version of the same hue, which stays legible at
   this size where solid colours with white text did not.
   ============================================================================ */

.badge {
  display: inline-block; padding: 2px 9px; border-radius: 20px;
  font-size: 0.74rem; font-weight: 650; white-space: nowrap;
}
.badge.local  { background: var(--success-soft); color: var(--success); }
.badge.sendit { background: #fdf3dc; color: var(--warning); }

/* Order statuses (§1). Keyed by the colour NAMES from order_status_colors(),
   never by the status itself, so a status can be recoloured in one place. */
.badge.status-blue    { background: #e7edfb; color: #2c5099; }
.badge.status-yellow  { background: #fdf3dc; color: #8a6414; }
.badge.status-purple  { background: #efe8fa; color: #6344a5; }
.badge.status-teal    { background: #dcf1ee; color: #1f6f68; }
.badge.status-green   { background: var(--success-soft); color: var(--success); }
.badge.status-orange  { background: #fdead9; color: #a9682c; }
.badge.status-red     { background: var(--danger-soft); color: var(--danger); }
.badge.status-brown   { background: #f0e8df; color: #7a5533; }
.badge.status-neutral { background: #eef0f3; color: var(--text-2); }

/* ============================================================================
   Feedback: banners, empty and loading states
   ============================================================================ */

.banner {
  border-radius: var(--radius); padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-4); font-size: 0.87rem; line-height: 1.5;
  border: 1px solid transparent;
}
.banner.info    { background: var(--surface-2); border-color: var(--border); color: var(--text-2); }
.banner.ok      { background: var(--success-soft); border-color: #c3e3d2; color: var(--success); }
.banner.warn    { background: var(--warning-soft); border-color: #eedcb0; color: var(--warning); }
.banner.danger  { background: var(--danger-soft); border-color: #eec7c4; color: var(--danger); }

.error { color: var(--danger); font-size: 0.87rem; }

/* §13 asked for real empty states: a blank screen leaves people wondering
   whether it is broken or simply has nothing in it. */
.empty-state {
  text-align: center; padding: var(--sp-7) var(--sp-4); color: var(--text-2);
  background: var(--surface); border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}
.empty-state .big { font-size: 1.6rem; margin-bottom: var(--sp-2); opacity: 0.55; }
.empty-state .title { font-weight: 650; color: var(--text); margin-bottom: 3px; }
.empty-state .hint { font-size: 0.85rem; }

/* Shown while a fetch is in flight, so a slow sync does not look like nothing
   happening. */
.spinner {
  display: inline-block; width: 14px; height: 14px; vertical-align: -2px;
  border: 2px solid var(--border-strong); border-top-color: var(--accent);
  border-radius: 50%; animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.is-loading { opacity: 0.6; pointer-events: none; }

/* ============================================================================
   Notification bell (§9)
   ============================================================================ */

.bell-wrap { position: relative; display: inline-block; }
.bell {
  background: none; border: none; cursor: pointer; font-size: 1.1rem;
  padding: 5px 7px; position: relative; line-height: 1; border-radius: var(--radius-sm);
}
.bell:hover { background: var(--surface-2); border-color: transparent; }
.bell-badge {
  position: absolute; top: -2px; right: -2px; background: var(--danger); color: #fff;
  border-radius: 20px; font-size: 0.64rem; font-weight: 700; min-width: 15px;
  padding: 1px 4px; text-align: center; line-height: 1.35;
}
.bell-panel {
  position: absolute; right: 0; top: 36px; z-index: 50; width: 340px; max-width: 90vw;
  max-height: 72vh; overflow-y: auto; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: var(--sp-1);
}
.bell-item {
  display: flex; gap: var(--sp-2); align-items: flex-start; padding: 9px 10px;
  border-radius: var(--radius-sm); text-decoration: none; color: var(--text);
}
.bell-item:hover { background: var(--surface-2); }
.bell-item .bell-icon { font-size: 0.92rem; line-height: 1.35; }
.bell-title  { display: block; font-size: 0.85rem; font-weight: 600; line-height: 1.35; }
.bell-detail { display: block; font-size: 0.77rem; color: var(--text-2); line-height: 1.4; margin-top: 1px; }
.bell-item.loud { background: var(--danger-soft); }
.bell-item.loud:hover { background: #f8ddda; }
.bell-item.loud .bell-title { color: var(--danger); }
.bell-item.warn .bell-title { color: var(--warning); }
.bell-empty { padding: var(--sp-5); text-align: center; color: var(--text-2); font-size: 0.85rem; }

/* ============================================================================
   Undo toast
   ============================================================================ */

.undo-toast {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 22px; z-index: 60;
  background: var(--text); color: #fff; border-radius: var(--radius);
  padding: 11px var(--sp-4); display: flex; gap: var(--sp-3); align-items: center;
  box-shadow: var(--shadow-lg); font-size: 0.86rem; max-width: 92vw;
}
.undo-toast button {
  background: rgba(255,255,255,0.15); color: #fff; border: none;
  border-radius: var(--radius-sm); padding: 5px 11px; font-weight: 700; font-size: 0.83rem;
}
.undo-toast button:hover { background: rgba(255,255,255,0.28); }

/* ============================================================================
   Login — the one screen with no shell
   ============================================================================ */

/* Screens with no sidebar (login, and the print documents) drop the inset. */
body.no-shell { padding-left: 0; }

.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--sp-4); }
.login-wrap .card { width: 100%; max-width: 380px; margin: 0; }
.login-wrap h2 { text-align: center; margin: 0 0 var(--sp-5); }
.login-wrap input { width: 100%; margin-bottom: var(--sp-3); }
.login-wrap button { width: 100%; justify-content: center; }

/* ============================================================================
   The hidden print frame
   ============================================================================
   Deliberately NOT display:none. A frame with no layout box is never rendered,
   and a browser will not print a document it has not laid out. One pixel,
   transparent, pinned out of the way and not clickable.
   ============================================================================ */

.print-frame {
  position: fixed; right: 0; bottom: 0;
  width: 1px; height: 1px;
  opacity: 0; border: 0;
  pointer-events: none;
}

/* ============================================================================
   Notices — the answer to "did that work?"
   ============================================================================
   settings.php has emitted <div class="notice ok|bad"> since §10 and nothing in
   this file ever styled it, so every confirmation and every refusal in
   Paramètres rendered as a bare line of text among the headings. Retours uses it
   now too, to say why a return was refused.
   ============================================================================ */

.notice {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  margin-bottom: var(--sp-4);
  font-size: 0.88rem;
}
.notice.ok  { background: var(--success-soft); border-color: #bfe0cd; color: var(--success); }
.notice.bad { background: var(--danger-soft);  border-color: #f2c9c6; color: var(--danger); }

/* ============================================================================
   Charts
   ============================================================================
   Marks carry the colour; every piece of text wears a text token. The grid is a
   hairline one step off the surface, the line is 2px, fills are thin and the
   area under the trend is a 10% wash rather than a block — a chart should be the
   quietest thing on the page until you look at it.

   Only the trend is SVG, and it holds no text: text inside a stretched SVG
   stretches with it. See includes/charts.php.
   ============================================================================ */

.chart {   /* same shell as .card, so a chart sits on the page like anything else */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--sp-4);
  margin: 0 0 var(--sp-4);
}
.chart-title {
  font-size: 0.9rem; font-weight: 600; color: var(--text);
  margin-bottom: var(--sp-4);
}
.chart-empty { color: var(--text-3); font-size: 0.85rem; margin: 0; }

/* --- trend ---------------------------------------------------------------- */

.chart-plot { display: flex; gap: var(--sp-2); }

.chart-yaxis {
  position: relative; width: 48px; flex: 0 0 48px;
  height: 180px;
}
.chart-yaxis span {
  position: absolute; right: 0; transform: translateY(50%);
  font-size: 0.7rem; color: var(--text-3);
  font-variant-numeric: tabular-nums;   /* ticks align vertically */
}

/* The right padding is the end dot's room: without it the last point sits on
   the card's edge and loses its outer half. */
.chart-canvas { position: relative; flex: 1 1 auto; min-width: 0; height: 180px; padding-right: 7px; }
.chart-svg    { display: block; width: 100%; height: 100%; overflow: visible; }

.ch-grid { stroke: var(--chart-grid); stroke-width: 1; fill: none; }
.ch-area { fill: var(--chart-1); fill-opacity: 0.10; stroke: none; }
.ch-line { stroke: var(--chart-1); stroke-width: 2; fill: none; stroke-linejoin: round; stroke-linecap: round; }

/* Zero-length round-capped strokes: a circle that survives a stretched
   viewBox, where a <circle> would render as an ellipse. The ring is the
   surface colour and 4px wider, so the dot stays legible over the line. */
.ch-dot-ring { stroke: var(--surface); stroke-width: 12; stroke-linecap: round; fill: none; }
.ch-dot      { stroke: var(--chart-1); stroke-width: 8;  stroke-linecap: round; fill: none; }

/* Full-height hit bands — the tooltip never asks you to land on the line. */
.ch-hit { fill: transparent; }
.ch-hit:hover { fill: var(--chart-1); fill-opacity: 0.06; }

/* Sits just ABOVE the last point, not on it — the chip's white background was
   covering the dot it is supposed to label. The 15% headroom in the y-scale is
   what guarantees there is room up there. */
.chart-endlabel {
  position: absolute; right: 0; transform: translateY(-9px);
  background: var(--surface); padding: 1px var(--sp-1);
  border-radius: var(--radius-sm);
  font-size: 0.72rem; font-weight: 600; color: var(--text);
  white-space: nowrap;
}

.chart-xaxis {
  position: relative; height: 16px;
  margin-left: 56px; margin-top: var(--sp-1);
}
.chart-xaxis span {
  position: absolute; transform: translateX(-50%);
  font-size: 0.7rem; color: var(--text-3); white-space: nowrap;
}
.chart-xaxis span.at-start { transform: none; left: 0; }
.chart-xaxis span.at-end   { transform: none; left: auto; right: 0; }

/* --- bars ----------------------------------------------------------------- */

.chart-bars { list-style: none; margin: 0; padding: 0; }
.chart-bars li { margin-bottom: var(--sp-3); }
.chart-bars li:last-child { margin-bottom: 0; }

.chart-bar-name {
  display: block; font-size: 0.78rem; color: var(--text-2);
  margin-bottom: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The value rides the tip of its own bar, and the track reserves 52px on the
   right for it to sit in.
   Two nested boxes, not one: the reservation is padding on the TRACK, and both
   percentages — the fill's width and the value's offset — are read against the
   RAIL inside it, so they always agree. Putting the value on the same flex line
   as the bar (the obvious way) makes a wider number squeeze its own bar, and
   then the lengths stop being comparable, which is the one thing a bar chart
   has to get right. */
.chart-bar-track { display: block; padding-right: 52px; }
.chart-bar-rail  { display: block; position: relative; }
.chart-bar-fill {
  display: block;
  height: 10px; min-width: 2px;          /* capped well under 24px: thin marks */
  background: var(--chart-1);
  border-radius: 0 4px 4px 0;            /* rounded data-end, square at baseline */
}
.chart-bar-value {
  position: absolute; top: 50%; transform: translateY(-50%);
  margin-left: var(--sp-2);
  font-size: 0.75rem; font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* --- share bar ------------------------------------------------------------ */

.chart-share { display: flex; gap: 2px; height: 22px; }   /* 2px of surface, not a border */
.chart-share-seg {
  display: flex; align-items: center; justify-content: center;
  min-width: 2px; overflow: visible;
}
.chart-share-seg:first-child { border-radius: 4px 0 0 4px; }
.chart-share-seg:last-child  { border-radius: 0 4px 4px 0; }
.chart-share-seg span {
  font-size: 0.72rem; font-weight: 600; color: #fff;   /* inside a dark fill */
}
.chart-share-seg.s1 { background: var(--chart-1); }
.chart-share-seg.s2 { background: var(--chart-2); }

.chart-legend {
  list-style: none; margin: var(--sp-3) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
  font-size: 0.78rem; color: var(--text-2);
}
.chart-legend li { display: flex; align-items: center; gap: var(--sp-1); }
.chart-legend b  { color: var(--text); font-variant-numeric: tabular-nums; }
.chart-legend span { color: var(--text-3); }
.chart-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }
.chart-swatch.s1 { background: var(--chart-1); }
.chart-swatch.s2 { background: var(--chart-2); }

/* Two charts side by side where there is room; see the Responsive block. */
.chart-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }

/* ============================================================================
   Sections on a long page
   ============================================================================

   Every working screen is a long scroll made of groups — the dispatch board by
   destination, Stock by category, Retours and Caisse by what stage something is
   at. The headings between them were a plain <h2> in the accent colour, which
   disappears the moment it scrolls off: you end up reading a row of numbers with
   no idea which group you are in.

   Two things fix that, and they are the same two the dispatch board proved out:

   1. The heading STICKS at the top while its own section is on screen. It must
      pin at --topbar-h, not 0 — .topbar is itself sticky at 0 with z-index 30,
      so anything pinned at 0 slides underneath it and is simply gone. That bug
      shipped twice (pick-list's bulk bar, and the board's first version), which
      is why the rule lives here now rather than in each page.

   2. It carries a COLOUR — a band down the left plus matching text — so the
      boundaries are findable at arm's length rather than read one at a time.

   Each section needs its own wrapper, because a sticky element is released by
   the end of its containing block. Sibling headings sharing one parent would
   stack on top of each other at the top of the window instead of handing over.
   ============================================================================ */

.section { margin-bottom: var(--sp-5); }

.section-head {
  position: sticky; top: var(--topbar-h); z-index: 20;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-left: 6px solid var(--text-3);
  border-radius: var(--radius);
  padding: 10px var(--sp-4); margin: 0 0 var(--sp-3);
  display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
  box-shadow: var(--shadow-md);
  font-size: 1rem; font-weight: 700; letter-spacing: 0.01em;
}
.section-head .count { font-size: 0.84rem; font-weight: 600; color: var(--text-2);
                       font-variant-numeric: tabular-nums; }
.section-head .spacer { flex: 1; }
/* Anything that follows the title on the same line — a total, a hint, a button. */
.section-head .note { font-size: 0.84rem; font-weight: 500; color: var(--text-2); }

/* The four meanings, using the status palette rather than new colours. */
.section-head.accent { border-color: #c3cdf5; border-left-color: var(--accent);
                       background: var(--accent-soft); color: var(--accent); }
.section-head.good   { border-color: #bfdfcd; border-left-color: var(--success);
                       background: var(--success-soft); color: var(--success); }
.section-head.warn   { border-color: #eedcb0; border-left-color: var(--warning);
                       background: var(--warning-soft); color: var(--warning); }
.section-head.danger { border-color: #eec7c4; border-left-color: var(--danger);
                       background: var(--danger-soft); color: var(--danger); }

/* ============================================================================
   Client confirmation and the WhatsApp to-do list (28/09)
   ============================================================================ */

/* This client refused a parcel or never answered before — on every card. */
.client-warn { background: var(--danger-soft); border: 1px solid #f2c9c6; color: var(--danger);
               border-radius: 8px; padding: 6px 12px; font-size: 0.8rem; line-height: 1.45;
               margin: 6px 0 10px; }

/* One order waiting for a message, or for the client's answer. */
.msg-row { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
           padding: 12px 14px; margin-bottom: 8px;
           display: flex; gap: 14px; justify-content: space-between; flex-wrap: wrap; }
.msg-row.overdue { border-color: #eedcb0; background: #fffaf0; }
.msg-main { flex: 1 1 360px; min-width: 0; }
.msg-title { font-size: 0.92rem; line-height: 1.45; }
.msg-sub  { font-size: 0.82rem; color: var(--text-2); margin-top: 3px; line-height: 1.5; }
.msg-sent { font-size: 0.8rem; color: var(--success); margin-top: 4px; }
.msg-due  { font-size: 0.8rem; color: var(--warning); font-weight: 600; margin-top: 4px; }
.msg-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; align-self: center; }
.msg-actions select, .msg-actions button, .msg-actions .btn-link { width: auto; margin: 0; }
.msg-actions form { display: inline-flex; gap: 6px; margin: 0; align-items: center; }
.msg-actions .btn-link { font-size: 0.82rem; padding: 6px 10px; border-radius: 8px; text-decoration: none;
                         border: 1px solid var(--border-strong); color: var(--text); background: var(--surface); }

/* ============================================================================
   The order review (30/09): « Modifier », badges, messages from other scripts
   ============================================================================ */

/* « ✏️ Modifier l'adresse / le téléphone » — closed until someone needs it. */
.contact-edit { margin: 6px 0 8px; font-size: 0.82rem; }
.contact-edit summary { cursor: pointer; color: var(--accent); font-weight: 600; list-style: none; width: fit-content; }
.contact-edit summary::-webkit-details-marker { display: none; }
.contact-form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin-top: 8px;
                background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 10px; }
.contact-form label { display: flex; flex-direction: column; gap: 3px; font-size: 0.76rem; color: var(--text-2); margin: 0; }
.contact-form input { margin: 0; min-width: 150px; }
.contact-form label:first-of-type input { min-width: 260px; }
.contact-form button { width: auto; margin: 0; }
.contact-changed { font-size: 0.8rem; color: var(--warning); font-weight: 600; margin: 4px 0; }
/* The bon in the bag shows the old address: as loud as a WooCommerce edit after printing. */
.contact-changed.loud { background: var(--danger-soft); border: 1px solid #f2c9c6; color: var(--danger);
                        border-radius: 8px; padding: 6px 12px; line-height: 1.45; }

/*
 * A client-typed address with no spaces (a pasted map link, « rue5hayriad… »)
 * pushed cards wider than a phone screen (QA, 30/09). Break anywhere rather
 * than scroll sideways.
 */
.order-meta, .msg-title, .msg-sub, .card-note, .client-warn, .contact-changed, .as-row, .as-note,
.flash-toast, .notice, .banner, .kv dd,
.sync-fail, .sync-fail-row { overflow-wrap: anywhere; }   /* WooCommerce's error text is raw JSON */
/* « Remises / Dispatchées récemment »: the name wraps, the buttons stay on screen. */
.recent-row { flex-wrap: wrap; }
.recent-row > span:first-child { min-width: 0; flex: 1 1 180px; overflow-wrap: anywhere; }

/*
 * A card or panel a link or a redirect lands on (#o123, #apres-vente) stops
 * below the sticky top bar instead of under it.
 */
.msg-row[id], .section[id], .panel[id], [data-focus] { scroll-margin-top: calc(var(--topbar-h) + 12px); }
/* Cards on Assemblage / Dispatch sit under a sticky bulk bar or group heading too (~63 px). */
.order-card[id] { scroll-margin-top: calc(var(--topbar-h) + 80px); }
/* The row a link from Assemblage asked for. */
.msg-row.focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* A row whose message is being opened: nothing else to click until it is done. */
.msg-row[data-busy] { opacity: 0.6; }

/* A same-client or exchange note on a card. */
.card-note { font-size: 0.8rem; border-radius: 8px; padding: 6px 12px; margin: 6px 0 10px; line-height: 1.45;
             background: var(--accent-soft); color: var(--accent); border: 1px solid #c3cdf5; }

/* The outcome of an action posted to another script, top right, for a few seconds. */
.flash-toast { position: fixed; top: calc(var(--topbar-h) + 10px); right: 16px; z-index: 60; max-width: min(520px, 92vw);
               padding: 10px 40px 10px 14px; border-radius: var(--radius); font-size: 0.86rem; line-height: 1.45;
               box-shadow: var(--shadow-lg); }
.flash-toast.ok  { background: var(--success-soft); color: var(--success); border: 1px solid #bfe0cd; }
.flash-toast.bad { background: var(--danger-soft);  color: var(--danger);  border: 1px solid #f2c9c6; }
.flash-toast button { position: absolute; top: 6px; right: 8px; width: auto; margin: 0; padding: 2px 6px;
                      background: transparent; border: none; color: inherit; cursor: pointer; }

/* ============================================================================
   Responsive
   ============================================================================
   Last in the file on purpose: these override base rules of the same
   specificity, and CSS resolves that by source order.
   ============================================================================ */

@media (max-width: 900px) {
  /* The sidebar becomes a drawer. A permanent 224px column on a phone leaves
     nothing for the actual work, and Bilal and Aya are on phones. */
  body { padding-left: 0; overflow-x: hidden; }
  .sidebar { transform: translateX(-100%); box-shadow: var(--shadow-lg); }
  .sidebar.open { transform: translateX(0); }
  .scrim.open { display: block; }
  .burger { display: block; }
  .topbar { padding: 0 var(--sp-3); }
  main, main.dense { padding: var(--sp-3); }
  .bulk-bar { top: 0; }
}

@media (max-width: 560px) {
  /* Two 180px tiles plus the gap do not fit a 390px phone — they were cut off
     at the right edge. One per row below this width. */
  .stat-grid { grid-template-columns: 1fr; }

  /* Filters stack instead of being squeezed off screen. */
  .toolbar { padding: var(--sp-2); }
  .toolbar input, .toolbar select { flex: 1 1 100%; min-width: 0; width: 100%; }

  .bulk-bar { padding: var(--sp-2) var(--sp-3); }

  /* The primary action goes full width: it is the thing being reached for,
     usually one-handed and mid-task. */
  .bulk-bar button.primary, .actions button.primary { flex: 1 1 100%; justify-content: center; }

  .order-header { gap: var(--sp-2); }
  .item-row { flex-wrap: wrap; }
  .bell-panel { width: calc(100vw - 24px); right: -8px; }

  /* The card « Modifier » comes back to (#o123) clears Dispatch's sticky group
     heading, which wraps to ~160 px on a phone — it hid the card's own header
     (QA, 30/09). And the outcome toast sits at the bottom, not over that
     heading and its « Dispatcher » button. */
  .order-card[id] { scroll-margin-top: 230px; }
  .flash-toast { top: auto; bottom: 12px; left: 12px; right: 12px; max-width: none; }

  /* Charts: one per row, a narrower y-axis gutter, and a shorter plot so the
     card still fits on screen with its axis labels — the axis band is part of
     the card's height here, never a nested scroll. */
  .chart-row { grid-template-columns: 1fr; }
  .chart { padding: var(--sp-3); }
  .chart-yaxis { width: 38px; flex-basis: 38px; height: 140px; }
  .chart-canvas { height: 140px; }
  .chart-xaxis { margin-left: 46px; }
  .chart-xaxis span { font-size: 0.65rem; }
}

/* ============================================================================
   Print — the shell must not appear on paper
   ============================================================================ */

@media print {
  .sidebar, .topbar, .bell-wrap, .undo-toast, .scrim, .no-print { display: none !important; }
  body { background: #fff; padding-left: 0; }
  main { padding: 0; max-width: none; }
}
