/* app.css — CUSTOM STRUCTURES (components & layout shell).
   Colors come from theme.css variables; spacing/alignment helpers live in
   utils.css. Load order: theme.css -> app.css -> utils.css. */

/* ---- Order count badges (customer list) -------------------------------- */
.order-badge {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 24px; height: 24px; border-radius: 6px;
    font-size: 12px; font-weight: 700; padding: 0 7px;
    cursor: default; user-select: none;
}
.badge-blue  { background: var(--blue-100,  #dbeafe); color: var(--blue-700,  #1d4ed8); }
.badge-amber { background: var(--amber-100, #fef3c7); color: var(--amber-700); }
.badge-green { background: var(--green-100, #dcfce7); color: var(--green-700, #15803d); }
/* On a phone there is no hover tooltip: each count says what it counts. */
.order-badge-word { display: none; }
@media (max-width: 900px) {
  .order-badge-word { display: inline; margin-inline-end: 4px; font-weight: 500; }
  .order-badge.opacity-25 { opacity: .55; }
}

/* ---- Info-bulle (tooltip.js) -------------------------------------------
   One tooltip for the whole app (picked 2026-10-23 from the gallery: A1 ink
   bubble, B1 grey "i", C2 bold title + sentence, E1 above). The bubble is
   --ink on --surface, so in dark mode it turns light on its own. tip() /
   tip_attrs() in core/view.php write the markup; any native title is shown
   in it too. On a phone the same text opens in .tip-sheet. */
.tip-popup {
    position: absolute; z-index: 9999; display: block;
    background: var(--ink); color: var(--surface);
    border-radius: 10px; padding: 9px 13px;
    font-size: 13px; font-weight: 400; line-height: 1.45; text-align: start;
    max-width: 280px; white-space: pre-line; overflow-wrap: anywhere;
    box-shadow: 0 8px 24px rgba(15, 23, 42, .22);
    pointer-events: none;
}
.tip-popup::after {
    content: ''; position: absolute; top: 100%; left: var(--tip-arrow, 50%); transform: translateX(-50%);
    border: 6px solid transparent; border-top-color: var(--ink);
}
.tip-popup.is-below::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--ink); }
.tip-title { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 3px; white-space: normal; }
.tip-title .icon { width: 14px; height: 14px; flex: none; color: #7cc7dc; }
/* Pill rows ([{label, count, class}] in data-tip): one line each. */
.tip-popup.tip-rows { white-space: nowrap; max-width: none; }
.tip-row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.tip-label { opacity: .8; }
.tip-pill {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 18px; border-radius: 6px;
    font-size: 11px; font-weight: 700; padding: 0 6px;
}
.tip-pill.pill-blue  { background: #1d4ed8; color: #fff; }
.tip-pill.pill-amber { background: #b45309; color: #fff; }
.tip-pill.pill-green { background: #15803d; color: #fff; }

/* The "i" after a label, column head or tile label — tip(). */
.tip-i {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 15px; height: 15px; margin-inline-start: 4px; vertical-align: -2px;
    color: var(--faint); cursor: help; border-radius: 50%;
    font-weight: 400; text-transform: none; letter-spacing: 0;
}
.tip-i .icon { width: 15px; height: 15px; }
.tip-i:hover, .tip-i:focus-visible { color: var(--primary); }
.tip-i:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* A finger needs more than 15px: the tap area grows, the icon does not. */
@media (pointer: coarse) { .tip-i { position: relative; } .tip-i::before { content: ''; position: absolute; inset: -10px; } }
/* An element explained by tip_attrs() (a pill, a flag) says so on hover. */
[data-tip]:not(a):not(button):not(.tip-i) { cursor: help; }

/* Phone: the explanation in a sheet from the bottom (F2). */
.tip-sheet { position: fixed; inset: 0; z-index: 10000; visibility: hidden; }
.tip-sheet.is-open { visibility: visible; }
.tip-sheet-back { position: absolute; inset: 0; background: rgba(15, 23, 42, .42); opacity: 0; transition: opacity .18s ease; }
.tip-sheet-panel {
    position: absolute; left: 0; right: 0; bottom: 0;
    background: var(--surface); color: var(--text);
    border-radius: 16px 16px 0 0; padding: 10px 20px calc(18px + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 28px rgba(15, 23, 42, .2);
    transform: translateY(100%); transition: transform .2s ease;
}
.tip-sheet.is-open .tip-sheet-back { opacity: 1; }
.tip-sheet.is-open .tip-sheet-panel { transform: none; }
.tip-sheet-grip { width: 38px; height: 4px; border-radius: 4px; background: var(--slate-300); margin: 0 auto 14px; }
.tip-sheet-body { font-size: 15px; line-height: 1.5; margin-bottom: 16px; white-space: pre-line; }
.tip-sheet-body .tip-title { font-size: 16px; color: var(--ink); margin-bottom: 6px; }
.tip-sheet-body .tip-title .icon { width: 18px; height: 18px; color: var(--primary); }
.tip-sheet-body .tip-row { padding: 4px 0; }
.tip-sheet-ok { width: 100%; justify-content: center; padding-block: 11px; }

/* Reset & base typography */
*, *::before, *::after { box-sizing: border-box; }
/* Wide, legitimately-scrollable content nested in an overflow-x:auto box
   (.table-wrap and friends) clips visually, but its un-clipped intrinsic
   width still leaks into the ROOT element's own scrollWidth/scrollable
   range in this engine — so without this, a sideways swipe anywhere on
   the page (not just on the table) drags the whole viewport into that
   phantom empty space past the real content, rendering a blank page.
   This is the containment that stops it; inner elements keep their own
   overflow-x:auto for real horizontal scrolling.
   html only, NOT body: setting overflow-x on body too forces body's
   overflow-y to compute as 'auto' (the CSS overflow-x/y pairing rule),
   which turns body into its own scroll container. Since body never
   actually overflows itself (it just grows to fit .app), it doesn't
   scroll — but it still becomes the nearest scroll ancestor for
   .sidebar's position:sticky, so the sidebar stops tracking the real
   page scroll (which happens on html) and scrolls away with the page
   instead of staying pinned. */
html { overflow-x: hidden; }
html, body { margin: 0; padding: 0; }
/* The two type families, as tokens so a skin swaps them in one place
   (readable.css points them at the vendored IBM Plex). */
:root {
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}
body {
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  font-size: 14px;
  line-height: 1.5;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font-family: inherit; }
.icon { width: 20px; height: 20px; flex: 0 0 auto; }
.icon-sm { width: 16px; height: 16px; flex: 0 0 auto; }

/* App shell */
.app { display: flex; min-height: 100vh; }
/* --app-banner-h: measured live by footer.php from whatever [data-app-banner]
   elements render above .app (preview mode / driver offline) — a nested-flex
   "stretch to fill the remaining space" approach was tried and reverted here:
   it doesn't reliably cap .sidebar when its OWN content (a long nav list) is
   naturally taller than one viewport, which .nav's own overflow-y:auto is
   supposed to handle — the stretch chain let it grow the whole page instead.
   calc() against a JS-measured height is simple and doesn't fight that. */
.sidebar {
    width: 200px; flex: 0 0 200px; border-right: 1px solid var(--line); display: flex; flex-direction: column;
    position: sticky; top: var(--app-banner-h, 0px); height: calc(100vh - var(--app-banner-h, 0px));
    transition: width .15s ease, flex-basis .15s ease;
    background-color: var(--surface);
}

/* Manual desktop collapse (persisted via localStorage) — reuses the same
   icon-only rail look the ≤880px breakpoint already falls back to. Targets
   both sidebars (.sidebar admin, .m-sidebar field shell) — everything below
   this (.nav-item, .nav-group-head, .nav-foot-row, …) is already unscoped to
   either one, so it applies to both automatically once the width collapses. */
html.sidebar-collapsed .sidebar, html.sidebar-collapsed .m-sidebar { width: 68px; flex-basis: 68px; }
html.sidebar-collapsed .brand-text,
html.sidebar-collapsed .nav-label,
html.sidebar-collapsed .nav-group-label { display: none; }
html.sidebar-collapsed .nav-item { justify-content: center; padding: 10px; }
html.sidebar-collapsed .brand { justify-content: center; padding: 16px 0; }
/* Two distinct chevrons swapped via the same class, not one icon flipped —
   pure CSS (driven by the class the no-FOUC <head> script already sets
   before paint) so there's never a flash of the wrong direction on load. */
.sidebar-collapse-toggle .icon-collapse,
.sidebar-collapse-toggle .icon-expand { display: grid; grid-area: 1 / 1; place-items: center; }
.sidebar-collapse-toggle .icon-expand { visibility: hidden; }
html.sidebar-collapsed .sidebar-collapse-toggle .icon-collapse { visibility: hidden; }
html.sidebar-collapsed .sidebar-collapse-toggle .icon-expand { visibility: visible; }
.brand { position: relative; display: flex; align-items: center; gap: 10px; padding: 12px; border-bottom: 1px solid var(--line-soft); }
.brand-mark {
    position: relative;
    width: 32px; height: 32px; border-radius: 6px; color: var(--white); display: grid; place-items: center;
    background: linear-gradient(135deg, var(--emerald-400), var(--emerald-700));
    box-shadow: 0 2px 8px -3px rgba(5, 150, 105, .5), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.brand-mark .icon { width: 17px; height: 17px; }
.brand-name { font-weight: 700; font-size: 15px; letter-spacing: -.01em; color: var(--ink); }
.brand-sub { font-size: 11px; color: var(--faint); }

/* Persistent sidebar revenue card (admin only — resources/views/partials/header.php)
   Same dark-gradient family as the dashboard's .hero-kpi banner, scaled down
   to the sidebar's width. */
.sidebar-kpi {
    margin: 8px 8px 2px;
    padding: 8px 10px 7px;
    border-radius: 6px;
    background: linear-gradient(135deg, var(--slate-900) 0%, var(--ink) 55%, var(--emerald-900) 150%);
    color: var(--white);
}
.sidebar-kpi-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sidebar-kpi-label { font-size: 9px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--slate-300); }
.sidebar-kpi-badge { display: inline-flex; align-items: center; gap: 2px; font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sidebar-kpi-badge.is-up { color: var(--success-400); }
.sidebar-kpi-badge.is-down { color: var(--red-400); }
.sidebar-kpi-arrow { font-size: 8px; }
.sidebar-kpi-value { margin-top: 3px; font-size: 16px; font-weight: 800; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.sidebar-kpi-unit { font-size: 10px; font-weight: 600; color: var(--slate-400); }
.sidebar-kpi-sub { margin-top: 1px; font-size: 10px; color: var(--slate-400); }
html.sidebar-collapsed .sidebar-kpi { display: none; }

.nav { flex: 1; overflow-y: auto; padding: 10px 8px; scrollbar-width: thin; scrollbar-color: var(--slate-300) transparent; }
/* A scrolled menu passes through the top padding right under the revenue card;
   fade it there so a group label slides away instead of showing cut in half. */
.nav { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12px); mask-image: linear-gradient(to bottom, transparent, #000 12px); }
.nav::-webkit-scrollbar { width: 6px; }
.nav::-webkit-scrollbar-thumb { background: var(--slate-300); border-radius: 999px; }
.nav::-webkit-scrollbar-thumb:hover { background: var(--emerald-400); }
.nav::-webkit-scrollbar-track { background: transparent; }
.nav-group { margin-bottom: 12px; }
.nav-group:last-child { margin-bottom: 0; }
.nav-group-label { padding: 0 6px 6px 4px; font-size: 10px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--faint); transition: color .15s ease; }
.nav-group-label.is-active { color: var(--primary-strong); }

/* Collapsible group (>2 items) — header button that folds its items away
   instead of always taking full vertical space. Items sit flush with the
   top-level rows (no indent, no connector line). */
.nav-group-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    width: 100%; padding: 0 6px 6px 4px; border: 0; background: transparent; cursor: pointer; font: inherit; text-align: left;
}
.nav-group-head .nav-group-label { padding: 0; }
.nav-group-chevron { color: var(--faint); transition: transform .15s ease, color .15s ease; }
.nav-group-head:hover .nav-group-chevron { color: var(--slate-500); }
.nav-group-head.is-active .nav-group-label,
.nav-group-head.is-active .nav-group-chevron { color: var(--primary-strong); }
.nav-group.is-collapsed .nav-group-chevron { transform: rotate(-90deg); }
.nav-group.is-collapsed .nav-group-items { display: none; }
html.sidebar-collapsed .nav-group-head { display: none; }
/* Icon rail has no chevron to re-expand a group with — never hide items there. */
html.sidebar-collapsed .nav-group.is-collapsed .nav-group-items { display: block; }

.nav-item {
    position: relative; display: flex; align-items: center; gap: 8px; padding: 6px 8px 6px 4px;
    border-radius: 6px; color: var(--slate-600); font-weight: 500; font-size: 12.5px; margin-bottom: 1px;
    transition: background .12s ease, color .12s ease;
}
.nav-item .icon {
    width: 15px; height: 15px; padding: 5px; box-sizing: content-box;
    border-radius: 6px; background: transparent; color: var(--faint);
    transition: background .15s ease, color .15s ease;
}
.nav-item:hover { background: var(--hover); color: var(--ink); }
/* 200px sidebar: one line per row — a long label ends in "…" (the full text
   is the row's title tooltip) instead of wrapping to a taller row. */
.nav-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-item:hover .icon { background: var(--slate-100); color: var(--slate-600); }
.nav-item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.nav-item.active {
    background: var(--primary-soft);
    color: var(--primary-strong); font-weight: 600;
}
.nav-item.active .icon {
    background: var(--surface); color: var(--primary);
    box-shadow: inset 0 0 0 1px rgba(5, 150, 105, .15);
}
/* Left accent bar on the active item — sits in the gutter to its left, so it
   reaches the sidebar's own edge. */
.nav-item.active::before {
    content: ""; position: absolute; left: -8px; top: 6px; bottom: 6px;
    width: 3px; border-radius: 0 6px 6px 0; background: var(--primary);
}
html.sidebar-collapsed .nav-item .icon { background: transparent; }
html.sidebar-collapsed .nav-item.active .icon { background: var(--surface); }
html.sidebar-collapsed .nav-item.active::before { display: none; }

/* Super admin's eye on each sidebar row: hide / show that page for everyone
   else (modules/page_visibility/). Shown on hover, always on a hidden row
   (which is dimmed) and on touch screens. */
.nav-row { position: relative; }
.nav-row .nav-item { padding-inline-end: 28px; }
.nav-row-toggle {
    position: absolute; inset-inline-end: 4px; top: 50%; transform: translateY(-50%);
    display: flex; padding: 4px; border: 0; border-radius: 6px;
    background: transparent; color: var(--faint); cursor: pointer; opacity: 0;
}
.nav-row:hover .nav-row-toggle, .nav-row-toggle:focus-visible, .nav-row.is-page-hidden .nav-row-toggle { opacity: 1; }
.nav-row-toggle:hover { background: var(--slate-100); color: var(--ink); }
.nav-row.is-page-hidden .nav-item { opacity: .45; }
@media (hover: none) { .nav-row-toggle { opacity: 1; } }
html.sidebar-collapsed .nav-row-toggle { display: none; }
html.sidebar-collapsed .nav-row .nav-item { padding-inline-end: 10px; }

/* Group colors — each sidebar group (header.php's data-group slug) gets its
   own color (theme.css --nav-*) on its label, icons and active
   row, so Catalogue / Achats / Ventes / … read as separate blocks. A group
   with no entry here keeps the neutral look above. Selectors carry
   .nav-group[data-group] so they outrank the skin's .nav-item.active rules. */
.nav-group[data-group="main"]                  { --group-c: var(--nav-main); }
.nav-group[data-group="catalog"],
.nav-group[data-group="suppliers-products"],
.nav-group[data-group="pricing"]               { --group-c: var(--nav-catalog); }
.nav-group[data-group="purchasing"],
.nav-group[data-group="receiving"]             { --group-c: var(--nav-purchasing); }
.nav-group[data-group="sales"],
.nav-group[data-group="shipping"],
.nav-group[data-group="delivery"]              { --group-c: var(--nav-sales); }
.nav-group[data-group="operations"],
.nav-group[data-group="preparation"]           { --group-c: var(--nav-operations); }
.nav-group[data-group="finance"]               { --group-c: var(--nav-finance); }
.nav-group[data-group="insights"],
.nav-group[data-group="monitoring"]            { --group-c: var(--nav-insights); }
.nav-group[data-group="warehouse"]             { --group-c: var(--nav-warehouse); }
.nav-group[data-group="administration"]        { --group-c: var(--nav-admin); }

.nav-group[data-group] .nav-group-label,
.nav-group[data-group] .nav-group-head .nav-group-chevron { color: var(--group-c, var(--faint)); }
.nav-group[data-group] .nav-item .icon { color: var(--group-c, var(--faint)); }
.nav-group[data-group] .nav-item:hover .icon { background: color-mix(in srgb, var(--group-c, var(--slate-500)) 12%, transparent); }
.nav-group[data-group] .nav-item.active { background: color-mix(in srgb, var(--group-c, var(--slate-500)) 10%, transparent); }
.nav-group[data-group] .nav-item.active .icon {
    color: var(--group-c, var(--ink));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--group-c, var(--slate-500)) 30%, transparent);
}
.nav-group[data-group] .nav-item.active::before { background: var(--group-c, var(--ink)); }
html.sidebar-collapsed .nav-group[data-group] .nav-item .icon { background: transparent; }
html.sidebar-collapsed .nav-group[data-group] .nav-item.active .icon { background: var(--surface); }

/* Sidebar sub-menu (partials/nav_items.php) — a row that opens its links
   underneath, e.g. Reliquats -> Fournisseur / Client. */
.nav-sub-toggle { width: 100%; border: 0; background: transparent; font: inherit; text-align: left; cursor: pointer; }
.nav-sub-toggle.is-active { color: var(--primary-strong); }
.nav-sub-chevron { width: 14px; height: 14px; margin-left: auto; color: var(--faint); transition: transform .15s ease; }
.nav-sub:not(.is-open) .nav-sub-chevron { transform: rotate(-90deg); }
.nav-sub-items { display: none; margin: 1px 0 4px 12px; padding-left: 8px; border-left: 1px solid var(--line-soft); }
.nav-sub.is-open .nav-sub-items { display: block; }
.nav-sub-items .nav-item.active::before { left: -9px; }
/* Icon rail: no label to click open — show the links themselves. */
html.sidebar-collapsed .nav-sub-toggle { display: none; }
html.sidebar-collapsed .nav-sub-items { display: block; margin: 0; padding-left: 0; border-left: 0; }

/* Sidebar bells (notifications_nav_bell_html(), modules/notifications/) — a
   ringing bell and a count at the end of each row with unread notifications
   under it. The topbar badge's red: both mean "unread". It swings once when
   the page opens and again when the live poll brings one more (.is-new). */
.nav-bell {
    flex: none; display: inline-flex; align-items: center; gap: 3px;
    height: 18px; min-width: 18px; padding: 0 6px 0 5px; margin-inline-start: auto;
    border-radius: 999px; background: var(--red-500); color: var(--white);
    font-size: 10.5px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .2);
}
.nav-bell[hidden] { display: none; }
.nav-bell-icon, .nav-group-bell-icon {
    width: 11px; height: 11px; flex: none; transform-origin: 50% 1px;
    animation: nav-bell-ring 1.6s ease-in-out .35s 1;
}
.nav-bell.is-new { animation: nav-bell-pop .45s ease-out; }
.nav-bell.is-new .nav-bell-icon { animation: nav-bell-ring 1.6s ease-in-out 1; }
/* The current row is a solid colour in the readable skin: a white pill reads on all of them. */
.nav-item.active .nav-bell { background: var(--white); color: var(--red-600); }
/* Sub-menu toggle: the total of its links, only while they are folded away. */
.nav-sub-toggle .nav-bell:not([hidden]) + .nav-sub-chevron { margin-left: 4px; }
.nav-sub.is-open > .nav-sub-toggle .nav-bell { display: none; }
/* A folded group's header rings for the rows it hides. */
.nav-group-bell { display: none; margin-inline-start: auto; margin-inline-end: 4px; color: var(--red-500); }
.nav-group-bell-icon { width: 13px; height: 13px; }
.nav-group.is-collapsed:has(.nav-bell:not([hidden])) .nav-group-bell { display: inline-flex; }
/* Icon rail: no room for a pill beside the label — the count sits on the icon's corner. */
html.sidebar-collapsed .nav-bell {
    position: absolute; top: 0; right: 3px; height: 16px; min-width: 16px; padding: 0 4px;
    justify-content: center; font-size: 9.5px; border: 2px solid var(--surface); box-shadow: none;
}
html.sidebar-collapsed .nav-bell-icon { display: none; }
@keyframes nav-bell-ring {
    0%, 100% { transform: rotate(0); }
    8%  { transform: rotate(18deg); }
    16% { transform: rotate(-16deg); }
    24% { transform: rotate(12deg); }
    32% { transform: rotate(-9deg); }
    40% { transform: rotate(5deg); }
    48% { transform: rotate(-2deg); }
    56% { transform: rotate(0); }
}
@keyframes nav-bell-pop { 0% { transform: scale(.6); } 60% { transform: scale(1.15); } 100% { transform: scale(1); } }

/* "Nouveau" — the list rows whose notifications this visit just read
   (fresh-rows.js): a red edge, a faint red tint and a tag beside the link,
   so a cleared sidebar bell still says which rows changed. */
html body .table tbody tr.is-fresh:nth-child(n) > td { background: color-mix(in srgb, var(--red-50) 70%, var(--surface)); }
html body .table tbody tr.is-fresh:nth-child(n) > td:first-child { box-shadow: inset 3px 0 0 var(--red-500); }
html[dir="rtl"] body .table tbody tr.is-fresh:nth-child(n) > td:first-child { box-shadow: inset -3px 0 0 var(--red-500); }
html body .table tbody tr.is-fresh:nth-child(n):hover > td { background: var(--red-50); }
body .table-wrap .table tbody tr.is-fresh > td:last-child:has(> .row-actions) { background: color-mix(in srgb, var(--red-50) 70%, var(--surface)); }
.is-fresh-card { box-shadow: inset 3px 0 0 var(--red-500), var(--shadow-card); }
.fresh-tag {
    display: inline-flex; align-items: center; gap: 4px; margin-inline-start: 6px; padding: 1px 7px 1px 6px;
    border-radius: 999px; background: var(--red-500); color: var(--white);
    font-size: 10.5px; font-weight: 700; letter-spacing: .02em; line-height: 1.5; vertical-align: middle; white-space: nowrap;
    animation: nav-bell-pop .45s ease-out;
}
.fresh-tag::before { content: ""; width: 5px; height: 5px; border-radius: 999px; background: currentColor; }
@media (max-width: 900px) {
    body .table.tc-auto tr.is-fresh, body .table-cards tr.is-fresh { box-shadow: inset 3px 0 0 var(--red-500); }
}

/* Top bar search (modules/search/, search-bar.js): a search icon after the
   page title; a click stretches it into a rounded field right there,
   suggestions grouped by kind under it. It folds back to the icon when left
   empty. Under 1100px the field opens across the top bar instead. */
.topsearch { position: relative; flex: 0 0 auto; min-width: 0; margin-inline-start: 12px; }
.topbar:has(.topsearch) h1 { flex: 0 1 auto; min-width: 7ch; }
.topsearch-field { position: relative; display: flex; align-items: center; }
.topsearch-icon { position: absolute; inset-inline-start: 12px; color: var(--faint); pointer-events: none; transition: color .15s ease; }
.topsearch-input {
    width: 100%; height: 36px; padding-inline: 36px 14px;
    border: 1px solid var(--line); border-radius: 999px; background: var(--hover); color: var(--text);
    font: inherit; font-size: 13.5px; transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.topsearch-input::placeholder { color: var(--faint); }
.topsearch-input::-webkit-search-cancel-button { cursor: pointer; }
.topsearch-input:focus { outline: none; border-color: var(--primary); background: var(--surface); box-shadow: var(--ring-primary); }
.topsearch-field:focus-within .topsearch-icon, .topsearch.is-loading .topsearch-icon { color: var(--primary); }
.topsearch-panel {
    position: absolute; top: calc(100% + 8px); inset-inline-start: 0; z-index: 120;
    width: max(100%, 460px); max-height: min(70vh, 540px); overflow-y: auto; padding: 6px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 18px 40px -16px rgba(15, 23, 42, .35);
    animation: stock-pop-in .14s ease-out;
}
.topsearch-group {
    display: flex; align-items: center; gap: 6px; padding: 10px 10px 4px;
    font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--faint);
}
.topsearch-group:first-child { padding-top: 4px; }
.topsearch-group-icon { display: inline-flex; }
.topsearch-group-icon svg { width: 13px; height: 13px; }
.topsearch-item { display: flex; flex-direction: column; gap: 1px; padding: 7px 10px; border-radius: 8px; color: var(--text); text-decoration: none; }
.topsearch-item:hover, .topsearch-item.is-active { background: var(--primary-soft); }
.topsearch-title { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.topsearch-title mark { padding: 0 1px; border-radius: 3px; background: var(--amber-50); color: inherit; box-shadow: inset 0 -2px 0 var(--amber-600); }
.topsearch-meta { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topsearch-all { margin-top: 4px; padding-top: 9px; border-top: 1px solid var(--line-soft); border-radius: 0 0 8px 8px; font-size: 12.5px; font-weight: 600; color: var(--primary-strong); }
.topsearch-empty { padding: 12px 10px 6px; font-size: 13px; color: var(--muted); }
/* Laptop: the icon stretches into the field in place (after the field's own rules, so it wins). */
@media (min-width: 1101px) {
    .topsearch { display: flex; align-items: center; flex: 0 0 38px; transition: flex-basis .22s cubic-bezier(.2, .7, .2, 1); }
    .topsearch-field { display: none; flex: 1; min-width: 0; }
    .topsearch.is-open { flex: 0 1 400px; }
    .topsearch.is-open .topsearch-open { display: none; }
    .topsearch.is-open .topsearch-field { display: flex; animation: fade-in .18s ease-out; }
}
@media (min-width: 1101px) and (prefers-reduced-motion: reduce) {
    .topsearch { transition: none; }
    .topsearch.is-open .topsearch-field { animation: none; }
}
@media (max-width: 1100px) {
    .topsearch { position: static; flex: 0 0 auto; min-width: 0; margin: 0; }
    .topsearch .topsearch-open { display: inline-flex; }
    .topsearch-field { display: none; }
    .topsearch.is-open .topsearch-field { display: flex; position: absolute; inset: 12px 16px auto; z-index: 121; }
    .topsearch.is-open .topsearch-input { height: 40px; background: var(--surface); }
    .topsearch-panel { inset-inline: 12px; top: 58px; width: auto; }
}

/* /search — the full results (modules/search/pages/index.php). */
.search-results { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 16px; align-items: start; }
.search-list { list-style: none; margin: 0; padding: 4px 6px 8px; }
.search-list-item { display: grid; grid-template-columns: 1fr auto; column-gap: 10px; padding: 8px 10px; border-radius: 8px; color: var(--text); text-decoration: none; }
.search-list-item:hover { background: var(--primary-soft); }
.search-list-title { font-weight: 600; color: var(--ink); }
.search-list-meta { grid-column: 1; font-size: 12.5px; color: var(--muted); }
.search-list-go { grid-column: 2; grid-row: 1 / span 2; align-self: center; color: var(--faint); }
.search-empty { display: flex; align-items: center; gap: 14px; padding: 18px 20px; margin-bottom: 16px; }
.search-empty > .icon { color: var(--faint); }
.search-empty-title { font-weight: 600; color: var(--ink); }
@media (max-width: 600px) { .search-results { grid-template-columns: 1fr; } }

/* Light / dark switch (modules/ui_theme/): the moon in light, the sun in dark.
   On a phone the top bar has no room: it moves to the side menu's foot, and
   the language switch drops its flags, and the page title may shrink to
   an ellipsis (a detail page also has the back arrow). */
.nav-foot-theme { display: none; }
@media (max-width: 600px) {
    .topbar:has(.topsearch) h1 { min-width: 0; }
    .topbar .theme-toggle { display: none; }
    .nav-foot-theme { display: inline-flex; }
    .topbar .lang-switch .lang-flag { display: none; }
    .topbar .lang-switch-btn { padding: 0 7px; }
}
.theme-toggle .theme-toggle-sun { display: none; }
html[data-theme="dark"] .theme-toggle .theme-toggle-moon { display: none; }
html[data-theme="dark"] .theme-toggle .theme-toggle-sun { display: inline-flex; }
.theme-toggle > span { display: inline-flex; }
/* The switch itself cross-fades the colours instead of snapping. */
html.theme-switching, html.theme-switching * { transition: background-color .22s ease, border-color .22s ease, color .22s ease !important; }
@media (prefers-reduced-motion: reduce) {
    .fresh-tag { animation: none; }
    html.theme-switching, html.theme-switching * { transition: none !important; }
}
@media (prefers-reduced-motion: reduce) {
    .nav-bell, .nav-bell-icon, .nav-group-bell-icon, .nav-bell.is-new .nav-bell-icon, .app-toast--notice .app-toast__icon svg { animation: none; }
}

.nav-user { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; border-radius: 6px; }
.nav-user:hover .nav-user-name { color: var(--primary-strong); }
.nav-user:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.nav-user-avatar {
    width: 26px; height: 26px; border-radius: 999px; flex: none; display: grid; place-items: center;
    font-size: 10.5px; font-weight: 700; color: var(--white);
    background: linear-gradient(135deg, var(--emerald-400), var(--emerald-700));
    box-shadow: 0 2px 6px -2px rgba(5, 150, 105, .55);
}
.nav-user-copy { min-width: 0; display: flex; flex-direction: column; }
.nav-user-name { font-size: 13px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-user-role { font-size: 11px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.nav-foot { padding: 10px 8px; margin-top: 4px; }
.nav-foot::before { content: ""; display: block; height: 1px; background: var(--line-soft); margin: 0 4px 10px; }
.nav-foot form { margin: 0; }
/* One compact row: avatar + name/role on the left, settings/logout icon
   buttons on the right — falls back to two stacked rows in the icon-only
   collapsed rail, where there's no width left for a single row. */
.nav-foot-row { display: flex; align-items: center; gap: 6px; padding: 6px 4px 6px 7px; border-radius: 6px; background: var(--hover); }
.nav-foot-actions { display: flex; gap: 2px; flex: none; }
.nav-foot-btn {
    display: flex; align-items: center; justify-content: center; flex: none;
    width: 28px; height: 28px; border: 0; border-radius: 8px; background: transparent; color: var(--slate-600);
    cursor: pointer; transition: background .15s ease, color .15s ease;
}
.nav-foot-btn:hover { background: var(--surface); color: var(--primary-strong); }
.nav-foot-btn .icon { width: 16px; height: 16px; padding: 0; background: transparent; }

html.sidebar-collapsed .nav-foot-row { flex-direction: column; gap: 6px; padding: 6px 0; background: transparent; }
html.sidebar-collapsed .nav-user { justify-content: center; }
html.sidebar-collapsed .nav-user-copy { display: none; }
html.sidebar-collapsed .nav-foot-actions { flex-direction: row; }

.content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; gap: 14px; height: 64px; padding: 0 28px; background: var(--surface); border-bottom: 1px solid var(--line); }
.topbar h1 { font-size: 18px; font-weight: 600; margin: 0; color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Universal "Back" link — rendered by every shell (see core/view.php's
   layout_back_path()), so it sits left of the page title in the topbar. */
.topbar-back {
    display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
    height: 32px; padding: 0 10px; border-radius: 6px;
    border: 1px solid var(--line); background: var(--surface);
    color: var(--muted); font-size: 13px; font-weight: 600; white-space: nowrap;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.topbar-back:hover { background: var(--slate-100); color: var(--ink); border-color: var(--slate-300); }
.iconbtn { position: relative; display: grid; place-items: center; width: 38px; height: 38px; border-radius: 8px; color: var(--slate-600); background: transparent; border: 0; cursor: pointer; flex-shrink: 0; }
/* Topbar FR/EN switch — segmented pill, same visual language as .trend-range. */
.lang-switch {
  display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0;
  height: 34px; padding: 3px;
  background: var(--slate-100); border: 1px solid var(--line-soft); border-radius: 8px;
}
/* Flag beside each language (locale_flag()) — a 3:2 chip with a hairline
   edge so the white band of the French flag still reads on a white tab. */
.lang-flag {
  flex: 0 0 auto; width: 18px; height: 12px; border-radius: 2px;
  box-shadow: 0 0 0 1px rgb(15 23 42 / .14);
}
.lang-switch-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 34px; height: 100%; padding: 0 9px; border-radius: 6px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em; line-height: 1;
  color: var(--slate-500); text-decoration: none;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.lang-switch-btn:hover { color: var(--ink); background: rgb(255 255 255 / .6); }
.lang-switch-btn.is-active {
  background: var(--white); color: var(--primary-strong);
  box-shadow: 0 1px 2px rgb(15 23 42 / .08), 0 0 0 1px var(--line);
}
.iconbtn:hover { background: var(--slate-100); }
/* Mobile nav toggle — hidden except on the off-canvas breakpoint further down */
.sidebar-toggle { display: none; }
.sidebar-backdrop { display: none; }
.avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--ink); color: var(--white); display: grid; place-items: center; font-size: 12px; font-weight: 600; }
.avatar-lg { width: 52px; height: 52px; border-radius: 50%; background: var(--ink); color: var(--white); display: grid; place-items: center; font-size: 18px; font-weight: 700; flex: none; letter-spacing: .01em; }
/* No fill-mode: "both" keeps a finished CSS animation permanently "in
   effect", which per spec makes the element a stacking context for as
   long as the page lives — that trapped every in-page fixed-position
   modal declared inside <main> (the order/supplier-order product pickers,
   the stock page's picker) below .topbar's z-index:10, no matter how high
   the modal's own z-index was set, since .topbar sits outside .main's
   now-separate stacking context. The end state already matches .main's
   resting style, so dropping "both" changes nothing about the fade-in
   itself. */
.main { padding: 28px; flex: 1; animation: fade-in .3s ease; }

/* Admin "view as" preview mode */
/* Sticky, not just normal flow — .sidebar sticks right below it
   (top: var(--app-banner-h)), so if this stack scrolled away normally, a
   gap would open above the sidebar the moment it caught and pinned at
   that offset. Pinning it keeps the offset valid at any scroll position,
   and keeps a persistent status banner actually visible while scrolling,
   which is the more sensible behavior for it anyway. One wrapper, not each
   banner individually sticky — two independently-sticky top:0 siblings
   would overlap each other on scroll instead of stacking. */
.app-banners { position: sticky; top: 0; z-index: 30; display: flex; flex-direction: column; }
.preview-banner { display: flex; align-items: center; gap: 10px; padding: 8px 20px; background: var(--amber-50); color: var(--amber-700); border-bottom: 1px solid var(--amber-600); font-size: 13px; }
.preview-banner .icon { width: 16px; height: 16px; flex: none; }
.preview-banner span { flex: 1; }
.preview-banner-exit { border: 1px solid var(--amber-600); background: transparent; color: var(--amber-700); border-radius: 6px; padding: 5px 10px; font-size: 12px; font-weight: 600; cursor: pointer; }
.preview-banner-exit:hover { background: var(--amber-600); color: var(--white); }
.preview-banner-min { position: sticky; top: 0; z-index: 20; }

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* Cards */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: 6px; box-shadow: var(--shadow-card); }
.card-pad { padding: 16px; }
.card-head { display: flex; align-items: center; justify-content: space-between; padding: 13px 16px; border-bottom: 1px solid var(--line-soft); }
.card-title { font-size: 15px; font-weight: 600; color: var(--ink); }
.link { color: var(--primary); font-size: 13px; font-weight: 600; }

/* Buttons */
.btn { display: inline-flex; align-items: center; gap: 8px; border: 1px solid transparent; border-radius: 8px; padding: 9px 14px; font-size: 14px; font-weight: 600; cursor: pointer; transition: background .15s, border-color .15s; }
.btn .icon { width: 18px; height: 18px; }
.btn-primary { background: var(--ink); color: var(--white); }
.btn-primary:hover { background: var(--slate-800); }
.btn-dark { background: var(--ink); color: var(--white); }
.btn-dark:hover { background: var(--slate-800); }
.btn-outline { background: var(--surface); border-color: var(--line); color: var(--slate-700); }
.btn-outline:hover { background: var(--hover); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { background: var(--slate-100); }
.btn-danger { background: var(--red-600); color: var(--white); }
.btn-danger:hover { filter: brightness(0.92); }
/* Destructive, but not the loudest thing on the page — for a delete that
   sits among ordinary .btn-outline actions (the warehouse map sidebar's
   "Delete this depot" next to "Edit this depot") and shouldn't out-shout
   them until you're actually hovering it. */
.btn-danger-outline { background: var(--surface); border-color: var(--line); color: var(--red-600); }
.btn-danger-outline:hover { background: var(--red-50); border-color: var(--red-400); }
.btn-sm { padding: 4px 10px; font-size: 13px; height: 32px; }
/* .btn sets display:inline-flex, which beats the bare [hidden] UA rule. */
.btn[hidden] { display: none; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* Shortcut to a catalog list (products page head: Marques / Catégories) —
   tinted icon tile + name + count, same height as a .btn beside it. */
.catalog-link { display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 10px 0 5px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); box-shadow: var(--shadow-card); color: var(--ink); font-size: 14px; font-weight: 600; transition: border-color .15s, background .15s; }
.catalog-link:hover { border-color: var(--primary); background: var(--primary-soft); }
.catalog-link:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.catalog-link .stat-compact-ico { width: 28px; height: 28px; border: 1px solid transparent; }
.catalog-link .stat-compact-ico .icon { width: 15px; height: 15px; }
.catalog-link-count { min-width: 22px; padding: 1px 7px; border-radius: 999px; background: var(--slate-100); color: var(--slate-600); font-family: var(--font-mono); font-size: 12px; font-weight: 600; text-align: center; }
.catalog-link-arrow { width: 14px; height: 14px; color: var(--muted); transition: translate .15s, color .15s; }
.catalog-link:hover .catalog-link-arrow { color: var(--primary); translate: 2px 0; }
[dir="rtl"] .catalog-link:hover .catalog-link-arrow { translate: -2px 0; }

/* Shared destructive-confirmation modal (public/assets/js/confirm-modal.js) —
   opt in per form with data-confirm="..."; the script builds and injects
   this markup itself, so nothing else to include per page. */
.confirm-modal-backdrop {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(15, 23, 42, .45);
  opacity: 0; pointer-events: none;
  transition: opacity .15s ease;
}
.confirm-modal-backdrop.is-open { opacity: 1; pointer-events: auto; }
.confirm-modal {
  width: min(360px, 100%);
  background: var(--surface);
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, .28);
  padding: 24px;
  text-align: center;
  transform: translateY(6px) scale(.98);
  transition: transform .15s ease;
}
.confirm-modal-backdrop.is-open .confirm-modal { transform: translateY(0) scale(1); }
.confirm-modal-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: 0 auto 14px;
  border-radius: 999px; background: var(--red-50); color: var(--red-600);
}
.confirm-modal-icon svg { width: 22px; height: 22px; }
.confirm-modal.is-primary .confirm-modal-icon { background: var(--primary-soft); color: var(--primary); }
.confirm-modal-title { font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.4; white-space: pre-line; }
.confirm-modal-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 20px; }
/* A label never breaks inside its button ("Passer « En préparation »" did):
   the longer one takes the room it needs, too long for one line = own row. */
.confirm-modal-actions .btn { flex: 1 1 auto; justify-content: center; white-space: nowrap; }

/* Shared toast (public/assets/js/toast.js builds it): a white card stacked
   top-end — tinted icon, bold heading (a reference in mono), the detail under
   it with « quoted » values as chips, and a countdown bar that pauses on hover. */
.app-toasts {
  position: fixed; top: 16px; inset-inline-end: 16px; z-index: 9999;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
  width: min(380px, calc(100vw - 32px)); pointer-events: none;
}
.app-toast {
  --toast-tone: var(--success); --toast-soft: var(--success-soft);
  position: relative; overflow: hidden; pointer-events: auto;
  display: flex; align-items: flex-start; gap: 12px; width: 100%;
  padding: 14px 40px 16px 14px; padding-inline: 14px 40px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 12px 32px -6px rgba(15, 23, 42, .22);
  animation: app-toast-in .28s cubic-bezier(.21, 1.02, .73, 1) both;
}
.app-toast--error { --toast-tone: var(--red-600); --toast-soft: var(--red-50); }
.app-toast.is-leaving { animation: app-toast-out .2s ease-in both; }
.app-toast__icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 10px;
  background: var(--toast-soft); color: var(--toast-tone);
}
.app-toast__icon svg { width: 19px; height: 19px; }
.app-toast__body { flex: 1; min-width: 0; padding-top: 1px; }
.app-toast__title { font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--ink); overflow-wrap: anywhere; }
.app-toast__title.is-ref { font-family: var(--font-mono); font-size: 13px; letter-spacing: .01em; }
.app-toast__text { margin-top: 3px; font-size: 13.5px; line-height: 1.45; color: var(--muted); overflow-wrap: anywhere; }
.app-toast__chip {
  display: inline-block; margin: 0 2px; padding: 1px 8px; border-radius: 999px;
  background: var(--primary-soft); color: var(--primary);
  font-size: 12.5px; font-weight: 600; line-height: 1.5; white-space: nowrap;
}
.app-toast--error .app-toast__chip { background: var(--red-50); color: var(--red-600); }
/* A new notification (nav-bells.js): the sidebar bells' red, a small type
   line above the title, and the whole text opens it. */
.app-toast--notice { --toast-tone: var(--red-500); --toast-soft: var(--red-50); }
.app-toast--notice .app-toast__icon svg { transform-origin: 50% 2px; animation: nav-bell-ring 1.6s ease-in-out .2s 1; }
.app-toast__label { margin-bottom: 1px; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--toast-tone); }
a.app-toast__body { color: inherit; text-decoration: none; border-radius: 6px; }
a.app-toast__body:hover .app-toast__title { color: var(--primary-strong); text-decoration: underline; text-underline-offset: 2px; }
a.app-toast__body:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.app-toast__close {
  position: absolute; top: 10px; inset-inline-end: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; border: 0; border-radius: 7px;
  background: transparent; color: var(--faint); cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.app-toast__close:hover { background: var(--slate-100); color: var(--ink); }
.app-toast__close:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.app-toast__close svg { width: 15px; height: 15px; }
.app-toast__bar {
  position: absolute; inset-inline-start: 0; bottom: 0; height: 3px; width: 100%;
  background: var(--toast-tone); opacity: .55;
  transform-origin: left center; animation: app-toast-bar linear both;
}
[dir="rtl"] .app-toast__bar { transform-origin: right center; }
.app-toast.is-paused .app-toast__bar { animation-play-state: paused; }
@keyframes app-toast-in { from { opacity: 0; transform: translateY(-8px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes app-toast-out { to { opacity: 0; transform: translateY(-6px) scale(.97); } }
@keyframes app-toast-bar { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (max-width: 600px) {
  .app-toasts { top: 10px; inset-inline-end: 10px; width: calc(100vw - 20px); }
}
@media (prefers-reduced-motion: reduce) {
  .app-toast, .app-toast.is-leaving { animation-duration: .01s; }
}

/* Shared floating popover shell — position:fixed, built + appended to <body>
   once by page-local JS (see the order picker's "See orders" and the
   inventory page's "see stock by site"), so it always escapes whatever
   scrollable container it was opened from without ever being clipped. Only
   the container/head/body/close chrome lives here; each page supplies its
   own body content and markup. */
.stock-hint-popover {
  position: fixed; z-index: 200; white-space: normal; text-align: left;
  border: 1px solid var(--line); border-radius: 6px; background: var(--surface);
  box-shadow: 0 12px 32px rgba(15, 23, 42, .18); overflow: hidden;
  animation: stock-pop-in .12s ease-out both;
}
@keyframes stock-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.stock-hint-popover-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 10px; border-bottom: 1px solid var(--line-soft);
  background: var(--slate-50); font-size: 12px; font-weight: 700; color: var(--ink);
}
.stock-hint-popover-close { display: flex; background: none; border: none; padding: 2px; cursor: pointer; color: var(--muted); border-radius: 6px; }
.stock-hint-popover-close:hover { background: var(--hover); color: var(--ink); }
.stock-hint-popover-body { max-height: 260px; overflow-y: auto; padding: 6px; }

/* Inventory page's "see stock by site" popover body — one row per wh_site,
   plus a pinned global-total row (modules/stock/pages/index.php). */
.stock-site-row { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: 6px; font-size: 12px; }
.stock-site-row + .stock-site-row { margin-top: 1px; }
.stock-site-row-global { font-weight: 700; border-bottom: 1px solid var(--line-soft); border-radius: 0; margin-bottom: 4px; padding-bottom: 10px; }
.stock-site-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.stock-site-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.stock-site-name { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stock-site-loc { color: var(--muted); font-size: 11px; }
.stock-site-qty { flex: none; font-weight: 600; }

/* Pills */
.pill { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px; padding: 3px 10px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pill-green { background: var(--primary-soft); color: var(--primary-strong); }
.pill-blue { background: var(--blue-50); color: var(--blue-700); }
.pill-amber { background: var(--amber-50); color: var(--amber-700); }
.pill-slate { background: var(--slate-100); color: var(--slate-600); }
.pill-red { background: var(--red-50); color: var(--red-600); }

/* Inline status/payment editor (orders list — statusCell() in pages/index.php).
   Pairs with .wh-icon-btn so save/cancel match the row-actions icon buttons
   instead of looking like a bare "x" next to an oversized filled button.
   Selector is qualified (select.status-edit-select, not just .status-edit-select)
   so it outranks .input's own padding/font-size later in this file regardless
   of source order. Width is field-specific: payment's longest label ("En
   attente") needs far less room than status's ("En cours de livraison"). */
.status-edit { display: inline-flex; align-items: center; gap: 6px; }
select.status-edit-select { width: auto; padding: 3px 8px; font-size: 12px; line-height: 1.3; }
select.status-edit-select.is-narrow { min-width: 116px; }
select.status-edit-select.is-wide { min-width: 165px; }
.status-edit .wh-icon-btn { flex: none; }
.status-edit .wh-icon-save:hover { background: var(--primary-soft); color: var(--primary-strong); border-color: var(--primary); }
.status-edit .wh-icon-cancel:hover { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }

/* Stat tiles */
.stats { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 24px; }
.stat {
    display: block; flex: 1 1 160px; background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
    padding: 10px 12px; box-shadow: var(--shadow-card); text-decoration: none; color: inherit;
    animation: rise .4s ease both; transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.stats .stat:nth-child(1) { animation-delay: .02s; }
.stats .stat:nth-child(2) { animation-delay: .06s; }
.stats .stat:nth-child(3) { animation-delay: .1s; }
.stats .stat:nth-child(4) { animation-delay: .14s; }
.stat:hover { transform: translateY(-2px); box-shadow: 0 10px 24px -14px rgba(15, 23, 42, .18); border-color: var(--slate-300); }
.stat:active { transform: translateY(0); box-shadow: var(--shadow-card); }
.stat:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.stat-ico {
    width: 26px; height: 26px; border-radius: 6px; display: grid; place-items: center; flex: none;
    box-shadow: inset 0 0 0 1px rgba(15, 23, 42, .04); transition: transform .18s ease;
}
.stat-ico .icon { width: 14px; height: 14px; }
.stat-ico.green { background: var(--primary-soft); color: var(--primary); }
.stat-ico.blue { background: var(--blue-50); color: var(--blue-600); }
.stat-ico.amber { background: var(--amber-50); color: var(--amber-600); }
.stat-ico.red { background: var(--red-50); color: var(--red-600); }
.stat-ico.slate { background: var(--slate-100); color: var(--slate-600); }
.stat:hover .stat-ico { transform: scale(1.08) rotate(-4deg); }
.stat-value { font-size: 20px; font-weight: 700; margin-top: 6px; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }

/* Dense stat tile, used app-wide (every list and detail page; the dashboard's
   big .kpi-card tiles are a separate component and keep their two rows).
   ONE LINE: icon · value · "Label · sub", in the markup's own DOM order, so
   none of the ~30 pages that render it change. The value never shrinks or
   wraps — it can be a long total like "47,943.88 MAD HT", and the old
   one-row layout overflowed precisely because the value had no room. Here
   the copy gives way instead: the sub hides first on a narrow tile (container
   query below), then the label ends in "…" — and either way the tile gets
   its full text as a hover tooltip (tooltip.js). Tiles are at least 260px so
   a row of five money totals wraps instead of cutting every label to
   "Ventes…" (Rapports › Bénéfice). */
.stats-compact { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 8px; margin-bottom: 20px; }
.stat-compact {
    display: flex; align-items: center; gap: 10px; min-width: 0;
    container-type: inline-size;
    background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
    padding: 10px 14px; text-decoration: none; color: inherit;
    box-shadow: var(--shadow-card);
    animation: rise .4s ease both;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.stats-compact .stat-compact:nth-child(1) { animation-delay: .02s; }
.stats-compact .stat-compact:nth-child(2) { animation-delay: .06s; }
.stats-compact .stat-compact:nth-child(3) { animation-delay: .1s; }
.stats-compact .stat-compact:nth-child(4) { animation-delay: .14s; }
.stat-compact:hover { border-color: var(--slate-300); box-shadow: 0 6px 16px -10px rgba(15, 23, 42, .16); }
.stat-compact:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.stat-compact-ico { flex: none; width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center; }
.stat-compact-ico .icon { width: 13px; height: 13px; }
.stat-compact-ico.green { background: var(--primary-soft); color: var(--primary); }
.stat-compact-ico.blue { background: var(--blue-50); color: var(--blue-600); }
.stat-compact-ico.amber { background: var(--amber-50); color: var(--amber-600); }
.stat-compact-ico.slate { background: var(--slate-100); color: var(--slate-600); }
.stat-compact-ico.red { background: var(--red-50); color: var(--red-600); }
.stat-compact-value { flex: none; white-space: nowrap; line-height: 1.2; font-size: 19px; font-weight: 800; letter-spacing: -.01em; color: var(--ink); font-variant-numeric: tabular-nums; }
.stat-compact-value span { color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: 0; }
.stat-compact-copy { flex: 1 1 auto; min-width: 0; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.35; }
.stat-compact-label { font-size: 12px; font-weight: 600; color: var(--text); }
.stat-compact-sub { font-size: 11px; color: var(--muted); }
/* The sub only shows where it fits whole beside the value and label; a cut-off
   "Sur …" reads worse than no sub. tooltip.js then gives the tile its full
   text on hover. */
@container (max-width: 440px) { .stat-compact-sub { display: none; } }

/* Small colored icon mark inline before a .card-title — opt-in per card via
   an extra <span>, doesn't touch the base .card-title rule other modules use. */
.card-icon-chip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; border-radius: 6px;
    margin-right: 8px; vertical-align: middle;
}
.card-icon-chip .icon { width: 11px; height: 11px; }
.card-icon-chip.green { background: var(--primary-soft); color: var(--primary); }
.card-icon-chip.blue  { background: var(--blue-50); color: var(--blue-600); }
.card-icon-chip.amber { background: var(--amber-50); color: var(--amber-600); }
.card-icon-chip.slate { background: var(--slate-100); color: var(--slate-600); }

/* Small count badge right after a .card-title's text — opt-in via an extra
   <span>, e.g. "Recent orders 5". */
.card-title-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; margin-left: 8px; padding: 0 6px;
    border-radius: 999px; background: var(--slate-100); color: var(--slate-600);
    font-size: 11px; font-weight: 700; vertical-align: middle;
    font-variant-numeric: tabular-nums;
}

/* ---- KPI stat cards (dashboard-only headline row) -----------------------
   Four equal cards, deliberately compact: icon chip + "view all" link, big
   tabular value (with an optional "+N new" delta), and an up/down badge vs.
   the previous 7-day window (omitted where a live snapshot can't honestly
   have one). Nothing invented, and no decorative
   bar: the number and its real change are the whole card. */
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; margin-bottom: 20px; }
.kpi-card {
    background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
    padding: 11px 13px; box-shadow: var(--shadow-card);
    animation: rise .4s ease both;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.kpi-grid .kpi-card:nth-child(1) { animation-delay: .02s; }
.kpi-grid .kpi-card:nth-child(2) { animation-delay: .06s; }
.kpi-grid .kpi-card:nth-child(3) { animation-delay: .1s; }
.kpi-grid .kpi-card:nth-child(4) { animation-delay: .14s; }
.kpi-card:hover { border-color: var(--slate-300); box-shadow: 0 10px 24px -14px rgba(15, 23, 42, .16); transform: translateY(-2px); }
.kpi-card-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kpi-card-ico { position: relative; width: 28px; height: 28px; border-radius: 6px; display: grid; place-items: center; flex: none; }
.kpi-card-ico .icon { width: 15px; height: 15px; }
.kpi-card-ico.green { background: var(--primary-soft); color: var(--primary); }
.kpi-card-ico.blue  { background: var(--blue-50); color: var(--blue-600); }
.kpi-card-ico.amber { background: var(--amber-50); color: var(--amber-600); }
.kpi-card-ico.slate { background: var(--slate-100); color: var(--slate-600); }
.kpi-card-link {
    display: grid; place-items: center; width: 24px; height: 24px; border-radius: 6px;
    color: var(--faint); transition: background .15s ease, color .15s ease;
}
.kpi-card-link:hover { background: var(--slate-100); color: var(--ink); }
.kpi-card-link .icon { width: 13px; height: 13px; }
.kpi-card-label { margin-top: 9px; font-size: 12px; font-weight: 600; color: var(--text); }
.kpi-card-sub { font-size: 11px; color: var(--muted); }
.kpi-card-value { margin-top: 2px; font-size: 21px; font-weight: 800; letter-spacing: -.01em; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.25; }
.kpi-card-value .unit { font-size: 12px; font-weight: 600; color: var(--muted); }
.kpi-card-change { margin-top: 2px; display: flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.kpi-card-change.is-up { color: var(--success-strong); }
.kpi-card-change.is-down { color: var(--red-600); }
.kpi-card-change.is-flat { color: var(--muted); font-weight: 600; }
.kpi-card-change-note { font-weight: 500; color: var(--faint); }
.kpi-card-live { display: inline-flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 11.5px; font-weight: 700; color: var(--amber-700); }
.kpi-card-live-dot { position: relative; width: 7px; height: 7px; border-radius: 50%; background: var(--amber-600); animation: live-dot-pulse 1.8s ease-in-out infinite; }
@media (max-width: 480px) {
    .kpi-card-value { font-size: 19px; }
}
/* "K5" number card (dashboard, design gallery 2026-10-20): round icon beside
   the number and its label; a grey foot holds the trend and a "View" link. */
.kpi-card--k5 { display: flex; flex-direction: column; overflow: hidden; }
body .kpi-grid > .kpi-card.kpi-card--k5 { padding: 0; }
.kpi-card--k5 .kpi-card-main { display: flex; align-items: center; gap: 14px; padding: 16px; flex: 1; }
.kpi-card--k5 .kpi-card-ico { width: 48px; height: 48px; border-radius: 50%; }
.kpi-card--k5 .kpi-card-ico .icon { width: 22px; height: 22px; }
.kpi-card--k5 .kpi-card-copy { min-width: 0; }
.kpi-card--k5 .kpi-card-value { margin-top: 0; line-height: 1.15; }
.kpi-card--k5 .kpi-card-label { margin-top: 2px; font-weight: 500; color: var(--muted); }
.kpi-card--k5 .kpi-card-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    min-height: 38px; padding: 8px 16px; border-top: 1px solid var(--line-soft); background: var(--slate-50);
}
.kpi-card--k5 .kpi-card-foot :is(.kpi-card-change, .kpi-card-live) {
    display: block; flex: 1 1 auto; min-width: 0; margin-top: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi-card--k5 .kpi-card-foot .kpi-card-change > span { margin-right: 3px; }
.kpi-card--k5 .kpi-card-foot .kpi-card-live-dot { display: inline-block; margin-right: 6px; vertical-align: 1px; }
.kpi-card-more { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; flex: none; font-size: 12.5px; font-weight: 600; color: var(--primary); text-decoration: none; }
.kpi-card-more:hover { text-decoration: underline; }
@media (max-width: 640px) {
    .kpi-card--k5 .kpi-card-main { padding: 12px; gap: 10px; }
    .kpi-card--k5 .kpi-card-ico { width: 38px; height: 38px; }
    .kpi-card--k5 .kpi-card-foot { padding: 6px 12px; }
    .kpi-card--k5 .kpi-card-change-note { display: none; }
}

/* Compact variant — smaller tiles for a secondary stat row (e.g. supplier/
   customer detail headers), where four dashboard-sized KPI cards read as
   oversized next to a profile card. Same markup, just tighter metrics. */
.kpi-grid--compact { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 18px; }
/* Phones: the dashboard's KPI cards two per row; the one-line stat tiles one
   per row (a line each, so three or four still fit above the data). */
@media (max-width: 640px) {
    .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 16px; }
    /* One-line tiles: one per row, full width, so the label has room. */
    .stats-compact:not(.stats-inline), .kpi-grid--compact { grid-template-columns: minmax(0, 1fr); gap: 6px; margin-bottom: 16px; }
    .stats { gap: 8px; margin-bottom: 16px; }
    .stats .stat { flex: 1 1 calc(50% - 4px); min-width: 0; }
    .stat-compact, .kpi-card { padding: 9px 12px; }
    .kpi-card-sub { display: none; }
    .stat-compact-value, .kpi-card-value { font-size: 17px; }
}
/* One line, like .stat-compact: icon · value · label · note. The value keeps
   its width; the note gives way first, then the label ends in "…". */
.kpi-grid--compact { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.kpi-grid--compact .kpi-card { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 10px 14px; }
.kpi-grid--compact .kpi-card-top { order: 0; flex: none; }
.kpi-grid--compact .kpi-card-value { order: 1; flex: none; margin-top: 0; font-size: 16px; line-height: 1.2; white-space: nowrap; }
.kpi-grid--compact .kpi-card-label { order: 2; flex: 0 1 auto; min-width: 0; margin-top: 0; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-grid--compact .kpi-card-change,
.kpi-grid--compact .kpi-card-sub { order: 3; flex: 1 1 0; min-width: 0; margin-top: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-grid--compact .kpi-card-ico { width: 22px; height: 22px; border-radius: 6px; }
.kpi-grid--compact .kpi-card-ico .icon { width: 13px; height: 13px; }

/* ---- Analytics card — two panels (trend + pipeline) sharing one card ---
   Composite instead of two same-width cards in a row: one considered unit
   rather than a repeat of the .grid-2 pattern used above it. */
.analytics-split { display: flex; }
.analytics-panel { flex: 1 1 0; min-width: 0; }
.analytics-panel--wide { flex: 1.6 1 0; border-right: 1px solid var(--line-soft); }
@media (max-width: 720px) {
    .analytics-split { flex-direction: column; }
    .analytics-panel--wide { border-right: 0; border-bottom: 1px solid var(--line-soft); }
}

/* ---- Trend chart (SVG line/area — dashboard "Order volume") ------------ */
.trend-wrap { position: relative; }
.trend-svg { display: block; width: 100%; height: auto; }
.trend-svg .trend-grid { stroke: var(--line-soft); stroke-width: 1; }
.trend-svg .trend-area { fill: var(--primary); opacity: .1; }
.trend-svg .trend-line { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.trend-svg .trend-dot { fill: var(--primary); stroke: var(--surface); stroke-width: 2; }
.trend-svg .trend-hit { fill: transparent; cursor: pointer; }
.trend-svg .trend-guide { stroke: var(--slate-300); stroke-width: 1; opacity: 0; pointer-events: none; transition: opacity .1s ease; }
.trend-caption { display: flex; justify-content: space-between; margin-top: 8px; font-size: 12px; color: var(--faint); }
.trend-tooltip {
    position: absolute; pointer-events: none; opacity: 0; transition: opacity .1s ease;
    background: var(--ink); color: var(--white); border-radius: 6px; padding: 6px 10px;
    font-size: 12px; line-height: 1.4; white-space: nowrap; transform: translate(-50%, calc(-100% - 10px)); z-index: 5;
}
.trend-tooltip .tt-value { font-weight: 700; font-variant-numeric: tabular-nums; }
.trend-tooltip .tt-date { color: var(--slate-300); }

/* Order-volume range pills (7D/14D/30D/90D) — plain GET links, full reload */
.trend-range { display: inline-flex; gap: 2px; background: var(--slate-100); border-radius: 8px; padding: 3px; }
.trend-range-btn { padding: 5px 11px; border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--slate-600); text-decoration: none; transition: background .15s ease, color .15s ease; }
.trend-range-btn:hover { color: var(--ink); }
.trend-range-btn.is-active { background: var(--ink); color: var(--white); }

/* ---- Order-pipeline legend (ordinal ramp — funnel stages, one hue) ----- */
.pipe-legend { display: flex; flex-direction: column; gap: 2px; }
.pipe-legend-row { display: flex; align-items: center; gap: 10px; padding: 6px; border-radius: 6px; text-decoration: none; color: inherit; transition: background .15s ease; }
.pipe-legend-row:hover, .pipe-legend-row:focus-visible { background: var(--slate-50); }
.pipe-legend-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; }
.pipe-legend-label { flex: 1 1 auto; min-width: 0; font-size: 12.5px; color: var(--slate-600); }
.pipe-legend-value { flex: 0 0 auto; text-align: right; font-size: 13px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ---- Activity feed (dashboard right rail — real order_status_history rows,
   dashboard_activity_feed()) — a bulleted timeline, newest first. --------- */
.activity-list { display: flex; flex-direction: column; }
.activity-item { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.activity-item:last-child { border-bottom: 0; padding-bottom: 0; }
.activity-item:first-child { padding-top: 0; }
.activity-dot { flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: var(--slate-300); }
.activity-dot.pill-green { background: var(--primary); }
.activity-dot.pill-blue { background: var(--blue-600); }
.activity-dot.pill-amber { background: var(--amber-600); }
.activity-dot.pill-red { background: var(--red-500); }
.activity-dot.pill-slate { background: var(--slate-400); }
.activity-body { min-width: 0; }
.activity-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.activity-desc { margin-top: 1px; font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.activity-time { margin-top: 2px; font-size: 11.5px; color: var(--faint); }

/* Small round initials chip for a table row's customer — reuses the topbar
   .avatar's visual language at a smaller size. */
.avatar-sm { width: 26px; height: 26px; border-radius: 50%; background: var(--slate-200); color: var(--slate-700); display: grid; place-items: center; font-size: 11px; font-weight: 700; flex: none; }
.cell-customer { display: flex; align-items: center; gap: 9px; }
.cell-time { font-size: 12px; color: var(--muted); }

/* Table — compact rows app-wide (8px / 12px cells, 32px thumbnails); the
   first / last column keep 16px so they line up with the .card-head above. */
.table { width: 100%; border-collapse: collapse; }
.table th { text-align: left; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--faint); padding: 9px 12px; border-bottom: 1px solid var(--line-soft); background: var(--slate-50); }
.table td { padding: 8px 12px; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; color: var(--slate-700); vertical-align: middle; }
.table th:first-child, .table td:first-child { padding-left: 16px; }
.table th:last-child, .table td:last-child { padding-right: 16px; }
.table .cell-thumb { width: 32px; height: 32px; }
/* One line per cell so rows stay one line tall; a table wider than its card
   scrolls inside .table-wrap instead. Free-text columns (notes, messages,
   descriptions) opt back into wrapping with .wrap; full-width colspan rows
   (empty states, totals) and inline field errors always wrap. */
.table td { white-space: nowrap; }
.table td.wrap, .table td[colspan], .table td .field-error { white-space: normal; }
/* When a wide table scrolls sideways, its row-actions column stays pinned
   to the right edge so the buttons never scroll out of reach. */
.table-wrap .table tbody td:last-child:has(> .row-actions),
.table-wrap .table:has(tbody td:last-child > .row-actions) thead th:last-child {
  position: sticky; right: 0; z-index: 1;
  box-shadow: -8px 0 8px -8px rgba(15, 23, 42, .18);
}
.table-wrap .table tbody td:last-child:has(> .row-actions) { background: var(--surface); }
.table-wrap .table tbody tr:hover td:last-child:has(> .row-actions) { background: var(--hover); }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--hover); }
.table tbody tr.product-row { cursor: pointer; }
.table .right { text-align: right; }
.mono { font-family: var(--font-mono); font-size: 13px; color: var(--slate-600); }

/* Compact variant — a long single-column picker list (e.g. the stock
   transfer source table) reads better with tighter rows than the default
   list/detail tables app-wide. */
.table-compact th { padding: 7px 10px; }
.table-compact td { padding: 6px 10px; font-size: 13px; }
.table-compact .input { padding: 5px 8px; font-size: 13px; }

/* The products list photo: a button that shows it full size (the shared
   lightbox, [data-zoom-src]); without a photo, a plain placeholder. */
.product-list-image {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  background: var(--slate-50);
}
button.product-list-image { cursor: zoom-in; transition: border-color .15s; }
button.product-list-image:hover { border-color: var(--primary); }
button.product-list-image:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.product-list-image-preview {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--surface);
}

.product-cell { display: flex; align-items: center; gap: 12px; }
.product-cell-text { min-width: 0; }
.product-cell-name {
  display: block;
  font-size: 14px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.product-cell-code { font-size: 12px; color: var(--muted); margin-top: 1px; }
/* SKU count and EAN on the same small line as the code (no SKUs / EAN columns in the list). */
.product-cell-sku, .product-cell-ean { font-size: 11px; color: var(--faint); }
.table .right.mono { font-variant-numeric: tabular-nums; }

/* Forms */
.field { margin-bottom: 16px; }
.label { display: block; font-size: 13px; color: var(--slate-600); margin-bottom: 6px; font-weight: 500; }
.input { width: 100%; border: 1px solid var(--slate-300); border-radius: 6px; padding: 10px 12px; font-size: 14px; outline: 0; transition: border-color .15s, box-shadow .15s; }
.input:focus { border-color: var(--primary); box-shadow: var(--ring-primary); }
.input.invalid { border-color: var(--red-400); }
/* A <textarea class="input"> otherwise keeps the browser default resize:both,
   letting a drag on the corner handle grow it wider than its own 100% width
   and spill out of the surrounding card. Vertical-only resize still lets
   people grow the box for a long note without ever overflowing sideways. */
textarea.input { resize: vertical; }
.field-error { margin-top: 6px; font-size: 13px; color: var(--red-600); }
.field-hint { margin-top: 5px; font-size: 12px; color: var(--muted); }
/* A short numeric value (qty, %, price) doesn't need to stretch across its
   whole grid column — cap the input itself so it reads as a compact field
   even inside a wide cell; the label/hint above and below stay full width. */
.field-narrow .input,
.field-narrow .input-affix { max-width: 150px; }

/* Product editor */
.product-editor {
  display: grid;
  grid-template-columns: minmax(220px, .5fr) minmax(0, 1.5fr);
  gap: 20px;
  align-items: stretch;
}
/* Positioned + a real z-index so this card's stacking order is fixed
   relative to .product-sku-card below it at all times — not just after
   their shared `rise` entrance animation settles. While that animation's
   opacity is still < 1 (the first ~0.4s after the page paints), each card
   briefly gets its own stacking context; two such auto-z-index contexts
   fall back to DOM order, so .product-sku-card (later in the markup) would
   paint over whatever the Brand/Sub-category dropdown (z-index 20, see
   .catalog-options) overflows past this card's own box — the dropdown
   would render "under" the SKU card and look empty. A plain z-index here
   keeps this card above it unconditionally, animation or not. */
.product-editor-fields { min-width: 0; position: relative; z-index: 1; }
/* Entrance — same `rise` keyframe the stat tiles already use, lightly
   staggered across the form's three blocks (never per-row; see .sku-row
   below for why SKU rows stay unstaggered). */
.product-media-card,
.product-editor-fields,
.product-sku-card { animation: rise .4s ease both; }
.product-editor-fields { animation-delay: .05s; }
.product-sku-card { animation-delay: .1s; }
.product-section-head,
.product-status-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.product-section-head { margin-bottom: 14px; }
.product-section-copy { font-size: 12px; margin-top: 3px; }
/* "Add SKU" — a flex sibling of the (often two-line) title/description block,
   so with no shrink protection it could get squeezed narrower than its own
   text and wrap ("Ajouter un" / "SKU"). flex-shrink:0 + nowrap keep it at
   its natural size regardless of how wide the paragraph next to it gets;
   the radius matches the same 6-8px scale as the rest of this compacted
   form instead of the default .btn 10px. */
.product-section-head .btn {
  flex-shrink: 0;
  white-space: nowrap;
  border-radius: 8px;
}
.product-fields-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 20px;
  margin-bottom: 10px;
}
/* Opt-in full-row field (e.g. the product name), so the grid stays aligned
   in pairs instead of leaving a ragged half-width headline field. */
.product-fields-grid .field-wide { grid-column: 1 / -1; }
/* These "Basic information" grids read too roomy at the default .input
   size — tighten padding/radius just here rather than shrinking every
   input app-wide. */
.product-fields-grid .input { padding: 9px 10px; font-size: 13px; border-radius: 6px; }
/* Brand/Category use the combobox control, not a plain .input — match its
   height and corner radius to the plain fields above so the row doesn't
   look mismatched. */
.product-fields-grid .catalog-combobox-control { border-radius: 6px; }
.product-fields-grid .catalog-combobox-control input { padding: 9px 0; font-size: 13px; }
/* Name / Pivap code / VAT / Min / Max stock on one line above the rest of the
   grid — Name gets the most room, the code a bit less, VAT and Min / Max stock
   stay narrow since they're short numeric inputs. */
.product-identity-row {
  display: grid;
  grid-template-columns: 2fr 1.2fr .7fr .8fr .8fr;
  gap: 0 20px;
  margin-bottom: 10px;
}
.product-identity-row .input { padding: 9px 10px; font-size: 13px; border-radius: 6px; }
.product-identity-row .field-narrow .input,
.product-identity-row .field-narrow .input-affix { max-width: none; }
/* Each field's own default bottom margin (.field, 16px) is meant to
   separate stacked rows — these two rows sit one-per-line already, so it
   only padded out the row's own container. Zero it here and let the row's
   margin-bottom above be the single source of the gap between rows. */
.product-identity-row .field,
.product-fields-grid .field { margin-bottom: 0; }
.product-status-row {
  margin: 0 -16px -16px;
  padding: 8px 16px;
  border-top: 1px solid var(--line-soft);
  background: var(--slate-50);
  border-radius: 0 0 6px 6px;
}
/* Title + description on one line instead of stacked — this row is a status
   strip, not a form field, so it doesn't need the two-line label treatment. */
.product-status-text {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  font-size: 13px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.product-status-text strong { color: var(--slate-700); flex: 0 0 auto; }
.product-status-text .muted { overflow: hidden; text-overflow: ellipsis; }
.product-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-weight: 600; color: var(--slate-700); flex: 0 0 auto; }
.product-switch input { position: absolute; opacity: 0; pointer-events: none; }
.product-switch-track { width: 32px; height: 18px; padding: 2px; border-radius: 999px; background: var(--slate-300); transition: background .15s; }
.product-switch-track::after { content: ""; display: block; width: 14px; height: 14px; border-radius: 50%; background: var(--white); box-shadow: 0 1px 3px rgba(15, 23, 42, .25); transition: transform .15s; }
.product-switch input:checked + .product-switch-track { background: var(--primary); }
.product-switch input:checked + .product-switch-track::after { transform: translateX(14px); }
.product-switch input:focus-visible + .product-switch-track { box-shadow: var(--ring-primary); }

.product-media-card { display: flex; flex-direction: column; min-width: 0; overflow: hidden; padding: 14px; }
.product-media-head { padding: 2px 4px 12px; }
.product-media-preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 1 1 auto;
  /* Deliberately low — .product-editor stretches both cards to the same
     row height, so whichever card's natural height is tallest wins and the
     other one is forced to match it. This card's own content (header +
     preview + caption) is the only thing keeping this one short enough
     that "Informations de base" (usually taller once its four rows are
     laid out) sets the row height instead — the preview's flex:1 1 auto
     then grows to fill up to that height. Raising this value can flip
     which side drives the row and bring back dead space in the other
     card, so keep it low rather than sizing this box on its own. */
  min-height: 90px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--slate-50);
  border: 1.5px dashed var(--slate-300);
  transition: border-color .15s, background .15s;
}
/* Empty state reads as an upload dropzone (dashed border, no image behind
   it) rather than a checkerboard box asking to be filled — the checkerboard
   pattern only earns its keep once there's a real (possibly transparent)
   image to show it through. */
.product-media-preview:hover { border-color: var(--primary); background: var(--primary-soft); }
.product-media-preview.has-image {
  border-style: solid;
  border-color: var(--line-soft);
  background:
    linear-gradient(45deg, var(--slate-100) 25%, transparent 25%),
    linear-gradient(-45deg, var(--slate-100) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--slate-100) 75%),
    linear-gradient(-45deg, transparent 75%, var(--slate-100) 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}
.product-media-preview.has-image:hover { border-color: var(--slate-300); background-color: transparent; }
/* Absolutely positioned rather than a normal-flow width/height:100% image:
   a percentage height against this flex child's indeterminate own height
   resolves to "auto", so the <img> was rendering at its own intrinsic
   (square, for the placeholder SVG) size and silently dictating this box's
   height regardless of min-height above — which in turn dictated the whole
   .product-editor row height via the grid stretch. Taking it out of flow
   removes it from that sizing calculation entirely; object-fit still makes
   it fill/contain the box exactly as before. Hidden until .has-image so the
   placeholder SVG never shows through behind the empty-state copy below. */
.product-media-img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: contain; background: rgba(255,255,255,.78); opacity: 0; }
.product-media-preview.has-image .product-media-img { opacity: 1; }
/* Empty state: a single centered block (icon + label + format hint) —
   no image, no dark scrim, nothing layered behind it. */
.product-media-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 20px;
  text-align: center;
  pointer-events: none;
}
.product-media-preview.has-image .product-media-empty { display: none; }
.product-media-empty-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  background: var(--white);
  color: var(--slate-400);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .08);
  transition: color .15s;
}
.product-media-preview:hover .product-media-empty-icon { color: var(--primary); }
.product-media-empty-icon svg { width: 19px; height: 19px; }
.product-media-empty-title { font-size: 13px; font-weight: 600; color: var(--slate-700); }
/* Existing-image mode: a compact edit icon in the top-right corner. */
.product-media-edit {
  position: absolute;
  top: 12px;
  right: 12px;
  display: none;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 6px;
  color: var(--white);
  background: rgba(15, 23, 42, .82);
  box-shadow: 0 6px 18px rgba(15, 23, 42, .28);
  pointer-events: none;
  transition: background .15s, transform .12s;
}
.product-media-preview.has-image .product-media-edit { display: inline-flex; }
.product-media-edit svg { width: 18px; height: 18px; }
.product-media-preview.has-image:hover .product-media-edit { background: var(--slate-900); transform: translateY(-1px); }
.product-media-format { font-size: 11px; color: var(--muted); }
.product-file-input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.product-media-caption { padding: 12px 6px 0; color: var(--muted); font-size: 12px; text-align: center; overflow-wrap: anywhere; }
.product-media-error { padding: 0 6px 4px; text-align: center; }

/* Product line thumbnail (order / supplier-order detail tables). */
.cell-product { display: flex; align-items: center; gap: 10px; }
.cell-thumb {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: 6px;
  object-fit: contain;
  background: var(--slate-50);
  border: 1px solid var(--line-soft);
}
/* Preparator product lines: a bigger thumbnail (easy to recognise and tap on a
   phone); with an image it is a button that opens the product image lightbox. */
.cell-thumb.cell-thumb-lg, .table .cell-thumb.cell-thumb-lg { width: 48px; height: 48px; }
.cell-thumb-zoom { flex: 0 0 auto; display: inline-flex; padding: 0; border: 0; background: none; border-radius: 6px; cursor: zoom-in; }
.cell-thumb-zoom:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.catalog-combobox { position: relative; }
.catalog-combobox-control {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--slate-300);
  border-radius: 6px;
  background: var(--surface);
  padding-left: 11px;
  transition: border-color .15s, box-shadow .15s;
}
.catalog-combobox:focus-within .catalog-combobox-control { border-color: var(--primary); box-shadow: var(--ring-primary); }
.catalog-combobox.invalid .catalog-combobox-control { border-color: var(--red-400); }
.catalog-combobox-control > .icon { color: var(--faint); }
.catalog-combobox-control input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  padding: 10px 0;
  background: transparent;
  color: var(--text);
  font: inherit;
}
.catalog-combobox-control input::-webkit-search-cancel-button { display: none; }
.catalog-combobox-control button {
  display: grid;
  place-items: center;
  align-self: stretch;
  width: 40px;
  border: 0;
  border-left: 1px solid var(--line-soft);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
/* Clear ("x") button — sits ahead of the chevron toggle, only shown once a
   pick has actually been made (each combobox's own JS toggles [hidden]). The
   [hidden] override is required: the plain-attribute UA rule ([hidden] {
   display: none }) is LOWER specificity than ".catalog-combobox-control
   button" above (a class + a type selector), so without this the button's
   own `display: grid` silently wins and the "x" renders all the time.
   It also opts out of that same rule's stretched-height / divider-line
   look (would otherwise sit boxed in between the input's own right edge and
   the toggle button's left border) for a small, borderless, inline icon. */
.catalog-combobox-control button[hidden] { display: none; }
.catalog-combobox-control button.catalog-combobox-clear {
  align-self: center;
  width: 26px;
  height: 26px;
  margin-right: -4px;
  border-left: 0;
  border-radius: 6px;
}
.catalog-combobox-control button.catalog-combobox-clear:hover,
.catalog-combobox-control button.catalog-combobox-clear:focus-visible { color: var(--red-500, var(--red-400)); background: rgba(248, 113, 113, .12); }
.catalog-options {
  position: absolute;
  z-index: 20;
  top: calc(100% + 7px);
  left: 0;
  right: 0;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  box-shadow: 0 18px 45px rgba(15, 23, 42, .16);
}
.catalog-options:not([hidden]) { animation: catalog-pop .16s ease both; }
/* combobox.js `floating`: placed in viewport coordinates by placeList(), so a
   scrolling parent can't clip it. */
.catalog-options.is-floating { position: fixed; right: auto; z-index: 70; }
@keyframes catalog-pop { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.catalog-option {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--slate-700);
  text-align: left;
  cursor: pointer;
}
.catalog-option:hover,
.catalog-option.active { background: var(--primary-soft); color: var(--primary-strong); }
.catalog-option-category { font-weight: 650; }
.catalog-option-separator { color: var(--slate-300); }
.catalog-option-subcategory { color: var(--muted); }
.catalog-option:hover .catalog-option-subcategory,
.catalog-option.active .catalog-option-subcategory { color: var(--primary-strong); }
.catalog-option-empty { padding: 14px 12px; color: var(--muted); text-align: center; }
/* option.meta (combobox.js): a second line under the label — plain muted text,
   or a row of chips with the figure in bold. */
.catalog-option-has-meta { flex-direction: column; align-items: flex-start; gap: 5px; }
.catalog-option-has-meta .catalog-option-label { font-weight: 600; color: var(--ink); }
/* combobox option.badge — a round marker before the label; its class sets --ref (the BC form's reference colors). */
.catalog-option-badge { display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 5px; margin-right: 8px; border-radius: 999px; background: var(--ref, var(--slate-500)); color: var(--white); font-size: 11px; font-weight: 700; vertical-align: middle; }
.catalog-option-meta { font-size: 12px; color: var(--muted); }
.catalog-option-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.catalog-option-chip {
  display: inline-flex; align-items: baseline; gap: 4px;
  padding: 2px 8px; border-radius: 999px;
  background: var(--primary-soft); color: var(--primary-strong);
  font-size: 12px; line-height: 1.5;
}
.catalog-option-chip strong { font-weight: 700; }
.catalog-option-chip-label { color: var(--slate-600); }
.catalog-option-chip.is-warn { background: var(--amber-50); color: var(--amber-700); }
.catalog-option-chip.is-good { background: var(--success-50); color: var(--success-700); font-weight: 600; }
.line-cheaper { margin-top: 3px; color: var(--success-700); white-space: normal; }
.catalog-option:hover .catalog-option-chip,
.catalog-option.active .catalog-option-chip { background: var(--surface); }

/* Nested brand / sub-brand combobox panel (products list' Brand filter, and
   the product form's own Brand field): a brand header row followed by its
   sub-brands right underneath, indented with a small tree connector — the
   whole hierarchy visible at once instead of one flat "Brand / Sub-brand"
   line or a second control that only appears after a pick. */
.catalog-option-group { font-weight: 650; }
.catalog-option-child {
  padding-left: 8px;
  color: var(--muted);
}
.catalog-option-child:hover,
.catalog-option-child.active { color: var(--primary-strong); }
.catalog-option-branch {
  position: relative;
  width: 14px;
  height: 20px;
  flex: 0 0 14px;
  margin-left: -2px;
  border-left: 1px solid var(--slate-300);
  border-bottom: 1px solid var(--slate-300);
  border-radius: 0 0 0 6px;
  transform: translateY(-6px);
}
.catalog-option-child:hover .catalog-option-branch,
.catalog-option-child.active .catalog-option-branch { border-color: var(--primary); }
/* Third level (sous-sous-catégorie, category-tree.js): one step further in. */
.catalog-option-grandchild { padding-left: 30px; }

/* Per-group open/close toggle on a .catalog-option-group row (brand or
   category header) — lets a long list stay collapsed until a group's own
   sub-brands/sub-categories are asked for, instead of always dumping every
   one of them under every header. Hidden while the box is actively filtering
   a search term, since a match then always shows regardless of this state. */
.catalog-option-toggle {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  margin: -2px -2px -2px -6px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}
.catalog-option-toggle:hover,
.catalog-option-toggle:focus-visible { background: rgba(226, 232, 240, .72); color: var(--slate-700); }
.catalog-option-toggle svg { transition: transform .15s ease; }
.catalog-option-toggle.is-collapsed svg { transform: rotate(-90deg); }

/* Multi-select combobox (initCombobox `multiple: true`): the picks sit above the
   search control as removable chips, each backed by its own hidden input. */
.catalog-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.catalog-chips:not(:empty) { margin-bottom: 7px; }
.catalog-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 5px 6px 5px 10px;
  border: 1px solid var(--primary);
  border-radius: 999px;
  background: var(--primary-soft);
  color: var(--primary-strong);
  font-size: 13px;
  line-height: 1.2;
}
.catalog-chip-remove {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.catalog-chip-remove:hover { background: var(--primary); color: #fff; }
/* A combobox/date picker/range picker opened inside a horizontally-scrolling
   table would be clipped by the wrap's overflow (overflow-x:auto forces
   overflow-y to auto). Lift the clip only while its popup is open, so it
   shows in full; normal horizontal scrolling resumes once it closes.
   overflow:visible removes clipping on BOTH axes (that's the only way to stop
   the forced overflow-y:auto above), which on a table wider than its wrap
   also un-clips the horizontal side — extra columns that were scrolled out of
   view (e.g. Actions) would suddenly spill past the card's right edge. The
   clip-path reinstates a horizontal-only clip flush with the wrap's own
   edges (its two 0 insets) while leaving top/bottom effectively unclipped, so
   the dropdown still escapes vertically without the table breaking out
   sideways. clip-path isn't subject to overflow's x/y coupling rule, so this
   split is only possible here, not via overflow-x/-y directly.
   contain:layout (below) also makes .table-wrap its own stacking context, so
   even unclipped, the popup only paints within that context — a later,
   non-positioned sibling in the same card (e.g. the "Item not on the order?"
   block right after the receiving discharge table) still paints on top of
   the whole table-wrap box and covers the popup. position:relative (no
   offset, so layout doesn't shift) promotes the table-wrap itself into the
   positioned paint layer, above any non-positioned sibling regardless of DOM
   order; z-index:1 is just to be explicit rather than rely on auto. */
.table-wrap:has(.catalog-combobox-control input[aria-expanded="true"]),
.table-wrap:has(.drp-open),
.table-wrap:has(.multisel-open),
/* Compact filter-row comboboxes (the Client / Fournisseur search — e.g.
   #fcust-search on orders, #fsup-search on supplier orders) skip the full
   .catalog-combobox-control wrapper markup, but still flag open the same
   way via role="combobox"/aria-expanded on the search input itself. */
.table-wrap:has(input[role="combobox"][aria-expanded="true"]) {
  position: relative;
  z-index: 1;
  overflow: visible;
  clip-path: inset(-9999px 0 -9999px 0);
}
/* SKU editor — one card per SKU rather than a nine-column table. The table
   needed a 1060px min-width and scrolled horizontally inside the form, which
   hid the price/supplier columns; stacked cards keep every field labelled and
   on screen at any width. Each card is a header strip (number, live code,
   margin, remove) over three compact sections split by a hairline:
   Identification · Prix · Fournisseurs — titled in a left rail on wide
   screens (no extra line per section), above the fields below 1180px. */
.product-sku-card { padding-bottom: 14px; }
.product-sku-card .product-section-head { margin-bottom: 10px; }
.sku-list { counter-reset: sku; display: grid; gap: 12px; }
.sku-row {
  counter-increment: sku;
  position: relative;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.sku-row:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.sku-row-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px;
  min-height: 40px; padding: 5px 6px 5px 10px;
  background: var(--hover);
  border-bottom: 1px solid var(--line);
  border-radius: 7px 7px 0 0;
}
.sku-row-ident { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sku-row-num {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  min-width: 24px; height: 22px; padding: 0 7px;
  border-radius: 6px; background: var(--ink); color: var(--white);
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.sku-row-num::before { content: "SKU " counter(sku); }
/* The code typed in the field below, echoed live by skuSyncHead() so a
   collapsed glance at the list says which SKU is which. */
.sku-row-code {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; font-weight: 600; color: var(--ink);
}
.sku-row-code.is-empty { color: var(--faint); font-family: var(--font-sans); font-weight: 500; }
/* "Default" only applies to the first card — the first SKU is what listings
   show (see the note under the section head). */
.sku-row-default { display: none; flex: 0 0 auto; }
.sku-row:first-child .sku-row-default { display: inline-flex; }
.sku-row-tools { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.sku-row-remove { color: var(--red-600); }
.sku-row-remove:hover { background: var(--red-50); }
/* A single SKU is not removable — the product needs at least one. */
.sku-list .sku-row:first-child:last-child .sku-row-remove { display: none; }
/* Piece margin (PVP − PA, % of PVP — same rule as the profit report). */
.sku-margin {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); font-size: 12px; color: var(--muted); white-space: nowrap;
}
.sku-margin[hidden] { display: none; }
.sku-margin-value { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.sku-margin.is-negative { border-color: var(--red-400); background: var(--red-50); }
.sku-margin.is-negative .sku-margin-value { color: var(--red-600); }

.sku-row-body { padding: 0 12px; }
.sku-section {
  display: grid; grid-template-columns: 156px minmax(0, 1fr); gap: 0 14px; align-items: start;
  padding: 10px 0;
}
.sku-section + .sku-section { border-top: 1px solid var(--line-soft); }
.sku-section-body { min-width: 0; }
.sku-section-title {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding-top: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted);
}
@media (max-width: 1180px) {
  .sku-section { grid-template-columns: minmax(0, 1fr); }
  .sku-section-title { padding-top: 0; margin-bottom: 8px; }
}
.sku-section-title .icon { width: 13px; height: 13px; flex: 0 0 auto; }
.sku-section-title .card-title-count { text-transform: none; letter-spacing: 0; }

.sku-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 8px 10px; }
.sku-grid .field { margin-bottom: 0; }
.product-sku-card .label { margin-bottom: 4px; }
/* Same compact sizing as the "Informations de base" inputs above — a SKU
   card packs nine-plus fields into one box, so it reads oddly larger than
   the rest of the form at the default .input size. Covers the supplier
   sub-rows too (select + text inputs there also carry .input). */
.product-sku-card .input { padding: 6px 9px; font-size: 13px; border-radius: 6px; }
.sku-f-2 { grid-column: span 2; }
.sku-f-4 { grid-column: span 4; }
@media (max-width: 1180px) {
  .sku-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .sku-f-2, .sku-f-4 { grid-column: span 3; }
}
@media (max-width: 560px) {
  .sku-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sku-f-4 { grid-column: span 2; }
  .sku-f-2 { grid-column: span 1; }
}

/* Purchase/sale price pair — a box per side (Achat / Vente) holding its own
   Pièce + Carton inputs, instead of four flat fields that each repeated
   "PA"/"PVP" in their own label. The sale side carries the brand tint. */
.sku-price-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.sku-price-group {
  height: 100%;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 7px 10px 10px;
  background: var(--hover);
}
.sku-price-group-pvp {
  border-color: var(--emerald-200);
  background: color-mix(in oklab, var(--primary-soft) 60%, var(--surface));
}
.sku-price-group-head {
  display: flex; align-items: center;
  font-size: 12px; font-weight: 600; color: var(--slate-600);
  margin-bottom: 6px;
}
.sku-price-chip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 6px;
  margin-right: 6px; flex: 0 0 auto;
}
.sku-price-chip .icon { width: 10px; height: 10px; }
.sku-price-chip.slate { background: var(--slate-100); color: var(--slate-600); }
.sku-price-chip.green { background: var(--primary-soft); color: var(--primary); }
.sku-price-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 10px; }
.sku-price-sublabel { display: block; font-size: 11px; color: var(--muted); margin-bottom: 2px; }
@media (max-width: 900px) {
  .sku-price-row { grid-template-columns: minmax(0, 1fr); }
}

/* Supplier sub-list inside a SKU card — a SKU can be sourced from several
   suppliers at once, so this is a small bordered table (supplier / their PA /
   their article ref / primary flag) with one header row naming the columns.
   The last two columns are fixed so the header and every line stay aligned. */
.sku-supplier-table { border: 1px solid var(--line); border-radius: 6px; background: var(--surface); }
.sku-supplier-cols,
.sku-supplier-row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) 112px 30px;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 8px;
}
/* With the supplier's own PA (role sees purchase prices): supplier | PA | ref | primary | ✕ */
.sku-supplier-cols.has-pa,
.sku-supplier-row.has-pa { grid-template-columns: minmax(0, 1.4fr) minmax(104px, .8fr) minmax(0, 1fr) 112px 30px; }
.sku-supplier-cols {
  padding-top: 5px; padding-bottom: 5px;
  background: var(--hover); border-bottom: 1px solid var(--line); border-radius: 5px 5px 0 0;
  font-size: 11px; font-weight: 600; color: var(--muted);
}
.sku-supplier-list { display: grid; }
.sku-supplier-row + .sku-supplier-row { border-top: 1px solid var(--line-soft); }
.sku-supplier-row:last-child { border-radius: 0 0 5px 5px; }
.sku-supplier-row:has(.sku-supplier-primary input:checked) {
  background: color-mix(in oklab, var(--primary-soft) 55%, var(--surface));
}
.sku-supplier-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--muted);
  font-size: 12px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.sku-supplier-primary:hover { border-color: var(--slate-300); color: var(--text); }
.sku-supplier-primary:has(input:checked) {
  border-color: var(--primary); color: var(--primary-strong); background: var(--primary-soft);
}
.sku-supplier-remove { color: var(--red-600); width: 30px; height: 30px; padding: 0; justify-content: center; }
.sku-supplier-remove:hover { background: var(--red-50); }
/* A lone supplier line is blanked rather than removed (see removeSupplierRow). */
.sku-supplier-list .sku-supplier-row:first-child:last-child .sku-supplier-remove { visibility: hidden; }
.sku-supplier-add { margin-top: 6px; }
@media (max-width: 720px) {
  /* Each line packs into two rows: supplier · primary · ✕, then PA + ref —
     the column header no longer lines up, so it goes. */
  .sku-supplier-cols { display: none; }
  .sku-supplier-row,
  .sku-supplier-row.has-pa { grid-template-columns: minmax(0, 1fr) 104px 30px; padding: 8px 6px 8px 8px; }
  .sku-supplier-row .sku-supplier-pick { grid-column: 1; grid-row: 1; }
  .sku-supplier-row .sku-supplier-primary { grid-column: 2; grid-row: 1; padding: 0 9px; }
  .sku-supplier-row .sku-supplier-remove { grid-column: 3; grid-row: 1; }
  .sku-supplier-row .sku-supplier-pa-wrap { grid-column: 1; grid-row: 2; }
  .sku-supplier-row .sku-supplier-ref { grid-column: 1 / -1; grid-row: 2; }
  .sku-supplier-row.has-pa .sku-supplier-ref { grid-column: 2 / -1; }
}

/* Required-field marker + inline unit suffix, shared by the product form's
   own fields and the SKU cards. */
.req { color: var(--red-600); font-weight: 700; }
.input-affix { position: relative; display: block; }
.input-affix .input { padding-right: 52px; }
.input-affix-unit {
  position: absolute; top: 50%; right: 12px; transform: translateY(-50%);
  pointer-events: none; font-size: 12px; font-weight: 600; color: var(--muted);
}

/* Product detail page (show.php) — same compact-density pass as the create/
   edit form and /brands above. The hero/tabs shell here (the supplier-hero-
   and supplier-tabs-prefixed classes) is shared with the suppliers and
   customers show pages, so it's tightened via this page-scoped wrapper
   rather than editing those shared rules directly, which would also resize
   the other two modules' still-unrevised detail pages. */
.product-detail-page .supplier-hero-card { padding: 16px; margin-bottom: 14px; }
/* Actions pulled out of the state panel below and pinned to their own row
   at the top of the card, so they stay put regardless of how tall either
   panel below ends up (a long product name, the stock-breakdown box, etc.). */
.product-detail-page .supplier-hero-actions { display: flex; justify-content: flex-end; gap: 8px; margin-bottom: 14px; }
/* Info panel stacked above the state panel (was side-by-side, each in its
   own column). Two panels whose height varies independently and by a lot
   per product (the state panel alone can gain ~90px when the stock-
   breakdown box shows up) kept fighting either an empty or an awkwardly
   large gap under whichever side of a shared row was shorter — stacking
   them removes the shared row entirely, so neither ever needs to match
   the other's height. */
.product-detail-page .supplier-hero-grid { display: flex; flex-direction: column; gap: 14px; }
.product-detail-page .supplier-hero-main,
.product-detail-page .supplier-hero-side { padding: 14px; }
/* Both panels span the full card width now instead of a ~300px sidebar. The
   state panel squeezes all of its cards — the 4 mini-stats *and* the
   stock-breakdown box — onto that one wide row instead of stats-row-then-
   stock-box-below: display:contents drops the stats grid wrapper out of
   layout so its 4 children become direct flex items of .supplier-hero-side,
   sitting flush alongside the stock box; flex-wrap still lets everything
   wrap onto a second row gracefully on a narrow viewport. */
.product-detail-page .supplier-hero-side { display: flex; flex-direction: row; flex-wrap: wrap; gap: 6px; }
.product-detail-page .supplier-hero-stats { display: contents; }
.product-detail-page .supplier-mini-stat {
  flex: 1 1 84px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Each state tile reads top-down: icon + what it counts, the number, then one
   plain line saying what that number means (products/pages/show.php). */
.product-detail-page .supplier-mini-stat { flex: 1 1 130px; justify-content: flex-start; gap: 3px; background: var(--surface); border-color: var(--line); }
.product-detail-page .supplier-mini-label { display: flex; align-items: center; gap: 6px; margin: 0 0 2px; font-size: 12px; font-weight: 600; color: var(--slate-700); }
.product-detail-page .supplier-mini-value { font-size: 17px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.product-detail-page .supplier-mini-label .stat-compact-ico { width: 20px; height: 20px; }
.product-detail-page .supplier-mini-label .stat-compact-ico .icon { width: 12px; height: 12px; }
.product-detail-page .supplier-mini-sub { font-size: 11.5px; color: var(--muted); }
.product-detail-page .supplier-mini-bar { height: 4px; margin: 2px 0; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.product-detail-page .supplier-mini-bar::before { content: ""; display: block; width: var(--pct, 0%); height: 100%; border-radius: inherit; background: var(--primary); }
.product-detail-page .supplier-mini-stat.is-negative { background: var(--red-50); border-color: var(--red-400); }
.product-detail-page .supplier-mini-stat.is-negative :is(.supplier-mini-value, .supplier-mini-sub) { color: var(--red-600); }
.product-detail-page .supplier-mini-stat.is-negative .supplier-mini-sub { font-weight: 600; }
/* The stock box carries more content (a header plus a 2-value pair) than a
   plain mini-stat, so it claims a bigger, but still capped, share of the row
   instead of stretching to fill whatever space is left. */
.product-detail-page .supplier-stock-box { flex: 1.6 1 170px; max-width: 340px; }
/* Stock breakdown box — same bordered-box-with-icon-chip-header shape as the
   SKU form's PA/PVP price groups (.sku-price-group), but its own classes
   since this lives in the product detail hero, not a SKU card. Replaces a
   pill that used to sit inside the "On hand" mini-stat: a long string like
   "112 cartons + 9 pièces" wrapped inside that small grid cell and stretched
   it taller than its three siblings, throwing off the whole 2x2 stats grid —
   giving it its own box below the grid instead fixes that regardless of how
   long the numbers get. */
.product-detail-page .supplier-stock-box {
  display: flex; flex-direction: column; gap: 3px;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 10px;
  background: var(--surface);
}
.product-detail-page .supplier-stock-box-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0 6px;
  margin-bottom: 2px; font-size: 12px; font-weight: 600; color: var(--slate-700);
}
.product-detail-page .supplier-stock-box-head .card-icon-chip { width: 20px; height: 20px; margin: 0; }
.product-detail-page .supplier-stock-box-head .card-icon-chip .icon { width: 12px; height: 12px; }
/* The carton/piece counts below only say how the on-hand total splits up —
   this says what one carton actually holds, so "5 pièces restantes" reads
   against something (out of how many per carton) instead of floating free. */
.product-detail-page .supplier-stock-box-ratio { font-weight: 400; color: var(--muted); }
.product-detail-page .supplier-stock-box-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 6px; }
.product-detail-page .supplier-stock-box-value { font-size: 17px; font-weight: 700; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }
.product-detail-page .supplier-stock-box-sublabel { display: block; font-size: 11.5px; color: var(--muted); margin-top: 3px; }
/* The 3D pallet (modules/scene3d/) wraps onto its own line under the tiles. */
.product-detail-page .product-pallet {
  flex: 1 1 100%; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 16px;
  border: 1px solid var(--line); border-radius: 6px; padding: 8px 10px; background: var(--surface);
}
.product-detail-page .product-pallet-copy { flex: 1 1 180px; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; font-size: 13px; }
.product-detail-page .product-pallet-title { font-size: 12px; font-weight: 600; color: var(--slate-700); }
.product-detail-page .product-pallet .pallet-scene { flex: 0 1 480px; height: 190px; margin: 0; }
/* Avatar is a <button> here (it opens the lightbox below), not the plain
   <div> the shared .supplier-avatar rule was written for — reset the native
   button chrome so it still reads as the same round photo chip. */
.product-detail-page .supplier-avatar-zoomable { cursor: pointer; border: 0; padding: 0; font: inherit; position: relative; }
.product-detail-page .supplier-avatar-zoomable:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.product-detail-page .supplier-avatar-zoom-hint {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(15, 23, 42, 0); color: transparent;
  transition: background .15s, color .15s;
}
.product-detail-page .supplier-avatar-zoomable:hover .supplier-avatar-zoom-hint,
.product-detail-page .supplier-avatar-zoomable:focus-visible .supplier-avatar-zoom-hint {
  background: rgba(15, 23, 42, .45); color: var(--white);
}

/* Product image lightbox — same fixed-backdrop + centered-content + fade/
   scale-in shape as .confirm-modal-backdrop above, but its own classes since
   this one holds a plain image, not a confirm dialog. Markup + toggle script
   live in products/pages/show.php, and in preparator/pages/_product_image_viewer.php
   for the preparator's product thumbnails. */
.product-image-lightbox-backdrop {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  padding: 32px;
  background: rgba(15, 23, 42, .72);
  opacity: 0; pointer-events: none;
  transition: opacity .15s ease;
}
.product-image-lightbox-backdrop.is-open { opacity: 1; pointer-events: auto; }
/* Toggled via the `hidden` attribute (see show.php's script) — a bare
   [hidden] loses to the display:flex above on equal specificity (same
   gotcha as .wh-modal-backdrop[hidden]). */
.product-image-lightbox-backdrop[hidden] { display: none; }
.product-image-lightbox {
  position: relative;
  max-width: min(720px, 100%);
  max-height: 100%;
  transform: scale(.96);
  transition: transform .15s ease;
}
.product-image-lightbox-backdrop.is-open .product-image-lightbox { transform: scale(1); }
.product-image-lightbox img {
  display: block; margin: 0 auto;
  max-width: 100%; max-height: 82vh;
  border-radius: 6px; background: var(--white);
  box-shadow: 0 24px 60px rgba(15, 23, 42, .35);
  object-fit: contain;
}
.product-image-lightbox-close {
  position: absolute; top: -14px; right: -14px;
  width: 32px; height: 32px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--white); color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: 0 4px 12px rgba(15, 23, 42, .2);
  cursor: pointer;
}
.product-image-lightbox-close:hover { background: var(--slate-100); }
.product-image-lightbox-close .icon { width: 16px; height: 16px; }
.product-detail-page .supplier-contact-grid { margin-top: 14px; gap: 10px 16px; }
.product-detail-page .supplier-mini-stat { padding: 8px 10px; }
.product-detail-page .supplier-tabs-card { margin-bottom: 14px; }
.product-detail-page .supplier-tabs-card:last-child { margin-bottom: 0; }
.product-detail-page .supplier-tabs-card .card-head { padding: 12px 16px; min-height: 58px; }
.product-detail-page .supplier-tabs-copy { margin-top: 2px; }

/* Hide the native up/down spinner arrows on a number input. */
.input-no-spinner::-webkit-outer-spin-button,
.input-no-spinner::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.input-no-spinner { appearance: textfield; -moz-appearance: textfield; }

/* Form footer bar — right-aligned actions with the required-fields legend on
   the left, matching the Cancel-then-primary order used by the users form. */
.form-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--line-soft);
}
.form-actions-note { font-size: 12px; color: var(--muted); }
/* Flat (unstaggered) fade-in — every row shares one 0-delay animation instead
   of a per-row nth-child delay, so this stays cheap regardless of SKU count
   (unlike a big data table, this list is never more than a handful of rows,
   but the "no per-row stagger" habit still applies). */
.sku-row { animation: rise .3s ease both; }
.sku-row.is-removing { animation: sku-row-out .18s ease forwards; }
@keyframes sku-row-out { to { opacity: 0; transform: translateY(-4px); } }

/* Product import */
.import-card { margin-bottom: 18px; overflow: hidden; }
/* Same escape hatch as .import-table-wrap above it: this card's own
   overflow:hidden (for the table's rounded corners) still clips the Brand /
   Category dropdown's bottom even after the wrap lifts its clip, since the
   card is a further ancestor. Lift it too while a combobox is open. */
.import-card:has(.catalog-combobox-control input[aria-expanded="true"]) {
  overflow: visible;
}
.import-upload-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 14px;
  align-items: end;
}
.import-upload-actions { display: flex; align-items: center; }

/* File drop picker — replaces the bare native "Choose file" button. The real
   <input type="file"> stays put, invisibly covering the whole zone (inset:0,
   opacity:0), so a click anywhere opens the OS picker and a drag-and-drop
   from Finder/Explorer lands on the input natively — no manual DataTransfer
   plumbing needed, just a 'change' listener to reflect the picked file. */
.file-drop {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  border: 1.5px dashed var(--line);
  border-radius: 6px;
  background: var(--slate-50);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.file-drop:hover,
.file-drop.is-dragover { border-color: var(--primary); background: var(--primary-soft); }
.file-drop:focus-within { outline: 2px solid var(--primary); outline-offset: 2px; }
.file-drop.has-file { border-style: solid; background: var(--surface); }
.file-drop-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.file-drop-icon { display: grid; place-items: center; width: 40px; height: 40px; flex: none; border-radius: 6px; background: var(--primary-soft); color: var(--primary); }
.file-drop-icon .icon { width: 18px; height: 18px; }
.file-drop-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.file-drop-title { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-drop-hint { font-size: 12px; color: var(--muted); }
.file-drop-clear {
  position: relative;
  z-index: 1;
  margin-left: auto;
  display: none;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  flex: none;
}
.file-drop-clear:hover { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }
.file-drop.has-file .file-drop-clear { display: inline-flex; }
.import-sample {
  margin-top: 16px;
  padding: 14px 16px;
  border: 1px dashed var(--line);
  border-radius: 6px;
  background: var(--slate-50);
}
.import-sample code {
  display: block;
  margin-top: 8px;
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--surface);
  color: var(--slate-700);
  overflow-x: auto;
  contain: layout;
}
.import-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.import-table-wrap { overflow-x: auto; contain: layout; }
/* Same escape hatch as .table-wrap (below), clip-path included so a row
   wider than the wrap doesn't spill sideways past the card while open —
   see the comment there for why overflow alone can't do a horizontal-only
   clip. Without it the row editor's Brand / Category comboboxes pop their
   dropdown behind this wrapper's own overflow, clipping or hiding it
   entirely. */
.import-table-wrap:has(.catalog-combobox-control input[aria-expanded="true"]) {
  overflow: visible;
  clip-path: inset(-9999px 0 -9999px 0);
}
/* auto (not fixed) layout: with fixed layout and no per-column widths, every
   column got squeezed to roughly the same width regardless of content, so a
   full email address or an issue message had nowhere to go but to overflow
   its cell and visually run into the next column. Auto layout sizes each
   column to what it actually holds and lets the table grow past min-width
   when it needs to — the wrapper's overflow-x:auto already handles that. */
.import-table { min-width: 1680px; }
/* Pinned to the scroll container's right edge — the table is wider than the
   viewport, so a plain last column (the row-edit pencil) would sit off-screen
   until the user scrolls all the way over, with no hint it's even there. */
.import-actions-col { position: sticky; right: 0; z-index: 2; box-shadow: -6px 0 6px -6px rgba(15, 23, 42, .18); }
.import-table th,
.import-table td { font-size: 13px; overflow-wrap: anywhere; white-space: normal; }
.import-name-cell { color: var(--ink); font-weight: 600; }
.import-issues { display: flex; flex-wrap: wrap; gap: 6px; }
.import-issue {
  display: inline-flex;
  align-items: center;
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1.2;
}
.import-issue-missing,
.import-issue-invalid { background: var(--red-50); color: var(--red-600); }
.import-issue-duplicate { background: var(--amber-50); color: var(--amber-700); }
.import-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
}
.import-row-success td { background: rgba(236, 253, 245, .85); }
.import-row-warning td { background: rgba(255, 251, 235, .9); }
.import-row-error td { background: rgba(254, 242, 242, .85); }
.import-flash-error { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }

/* Step 2 inline row editor — fix one row's data in place instead of
   re-uploading the whole file. Toggled by JS between the display <tr> and
   its sibling .import-edit-row (see modules/customers/pages/import.php). */
.import-edit-row td { background: var(--slate-50); padding: 14px 16px; }
.import-edit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px 14px; }
.import-edit-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
/* Brand and Category/Sub-category are catalog-combobox widgets, which default
   to .input-sized padding — without this they stand taller than every other
   .input-sm field in the row (same fix as .filter-combobox below). */
.import-edit-grid .catalog-combobox-control { height: 32px; padding-left: 9px; }
.import-edit-grid .catalog-combobox-control input { padding: 0; font-size: 13px; }
.import-edit-grid .catalog-combobox-control > .icon { width: 14px; height: 14px; }
.import-edit-grid .catalog-combobox-control button { width: 30px; }
.import-edit-grid .catalog-options { font-size: 13px; }
.import-edit-actions { display: flex; gap: 8px; margin-top: 12px; }

/* Inline brand creation */
.brand-inline-card {
  display: grid;
  grid-template-columns: minmax(240px, .65fr) minmax(0, 1.35fr);
  gap: 22px;
  align-items: center;
  margin-bottom: 18px;
  padding: 15px 17px;
  border: 1px solid var(--emerald-200);
  border-radius: 6px;
  background: linear-gradient(110deg, var(--primary-soft), var(--surface) 46%);
  box-shadow: var(--shadow-card);
}
.brand-inline-intro { display: flex; align-items: center; gap: 12px; }
.brand-inline-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 6px; background: var(--primary); color: var(--white); }
.brand-inline-icon .icon { width: 19px; height: 19px; }
.brand-inline-copy { margin-top: 3px; font-size: 12px; }
.brand-inline-form { display: grid; grid-template-columns: minmax(180px, 1fr) auto auto; gap: 14px; align-items: end; }
.brand-inline-name .label { margin-bottom: 5px; }
/* Same compact sizing as the products form (padding/font/radius) — this
   input and button were still at the default, larger .input/.btn scale,
   which read oddly bigger than the rest of the app's forms now that. */
.brand-inline-form .input { padding: 9px 10px; font-size: 13px; border-radius: 6px; }
.brand-inline-status { min-height: 38px; padding-bottom: 1px; }
.brand-inline-form .btn { min-height: 38px; justify-content: center; border-radius: 8px; }
.brand-inline-form .btn:disabled { opacity: .65; cursor: wait; }
@keyframes brand-saving-spin { to { transform: rotate(360deg); } }
.table-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 20px;
  border-top: 1px solid var(--line-soft);
}
.table-pagination[hidden] { display: none; }
.table-page-link,
.table-page-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--slate-600);
  font-size: 13px;
  font-weight: 600;
}
.table-page-link { padding: 6px 11px; }
.table-page-number { min-width: 34px; padding: 6px; }
.table-page-link:hover,
.table-page-number:hover { border-color: var(--slate-300); background: var(--hover); color: var(--ink); }
.table-page-number.active { border-color: var(--ink); background: var(--ink); color: var(--white); }
.table-page-link.disabled { opacity: .42; pointer-events: none; }
.table-page-numbers { display: flex; align-items: center; gap: 6px; }
/* A row on another page of an auto-paged table (table-pager.js). Printing shows every row. */
tr[data-page-off] { display: none !important; }
@media print {
  tr[data-page-off]:not([hidden]):not([style*="none"]) { display: table-row !important; }
  .table-pagination { display: none !important; }
}

/* Inline taxonomy management */
.taxonomy-inline-card {
  display: grid;
  grid-template-columns: minmax(260px, .7fr) minmax(0, 1.3fr);
  gap: 24px;
  align-items: center;
  margin-bottom: 18px;
  padding: 15px 17px;
  border: 1px solid #bfdbfe;
  border-radius: 6px;
  background:
    linear-gradient(112deg, rgba(219, 234, 254, .72), rgba(255, 255, 255, .94) 48%),
    var(--surface);
  box-shadow: var(--shadow-card);
}
.taxonomy-inline-intro { display: flex; align-items: center; gap: 12px; }
.taxonomy-inline-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border-radius: 6px;
  background: var(--blue-600);
  color: var(--white);
  box-shadow: 0 8px 20px rgba(37, 99, 235, .18);
}
.taxonomy-inline-icon .icon { width: 20px; height: 20px; }
.taxonomy-inline-copy,
.taxonomy-table-copy { margin-top: 3px; font-size: 12px; }
.taxonomy-inline-form {
  display: grid;
  grid-template-columns: minmax(190px, 1fr) auto auto;
  gap: 14px;
  align-items: end;
}
.taxonomy-inline-status { min-height: 42px; padding-bottom: 1px; }
.taxonomy-inline-form .btn { min-height: 42px; justify-content: center; }
.taxonomy-inline-form .btn:disabled,
.taxonomy-subcategory-form .btn:disabled,
.taxonomy-subsubcategory-form .btn:disabled { opacity: .6; cursor: wait; }
.taxonomy-card { overflow: hidden; }
.taxonomy-card-head { min-height: 70px; }
.taxonomy-card-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.taxonomy-collapse-toggle { white-space: nowrap; }
.taxonomy-table-wrap { overflow-x: auto; contain: layout; }
.taxonomy-table { min-width: 800px; table-layout: fixed; }
.taxonomy-col-name { width: 44%; }
.taxonomy-col-type { width: 15%; }
.taxonomy-col-products { width: 11%; }
.taxonomy-col-status { width: 13%; }
.taxonomy-col-action { width: 17%; }
.taxonomy-table td { vertical-align: middle; }
.taxonomy-table tbody.taxonomy-group + tbody.taxonomy-group .taxonomy-category-row td,
.taxonomy-table tbody.taxonomy-group + tbody.taxonomy-group .taxonomy-brand-row td {
  border-top: 8px solid var(--slate-50);
}
.taxonomy-table tbody tr.taxonomy-category-row,
.taxonomy-table tbody tr.taxonomy-brand-row { background: rgba(248, 250, 252, .76); }
.taxonomy-table tbody tr.taxonomy-category-row:hover,
.taxonomy-table tbody tr.taxonomy-brand-row:hover { background: var(--slate-100); }
.taxonomy-category-row,
.taxonomy-brand-row { cursor: pointer; }
.taxonomy-category-row .taxonomy-name-cell,
.taxonomy-brand-row .taxonomy-name-cell { color: var(--ink); font-weight: 700; }
.taxonomy-subcategory-row .taxonomy-name-cell,
.taxonomy-subbrand-row .taxonomy-name-cell { padding-left: 34px; }
/* The third level sits one step further in — `body .table…` to beat the skin's 16px first cell. */
body .table.taxonomy-table tr.taxonomy-subsubcategory-row > td.taxonomy-name-cell { padding-left: 44px; color: var(--muted); }
.taxonomy-identity { display: flex; align-items: center; gap: 12px; min-width: 0; }
.taxonomy-group-toggle {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  margin-left: -4px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}
.taxonomy-group-toggle:hover,
.taxonomy-group-toggle:focus-visible { background: rgba(226, 232, 240, .72); color: var(--slate-700); }
.taxonomy-group-toggle .icon { transition: transform .15s ease, color .15s ease; }
.taxonomy-group.is-collapsed .taxonomy-subcategory-row,
.taxonomy-group.is-collapsed .taxonomy-subsubcategory-row,
.taxonomy-group.is-collapsed .taxonomy-subsubcategory-form-row,
.taxonomy-group.is-collapsed .taxonomy-subbrand-row { display: none; }
.taxonomy-group.is-collapsed .taxonomy-group-toggle .icon { transform: rotate(-90deg); }
.taxonomy-subcategory-identity,
.taxonomy-subbrand-identity { position: relative; }
.taxonomy-branch {
  position: relative;
  width: 16px;
  height: 24px;
  flex: 0 0 16px;
  margin-left: -14px;
  border-left: 1px solid var(--slate-300);
  border-bottom: 1px solid var(--slate-300);
  border-radius: 0 0 0 6px;
  transform: translateY(-7px);
}
.taxonomy-image {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .8);
  border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(15, 23, 42, .04);
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
}
.taxonomy-subsubcategory-row .taxonomy-image {
  width: 30px;
  height: 30px;
  flex-basis: 30px;
}
.taxonomy-subcategory-row .taxonomy-image {
  width: 34px;
  height: 34px;
  flex-basis: 34px;
  border-radius: 6px;
}
.taxonomy-image img { width: 100%; height: 100%; object-fit: cover; background: var(--white); }
.taxonomy-image-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.taxonomy-image-overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(15, 23, 42, .68);
  color: var(--white);
  opacity: 0;
  transition: opacity .15s;
}
.taxonomy-image:hover .taxonomy-image-overlay,
.taxonomy-image:focus-within .taxonomy-image-overlay,
.taxonomy-image.is-uploading .taxonomy-image-overlay { opacity: 1; }
.taxonomy-image.is-uploading { opacity: .6; cursor: wait; }
.taxonomy-image.is-uploading .taxonomy-image-overlay .icon { animation: brand-saving-spin .7s linear infinite; }
.taxonomy-image-1 { background: #dbeafe; color: #1e40af; }
.taxonomy-image-2 { background: #dcfce7; color: #166534; }
.taxonomy-image-3 { background: #fef3c7; color: #92400e; }
.taxonomy-image-4 { background: #fce7f3; color: #9d174d; }
.taxonomy-image-5 { background: #ede9fe; color: #5b21b6; }
.taxonomy-image-6 { background: #cffafe; color: #155e75; }
.taxonomy-name-editor {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 100%;
}
.taxonomy-name-display {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  max-width: 100%;
  gap: 8px;
  margin-left: -8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: inherit;
  text-align: left;
  cursor: text;
}
.taxonomy-name-text {
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}
.taxonomy-name-pencil {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--faint);
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity .15s, transform .15s;
}
.taxonomy-name-display:hover { background: rgba(226, 232, 240, .72); }
.taxonomy-name-display:hover .taxonomy-name-pencil,
.taxonomy-name-display:focus-visible .taxonomy-name-pencil { opacity: 1; transform: translateX(0); }
.taxonomy-name-input {
  display: none;
  width: min(360px, 100%);
  border: 1px solid var(--primary);
  border-radius: 6px;
  padding: 7px 9px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  outline: 0;
  box-shadow: var(--ring-primary);
}
.taxonomy-name-editor.is-editing .taxonomy-name-display { display: none; }
.taxonomy-name-editor.is-editing .taxonomy-name-input { display: block; }
.taxonomy-name-editor.is-saving .taxonomy-name-input {
  padding-right: 30px;
  color: var(--slate-400);
  background: var(--slate-100);
  border-color: var(--slate-300);
  box-shadow: none;
  cursor: wait;
}
.taxonomy-name-editor.is-saving::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 9px;
  width: 13px;
  height: 13px;
  margin-top: -7px;
  border: 2px solid var(--slate-300);
  border-top-color: var(--slate-600);
  border-radius: 50%;
  animation: brand-saving-spin .7s linear infinite;
}
.taxonomy-type {
  display: inline-flex;
  align-items: center;
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
.taxonomy-type-category { background: var(--blue-50); color: var(--blue-700); }
.taxonomy-type-subcategory { background: var(--slate-100); color: var(--slate-600); }
.taxonomy-type-subsubcategory { background: var(--slate-50); color: var(--slate-500); box-shadow: inset 0 0 0 1px var(--slate-200); }
.taxonomy-type-brand { background: var(--primary-soft); color: var(--primary); }
.taxonomy-type-subbrand { background: var(--slate-100); color: var(--slate-600); }
.taxonomy-table form { margin: 0; }
.taxonomy-table form > .pill { border: 0; cursor: pointer; }
.taxonomy-add-sub,
.taxonomy-add-subsub { white-space: nowrap; }
.taxonomy-row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.taxonomy-row-actions form { margin: 0; }
.wh-icon-btn.is-danger:hover { background: var(--red-50); color: var(--danger, #dc2626); border-color: #fecaca; }
.taxonomy-subcategory-form-row td,
.taxonomy-subbrand-form-row td { padding: 14px 20px 16px 68px; background: var(--blue-50); }
.taxonomy-subsubcategory-form-row td { padding: 14px 20px 16px 102px; background: var(--blue-50); }
.taxonomy-subcategory-form,
.taxonomy-subsubcategory-form,
.taxonomy-subbrand-form {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) auto auto;
  gap: 14px;
  align-items: end;
  padding: 14px;
  border: 1px solid #bfdbfe;
  border-radius: 6px;
  background: rgba(255, 255, 255, .86);
}
.taxonomy-subcategory-actions,
.taxonomy-subbrand-actions { display: flex; align-items: center; gap: 6px; min-height: 42px; }
.taxonomy-subcategory-form .btn,
.taxonomy-subsubcategory-form .btn,
.taxonomy-subbrand-form .btn { min-height: 38px; }
.taxonomy-empty-cell { padding: 44px 20px !important; color: var(--muted) !important; text-align: center; }
.taxonomy-row-new,
.taxonomy-group-new { animation: taxonomy-arrive .45s ease-out both; }
@keyframes taxonomy-arrive {
  from { opacity: 0; transform: translateY(-7px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Suppliers */
.supplier-form-card { overflow: hidden; }
.supplier-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 20px;
}
.supplier-form-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 8px -20px -20px;
  padding: 18px 20px;
  border-top: 1px solid var(--line-soft);
  background: var(--slate-50);
}
.supplier-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.supplier-hero-card { padding: 22px; margin-bottom: 18px; }
.supplier-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(280px, .9fr);
  gap: 18px;
  align-items: stretch;
}
.supplier-hero-main,
.supplier-hero-side {
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--surface);
}
.supplier-hero-main {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 18px;
  align-items: start;
  padding: 18px;
  background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(248,250,252,.96));
}
.supplier-avatar {
  width: 84px;
  height: 84px;
  border-radius: 6px;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--primary-soft), #dbeafe);
  color: var(--primary-strong);
  font-size: 28px;
  font-weight: 800;
  box-shadow: inset 0 0 0 1px rgba(37, 99, 235, .08);
  overflow: hidden;
}
.supplier-avatar img { width: 100%; height: 100%; object-fit: cover; }
.supplier-hero-copy { min-width: 0; }
.supplier-contact-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 18px;
  margin-top: 18px;
}
.supplier-label {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
}
.supplier-hero-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
}
.supplier-side-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.supplier-hero-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
.supplier-mini-stat {
  padding: 12px;
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--slate-50);
}
.supplier-mini-value {
  font-size: 20px;
  font-weight: 800;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.supplier-mini-label {
  margin-top: 3px;
  font-size: 12px;
  color: var(--muted);
}
.supplier-mini-stat .pill {
  margin-top: 8px;
  max-width: 100%;
  white-space: normal;
  text-align: left;
}
.supplier-tabs-card { overflow: hidden; }
.supplier-tabs-head { align-items: flex-start; gap: 16px; }
.supplier-tabs-copy { margin-top: 3px; font-size: 12px; }
.supplier-tabs {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: flex-end;
}
.supplier-tab {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--slate-600);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
a.supplier-tab { text-decoration: none; }   /* /reports: each tab is a link (?tab=) */
.supplier-tab:hover { background: var(--hover); color: var(--ink); }
.supplier-tab.is-active {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--white);
}
.supplier-panels { padding: 0; }
.supplier-panel { padding: 0; }
.supplier-panel-intro {
  padding: 18px 20px 20px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 16px;
}
.supplier-panel > .stats { padding: 18px 20px 0; }
.supplier-empty-state {
  padding: 18px 20px 0;
}
.supplier-empty-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 4px;
}
/* contain:layout keeps a wide, legitimately-scrollable table from inflating
   document.documentElement.scrollWidth — overflow-x:auto alone clips it
   visually and blocks interactive scrolling, but the root element's own
   scrollWidth still measures the un-clipped layout size regardless of the
   overflow value (hidden/clip included). That inflated root scrollWidth is
   what a browser's responsive-mode canvas sizing (or a screenshot/capture
   tool's "full page" sizing) can pick up instead of the true viewport width,
   squeezing real content into a corner with dead space beside it — most
   visible re-toggling device/responsive mode without a full page reload.

   THE COST, and it bites silently: contain:layout makes this element the
   containing block for position:fixed descendants. Anything meant to cover the
   viewport — a .dock-panel, a .dock-backdrop, a modal — is laid out against
   THIS box instead when rendered inside a cell, so it appears trapped in the
   table's card rather than over the page. Render those outside .table-wrap
   (see modules/incoming_orders/pages/review.php's stock docks: the Alpine
   scope wraps both the card and the docks, the docks sit after it). */
.table-wrap { overflow-x: auto; contain: layout; }

.supplier-tabs-card .card-head { min-height: 74px; }
.supplier-panel .table { min-width: 920px; }

@media (max-width: 880px) {
  .supplier-form-card .supplier-form-grid { grid-template-columns: 1fr; }
  .supplier-form-footer { align-items: flex-start; flex-direction: column; }
  .supplier-tabs-head { align-items: flex-start; flex-direction: column; }
  .supplier-tabs { justify-content: flex-start; }
  .supplier-hero-grid { grid-template-columns: 1fr; }
}

/* Supplier orders */
.supplier-order-list-card,
.supplier-order-lines-card { overflow: hidden; }
/* Same escape hatch as .import-card: .table-wrap already lifts its own
   overflow while a filter popup is open (see .table-wrap:has(...) above),
   but .supplier-order-list-card is a further ancestor with overflow:hidden
   of its own (for the table's rounded corners), which still clips the
   status multiselect's bottom. Lift it too while any such popup is open. */
.supplier-order-list-card:has(.catalog-combobox-control input[aria-expanded="true"]),
.supplier-order-list-card:has(.drp-open),
.supplier-order-list-card:has(.multisel-open) {
  overflow: visible;
}
/* A calendar inside a scrolling table floats at a fixed spot (datepicker.js,
   place()) instead: the table keeps its scroll, and only drops contain:layout —
   which would otherwise make it the containing block of that fixed popup. */
.table-wrap:has(.dp-open) { contain: none; }
.supplier-order-row { cursor: pointer; }
.supplier-order-row.row-partial-delivery td { background: rgba(255, 251, 235, .9); }
.supplier-order-card-copy { margin-top: 4px; font-size: 12px; }
.supplier-order-empty { padding: 44px 20px !important; text-align: center; color: var(--muted) !important; }
.supplier-order-lines-card { margin-top: 16px; margin-bottom: 20px; }
.supplier-order-lines { min-width: 1220px; }
.supplier-order-lines th:nth-child(1) { min-width: 280px; }
.supplier-order-lines th:nth-child(2) { min-width: 240px; }
.supplier-order-lines .line-quantity,
.supplier-order-lines .line-received,
.supplier-order-lines .line-price { min-width: 112px; text-align: right; }
.supplier-order-lines tr.row-partial-delivery td { background: rgba(255, 251, 235, .9); }
.supplier-order-title { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
/* BC header: "Envoyer l'email" + "Modifier la commande" wrap as one pair. From
   1200px the button row stops shrinking (the title's pills wrap instead), so
   all the buttons sit on one line; narrower, the pair drops under the PDF
   button together. The reference never splits at its hyphen ("CF-" / "2026-…"). */
.supplier-order-title .mono { white-space: nowrap; }
.supplier-order-main-actions { flex-wrap: nowrap; }
@media (min-width: 1200px) {
  .page-head > .supplier-order-actions { flex-shrink: 0; margin-inline-start: 16px; }
}
/* Phone: a touch tighter so the pair fits a 360px screen; it wraps only on
   anything narrower rather than running off the left edge. */
@media (max-width: 640px) {
  .page-head > .supplier-order-actions > .supplier-order-main-actions { flex: 1 1 auto; flex-wrap: wrap; gap: 8px; }
  .page-head .supplier-order-main-actions > .btn { flex: 1 1 auto; justify-content: center; padding-inline: 10px; gap: 6px; font-size: 13px; }
}
.supplier-order-detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.65fr) minmax(300px, .75fr);
  gap: 20px;
  align-items: stretch;
  margin-bottom: 20px;
}
.supplier-order-detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }
.supplier-order-value { color: var(--ink); font-size: 23px; font-weight: 800; white-space: nowrap; }
.supplier-order-value span { color: var(--muted); font-size: 12px; font-weight: 700; }
.supplier-order-overview {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px 24px;
  margin-top: 16px;
}
.supplier-order-overview > div > span,
.supplier-order-notes-box > span {
  display: block;
  margin-bottom: 5px;
  color: var(--faint);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.supplier-order-overview strong { color: var(--ink); }
.supplier-order-notes-box { margin-top: 22px; padding: 16px; border: 1px solid var(--line-soft); border-radius: 6px; background: var(--slate-50); }
.supplier-order-notes-box p { margin: 0; color: var(--slate-700); }
.supplier-order-status-form { margin-top: 14px; }
/* Same compact density as modules/supplier_orders/pages/_form.php and the
   product form (9px/10px padding, 13px type, 6px radius) — this was the one
   place on the order's own detail page still on the roomier default .input. */
.supplier-order-status-form .input { padding: 9px 10px; font-size: 13px; border-radius: 6px; }
.supplier-order-status-form .field { margin-bottom: 14px; }

@media (max-width: 980px) {
  .supplier-order-detail-grid { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
  .supplier-order-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
  .supplier-order-detail-head { flex-direction: column; }
}

/* Stepper */
.stepper { display: flex; align-items: flex-start; --step-line-top: 13px; --step-fill: var(--primary); }
.step { flex: 1; text-align: center; position: relative; }
.step .bullet { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; margin: 0 auto 8px; background: var(--surface); border: 2px solid var(--line); color: var(--slate-300); position: relative; z-index: 1; }
.step.done .bullet { background: var(--primary); border-color: var(--primary); color: var(--white); }
.step.done .bullet .icon { width: 15px; height: 15px; }
.step.current .bullet { box-shadow: 0 0 0 5px var(--emerald-50); transition: box-shadow .2s ease; }
/* ::before is the grey track, ::after the finished part laid over it — kept
   apart so the order page can draw the finished part in (.step.is-new below). */
.step::before, .step::after { content: ""; position: absolute; top: var(--step-line-top, 13px); left: -50%; width: 100%; height: 2px; z-index: 0; }
.step::before { background: var(--line); }
.step::after { background: var(--step-fill, var(--primary)); transform: scaleX(0); transform-origin: left center; }
.step.done::after { transform: none; }
.step:first-child::before, .step:first-child::after { display: none; }
.s-title { font-size: 13px; font-weight: 600; color: var(--slate-700); }
.s-sub { font-size: 12px; color: var(--faint); }

/* Flash / page header (components) */
.flash { display: flex; align-items: center; gap: 8px; background: var(--success-soft); color: var(--success-strong); border: 1px solid var(--success-200); border-radius: 6px; padding: 10px 14px; font-size: 14px; margin-bottom: 18px; }
/* BC list after a multi-supplier creation: the BCs it made, and one button mailing each to its supplier. */
.flash.so-created { align-items: flex-start; }
.so-created-body { display: grid; gap: 8px; }
.so-created-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.flash .icon { width: 18px; height: 18px; }
.flash-error { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }
.flash-warn { background: var(--amber-50); color: var(--amber-700); border-color: var(--amber-600); }
/* Preparator board: the order in progress locks the others — its "Resume" button sits at the end (start in RTL). */
.prep-open-order { flex-wrap: wrap; }
.prep-open-order .btn { margin-inline-start: auto; }
/* Duplicate warning on the client / product create forms. */
.duplicate-warning { align-items: flex-start; }
.duplicate-list { margin: 6px 0; padding-inline-start: 18px; display: grid; gap: 3px; }
/* A count beside a page title ("Commandes 260"). */
.title-count { display: inline-grid; place-items: center; min-width: 28px; height: 24px; padding: 0 9px; margin-left: 6px; border-radius: 999px; background: var(--slate-100); color: var(--muted); font-size: 13px; font-weight: 600; letter-spacing: 0; vertical-align: 4px; font-variant-numeric: tabular-nums; }

/* Status tabs under a list's title (orders list, "H2"): links, the active one underlined. */
.list-tabs { display: flex; gap: 22px; margin: -8px 0 18px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.list-tab { display: inline-flex; align-items: center; gap: 7px; padding: 10px 0; margin-bottom: -1px; border-bottom: 2px solid transparent; color: var(--muted); font-size: 14px; font-weight: 500; text-decoration: none; white-space: nowrap; }
.list-tab:hover { color: var(--ink); }
.list-tab.is-active { color: var(--ink); font-weight: 600; border-bottom-color: var(--primary); }
.list-tab-count { min-width: 22px; padding: 0 7px; border-radius: 999px; background: var(--slate-100); color: var(--muted); font-size: 11.5px; font-weight: 600; text-align: center; font-variant-numeric: tabular-nums; }
.list-tab.is-active .list-tab-count { background: var(--primary-soft); color: var(--primary-strong); }

/* /incoming-orders (picked 2026-10-20): the counts in one quiet line under the
   title ("A3"), status tabs as round pills ("B4"), a compact one-line table
   with a coloured dot for the state ("C1"), shortages on their own tab ("G2"). */
.io-numline { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 0; margin: -6px 0 16px; font-size: 13.5px; color: var(--muted); }
.io-count { display: inline-flex; align-items: baseline; gap: 5px; }
.io-count + .io-count { margin-left: 16px; padding-left: 16px; border-left: 1px solid var(--line); }
.io-count strong { font-size: 18px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 640px) { .io-numline { gap: 4px 16px; } .io-count + .io-count { margin-left: 0; padding-left: 0; border-left: 0; } }
.io-count-question strong { color: var(--rl-blue-ink, var(--blue-700)); }
.io-count-blocked strong, .io-count-shortage.is-alert strong { color: var(--rl-red-ink, var(--red-600)); }

.io-pills { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.io-pill-tab { display: inline-flex; align-items: center; gap: 7px; height: 32px; padding: 0 12px; border: 1px solid var(--rl-input-line, var(--line)); border-radius: 8px; background: var(--surface, #fff); color: var(--text); font-size: 13px; font-weight: 500; text-decoration: none; white-space: nowrap; }
.io-pill-tab:hover { border-color: var(--slate-300); color: var(--ink); }
.io-pill-tab.is-active { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-strong); font-weight: 600; }
.io-pill-count { color: var(--faint); font-size: 12px; font-weight: 500; font-variant-numeric: tabular-nums; }
.io-pill-tab.is-active .io-pill-count { color: var(--primary-strong); }
.io-pill-tab.is-alert:not(.is-active) { border-color: var(--rl-red-line, var(--red-400)); color: var(--rl-red-ink, var(--red-600)); }
.io-pill-tab.is-alert .io-pill-count { color: inherit; font-weight: 600; }

.io-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--rl-slate-dot, var(--slate-300)); vertical-align: 1px; }
.io-dot-todo { background: var(--rl-amber-dot, var(--amber-600)); }
.io-dot-question { background: var(--rl-blue-dot, var(--blue-600)); }
.io-dot-blocked, .io-dot-shortage { background: var(--rl-red-dot, var(--red-500)); }
.io-dot-created { background: var(--rl-green-dot, var(--green-600, #18ac77)); }

.io-toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.io-toolbar .input { height: 38px; font-size: 13px; padding: 0 12px; }
/* icon() REPLACES the svg's class (class="icon-sm", never "icon icon-sm"), so
   the magnifier is placed by the app's own .input-icon-wrap. */
.io-search { flex: 1 1 260px; min-width: 200px; }
.io-search .input { width: 100%; }
.io-toolbar .io-search .input { padding-left: 38px; }
.io-select { width: auto; min-width: 150px; }

.io-sort { display: inline-flex; align-items: center; gap: 4px; background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit; }
.io-sort:hover { color: var(--text); }
.io-sort-ind { font-size: 10px; color: var(--muted); }
.io-table td { vertical-align: middle; }
.io-table .io-dot { margin-right: 6px; }
.io-products { max-width: 360px; }
.io-product { display: inline-block; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; color: var(--ink); font-weight: 500; }
.io-more { display: inline-block; margin-left: 4px; padding: 0 7px; border-radius: 999px; background: var(--slate-100); color: var(--muted); font-size: 11.5px; font-weight: 600; }
.io-missing { display: inline-flex; margin-left: 6px; color: var(--rl-red-ink, var(--red-600)); vertical-align: -2px; }
.io-dest { display: inline-flex; align-items: center; gap: 5px; }
.io-dest > svg { color: var(--faint); width: 14px; height: 14px; flex: none; }
.io-btn-question { background: var(--rl-blue-ink, var(--blue-700)); border-color: var(--rl-blue-ink, var(--blue-700)); color: #fff; }
.io-btn-question:hover { filter: brightness(.92); color: #fff; }
.io-short-count { display: inline-flex; align-items: center; gap: 7px; color: var(--rl-red-ink, var(--red-600)); font-weight: 500; }

.io-empty { text-align: center; padding: 32px; font-size: 13px; }
.io-empty-card { text-align: center; padding: 40px; }
.io-pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-top: 1px solid var(--line-soft); font-size: 13px; flex-wrap: wrap; }
.io-pager-controls { display: flex; align-items: center; gap: 8px; }
.io-pager .io-size { min-width: 0; width: auto; height: 32px; font-size: 13px; padding: 0 8px; }
/* Warnings about the link to proxy.woliz (the list and the product-mapping page). */
.io-notice-warn { display: flex; align-items: center; gap: 10px; padding: 10px 14px; margin-bottom: 16px; background: var(--amber-50); color: var(--amber-700); border: 1px solid var(--amber-600); border-radius: 8px; font-size: 13px; }
.io-notice-warn > svg { flex: none; }

/* A list's filters ("F4", orders list): a "Filters" button in the bar above the
   table opens a panel from the right; the active filters show as chips. */
.list-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
.list-bar .filter-search-wrap { flex: 0 1 320px; }
.list-bar-count { margin-left: auto; }
.filter-open { gap: 7px; }
.filter-open-count { display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px; background: var(--primary); color: #fff; font-size: 11.5px; font-weight: 700; }
.filter-chips { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.filter-chip { display: inline-flex; align-items: center; gap: 5px; height: 28px; padding: 0 8px 0 11px; border-radius: 999px; border: 1px solid var(--rl-primary-line, var(--line)); background: var(--primary-soft); color: var(--primary-strong); font-size: 12.5px; font-weight: 600; text-decoration: none; white-space: nowrap; }
.filter-chip .icon { width: 13px; height: 13px; opacity: .7; }
.filter-chip:hover .icon { opacity: 1; }
.filter-chips-reset { font-size: 12.5px; font-weight: 500; color: var(--muted); text-decoration: none; margin-left: 4px; }
.filter-chips-reset:hover { color: var(--primary); text-decoration: underline; }

.filter-drawer { position: fixed; inset: 0; z-index: 80; }
.filter-drawer-backdrop { position: absolute; inset: 0; background: rgba(15, 23, 42, .35); animation: fade-in .15s ease; }
.filter-drawer-panel {
    position: absolute; top: 0; right: 0; bottom: 0; width: min(380px, 100%);
    display: flex; flex-direction: column; background: var(--surface);
    box-shadow: -18px 0 40px -18px rgba(15, 23, 42, .35); animation: drawer-in .2s cubic-bezier(.16, 1, .3, 1);
}
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
.filter-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--line-soft); }
.filter-drawer-title { display: inline-flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 600; color: var(--ink); }
.filter-drawer-body { flex: 1; overflow-y: auto; padding: 4px 18px 18px; }
.filter-drawer-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--line-soft); background: var(--slate-50); }
html.drawer-open { overflow: hidden; }
[dir="rtl"] .filter-drawer-panel { right: auto; left: 0; }

.facet { padding: 14px 0 4px; border-top: 1px solid var(--line-soft); margin-top: 10px; }
.facets > .facet:first-of-type { border-top: 0; margin-top: 0; }
.facet-title { margin-bottom: 6px; font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.facet .filter-select, .facet .filter-drp-btn { width: 100%; }
.facet-opt { display: flex; align-items: center; gap: 9px; padding: 6px; margin: 0 -6px; border-radius: 6px; color: var(--text); font-size: 13.5px; text-decoration: none; }
.facet-opt:hover { background: var(--hover); }
/* The tick box: a small rounded square; ticked, a brand fill with a white check
   drawn well inside it (the svg is icon-sm, so it is sized here, not by .icon). */
.facet-box {
    flex: none; width: 18px; height: 18px; display: grid; place-items: center;
    border: 1.5px solid var(--slate-300); border-radius: 5px; background: var(--surface); color: transparent;
    transition: background-color .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.facet-box > svg { width: 12px; height: 12px; stroke-width: 3.2; }
.facet-opt:hover .facet-box { border-color: var(--primary); }
.facet-opt.is-on .facet-box { background: var(--primary); border-color: var(--primary); color: #fff; box-shadow: 0 1px 2px rgba(15, 23, 42, .12); }
.facet-opt.is-on:hover .facet-box { background: var(--primary-strong); border-color: var(--primary-strong); }
.facet-opt:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.facet-opt.is-on .facet-label { font-weight: 600; color: var(--ink); }
.facet-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.facet-label .pill { font-size: 13.5px; }
.facet-n { flex: none; font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
.facet-opt.is-empty { opacity: .5; }

/* Who prepares / who delivers, side by side on one line (orders list). */
.team-cell { white-space: nowrap; }
.team-who { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; }
.team-who + .team-who { margin-left: 12px; }
.team-who .icon { width: 13px; height: 13px; color: var(--faint); flex: none; }

/* A late order: a red warning beside its date (the words in its tooltip). */
.overdue-flag { display: inline-flex; vertical-align: -2px; margin-left: 6px; color: var(--red-600); cursor: help; }
/* What a BC's reception found, beside its reference: partial (amber), above the order (blue). */
.row-flag { display: inline-flex; vertical-align: -2px; margin-left: 6px; cursor: help; }
.row-flag.is-amber { color: var(--amber-600); }
.row-flag.is-blue { color: var(--blue-600); }

/* A product on promotion: a small label right after its name (products list). */
.product-cell-promo { margin-left: 8px; vertical-align: 1px; }

/* A compact list (orders): one line per row, tighter than the readable default. */
@media (min-width: 901px) {
    body .table.table-compact > thead > tr > th { height: 36px; white-space: nowrap; }
    body .table.table-compact > tbody > tr > td { padding-top: 6px; padding-bottom: 6px; }
    body .table.table-compact > tbody > tr > td:not(:first-child):not(:last-child),
    body .table.table-compact > thead > tr > th:not(:first-child):not(:last-child) { padding-left: 8px; padding-right: 8px; }
    body .table.table-compact .wh-icon-btn { width: 28px; height: 28px; }
    body .table.table-compact > tbody > tr > td.cell-wrap { white-space: normal; min-width: 140px; }
    /* A long product name ends in "…" instead of widening the list past the screen. */
    body .table.table-compact.product-table .product-cell-text { max-width: 280px; }
}

/* Trail above the page title (header.php, layout_nav_trail()): Ventes › Commandes › CMD-… */
.crumbs { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: -8px 0 10px; font-size: 12.5px; color: var(--muted); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--primary); text-decoration: underline; }
.crumbs [aria-current] { color: var(--ink); font-weight: 600; }
.crumbs .icon { width: 12px; height: 12px; color: var(--faint); }
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px; }
.page-head h2 { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -.02em; color: var(--ink); }
.page-head .sub { font-size: 13px; color: var(--muted); margin-top: 2px; }

/* Responsive shell adjustments */
@media (max-width: 880px) {
  .sidebar { width: 68px; flex-basis: 68px; }
  .brand-text, .nav-label, .nav-group-label { display: none; }
  .nav-item { justify-content: center; padding: 10px; }
  .brand { justify-content: center; padding: 16px 0; }
  .product-editor { grid-template-columns: 1fr; }
  .product-media-card { min-height: 280px; }
  .product-identity-row { grid-template-columns: 1fr 1fr; }
  .product-identity-row .field:first-child { grid-column: 1 / -1; }
  .brand-inline-card { grid-template-columns: 1fr; }
  .import-upload-form { grid-template-columns: 1fr; }
  .import-footer { align-items: flex-start; flex-direction: column; }
  .taxonomy-inline-card { grid-template-columns: 1fr; }
  .taxonomy-subcategory-form,
  .taxonomy-subsubcategory-form,
  .taxonomy-subbrand-form { grid-template-columns: 1fr auto; }
  .taxonomy-subcategory-field,
  .taxonomy-subbrand-field { grid-column: 1 / -1; }
}

/* Below this, the icon-only rail becomes an off-canvas drawer — a permanent
   68px rail with no visible labels is workable on a tablet, not on a phone. */
@media (max-width: 640px) {
  .sidebar-toggle { display: grid; }
  .sidebar-collapse-toggle { display: none; }
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 50; width: 240px; flex-basis: 240px;
    transform: translateX(-100%); transition: transform .22s cubic-bezier(.16, 1, .3, 1);
    box-shadow: 0 20px 40px -16px rgba(15, 23, 42, .35);
  }
  .sidebar.is-open { transform: translateX(0); }
  .brand-text, .nav-label, .nav-group-label { display: block; }
  .nav-item { justify-content: flex-start; padding: 8px 10px 8px 13px; }
  .brand { justify-content: flex-start; padding: 16px 18px; }
  .sidebar-backdrop {
    display: block; position: fixed; inset: 0; z-index: 49; background: rgba(15, 23, 42, .4);
    opacity: 0; pointer-events: none; transition: opacity .2s ease;
  }
  .sidebar-backdrop.is-open { opacity: 1; pointer-events: auto; }
}
@media (max-width: 520px) {
  .main { padding: 18px; }
  .topbar { padding: 0 16px; gap: 8px; }
  .topbar-back { padding: 0 8px; }
  .lang-switch-btn { padding: 0 7px; gap: 5px; }
  .topbar-back-label { display: none; }
  /* Wrap: a title plus one or two action buttons don't fit 390px side by
     side — the buttons were squeezed until their label spilled out of them
     ("Rangement par emplacement" on /receiving) or pushed past the screen
     edge ("Naviguer"/"Suivre l'itinéraire" on /livreur). */
  .page-head { align-items: flex-start; gap: 12px; flex-wrap: wrap; }
  .page-head > .row { flex-wrap: wrap; }
  .page-head .btn { white-space: nowrap; }
  .product-fields-grid { grid-template-columns: 1fr; }
  .product-identity-row { grid-template-columns: 1fr; }
  .product-section-head { align-items: flex-start; }
  .product-status-row { align-items: flex-start; flex-direction: column; }
  .product-status-text { flex-wrap: wrap; white-space: normal; max-width: 100%; }
  .product-media-preview { min-height: 220px; }
  .brand-inline-form { grid-template-columns: 1fr; }
  .taxonomy-inline-form,
  .taxonomy-subcategory-form,
  .taxonomy-subsubcategory-form,
  .taxonomy-subbrand-form { grid-template-columns: 1fr; }
  .brand-inline-status { min-height: auto; }
  .taxonomy-inline-status { min-height: auto; }
  .taxonomy-subcategory-field { grid-column: auto; }
  .taxonomy-subcategory-actions { justify-content: flex-start; }
  .supplier-form-grid { grid-template-columns: 1fr; }
  .supplier-form-footer { align-items: flex-start; flex-direction: column; }
  .supplier-hero-main { grid-template-columns: 1fr; }
  .supplier-contact-grid { grid-template-columns: 1fr; }
  .supplier-hero-stats { grid-template-columns: 1fr; }
  .table-pagination { padding: 12px; }
  .table-page-link { font-size: 0; }
  .table-page-link .icon { width: 17px; height: 17px; }
}

/* ---- Custom date picker ------------------------------------------ */
[x-cloak] { display: none !important; }

/* --- Side dock (slide-in drawer) + its history timeline --- */
.btn-icon { padding: 0; width: 38px; height: 38px; justify-content: center; }
.dock { display: inline-flex; }
.dock-backdrop { position: fixed; inset: 0; background: rgba(15, 23, 42, .45); z-index: 90; }
.dock-panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(440px, 92vw); background: var(--surface); z-index: 91; box-shadow: -14px 0 44px rgba(15, 23, 42, .20); display: flex; flex-direction: column; }
.dock-anim { transition: transform .25s ease, opacity .25s ease; }
.dock-off { transform: translateX(100%); opacity: 0; }
.dock-on { transform: translateX(0); opacity: 1; }
.dock-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.dock-head-sub { font-size: 13px; margin-top: 2px; }
.dock-close { background: transparent; border: 0; cursor: pointer; color: var(--muted); padding: 4px; border-radius: 6px; display: inline-flex; }
.dock-close:hover { background: var(--hover); color: var(--ink); }
.dock-body { overflow-y: auto; padding: 20px; }
.dock-empty { text-align: center; padding: 24px; }
.dock-timeline-item { display: flex; gap: 12px; padding-bottom: 18px; position: relative; }
.dock-timeline-item:not(:last-child)::before { content: ''; position: absolute; left: 11px; top: 24px; bottom: 0; width: 2px; background: var(--line); }
.dock-timeline-marker { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; background: var(--slate-100); color: var(--slate-400); z-index: 1; }
.dock-timeline-marker.is-current { background: var(--primary-soft); color: var(--primary-strong); }
.dock-timeline-content { flex: 1 1 auto; min-width: 0; }
.dock-timeline-note { margin: 6px 0 0; font-size: 13px; color: var(--slate-600); }
.dock-event { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 600; color: var(--slate-700); }
.dock-event svg { width: 14px; height: 14px; color: var(--slate-400); }
.dock-by { display: inline-flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 12px; color: var(--muted); }
.dock-by svg { width: 13px; height: 13px; }

/* Order line fulfilment qty (prepared/delivered) + livreur pointing */
.qty-short { color: var(--amber-600); font-weight: 700; }
.stock-summary { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 18px 20px 0; margin-bottom: 14px; }
.stock-summary .pill { display: inline-flex; align-items: center; gap: 5px; }
/* ---- Calendars: one style for every date control --------------------------
   datePicker (date_picker_field(), one day) and dateRangePicker (a period, in
   filter bars) share this popup, its header, weekday row and day cells, and
   both triggers carry the calendar icon at the start. Don't add a third look —
   and no <input type="date">: the browser's own calendar differs on every
   browser / phone (case 97). */
.datepicker-wrap { position: relative; }
.datepicker-control { position: relative; display: flex; align-items: center; }
/* !important on the start padding only: it is the calendar icon's room, not a
   look — a form's compact `.x .input { padding: … }` (same or higher weight,
   often later) would otherwise put the date text under the icon. */
.datepicker-control .datepicker-input { flex: 1; padding-inline-start: 34px !important; }
.datepicker-cal-btn {
    position: absolute; inset-inline-start: 10px; background: none; border: none;
    cursor: pointer; color: var(--muted); display: flex; align-items: center; padding: 0;
}
.datepicker-cal-btn:hover { color: var(--text); }
.datepicker-popup, .drp-popup {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 300;
    background: var(--surface, #fff); border: 1px solid var(--line);
    border-radius: 6px; box-shadow: 0 8px 32px rgba(15,23,42,.16);
    width: 280px; max-width: calc(100vw - 24px); padding: 14px;
    max-height: calc(100vh - 24px); overflow-y: auto;
}
/* Arabic (right-to-left): the popup opens under the field's start, i.e. its right edge. */
[dir="rtl"] .datepicker-popup { left: auto; right: 0; }
/* Flips the popup above its field instead of below — set by JS when the normal
   drop-down position would land past the viewport's bottom edge (short table,
   small window). See purchase_documents/pages/_filters.php for the range one. */
.datepicker-popup.dp-pos-up, .drp-popup.drp-popup-up { top: auto; bottom: calc(100% + 6px); }
/* Right-aligns the popup to its button instead of left-aligning — added when
   JS finds the normal left:0 position would run past the window's right
   edge (a filter bar anchored at the right end of a row, e.g. card-head's
   `space-between`). Confirmed live: without this the calendar's Sat/Sun
   columns were clipped by html's overflow-x:hidden, not just off-screen. */
.drp-popup.drp-popup-left { left: auto; right: 0; }
.datepicker-nav, .drp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.datepicker-month-label, .drp-month { font-weight: 600; font-size: 14px; }
.datepicker-nav-btn, .drp-nav {
    background: none; border: none; cursor: pointer; color: var(--muted);
    display: flex; align-items: center; border-radius: 6px; padding: 4px;
}
.datepicker-nav-btn:hover, .drp-nav:hover { background: var(--slate-100); color: var(--text); }
/* Right-to-left (Arabic): the month arrows point the reading way. */
[dir="rtl"] .datepicker-nav-btn .icon, [dir="rtl"] .drp-nav .icon { transform: scaleX(-1); }
.datepicker-weekdays, .drp-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: 4px; }
.datepicker-weekdays span, .drp-weekdays span { text-align: center; font-size: 11px; font-weight: 600; color: var(--muted); padding: 4px 0; }
.datepicker-grid, .drp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.datepicker-day, .drp-day {
    aspect-ratio: 1; width: 100%; display: flex; align-items: center; justify-content: center;
    border: none; background: none; border-radius: 6px; font-size: 13px;
    cursor: pointer; color: var(--text); padding: 0;
}
.datepicker-day:hover:not(:disabled):not(.dp-sel),
.drp-day:hover:not(:disabled):not(.drp-start):not(.drp-end) { background: var(--slate-100); }
.datepicker-day.dp-today, .drp-day.drp-today { font-weight: 700; color: var(--primary); }
.datepicker-day.dp-sel, .drp-day.drp-start, .drp-day.drp-end { background: var(--ink); color: #fff; font-weight: 600; border-radius: 6px; }
.drp-day.drp-range { background: var(--blue-50); border-radius: 0; }
.datepicker-day.dp-empty, .drp-day.drp-empty { visibility: hidden; pointer-events: none; }
.datepicker-day:disabled:not(.dp-empty), .drp-day:disabled:not(.drp-empty) { color: var(--muted); opacity: .4; cursor: not-allowed; }
/* A marked day (e.g. one that has orders): a small dot under the number. */
.datepicker-day.dp-marked { position: relative; font-weight: 700; }
.datepicker-day.dp-marked::after { content: ""; position: absolute; left: 50%; bottom: 3px; width: 5px; height: 5px; margin-left: -2.5px; border-radius: 50%; background: var(--blue-600); }
.datepicker-day.dp-marked.dp-sel::after { background: var(--white); }

/* ---- PDF split-button dropdown ---------------------------------------- */
.pdf-dropdown {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 100;
    background: var(--surface, #fff); border: 1px solid var(--line);
    border-radius: 6px; box-shadow: 0 6px 20px rgba(15,23,42,.12);
    min-width: 140px; overflow: hidden;
}
.pdf-dropdown-item {
    display: block; padding: 9px 14px; font-size: 13px; color: var(--text);
    text-decoration: none; white-space: nowrap;
}
.pdf-dropdown-item:hover { background: var(--slate-100); }

/* ---- Orders table filter row ------------------------------------------ */
.input-sm { padding: 5px 10px; font-size: 13px; height: 32px; }

tr.table-filter-row th {
    background: var(--slate-50, #f8fafc);
    padding: 8px 10px;
    border-bottom: 1px solid var(--line);
    /* top-align, not the th default of middle: the Reference cell grows
       taller than its siblings when the "Réinitialiser" link appears below
       its search box (only shown once a filter is active), and middle
       alignment then centers every other cell's control in that extra
       height — visibly offsetting them a few pixels below the search box
       instead of lining up with it. */
    vertical-align: top;
}
/* Filter controls fill their column but never widen it: with inline-size
   containment their own intrinsic width counts as zero, so each column is
   sized by its header + row content and the table stays as narrow as its
   data (popups are absolutely positioned, so they still open full size). */
tr.table-filter-row th > * { contain: inline-size; min-width: 0; }
/* ...but never narrower than their own default label: without a floor a
   column sized by short data cut controls down to "Tous p" / "Référen". */
tr.table-filter-row th > .filter-search-wrap { min-width: 132px; }
tr.table-filter-row th > input.filter-select { min-width: 108px; }
tr.table-filter-row th > select.filter-select,
tr.table-filter-row th > div:has(> .filter-drp-btn) { min-width: 88px; }
tr.table-filter-row .filter-select { text-overflow: ellipsis; }
.filter-select {
    width: 100%; padding: 6px 10px; font-size: 13px; height: 34px;
    border: 1px solid var(--line); border-radius: 6px;
    background: var(--surface); color: var(--text);
    font-family: inherit; cursor: pointer;
    transition: border-color .15s, box-shadow .15s;
}
.filter-select:hover { border-color: var(--slate-300); }
.filter-select:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring-primary); }
/* One dropdown arrow for every filter-row control, not three: a native
   <select> renders the OS's own arrow (differs by browser, doesn't match
   the app's icon set), while the compact Client/Fournisseur combobox
   pickers (#fcust-search, #fsup-search — a plain <input>, not a <select>)
   show no affordance at all. Every .filter-select except the free-text
   .filter-search variant (Reference/name search — genuinely not a
   dropdown, keeps its own left-side magnifier icon only) gets the same
   chevron-down glyph as .filter-drp-btn (Statut/date-range), hardcoded to
   --text's #1e293b since a background-image data URI can't read a CSS
   var() — update it by hand if --text is ever repainted. */
.filter-select:not(.filter-search) {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    padding-right: 30px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231e293b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 9px center;
    background-size: 14px 14px;
}
/* The two compact combobox pickers above are type="search" inputs that hold
   the picked label as text — WebKit would otherwise draw its own native
   clear-"x" over the same corner as the chevron just added. */
.filter-select:not(.filter-search)[type="search"]::-webkit-search-cancel-button { display: none; }
/* Our own clear-"x" for those pickers, sat just left of the chevron. */
.filter-select-clear {
    position: absolute; top: 50%; right: 28px; transform: translateY(-50%);
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border: 0; border-radius: 6px;
    background: transparent; color: var(--muted, #64748b); cursor: pointer;
}
.filter-select-clear[hidden] { display: none; }
.filter-select-clear:hover,
.filter-select-clear:focus-visible { color: var(--red-500, var(--red-400)); background: rgba(248, 113, 113, .12); }
.relative:has(> .filter-select-clear:not([hidden])) > .filter-select { padding-right: 52px; }

.filter-search-wrap { position: relative; }
.filter-search-wrap .filter-select { padding-left: 30px; }

/* Compact catalog-combobox variant for a table-filter-row header (the
   products list' Brand / Sub-brand filter) — same markup/JS as the full-size
   one, just sized to .filter-select's 34px so the filter row doesn't grow
   taller than its sibling cells. */
tr.table-filter-row th > .filter-combobox { min-width: 130px; }
.filter-combobox .catalog-combobox-control {
    height: 34px;
    padding-left: 9px;
    border-color: var(--line);
    border-radius: 6px;
}
.filter-combobox .catalog-combobox-control input { padding: 0; font-size: 13px; text-overflow: ellipsis; }
.filter-combobox .catalog-combobox-control > .icon { width: 14px; height: 14px; }
.filter-combobox .catalog-combobox-control button { width: 30px; }
.filter-combobox .catalog-options { font-size: 13px; min-width: 240px; right: auto; }
.filter-combobox .catalog-option > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-search-icon {
    position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
    display: grid; place-items: center; color: var(--faint); pointer-events: none;
}
.filter-search-icon .icon { width: 14px; height: 14px; }
.filter-drp-btn .filter-drp-label { flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis; }
.filter-drp-clear { display: flex; opacity: .6; }
.drp-hint { font-size: 11px; color: var(--muted); padding: 6px 12px 4px; }
.filter-drp-btn {
    display: flex; align-items: center; gap: 5px; width: 100%;
    padding: 6px 10px; font-size: 13px; height: 34px;
    border: 1px solid var(--line); border-radius: 8px;
    background: var(--surface); color: var(--text);
    font-family: inherit; cursor: pointer; white-space: nowrap; overflow: hidden;
    transition: border-color .15s, box-shadow .15s;
}
.filter-drp-btn:hover { border-color: var(--slate-300); }
.filter-drp-btn:focus-visible { outline: none; border-color: var(--primary); box-shadow: var(--ring-primary); }
.btn-filter-reset {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 11px; color: var(--muted); text-decoration: none;
    padding: 2px 6px; border-radius: 8px; white-space: nowrap;
}
.btn-filter-reset:hover { color: var(--danger, #dc2626); background: var(--red-50, #fef2f2); }

/* ---- Multiselect dropdown (filter row) --------------------------------- */
.multisel-popup {
    position: absolute; top: calc(100% + 4px); left: 0; z-index: 300;
    background: var(--card-bg, #fff); border: 1px solid var(--line);
    border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,.12);
    padding: 6px 4px; display: flex; flex-direction: column; gap: 1px;
    max-height: 240px; overflow-y: auto;
}
/* Right-aligns the popup to its button instead of left-aligning — same
   escape hatch as .drp-popup-left (see purchase_documents/pages/_filters.php),
   toggled by positionFilterPopup() for a filter near the right end of a wide
   table-filter-row (e.g. Paiement on /orders): left:0 would otherwise open
   the popup mostly or entirely past the viewport's right edge. */
.multisel-popup.multisel-popup-left { left: auto; right: 0; }
.multisel-option {
    display: flex; align-items: center; gap: 8px;
    padding: 5px 10px; border-radius: 6px; cursor: pointer;
    font-size: 12px; white-space: nowrap;
}
.multisel-option:hover { background: var(--slate-50, #f8fafc); }
.multisel-option input[type="checkbox"] { accent-color: var(--primary); cursor: pointer; }


/* ---- Order info card (show page) -------------------------------------- */
.order-info-card { display: flex; gap: 24px; align-items: flex-start; }
.order-info-main { flex: 1; min-width: 0; }
.order-status-aside { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 16px; background: var(--slate-50, #f8fafc); border-radius: 6px; text-align: center; }
.order-confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(15, 23, 42, .46);
}
.order-confirm-modal {
  width: min(100%, 420px);
  padding: 24px 28px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  text-align: left;
  box-shadow: 0 20px 50px rgba(15, 23, 42, .24);
}
.order-confirm-title { margin-bottom: 8px; color: var(--ink); font-size: 16px; font-weight: 700; }
.order-confirm-copy { margin-bottom: 20px; color: var(--muted); font-size: 13px; }
.order-confirm-copy strong { color: var(--ink); }
/* Oui / Non / Envoyer l'email — wraps rather than squeezing on a narrow aside. */
.order-confirm-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.order-confirm-actions .btn { flex: 1 1 auto; justify-content: center; }
.order-tracking-card { margin: 16px 0; overflow: hidden; }
.order-tracking-scroll { overflow-x: auto; contain: layout; padding: 26px 22px 22px; }
.order-status-stepper { min-width: 620px; --step-line-top: 19px; }
.order-status-stepper .step .bullet { width: 40px; height: 40px; margin-bottom: 10px; }
.order-status-stepper .step .bullet .icon { width: 19px; height: 19px; }
.order-status-stepper .step.current::after { transform: none; }
.order-status-stepper .step.current .bullet {
  border-color: var(--blue-600);
  background: var(--blue-50);
  color: var(--blue-600);
  box-shadow: 0 0 0 4px var(--blue-50);
}
.order-status-stepper .s-title { font-size: 14px; }
/* Steps not reached yet stay quiet; a reached step's label "lights up" (step-label-in). */
.order-status-stepper .step:not(.done, .current) .s-title { color: var(--muted); font-weight: 500; }
.order-status-stepper .s-sub { margin-top: 3px; }
.order-tracking-cancelled { display: flex; align-items: center; gap: 12px; padding: 22px; color: var(--red-600); background: var(--red-50); }
.order-tracking-cancelled > .icon { flex: 0 0 auto; width: 26px; height: 26px; }

/* Compact, header-less tracking stepper (supplier order detail, top of page) */
.sup-track-card { margin: 0 0 16px; padding: 12px 16px 6px; overflow: hidden; }
.sup-track-top { font-size: 12px; margin-bottom: 5px; }
.sup-track-scroll { overflow-x: auto; contain: layout; padding: 14px 4px 6px; }
/* min-width only for the full-width tracking cards above (4-5 steps need
   the room, and .sup-track-scroll/.sup-track-card's own overflow handles
   a narrow viewport) — NOT on the base class, since .stepper--compact is
   also reused inside a narrow sidebar card (invoices' Details aside,
   modules/invoices/pages/show.php) that has no such wrapper: a bare
   min-width there pushed the last step's label past the card's edge
   with nothing to clip or scroll it. */
.sup-track-card .stepper--compact { min-width: 480px; }
.stepper--compact .step .bullet { width: 28px; height: 28px; margin-bottom: 5px; }
.stepper--compact .step .bullet .icon { width: 14px; height: 14px; }
.stepper--compact .step.current::after { transform: none; }
.stepper--compact .step.current .bullet { border-color: var(--blue-600); background: var(--blue-50); color: var(--blue-600); box-shadow: 0 0 0 4px var(--blue-50); }
.stepper--compact .s-title { font-size: 12px; }
.stepper--compact .s-sub { font-size: 11px; margin-top: 1px; }
.order-tracking-cancelled strong,
.order-tracking-cancelled span { display: block; }
.order-tracking-cancelled span { margin-top: 2px; color: var(--slate-600); font-size: 13px; }

/* Order page: the steps reached since this user's last visit draw themselves
   in, one after another (.is-new, turn --k — modules/orders/pages/show.php):
   the line fills up to the step, its bullet pops from grey into its colour,
   a finished step's tick draws, its label lights up. Then the step the order
   is on keeps a slow ping (not once it is delivered — nothing left to wait for). */
@media (prefers-reduced-motion: no-preference) {
    .order-status-stepper { --step-beat: 260ms; }
    .order-status-stepper .step.is-new { --step-at: calc(var(--k, 0) * var(--step-beat)); --step-pop: calc(var(--step-at) + var(--step-beat) * .7); }
    .order-status-stepper .step.is-new::after { animation: step-line-fill var(--step-beat) cubic-bezier(.65, 0, .35, 1) var(--step-at) both; }
    .order-status-stepper .step.is-new .bullet { animation: step-bullet-pop .5s cubic-bezier(.34, 1.4, .64, 1) var(--step-pop) both; }
    .order-status-stepper .step.is-new.current .bullet .icon { animation: step-icon-in .35s ease-out calc(var(--step-pop) + .1s) both; }
    .order-status-stepper .step.is-new.done .bullet .icon path { stroke-dasharray: 24; animation: step-tick-draw .35s ease-out calc(var(--step-pop) + .15s) both; }
    .order-status-stepper .step.is-new :is(.s-title, .s-sub) { animation: step-label-in .4s ease-out var(--step-pop) both; }
    .order-status-stepper .step.current:not(:last-child) .bullet::after {
        content: ""; position: absolute; inset: -2px; border-radius: 50%; border: 2px solid currentColor;
        opacity: 0; pointer-events: none; animation: step-ping 2.2s cubic-bezier(0, 0, .2, 1) infinite;
    }
    .order-status-stepper .step.current.is-new:not(:last-child) .bullet::after { animation-delay: calc(var(--step-pop) + .6s); }
}
@keyframes step-line-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* Starts as an unreached step (grey ring) and settles into the step's own colours. */
@keyframes step-bullet-pop {
    0%   { background: var(--surface); border-color: var(--line); color: var(--slate-300); box-shadow: 0 0 0 0 transparent; transform: scale(1); }
    40%  { transform: scale(.82); }
    70%  { transform: scale(1.1); }
    100% { transform: scale(1); }
}
@keyframes step-icon-in { from { opacity: 0; transform: scale(.5) rotate(-20deg); } to { opacity: 1; transform: none; } }
@keyframes step-tick-draw { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
@keyframes step-label-in { from { color: var(--muted); transform: translateY(3px); } to { transform: none; } }
@keyframes step-ping { 0% { opacity: .55; transform: scale(1); } 75%, 100% { opacity: 0; transform: scale(1.65); } }

/* User administration */
.user-list-card { overflow: hidden; }
.user-name-cell { display: flex; align-items: center; gap: 10px; color: var(--ink); }
.user-name-cell .avatar { width: 32px; height: 32px; }
.user-status-button { border: 0; cursor: pointer; }
.user-empty { padding: 36px 20px !important; text-align: center; color: var(--muted) !important; }
.user-form-card + .user-form-card { margin-top: 16px; }
/* .product-fields-grid zeroes its row-gap and each .field's margin-bottom
   (fine for the product form, which only ever puts one row of paired
   fields in that grid) — but this form stacks several full-row fields
   (Role, Site, Customer, Supplier) in it, so without an explicit row-gap
   they'd sit flush against each other with no vertical breathing room. */
.user-form-card .product-fields-grid { row-gap: 16px; }

/* Authentication */
/* Auth — split: brand panel + dark glass card over a petrol night where
   parcels ride two conveyor belts through a scanner gate (Three.js,
   .auth-scene — modules/scene3d/; picked from the login gallery 2026-10-28:
   scene 2 "Tapis et scanner" + card C1 "Verre sombre"). The colours are
   deliberately the same in every skin and theme: the scene's fog (#022633)
   is painted to match the page, and without WebGL the gradient alone stays.
   The colours are named once on .auth-body (literal on purpose: no skin or
   dark mode re-points them). Rules for what the skins also style (.label,
   .input, .btn-primary, .brand-mark) start with body.auth-body so they win. */
.auth-body {
  --auth-ink: #eaf6f9;
  --auth-strong: #fff;
  --auth-muted: #9cc3cf;
  --auth-label: #cfe6ee;
  --auth-icon: #7fa9b6;
  --auth-petrol: #006a88;
  --auth-amber: #f0a640;
  --auth-amber-hi: #f2b24f;
  --auth-amber-lo: #e08f22;
  --auth-on-amber: #1f1405;
  --auth-amber-ring: rgba(240, 166, 64, .2);
  --auth-amber-glow: rgba(240, 166, 64, .55);
  --auth-glass: rgba(2, 30, 40, .5);
  --auth-glass-line: rgba(159, 214, 232, .22);
  --auth-field: rgba(255, 255, 255, .07);
  --auth-field-focus: rgba(255, 255, 255, .1);
  --auth-field-line: rgba(159, 214, 232, .14);
  --auth-autofill: #0b3442;
  min-height: 100vh;
  background: linear-gradient(160deg, #00475e 0%, #00222f 75%);
  overflow: hidden;
}

/* Decorative background layer (the 3D belts), never interactive. The shade
   on its left keeps the white brand text readable over the moving parcels. */
.auth-bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.auth-scene { position: absolute; inset: 0; }
.auth-bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(1, 26, 36, .62) 0%, rgba(1, 26, 36, .28) 40%, transparent 62%);
}

.auth-shell {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
  width: min(1180px, 100%);
  margin: 0 auto;
  padding: 40px 48px;
}

/* Left brand panel — big wordmark over the belts. */
.auth-aside { flex: 1 1 auto; max-width: 620px; padding: 8px 8px 8px 0; user-select: none; }
.auth-aside-logo { display: flex; align-items: center; gap: 10px; margin-bottom: 56px;
  color: #9fd6e8; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; font-size: 12px;
  animation: rise .5s ease .05s both; }
.auth-aside-copy { display: flex; flex-direction: column; animation: rise .55s ease .12s both; }
.auth-aside-eyebrow { margin: 0; color: rgba(255, 255, 255, .55); letter-spacing: .55em;
  text-transform: uppercase; font-size: 13px; font-weight: 600; padding-left: 6px; }
.auth-aside-eyebrow-end { align-self: flex-end; padding-right: 8px; }
.auth-wordmark {
  margin: 2px 0;
  font-size: clamp(44px, 6.4vw, 82px);
  line-height: .94;
  font-weight: 800;
  letter-spacing: -.02em;
  background: linear-gradient(120deg, #ffffff 0%, #cfeaf3 55%, #9fd6e8 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.auth-aside-tag { margin: 40px 0 0; max-width: 360px; color: rgba(255, 255, 255, .72); font-size: 15px; line-height: 1.5;
  animation: rise .55s ease .2s both; }

/* Dark glass card — the belts show through it; amber button. */
.auth-card {
  position: relative;
  flex: 0 0 auto;
  width: min(100%, 420px);
  padding: 34px 34px 30px;
  color: var(--auth-ink);
  border: 1px solid var(--auth-glass-line);
  border-radius: 8px;
  background: var(--auth-glass);
  backdrop-filter: blur(18px) saturate(1.2);
  -webkit-backdrop-filter: blur(18px) saturate(1.2);
  box-shadow: 0 30px 70px -20px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .08);
  animation: rise .5s ease .1s both;
}
.auth-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 26px; }
.auth-brand .brand-mark { flex: 0 0 auto; }
body.auth-body .auth-card .brand-mark { background: var(--auth-petrol); color: var(--auth-strong); }
.auth-brand strong,
.auth-brand div > span { display: block; }
.auth-brand strong { color: var(--auth-strong); font-size: 16px; }
.auth-brand div > span { color: var(--auth-muted); font-size: 12px; }
.auth-heading { margin-bottom: 24px; }
.auth-heading h1 { margin: 0 0 6px; color: var(--auth-strong); font-size: 26px; letter-spacing: -.01em; }
.auth-heading p { margin: 0; color: var(--auth-muted); font-size: 14px; }
.auth-form { display: grid; gap: 16px; }
.input-icon-wrap { position: relative; display: flex; align-items: center; }
.input-icon-wrap > .icon-sm { position: absolute; left: 15px; color: var(--faint); pointer-events: none; transition: color .15s ease; z-index: 1; }
.input-icon-wrap:focus-within > .icon-sm { color: var(--primary); }
.auth-card .input-icon-wrap > .icon-sm { color: var(--auth-icon); }
.auth-card .input-icon-wrap:focus-within > .icon-sm { color: var(--auth-amber); }
body.auth-body .auth-card .label { color: var(--auth-label); }
body.auth-body .auth-card .auth-form .input {
  height: 46px;
  border-radius: 7px;
  color: var(--auth-strong);
  background: var(--auth-field);
  border-color: var(--auth-field-line);
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.auth-form .input.input-with-icon { padding-left: 42px; width: 100%; }
body.auth-body .auth-card .auth-form .input:focus {
  background: var(--auth-field-focus);
  border-color: var(--auth-amber);
  box-shadow: 0 0 0 3px var(--auth-amber-ring);
}
body.auth-body .auth-card .auth-form .input.invalid { border-color: #f87171; }
/* The browser's saved-password fill would paint the field pale yellow. */
body.auth-body .auth-card .auth-form .input:-webkit-autofill {
  -webkit-text-fill-color: var(--auth-strong);
  caret-color: var(--auth-strong);
  box-shadow: 0 0 0 1000px var(--auth-autofill) inset;
  transition: background-color 9999s ease 0s;
}
body.auth-body .auth-card .auth-form .input:-webkit-autofill:focus { box-shadow: 0 0 0 1000px var(--auth-autofill) inset, 0 0 0 3px var(--auth-amber-ring); }
body.auth-body .auth-card .field-error { color: #fca5a5; }
body.auth-body .auth-card .auth-submit {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 48px;
  justify-content: center;
  margin-top: 8px;
  border: 0;
  border-radius: 7px;
  font-size: 15px;
  font-weight: 600;
  color: var(--auth-on-amber);
  background: linear-gradient(120deg, var(--auth-amber-hi), var(--auth-amber-lo));
  box-shadow: 0 12px 24px -10px var(--auth-amber-glow);
  transition: transform .12s ease, box-shadow .15s ease, filter .15s ease;
}
.auth-submit::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-120%);
  background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, .35) 50%, transparent 60%);
  transition: transform .6s ease;
}
/* Colour and background again: the skins' .btn-primary:hover would repaint them. */
body.auth-body .auth-card .auth-submit:hover {
  color: var(--auth-on-amber);
  background: linear-gradient(120deg, var(--auth-amber-hi), var(--auth-amber-lo));
  filter: brightness(1.05);
  box-shadow: 0 16px 30px -10px var(--auth-amber-glow);
}
.auth-submit:hover::after { transform: translateX(120%); }
.auth-submit:active { transform: translateY(1px); }
body.auth-body .auth-card .auth-submit:focus-visible { outline: 2px solid var(--auth-strong); outline-offset: 2px; box-shadow: 0 12px 24px -10px var(--auth-amber-glow); }
.auth-alert,
.auth-success { margin-bottom: 18px; padding: 11px 13px; border-radius: 6px; font-size: 13px; }
.auth-alert { border: 1px solid rgba(248, 113, 113, .45); background: rgba(239, 68, 68, .14); color: #fecaca; }
.auth-success { border: 1px solid rgba(52, 211, 153, .4); background: rgba(16, 185, 129, .14); color: #a7f3d0; }
.auth-links { margin-top: 16px; text-align: center; font-size: 13px; }
.auth-links a { color: var(--auth-muted); text-decoration: none; }
.auth-links a:hover { color: var(--auth-amber); text-decoration: underline; }

/* Collapse to a single centred card on narrow screens. */
@media (max-width: 900px) {
  .auth-aside { display: none; }
  .auth-shell { justify-content: center; padding: 24px; }
  .auth-card { width: min(100%, 440px); }
  .auth-bg::after { background: linear-gradient(180deg, transparent 0%, rgba(1, 26, 36, .35) 100%); }
}

/* ============================================================================
 | Two-phase timers — preparation + delivery (supervisor board & order detail)
 | Phases never run at once: prep ticks until the order goes out for delivery,
 | then delivery ticks until completion. A running phase shows a live, colour-
 | coded clock with a pulsing dot; a finished phase shows start → end + a total.
 ========================================================================== */

.tl-timer { display: inline-flex; align-items: center; gap: 7px; font-variant-numeric: tabular-nums; line-height: 1.2; }
.tl-timer .tl-clock { font-weight: 700; font-size: 15px; letter-spacing: .3px; }

.tl-idle { color: var(--muted); font-size: 12.5px; font-style: italic; }

/* Running clock — coloured by ageing threshold, with a breathing dot. */
.tl-timer.lvl-ok     { color: var(--success-600); }
.tl-timer.lvl-warn   { color: var(--amber-600); }
.tl-timer.lvl-danger { color: var(--red-600); }
.tl-dot { width: 9px; height: 9px; border-radius: 50%; background: currentColor; flex: 0 0 auto; animation: tl-pulse 1.5s ease-in-out infinite; }
@keyframes tl-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.6); } }

/* Finished phase — quiet range plus an emphasised total badge. */
.tl-timer.tl-done { color: var(--muted); flex-wrap: wrap; }
.tl-done .tl-range { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 600; color: var(--slate-500); }
.tl-done .tl-range .tl-arrow { display: inline-flex; opacity: .55; }
.tl-done .tl-range .tl-arrow svg { width: 13px; height: 13px; }
.tl-done .tl-total { font-weight: 800; font-size: 12.5px; color: var(--success-strong); background: var(--success-soft); border: 1px solid var(--success-200); border-radius: 999px; padding: 1px 9px; }

/* Order-detail timeline: two phase columns linked by an arrow. */
.tl-card .tl-track { display: flex; align-items: stretch; gap: 14px; flex-wrap: wrap; }
.tl-card .tl-track--single .tl-phase { flex: 0 1 460px; }
.tl-phase { flex: 1 1 260px; min-width: 240px; border: 1px solid var(--line); border-radius: 6px; padding: 13px 14px; background: var(--surface); transition: border-color .2s, box-shadow .2s; }
.tl-phase.is-idle { border-style: dashed; background: var(--bg); }
.tl-phase.is-running { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.tl-phase.is-done { background: var(--bg); }

.tl-phase-head { display: flex; align-items: center; gap: 9px; margin-bottom: 13px; }
.tl-phase-ic { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 6px; background: var(--primary-soft); color: var(--primary-strong); flex: 0 0 auto; }
.tl-phase-ic svg { width: 17px; height: 17px; }
.tl-phase.is-idle .tl-phase-ic { background: var(--slate-100); color: var(--slate-400); }
.tl-phase-name { font-weight: 700; font-size: 15px; color: var(--ink); }
/* Workload badge lifted into the header (right after the name) to save a row. */
.tl-phase-badge { display: inline-flex; align-items: center; }
.tl-phase-state { margin-left: auto; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; padding: 2px 9px; border-radius: 999px; }
.tl-state-idle    { color: var(--slate-500); background: var(--slate-100); }
.tl-state-running { color: var(--primary-strong); background: var(--primary-soft); }
.tl-state-done    { color: var(--slate-600); background: var(--slate-100); }

/* Compact body — the picker (label stacked on top of its control) takes the
   row, the chrono sits to its right so the phase is two lines tall, not four.
   FALLBACK: this whole layout lives in supervisor_phase_column()
   (modules/supervisor/functions.php); the prior four-row version is one
   `git revert` away of that function + this CSS block. */
.tl-phase-body { display: flex; align-items: flex-end; gap: 16px; }
/* Picker capped at ~half the row so the chrono gets the rest of the width. */
.tl-phase-pick { flex: 0 1 50%; min-width: 0; }
.tl-phase-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin-bottom: 7px; }
.tl-phase-pick form, .tl-phase-pick .catalog-combobox { width: 100%; margin: 0; }
/* Same compact density as the Brand (Marque) combobox on products/create —
   see supervisor_prep_combobox()'s own doc comment, which already promised
   this pattern (icon + toggle) even though the toggle button itself, and
   this sizing, were both missing until now. */
.tl-phase-pick .catalog-combobox-control { border-radius: 6px; }
.tl-phase-pick .catalog-combobox-control input { padding: 9px 0; font-size: 13px; line-height: normal; }
.tl-phase-chrono { flex: 0 0 auto; margin-left: auto; padding-bottom: 9px; white-space: nowrap; }
.tl-locked { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: var(--ink); padding-bottom: 9px; }
.tl-locked svg { width: 16px; height: 16px; color: var(--slate-400); }
.tl-locked-note { font-size: 12px; margin-top: 8px; }
/* Preparator workload badge: 🛒 customer orders · 📦 supplier orders */
.prep-load-badge { display: inline-flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--muted); background: rgba(100, 116, 139, .14); border-radius: 999px; padding: 2px 8px; white-space: nowrap; cursor: help; }
.prep-load-seg { display: inline-flex; align-items: center; gap: 3px; }
/* Phone only (no hover card there): the words beside the workload counts and
   above a board timer ("En cours depuis 22:19"). */
.prep-load-word, .sup-timer-word, .phone-word { display: none; }
@media (max-width: 900px) {
  .phone-hide { display: none !important; }
  .pill > .phone-word { color: inherit; opacity: .75; font-size: inherit; margin-inline-end: 3px; }
  .prep-load-word { display: inline; font-weight: 500; }
  /* A pill that is really a button ("Marquer comme rapproché") looks like one
     on a phone, where it sits among status pills. */
  .pill.pill-action { border: 1px solid var(--line) !important; background: var(--surface); color: var(--ink); border-radius: 8px; padding: 5px 10px; }
  .pill.pill-action::before { display: none; }
  .phone-word { display: inline; margin-inline-end: 5px; font-family: var(--font-sans); font-size: 11.5px; font-weight: 500; color: var(--muted); }
  .sup-timer-word { display: block; margin-bottom: 2px; font-size: 11.5px; font-weight: 500; color: var(--muted); }
}
.prep-load-ic { width: 13px; height: 13px; }

.tl-connector { display: flex; align-items: center; justify-content: center; color: var(--slate-300); flex: 0 0 auto; }
.tl-connector svg { width: 22px; height: 22px; }
@media (max-width: 720px) {
    /* nowrap: a wrapping column sizes its line to the content's width, so the
       phase box ran past the card's right edge (state pill + clock cut off). */
    .tl-card .tl-track { flex-direction: column; flex-wrap: nowrap; }
    /* Stacked, a phase's flex-basis would become its height (460px of empty
       grey under a finished reception). */
    .tl-phase, .tl-card .tl-track--single .tl-phase { min-width: 0; flex-basis: auto; }
    /* Name + state pill on the first line, workload badge under the name. */
    .tl-phase-head { flex-wrap: wrap; row-gap: 6px; }
    .tl-phase-badge { order: 1; flex-basis: 100%; padding-inline-start: 39px; }
    /* Picker full width, chrono on its own line under it. */
    .tl-phase-body { flex-direction: column; align-items: stretch; gap: 10px; }
    .tl-phase-pick { flex: 0 0 auto; }
    .tl-phase-chrono { margin-left: 0; padding-bottom: 0; white-space: normal; }
    .tl-locked { padding-bottom: 0; }
    .tl-connector { transform: rotate(90deg); padding: 4px 0; }
}

/* ============================================================================
 | Warehouse — visual facility map + structure editor
 | Each area carries an accent colour via the inline custom property --wh-c.
 | Cells/sections tint to a soft wash of that colour; inactive areas go grey
 | and hatched. Occupancy colouring is layered on later (stock placement phase).
 ========================================================================== */

/* Accent dot reused at every level. */
.wh-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--wh-c, var(--slate-400)); flex: 0 0 auto; box-shadow: 0 0 0 3px color-mix(in srgb, var(--wh-c, #64748b) 18%, transparent); }
.wh-dot-lg { width: 14px; height: 14px; }

.wh-legend { display: flex; gap: 14px; }
.wh-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.wh-swatch { width: 13px; height: 13px; border-radius: 6px; display: inline-block; }
.wh-swatch-loaded { background: var(--primary); border: 1px solid var(--primary-strong); }
.wh-swatch-empty { background: var(--primary-soft); border: 1px solid var(--primary); }
.wh-swatch-off { background: repeating-linear-gradient(45deg, var(--slate-100), var(--slate-100) 3px, var(--slate-200) 3px, var(--slate-200) 6px); border: 1px solid var(--line); }

/* Product search + result chips. */
.wh-search { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.wh-search-box { display: inline-flex; align-items: center; gap: 8px; flex: 1 1 320px; min-width: 240px; padding: 0 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); color: var(--muted); }
.wh-search-box input { border: 0; outline: 0; background: transparent; padding: 11px 0; width: 100%; font-size: 14px; color: var(--ink); }
.wh-results { margin-bottom: 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 6px; background: var(--bg); }
.wh-results-head { display: flex; align-items: center; gap: 7px; font-weight: 600; font-size: 13px; margin-bottom: 10px; }
.wh-result { display: inline-flex; align-items: center; gap: 8px; padding: 6px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 12.5px; }
.wh-result.here { border-color: var(--amber-600); background: var(--amber-50); }
.wh-result .mono { font-weight: 700; }

/* Grouped locate results: product → SKU → locations. */
.wh-locate { display: flex; flex-direction: column; gap: 10px; }
.wh-locate-product { padding: 11px 12px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); }
.wh-locate-pname { font-size: 14px; margin-bottom: 8px; display: flex; align-items: baseline; gap: 8px; }
.wh-locate-sku { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 6px 0; }
.wh-locate-sku + .wh-locate-sku { border-top: 1px dashed var(--line-soft); }
.wh-locate-meta { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; min-width: 180px; }
.wh-locate-locs { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.wh-locate-none { font-size: 12px; font-style: italic; color: var(--muted); }

/* Occupancy gauge card — dark hero-gradient (same slate-900 → emerald-900
   used behind the dashboard's revenue banner), a thin ring with a rounded
   end-cap instead of the old flat 2-tone conic donut, and the Occupied/Free
   split pulled up next to the number instead of living in a separate grid. */
.wh-gauge-card {
    position: relative; overflow: hidden; border-radius: 6px; padding: 18px;
    background: linear-gradient(135deg, var(--slate-900) 0%, var(--ink) 55%, var(--emerald-900) 140%);
    color: var(--white);
}
.wh-gauge-eyebrow { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--slate-300); }
.wh-gauge-eyebrow .wh-dot { background: var(--wh-c, var(--emerald-400)); box-shadow: none; }
.wh-gauge-eyebrow strong { color: var(--white); font-weight: 700; }

.wh-gauge-split { display: flex; margin-top: 16px; border-top: 1px solid rgba(255, 255, 255, .10); padding-top: 14px; }
.wh-gauge-split-item { flex: 1; text-align: center; }
.wh-gauge-split-item + .wh-gauge-split-item { border-left: 1px solid rgba(255, 255, 255, .10); }
.wh-gsi-value { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; }
.wh-gsi-value.dim { color: var(--slate-400); }
.wh-gsi-label { font-size: 10.5px; color: var(--slate-400); margin-top: 2px; text-transform: uppercase; letter-spacing: .03em; }

/* Sidebar detail card — the administrative facts (sections/étages/products)
   that don't need the gauge card's visual weight. */
.wh-detail-card { display: flex; flex-direction: column; gap: 12px; }
.wh-detail-row { display: flex; justify-content: space-between; align-items: center; }
.wh-detail-row .k { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.wh-detail-row .k .ico { width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center; background: var(--slate-100); color: var(--slate-600); }
.wh-detail-row .k .ico .icon-sm { width: 12px; height: 12px; }
.wh-detail-row .v { font-size: 14px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.wh-detail-divider { height: 1px; background: var(--line-soft); }

/* Étage detail breadcrumb. */
.wh-crumb { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
.wh-crumb svg { opacity: .6; }

/* Empty state. */
.wh-empty { text-align: center; padding: 40px 24px; }
.wh-empty-ic { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; border-radius: 6px; background: var(--primary-soft); color: var(--primary-strong); margin-bottom: 12px; }
.wh-empty-ic svg { width: 28px; height: 28px; }

/* City -> zone chooser (/warehouse, nothing picked yet) — "Recherche
   d'abord", picked from the whole-page gallery 2026-10-23: a big search, the
   busiest zones as large buttons, then every city on one line with its zones
   as chips. */
.wg-launch { text-align: center; padding: 6px 0 18px; }
.wg-launch h2 { margin: 0; font-size: 24px; letter-spacing: -.01em; }
.wg-search {
    display: flex; align-items: center; gap: 12px; max-width: 640px; margin: 14px auto 0; padding: 0 16px;
    background: var(--surface); border: 1.5px solid var(--line); border-radius: 14px;
    box-shadow: 0 8px 26px -16px rgba(15, 23, 42, .35); color: var(--muted); cursor: text;
}
.wg-search:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.wg-search input { flex: 1; min-width: 0; padding: 14px 0; border: 0; outline: 0; background: transparent; font: inherit; font-size: 16px; color: var(--ink); }
.wg-search kbd { font: 600 12px var(--font-mono); border: 1px solid var(--line); border-radius: 6px; padding: 2px 7px; color: var(--muted); }
@media (hover: none) { .wg-search kbd { display: none; } }
.wg-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.wg-city-form { max-width: 640px; margin: 0 auto 16px; text-align: left; }

.wg-subt { margin: 18px 0 10px; font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.wg-recents { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.wg-recent {
    display: flex; align-items: center; gap: 12px; padding: 14px; background: var(--surface);
    border: 1px solid var(--line); border-radius: 12px; color: var(--text); text-decoration: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.wg-recent:hover { border-color: var(--wh-c, var(--primary)); box-shadow: 0 6px 20px -14px rgba(15, 23, 42, .4); }
.wg-recent-ico {
    width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center; flex: none;
    background: color-mix(in srgb, var(--wh-c, #64748b) 14%, var(--surface)); color: color-mix(in srgb, var(--wh-c, #64748b) 75%, var(--ink));
}
.wg-recent-txt { flex: 1; min-width: 0; display: grid; }
.wg-recent-txt b { font-size: 16px; color: var(--ink); }
.wg-recent-txt small { font-size: 12.5px; color: var(--muted); }
.wg-go { color: var(--slate-400); flex: none; }
.wg-recent:hover .wg-go { color: var(--primary); }

.wg-cities { padding: 0; overflow: hidden; }
.wg-city { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 14px; align-items: center; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
.wg-city:first-child { border-top: 0; }
.wg-city-name { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.wg-city-name b { color: var(--ink); }
.wg-n { padding: 0 7px; border-radius: 999px; background: var(--slate-100); font-size: 12px; font-weight: 600; color: var(--muted); }
.wg-zones { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.wg-none { font-size: 13px; }
.wg-zone-item { position: relative; display: inline-flex; }
.wg-zone {
    display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border: 1px solid var(--line); border-radius: 999px;
    background: var(--surface); color: var(--text); font: inherit; font-size: 13.5px; text-decoration: none; cursor: pointer;
}
a.wg-zone:hover { border-color: var(--primary); }
.wg-zone small { font-size: 12px; font-weight: 700; color: var(--muted); }
.wg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--wh-c, var(--slate-400)); flex: none; }
.wg-zone-item.is-off .wg-zone { color: var(--muted); border-style: dashed; }
.wg-zone-item.is-off .wg-dot { opacity: .45; }
.wg-zone-item.is-pending .wg-zone { cursor: default; border-style: dashed; border-color: var(--amber-600); background: var(--amber-50); color: var(--amber-700); }
.wg-zone-item.is-rejected .wg-zone { cursor: default; border-style: dashed; border-color: var(--red-400); background: var(--red-50); color: var(--red-600); }
.wg-zone-item.is-pending small, .wg-zone-item.is-rejected small { color: inherit; font-weight: 600; }
.wg-zone-add { border-style: dashed; color: var(--primary); font-weight: 600; background: transparent; }
.wg-zone-add:hover { border-color: var(--primary); background: var(--primary-soft); }
/* A zone's delete: a small round × on the chip's corner, shown on hover/focus
   (always on a touch screen). It is a sibling of the chip's link, never inside it. */
.wg-zone-del { position: absolute; top: -6px; right: -6px; margin: 0; }
.wg-zone-del button {
    display: grid; place-items: center; width: 18px; height: 18px; padding: 0; border: 1px solid var(--line); border-radius: 50%;
    background: var(--surface); color: var(--muted); cursor: pointer;
}
.wg-zone-del button:hover { color: var(--red-600); border-color: var(--red-400); background: var(--red-50); }
.wg-zone-del .icon-sm { width: 11px; height: 11px; }
@media (hover: hover) {
    .wg-zone-del { opacity: 0; transition: opacity .12s ease; }
    .wg-zone-item:hover .wg-zone-del, .wg-zone-item:focus-within .wg-zone-del { opacity: 1; }
}
.wg-noresult { padding: 16px; text-align: center; }
@media (max-width: 760px) {
    .wg-recents { grid-template-columns: minmax(0, 1fr); }
    .wg-city { grid-template-columns: minmax(0, 1fr); gap: 8px; }
}

/* Generic modal shell (`.wh-modal*`). Born as the warehouse "create a city /
   a zone" dialog — hence the prefix — and since reused as-is by the order
   form's quick-create customer modal, so treat these as shared: same visual
   language as
   .confirm-modal (fixed backdrop, centered card, rise-in transition) but
   its own classes: that one is a JS-built yes/no dialog with fixed
   content, this one holds a real form authored in the page, so coupling
   them would make either harder to change alone. Opening/closing is left
   to the caller — Alpine's x-show on the warehouse page, the `hidden`
   attribute on the order form. One instance on the page
   regardless of how many cities exist; opening it in "new zone" mode just
   fixes the city and re-points the form via JS (see the page's own
   <script> block) rather than rendering one modal per city. */
.wh-modal-backdrop {
    position: fixed; inset: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center; padding: 20px;
    background: rgba(15, 23, 42, .5);
    animation: wh-modal-fade .15s ease both;
}
/* The warehouse page toggles this with Alpine's x-show (inline display), but
   the order form's quick-create modal toggles the `hidden` attribute — and a
   bare [hidden] loses to the display:flex above on equal specificity. */
.wh-modal-backdrop[hidden] { display: none; }
@keyframes wh-modal-fade { from { opacity: 0; } to { opacity: 1; } }
.wh-modal {
    width: min(440px, 100%); max-height: calc(100vh - 40px); overflow-y: auto;
    background: var(--surface); border-radius: 6px; box-shadow: 0 24px 60px rgba(15, 23, 42, .32);
    padding: 24px;
    animation: wh-modal-rise .18s cubic-bezier(.2, .8, .2, 1) both;
}
@keyframes wh-modal-rise { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: translateY(0) scale(1); } }
.wh-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.wh-modal-title { font-size: 17px; font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.wh-modal-close { display: flex; background: none; border: none; padding: 7px; margin: -7px; cursor: pointer; color: var(--muted); border-radius: 6px; transition: background .15s ease, color .15s ease; }
.wh-modal-close:hover { background: var(--slate-100); color: var(--ink); }
.wh-modal-close .icon { width: 18px; height: 18px; }
.wh-modal-section + .wh-modal-section { margin-top: 6px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
.wh-modal-fixed-city {
    display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-radius: 6px;
    background: var(--blue-50); color: var(--blue-700); font-size: 13px; font-weight: 600; margin-bottom: 4px;
}
.wh-modal-fixed-city .icon { flex: none; }
/* The "propose a new one" switch — a real small button (light fill, icon,
   border) rather than a bare text link, so it reads as an alternative
   action and not an afterthought caption under the field above it. */
.wh-modal-switch {
    display: inline-flex; align-items: center; gap: 6px; margin-top: 10px;
    padding: 6px 12px; border-radius: 8px; border: 1px solid var(--line);
    background: var(--surface); color: var(--slate-700);
    font-size: 12.5px; font-weight: 600; cursor: pointer;
    transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.wh-modal-switch:hover { border-color: var(--primary); color: var(--primary-strong); background: var(--primary-soft); }
.wh-modal-switch .icon { width: 13px; height: 13px; }
.wh-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line-soft); }

/* ---- Map page (/warehouse?site=) — "Tableau", picked from a gallery of
   whole-page designs 2026-10-23: depot tabs, then the open depot's étages as
   one filterable table beside a right-hand column (depot card + preparation
   queue); stacks under 1200px. ---- */
.wt-depots { margin: 0 0 14px; }
.wt-depots .list-tab.is-off { opacity: .55; }
.wt-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--wh-c, var(--slate-400)); flex: none; }
.wt-layout { display: grid; gap: 16px; align-items: start; margin-bottom: 16px; }
@media (min-width: 1200px) { .wt-layout { grid-template-columns: minmax(0, 1fr) 300px; } }
.wt-main, .wt-aside { min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.wt-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.wt-toolbar .wh-search { flex: 1 1 360px; margin: 0; flex-wrap: nowrap; }
.wt-toolbar .wh-search-box { min-width: 0; }
/* A long result list scrolls in its own box so the table stays in reach. */
.wt-main > .wh-results { margin: 0; max-height: 340px; overflow-y: auto; }
.wt-filters { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--surface); }
.wt-filter { padding: 8px 11px; border: 0; border-left: 1px solid var(--line); background: none; font: inherit; font-size: 13px; color: var(--muted); cursor: pointer; white-space: nowrap; }
.wt-filter:first-child { border-left: 0; }
.wt-filter span { font-weight: 700; color: var(--ink); margin-left: 3px; }
.wt-filter:hover { background: var(--hover); }
.wt-filter.is-on { background: var(--primary-soft); color: var(--ink); font-weight: 600; }
.wt-table tr.is-off td { color: var(--muted); }
.wt-table tr.is-off .wt-sq { opacity: .45; }
.wt-table tr.is-match td { background: color-mix(in srgb, var(--amber-600) 10%, var(--surface)); }
.wt-table tr.is-match td:first-child { box-shadow: inset 3px 0 0 var(--amber-600); }
.wt-sq { display: inline-block; width: 12px; height: 12px; border-radius: 4px; background: var(--wh-c, var(--slate-400)); margin-right: 8px; vertical-align: -1px; }
.wt-meta { font-size: 12px; color: var(--muted); }
@media (min-width: 900px) { .wt-open-col { width: 1%; white-space: nowrap; text-align: right; } }

/* Étage page (modules/warehouse/pages/etage.php, gallery picks A4·B3·C2·D3·E1·F2·G3·H2·L0, case 197). */
.et-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 14px; margin-bottom: 14px; }
.et-bar-code { margin: 0; font-size: 17px; color: var(--ink); }
.et-bar-path { min-width: 0; }
.et-bar-num { margin-left: auto; color: var(--muted); white-space: nowrap; }
.et-bar-num b { color: var(--ink); font-variant-numeric: tabular-nums; }
.et-bar-btns { display: flex; gap: 6px; }
.et-pager { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; margin-bottom: 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.et-pager .is-off { opacity: .45; pointer-events: none; }
.et-pager-at { text-align: center; }
.et-pager-at b { color: var(--ink); }
.et-line { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; margin-bottom: 16px; color: var(--muted); font-size: 14px; }
.et-line > span, .et-line > a { display: inline-flex; align-items: center; gap: 6px; }
.et-line b { color: var(--ink); font-variant-numeric: tabular-nums; }
.et-line > i { width: 1px; height: 16px; background: var(--line); }
.et-dot { width: 8px; height: 8px; border-radius: 50%; }
.et-dot.green { background: var(--rl-green-dot, var(--success, #16a34a)); }
.et-dot.slate { background: var(--slate-400); }
.et-dot.amber { background: var(--rl-amber-dot, var(--amber-600)); }
.et-tools { display: flex; align-items: flex-end; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.et-tools .list-tabs { margin: 0; border-bottom: 0; }
.et-tabs .list-tab { background: none; border: 0; border-bottom: 2px solid transparent; font: inherit; cursor: pointer; }
.et-tabs .list-tab.is-active { border-bottom-color: var(--primary); }
.et-search { flex: 0 1 320px; margin-bottom: 6px; }
.et-list { position: relative; margin-bottom: 18px; }
.et-chk { width: 34px; }
.et-chk input { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; }
.et-table .product-cell-text, body .et-table .product-cell-name, .et-table .product-cell-code { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
.et-cat { font-family: var(--font-sans); }
.et-ean { display: block; font-size: 11.5px; }
.et-qty { font-family: var(--font-mono); font-weight: 600; color: var(--ink); white-space: nowrap; }
.et-qty.is-zero { color: var(--faint); font-weight: 400; }
.et-bulk { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap; padding: 10px 14px; border-top: 1px solid var(--line); border-radius: 0 0 11px 11px; background: var(--ink); color: var(--surface); font-size: 13.5px; }
.et-bulk[hidden] { display: none; }
.et-bulk-names { min-width: 0; opacity: .75; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 160px; }
.et-bulk-btns { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }
body.skin-readable .et-bulk .btn-ghost, .et-bulk .btn-ghost { color: var(--surface); background: transparent; }
.et-bulk .btn-outline { background: var(--surface); }
.et-bulk .is-off { opacity: .55; cursor: not-allowed; }
.et-moves, .et-info { margin-bottom: 18px; }
.et-legend { display: flex; gap: 12px; font-size: 12.5px; color: var(--muted); }
.et-legend > span { display: inline-flex; align-items: center; gap: 5px; }
.et-sq { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.et-sq.in, .et-chart-bars i.in { background: var(--rl-green-dot, var(--success, #16a34a)); }
.et-sq.out, .et-chart-bars i.out { background: var(--rl-red-dot, var(--red-600)); }
.et-chart { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; padding: 14px 16px 6px; }
.et-chart-col { display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); }
.et-chart-col .mono { min-height: 1.2em; color: var(--ink); }
.et-chart-bars { width: 100%; height: 90px; display: flex; align-items: flex-end; justify-content: center; gap: 3px; border-bottom: 1px solid var(--line); }
.et-chart-bars i { width: 12px; border-radius: 3px 3px 0 0; }
.et-move { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.et-move-ico { width: 26px; height: 26px; flex: none; border-radius: 50%; display: inline-grid; place-items: center; background: var(--slate-100); color: var(--slate-700); }
.et-move-ico.green { background: var(--rl-green-soft, var(--emerald-50, #ecfdf5)); color: var(--rl-green-ink, var(--success, #15803d)); }
.et-move-ico.blue  { background: var(--rl-blue-soft, var(--blue-50)); color: var(--rl-blue-ink, var(--blue-700)); }
.et-move-ico.amber { background: var(--rl-amber-soft, var(--amber-50)); color: var(--rl-amber-ink, var(--amber-700)); }
.et-move-ico.red   { background: var(--rl-red-soft, var(--red-50)); color: var(--rl-red-ink, var(--red-600)); }
.et-move-name { white-space: normal; min-width: 180px; }
.et-in { color: var(--rl-green-ink, var(--success, #15803d)); }
.et-out { color: var(--rl-red-ink, var(--red-600)); }
.et-more { margin: 0; padding: 10px 16px; border-top: 1px solid var(--line-soft); font-size: 13px; }
.et-more-btn { padding: 0; border: 0; background: none; font: inherit; font-weight: 600; cursor: pointer; }
.et-kv { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 8px 12px; margin: 0; padding: 14px 16px; font-size: 13.5px; }
.et-kv dt { color: var(--muted); }
.et-kv dd { margin: 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; color: var(--text); }
.et-info-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
.et-info-row > div { display: flex; flex-direction: column; }
.et-info-row b { color: var(--ink); font-size: 13.5px; }
.et-info-row small { font-size: 12px; }
@media (max-width: 640px) {
    .et-bar-num { margin-left: 0; }
    .et-pager-at { display: none; }
    .et-search { flex-basis: 100%; }
    .et-chart-bars i { width: 7px; }
}

.prep-queue { max-height: 720px; display: flex; flex-direction: column; }
.prep-list { overflow-y: auto; padding: 8px 10px 12px; display: flex; flex-direction: column; gap: 6px; }
.prep { border: 1px solid var(--line); border-radius: 6px; padding: 11px 12px; background: var(--surface); transition: border-color .15s ease; }
.prep[open] { border-color: var(--slate-300); }
summary.prep-row { list-style: none; cursor: pointer; }
summary.prep-row::-webkit-details-marker { display: none; }
.prep-row { display: flex; align-items: center; gap: 9px; }
.prep-avatar { width: 30px; height: 30px; border-radius: 6px; flex: 0 0 auto; display: grid; place-items: center; font-size: 11px; font-weight: 800; color: var(--white); }
.prep-who { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.prep-name { font-size: 13px; font-weight: 700; color: var(--ink); }
.prep-order { font-size: 11.5px; color: var(--muted); }
.prep-chev { width: 15px; height: 15px; color: var(--faint); flex: 0 0 auto; transition: transform .15s ease; }
.prep[open] .prep-chev { transform: rotate(180deg); }
.prep-progress { margin-top: 9px; }
.prep-progress-track { display: block; height: 5px; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.prep-progress-bar { display: block; height: 100%; background: var(--primary); border-radius: inherit; }
.prep-lines { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--line); display: flex; flex-direction: column; gap: 7px; }
.prep-line { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.prep-line .sku { flex: 0 0 auto; color: var(--slate-600); }
.prep-line .name { flex: 1; min-width: 0; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prep-line .qty { flex: 0 0 auto; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---- structure editor (/warehouse/manage) — "Colonnes", picked from the
   whole-page gallery 2026-10-23: the site on one line, then four columns like
   the Mac Finder (Dépôts | Sections | Étages | Détail). Forms open in a panel
   from the right; colours are round swatches ---- */
.wm-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--wh-c, var(--slate-400)); flex: 0 0 auto; }
.wm-sq { display: inline-block; width: 12px; height: 12px; border-radius: 4px; background: var(--wh-c, var(--slate-400)); flex: 0 0 auto; }
.wm-grow { flex: 1; }
.wm-link { display: inline-flex; align-items: center; gap: 5px; border: 0; background: none; padding: 2px 4px; color: var(--primary); font: inherit; font-weight: 600; font-size: 13px; cursor: pointer; }
.wm-link:hover { color: var(--primary-strong); }
.wm-danger { color: var(--red-600); }
.wm-toggle { margin: 0; display: inline-flex; }
.wm-toggle .product-switch { font-weight: 500; font-size: 13px; color: var(--muted); }
.wm-menu-form { display: block; margin: 0; }
.wm-type { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; color: var(--muted); }
.wm-type-reception { color: var(--blue-700); }
.wm-type-preparation { color: #7c3aed; }
.wm-type-expedition { color: var(--amber-700); }

.wm-site {
    display: flex; align-items: center; gap: 8px 10px; flex-wrap: wrap; margin-bottom: 14px; padding: 10px 14px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 12px; font-size: 14px;
}
.wm-site b { color: var(--ink); }
.wm-site.is-off b { color: var(--muted); }
.wm-site-sep { width: 1px; height: 18px; background: var(--line); }
.wm-zchip { display: inline-flex; align-items: center; gap: 4px; padding: 1px 4px 1px 9px; border-radius: 999px; background: var(--primary-soft); font-size: 12.5px; }
.wm-zchip.is-pending { background: var(--rl-amber-soft, var(--amber-50)); }
.wm-zchip.is-rejected { background: var(--rl-red-soft, var(--red-50)); }
.wm-zchip form { display: inline-flex; margin: 0; }
.wm-zchip button { display: inline-grid; place-items: center; width: 18px; height: 18px; border: 0; border-radius: 50%; background: none; color: var(--muted); cursor: pointer; }
.wm-zchip button:hover { background: var(--surface); color: var(--red-600); }

.wm-cols {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.15fr);
    background: var(--surface); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; min-height: 470px;
}
.wm-col { display: flex; flex-direction: column; min-width: 0; border-left: 1px solid var(--line); }
.wm-col:first-child { border-left: 0; }
.wm-col-head {
    display: flex; align-items: center; gap: 6px; padding: 12px 14px; border-bottom: 1px solid var(--line-soft);
    font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}
.wm-n { margin-left: auto; padding: 0 7px; border-radius: 999px; background: var(--slate-100); font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--muted); }
.wm-col-list { flex: 1; display: grid; gap: 2px; align-content: start; padding: 6px; max-height: 62vh; overflow-y: auto; }
.wm-col-empty { padding: 10px 8px; font-size: 13.5px; }
.wm-item { display: flex; align-items: center; gap: 8px; padding: 8px; border-radius: 8px; color: var(--text); text-decoration: none; }
.wm-item:hover { background: var(--hover); }
.wm-item.is-sel { background: var(--primary-soft); }
.wm-item.is-off .wm-item-txt b { color: var(--muted); }
.wm-item.is-off .wm-sq, .wm-item.is-off .wm-dot { opacity: .45; }
.wm-item-txt { flex: 1; display: grid; min-width: 0; }
.wm-item-txt b { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-item-txt small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--muted); }
.wm-item-n { font-size: 13px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.wm-off-tag { font-size: 11.5px; color: var(--muted); }
.wm-go { color: var(--slate-400); flex: none; }
.wm-item.is-sel .wm-go { color: var(--primary); }
.wm-col-add {
    display: flex; align-items: center; gap: 6px; margin: 4px 8px 8px; padding: 8px; border: 0; border-radius: 8px;
    background: none; color: var(--primary); font: inherit; font-weight: 600; font-size: 13.5px; cursor: pointer;
}
.wm-col-add:hover { background: var(--primary-soft); }
.wm-col-detail { background: var(--slate-50); }
.wm-detail { padding: 16px; }
.wm-detail-top { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.wm-detail-sq { width: 36px; height: 36px; border-radius: 9px; background: var(--wh-c, var(--slate-400)); flex: none; }
.wm-detail-code { font-size: 16px; font-weight: 700; color: var(--ink); word-break: break-all; }
.wm-dl { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 10px; margin: 0 0 16px; font-size: 13.5px; }
.wm-dl dt { color: var(--muted); }
.wm-dl dd { margin: 0; }
.wm-detail-actions { display: grid; gap: 8px; }
.wm-detail-actions .btn { width: 100%; justify-content: center; }
.wm-detail-actions form { margin: 0; }
/* Narrower screens: two columns a row, then one (the laptop keeps four). */
@media (max-width: 1100px) {
    .wm-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .wm-col { border-top: 1px solid var(--line); }
    .wm-col:nth-child(-n+2) { border-top: 0; }
    .wm-col:nth-child(odd) { border-left: 0; }
}
@media (max-width: 640px) {
    .wm-cols { grid-template-columns: minmax(0, 1fr); }
    .wm-col { border-left: 0; }
    .wm-col:nth-child(2) { border-top: 1px solid var(--line); }
    .wm-col-list { max-height: none; }
}

.wm-drawer .filter-drawer-panel { width: min(420px, 100%); }
.wm-drawer-body .field { margin-top: 14px; }
.wm-drawer-body .catalog-combobox { width: 100%; }

.wm-colors { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.wm-color {
    position: relative; width: 30px; height: 30px; border-radius: 50%; border: 0; padding: 0;
    background: var(--c); color: #fff; display: grid; place-items: center; cursor: pointer;
}
.wm-color svg { opacity: 0; }
.wm-color.is-on { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--c); }
.wm-color.is-on svg { opacity: 1; }
.wm-color-other { border: 1.5px dashed var(--slate-300); color: var(--muted); }
.wm-color-other svg { opacity: 1; }
.wm-color-other.is-on { border-style: solid; border-color: transparent; color: #fff; }
.wm-color-other input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; border: 0; padding: 0; }

.wh-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); color: var(--muted); cursor: pointer; transition: background .12s, color .12s, border-color .12s; }
.wh-icon-btn:hover { background: var(--hover); color: var(--ink); }
.wh-icon-btn:disabled { opacity: .4; cursor: not-allowed; }
.wh-icon-btn:disabled:hover { background: var(--surface); color: var(--muted); }
.wh-icon-danger:hover { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }

.row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.product-delete-form { display: inline-flex; }
/* Inside a stacked actions menu (.m-menu-pop) the delete form must span the
   row like its sibling .m-menu-item links, not shrink-wrap like it does
   sitting inline among the old horizontal icon row. */
.m-menu-pop .product-delete-form { display: block; }

/* ============================================================================
 | Livreur (delivery driver) board + supervisor dispatch
 ========================================================================== */

/* ------------------------------------------------------------------------
 | Field shell — livreur. Own system, not the admin one (see design.md
 | "Field shell system"): utilitarian, higher-contrast, no hover, structural
 | priority signal, one dominant CTA per screen, stepper qty entry.
 | ---------------------------------------------------------------------- */

/* "Call the client" is a button the driver taps at the door, not a text link. */
.lv-stop-phone { display: inline-flex; align-items: center; gap: 5px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); font-size: 13px; color: var(--primary-strong); font-weight: 600; margin-top: 6px; }
.lv-nav-group { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.lv-nav-btn {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 8px; background: var(--primary-soft); color: var(--primary-strong);
}
.lv-nav-btn:active { background: var(--emerald-100, #d1fae5); }
.lv-nav-btn--waze { background: var(--blue-50); color: var(--blue-600); }
.lv-nav-btn--waze:active { background: #dbeafe; }
/* Delivery board stops table (pages/index.php). The address cell wraps so its
   text and Maps/Waze buttons never spill under the Priority pill (the global
   `.table td { white-space: nowrap }` did that). Below 720px each row becomes
   a stacked card so a driver's phone never needs a sideways scroll. */
.lv-stops .lv-stop-sel { width: 36px; }
.lv-stops .lv-stop-num { width: 40px; }
.lv-selall { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.lv-m-only { display: none; }
.lv-stop-seq {
    display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    border-radius: 50%; background: var(--slate-100); color: var(--slate-700); font-size: 12px; font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.lv-stop.is-urgent > td:first-child { box-shadow: inset 3px 0 0 var(--red-500); }
.lv-stop.is-urgent .lv-stop-seq { background: var(--red-50); color: var(--red-600); }
.lv-stop-name { font-weight: 600; color: var(--ink); }
.lv-stops td.lv-stop-addr { white-space: normal; min-width: 260px; }
.lv-addr-cell { display: flex; align-items: center; gap: 8px; }
.lv-addr-cell > svg { flex: 0 0 auto; color: var(--faint); }
.lv-addr-line { flex: 1 1 auto; min-width: 0; line-height: 1.4; overflow-wrap: anywhere; }
@media (max-width: 720px) {
    .lv-m-only { display: inline; }
    .lv-stops, .lv-stops thead, .lv-stops tbody { display: block; }
    .lv-stops thead tr { display: block; }
    .lv-stops thead th { display: none; }
    .lv-stops thead th.lv-stop-sel { display: block; width: auto; padding: 10px 16px; text-transform: none; letter-spacing: 0; font-size: 13px; color: var(--muted); }
    .lv-stops tr.lv-stop {
        display: grid; grid-template-columns: auto auto 1fr auto; align-items: center; gap: 8px 10px;
        grid-template-areas: "sel num ref prio" "client client client items" "addr addr addr addr" "act act act act";
        padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
    }
    .lv-stops tr.lv-stop:last-child { border-bottom: 0; }
    .lv-stops tr.lv-stop[hidden] { display: none; }
    .table.lv-stops tbody tr.lv-stop > td { display: block; width: auto; padding: 0; border: 0; white-space: normal; min-width: 0; text-align: start; }
    .lv-stops .lv-stop-sel { grid-area: sel; }
    .lv-stops .lv-stop-num { grid-area: num; }
    .lv-stops .lv-stop-ref { grid-area: ref; }
    .lv-stops .lv-stop-prio { grid-area: prio; }
    .lv-stops .lv-stop-client { grid-area: client; }
    .lv-stops .lv-stop-addr { grid-area: addr; }
    .table.lv-stops tbody tr.lv-stop > td.lv-stop-items { grid-area: items; align-self: start; text-align: end; }
    .lv-stops .lv-stop-act { grid-area: act; }
    .lv-stops .lv-stop-act .btn { width: 100%; justify-content: center; }
    .lv-stop.is-urgent { box-shadow: inset 3px 0 0 var(--red-500); }
    .lv-stop.is-urgent > td:first-child { box-shadow: none; }
    .lv-stop-name { font-size: 15px; }
}
.lv-empty-ic { display: inline-flex; width: 56px; height: 56px; border-radius: 6px; background: var(--primary-soft); color: var(--primary-strong); align-items: center; justify-content: center; margin-bottom: 12px; }
.lv-empty-ic svg { width: 28px; height: 28px; }

/* Stop detail — address + navigate. */
.lv-addr-row { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
.lv-addr-text { display: flex; align-items: center; gap: 7px; font-size: 16px; font-weight: 600; margin-top: 4px; }

/* Handover list — read-only. A driver confirms delivery, full stop; a real
   shortfall goes through Returns afterward, not a number edited here. */
.lv-handover-name { font-size: 15px; font-weight: 700; color: var(--ink); }
.lv-handover-total {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 20px 18px; margin-top: 4px; border-top: 1px solid var(--line);
    font-size: 13px; font-weight: 700; color: var(--muted);
}
.lv-handover-total .num { color: var(--ink); font-variant-numeric: tabular-nums; }

/* Camera scanner (return-from-an-old-delivery flow) */
.lv-scan-wrap {
    position: relative; width: 100%; aspect-ratio: 3 / 4; border-radius: 6px;
    overflow: hidden; background: var(--slate-900); border: 1px solid var(--line);
}
.lv-scan-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; }
.lv-scan-reticle {
    position: absolute; inset: 12%; border: 3px solid rgba(255, 255, 255, .85);
    border-radius: 6px; pointer-events: none;
    box-shadow: 0 0 0 999px rgba(15, 23, 42, .35);
}

/* Fail is a real path, not a hidden one — just visually secondary to Deliver,
   so it never competes with the one dominant action on this screen. */
.lv-fail-toggle {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 12px 6px; background: none; border: none;
    color: var(--muted); font-size: 13.5px; font-weight: 600;
}
.lv-fail-toggle svg:first-child { color: var(--amber-600); }
.lv-fail-toggle:active { color: var(--text); }
.lv-fail-chevron { margin-left: auto; display: flex; }
.lv-fail-chevron.is-open { transform: rotate(180deg); }

/* Proof of delivery — real visual weight, not an afterthought box. */
.lv-photo-btn {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    height: 52px; border: 1.5px dashed var(--line); border-radius: 8px;
    background: var(--bg); color: var(--muted); font-weight: 600; font-size: 14px; cursor: pointer;
}
.lv-photo-btn.has-file { border-style: solid; border-color: var(--primary); background: var(--primary-soft); color: var(--primary-strong); }
.lv-photo-btn:active { background: var(--slate-100); }
.lv-photo-input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.lv-sign { position: relative; border: 1.5px dashed var(--line); border-radius: 6px; background: var(--bg); padding: 6px; }
.lv-sign-pad { width: 100%; height: 200px; touch-action: none; border-radius: 6px; background:
    repeating-linear-gradient(0deg, transparent, transparent 39px, var(--line-soft) 39px, var(--line-soft) 40px) var(--surface); cursor: crosshair; }
.lv-sign-clear { position: absolute; top: 12px; right: 12px; }

/* Dispatch board. */
.dispatch-group { margin-bottom: 16px; }
.dispatch-group-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
.dispatch-failed { background: var(--red-50); }
.dispatch-noaddr { color: var(--red-600); }
.dispatch-order { display: inline-flex; gap: 4px; }
.dispatch-move { font-size: 14px; font-weight: 800; line-height: 1; }
.dispatch-move:disabled { opacity: .35; cursor: default; }
.dispatch-group .filter-select { min-width: 130px; }

/* ============================================================================
 | Receiving (goods-in) + putaway + pick list
 ========================================================================== */
.rc-card { margin-bottom: 16px; }
.rc-locked { display: flex; align-items: center; gap: 8px; margin: 4px 0 12px; padding: 9px 12px; border-radius: 6px;
  background: var(--amber-50); color: var(--amber-700); border: 1px solid var(--amber-600); font-size: 13px; }
.rc-locked svg { flex: 0 0 auto; }
/* Inline-editable destination: collapsed clickable text ⇄ select. */
.dest-cell { min-width: 220px; }
.dest-text { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 7px 12px; border-radius: 6px;
  border: 1px solid var(--line); background: var(--hover); color: var(--ink); font-weight: 600; font-family: var(--font-mono);
  cursor: pointer; transition: border-color .15s, background .15s; }
.dest-text:hover { border-color: var(--primary); background: var(--primary-soft); }
.dest-text svg { flex: 0 0 auto; color: var(--primary); }
.dest-text.is-empty { color: var(--muted); font-weight: 500; }
.dest-edit { display: block; }
tr.rc-done td { opacity: .6; }
.rc-etage { margin-top: 12px; }
.rc-etage-head { display: flex; align-items: center; gap: 8px; font-weight: 600; margin: 6px 0; }

/* Pick list. */
.pick-qty { font-weight: 800; font-size: 15px; }

/* ============================================================================
 | Warehouse structure — interactive collapsible tree (depot → section → étage)
 | One card per depot; warehouse-tree.js draws bead nodes connected by curved
 | rails into each card's <svg>, and drives the shared side panel. Node colour
 | is each area's accent (--wh-c); branch nodes (depot/section) carry a thin
 | occupancy bar, étage leaves are solid when loaded / hollow when empty /
 | dashed when inactive — the same language as the map's legend.
 ========================================================================== */
.wh-depth-group { display: inline-flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; background: var(--surface); }
.wh-depth-btn { border: 0; border-left: 1px solid var(--line); background: transparent; color: var(--muted);
    font: inherit; font-size: 13px; font-weight: 600; padding: 7px 13px; cursor: pointer; }
.wh-depth-btn:first-child { border-left: 0; }
.wh-depth-btn:hover { background: var(--hover); color: var(--text); }
.wh-depth-btn.is-active { background: var(--ink); color: var(--white); }

.wh-tree-svg { display: block; }

.wh-tnode { cursor: pointer; }
.wh-tnode .wh-tlabel { fill: var(--ink); font-family: var(--font-sans); font-size: 12.5px; font-weight: 600; }
.wh-tnode .wh-tlabel.is-root { font-size: 13.5px; }
.wh-tnode .wh-tlabel.is-leaf { font-family: var(--font-mono); font-size: 11.5px; }
.wh-tnode .wh-tsub { fill: var(--faint); font-family: var(--font-mono); font-size: 10px; }
.wh-tnode:hover .wh-tlabel { fill: var(--primary-strong); }
.wh-tnode.is-sel .wh-tlabel { fill: var(--primary); }
.wh-tnode.is-off { opacity: .55; }
.wh-thit { fill: transparent; pointer-events: all; }

.wh-tlink { fill: none; stroke: var(--line); stroke-width: 1.4; }
.wh-tlink.is-hot { stroke: var(--wh-c, var(--primary)); stroke-width: 2; }

.wh-tdot { fill: var(--surface); stroke: var(--wh-c, var(--slate-400)); stroke-width: 2.5; }
.wh-tdot.is-fill { fill: var(--wh-c, var(--slate-400)); }
.is-off .wh-tdot { stroke-dasharray: 2 2; }

.wh-ttoggle { fill: var(--surface); stroke: var(--line); stroke-width: 1; }
.wh-tnode:hover .wh-ttoggle { stroke: var(--wh-c, var(--primary)); }
.wh-ttoggle-t { fill: var(--muted); font-family: var(--font-mono); font-size: 11px; font-weight: 700; text-anchor: middle; dominant-baseline: central; }

.wh-tbar-bg { fill: var(--line-soft); }
.wh-tbar { fill: var(--wh-c, var(--primary)); }

.wh-tpath { font-size: 11px; margin: -9px 0 14px; }
.wh-thint { font-size: 12px; line-height: 1.5; margin-top: 14px; }

/* ============================================================================
 | Warehouse structure canvas (/warehouse/structure) — a pannable, zoomable
 | diagram board. Three stacked layers, and the split matters:
 |   .wh-cv-viewport  clips + owns the dotted grid (its background-position is
 |                    driven from JS so the grid pans WITH the board),
 |   .wh-cv-world     the single transformed element — translate + scale live
 |                    here and nowhere else, so panning/zooming never touches
 |                    a node's own styles,
 |   .wh-cv-node / .wh-cv-links  the content, positioned in world coordinates.
 | Everything else (toolbar, detail panel, legend, minimap) floats ON TOP of
 | the viewport as an overlay rather than taking layout space beside it: the
 | board is the page here, not a widget inside it.
 ========================================================================== */
.wh-cv {
    position: relative; margin: -28px -28px -28px; border-top: 1px solid var(--line);
    background: var(--bg); overflow: hidden;
    /* height is set by JS (fills the viewport under the page head) */
}
@media (max-width: 1100px) { .wh-cv { margin: -18px; } }

.wh-cv-viewport {
    position: absolute; inset: 0; overflow: hidden; touch-action: none;
    cursor: grab; overscroll-behavior: contain;
    background-color: var(--bg);
    background-image: radial-gradient(circle, var(--slate-200) 1px, transparent 1px);
}
.wh-cv-viewport.is-panning { cursor: grabbing; }
.wh-cv-world { position: absolute; top: 0; left: 0; transform-origin: 0 0; will-change: transform; }
.wh-cv-links { position: absolute; overflow: visible; pointer-events: none; }

.wh-cv-link { fill: none; stroke: var(--slate-300); stroke-width: 1.6; }
.wh-cv-link.is-hot { stroke: var(--wh-c, var(--primary)); stroke-width: 2.4; }
.wh-cv-link.is-hidden { display: none; }

/* ---- Nodes ---------------------------------------------------------------
 | Absolutely positioned and moved with transform: translate() only — a drag
 | then costs one composited property per frame instead of a layout pass, and
 | the node keeps its box-shadow/hover styling untouched while it moves. */
.wh-cv-node {
    position: absolute; top: 0; left: 0; box-sizing: border-box;
    display: flex; flex-direction: column; justify-content: center; gap: 3px;
    padding: 9px 12px; border-radius: 6px; cursor: grab;
    background: var(--surface); border: 1px solid var(--line);
    box-shadow: var(--shadow-card); user-select: none;
    transition: box-shadow .15s ease, border-color .15s ease;
}
.wh-cv-node::before {
    content: ''; position: absolute; left: 0; top: 10px; bottom: 10px; width: 3px;
    border-radius: 0 6px 6px 0; background: var(--wh-c, var(--slate-400));
}
.wh-cv-node:hover { border-color: var(--wh-c, var(--primary)); box-shadow: 0 6px 18px -6px rgba(15, 23, 42, .22); }
.wh-cv-node:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.wh-cv-node.is-sel { border-color: var(--wh-c, var(--primary)); box-shadow: 0 0 0 2px color-mix(in srgb, var(--wh-c, #10b981) 35%, transparent), 0 8px 22px -8px rgba(15, 23, 42, .3); }
.wh-cv-node.is-drag { cursor: grabbing; box-shadow: 0 16px 34px -12px rgba(15, 23, 42, .4); z-index: 5; }
/* On the ancestor chain of the selected box — the branch it belongs to. */
.wh-cv-node.is-lit:not(.is-sel) { border-color: color-mix(in srgb, var(--wh-c, #10b981) 55%, var(--line)); }
.wh-cv-node.is-hidden { display: none; }
/* Inactive area — same dashed language as the map's cells. */
.wh-cv-node.is-off { border-style: dashed; opacity: .6; }

.wh-cv-node.is-depot { background: color-mix(in srgb, var(--wh-c, #64748b) 5%, var(--surface)); }
.wh-cv-node.is-etage { flex-direction: row; align-items: center; gap: 8px; padding: 0 12px; border-radius: 6px; }

.wh-cv-head { display: flex; align-items: center; gap: 7px; min-width: 0; }
.wh-cv-code { font-size: 12.5px; font-weight: 800; color: var(--ink); flex: 0 0 auto; }
.is-depot .wh-cv-code { font-size: 13.5px; }
.wh-cv-name { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wh-cv-meta { font-size: 10.5px; color: var(--faint); font-family: var(--font-mono); }
.wh-cv-meta strong { color: var(--text); }
.wh-cv-state { font-size: 10.5px; color: var(--faint); margin-left: auto; white-space: nowrap; }

.wh-cv-dot {
    flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%;
    background: var(--surface); border: 2px solid var(--wh-c, var(--slate-400));
}
.wh-cv-dot.is-fill { background: var(--wh-c, var(--slate-400)); }
.is-off .wh-cv-dot { border-style: dashed; }

.wh-cv-bar { height: 3px; border-radius: 6px; background: var(--line-soft); overflow: hidden; margin-top: 3px; }
.wh-cv-bar i { display: block; height: 100%; border-radius: 6px; background: var(--wh-c, var(--primary)); }

/* Collapse handle — sits on the node's right edge, straddling the border so
   it reads as the point the connectors leave from. */
.wh-cv-chev {
    position: absolute; right: -11px; top: 50%; transform: translateY(-50%);
    width: 22px; height: 22px; border-radius: 50%; padding: 0; cursor: pointer;
    background: var(--surface); border: 1px solid var(--line); color: var(--muted);
}
.wh-cv-chev::before, .wh-cv-chev::after {
    content: ''; position: absolute; left: 50%; top: 50%; background: currentColor;
    transform: translate(-50%, -50%); border-radius: 1px;
}
.wh-cv-chev::before { width: 9px; height: 1.6px; }
.wh-cv-chev::after  { width: 1.6px; height: 9px; transition: opacity .15s ease; }
.wh-cv-node.is-open .wh-cv-chev::after { opacity: 0; }   /* "+" becomes "−" */
.wh-cv-chev:hover { border-color: var(--wh-c, var(--primary)); color: var(--wh-c, var(--primary)); }
/* Vertical board: the branch runs downward, so the handle moves to the edge
   the connectors now leave from. */
.wh-cv-world.is-tb .wh-cv-chev { right: auto; top: auto; left: 50%; bottom: -11px; transform: translateX(-50%); }
/* Orientation control — icon-only buttons in the same segmented group the
   depth control uses, so the two read as one row of settings. */
.wh-cv-tools .wh-depth-btn[data-dir] { display: inline-flex; align-items: center; padding: 6px 9px; }
.wh-cv-tools .wh-depth-btn[data-dir] svg { width: 15px; height: 15px; }

/* Level of detail: zoomed far out, text is unreadable anyway — drop the
   secondary lines and shrink the étages to coloured beads so the SHAPE of
   the warehouse stays legible instead of turning into grey mush. */
.wh-cv-world.is-far .wh-cv-meta,
.wh-cv-world.is-far .wh-cv-bar,
.wh-cv-world.is-far .wh-cv-state,
.wh-cv-world.is-far .wh-cv-chev { display: none; }
.wh-cv-world.is-far .wh-cv-name { display: none; }
.wh-cv-world.is-far .wh-cv-code { font-size: 15px; }
.wh-cv-world.is-far .wh-cv-node.is-etage .wh-cv-code { font-size: 13px; }

/* ---- Floating chrome ---------------------------------------------------- */
.wh-cv-float {
    position: absolute; z-index: 6; background: var(--surface);
    border: 1px solid var(--line); border-radius: 6px;
    box-shadow: 0 10px 30px -12px rgba(15, 23, 42, .28);
}
/* Grip — the only part of the bar that starts a move, so every button on it
   stays a button. */
.wh-cv-grip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 30px; padding: 0; flex: 0 0 auto;
    border: 0; background: transparent; color: var(--slate-300);
    cursor: grab; touch-action: none; border-radius: 6px;
}
.wh-cv-grip:hover { color: var(--muted); background: var(--hover); }
.wh-cv-grip svg { width: 16px; height: 16px; }
.wh-cv-tools.is-moving { cursor: grabbing; box-shadow: 0 18px 40px -14px rgba(15, 23, 42, .38); }
.wh-cv-tools.is-moving .wh-cv-grip { cursor: grabbing; color: var(--primary); }
/* Beats `.wh-cv-tools { display: flex }` on specificity, which a bare
   [hidden] rule would not. */
.wh-cv-tools[hidden] { display: none; }

/* The stand-in while the bar is hidden. */
.wh-cv-toolshow {
    top: 14px; left: 14px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; padding: 0; cursor: pointer;
    color: var(--muted);
}
.wh-cv-toolshow:hover { color: var(--ink); border-color: var(--primary); }
.wh-cv-toolshow[hidden] { display: none; }

.wh-cv-tools { top: 14px; left: 14px; display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 6px; max-width: calc(100% - 270px); }
.wh-cv-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    height: 30px; min-width: 30px; padding: 0 9px; border: 0; border-radius: 8px;
    background: transparent; color: var(--muted); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.wh-cv-btn:hover { background: var(--hover); color: var(--ink); }
.wh-cv-zoom { min-width: 46px; text-align: center; font-size: 12px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.wh-cv-sep { width: 1px; height: 20px; background: var(--line); }
.wh-cv-select {
    height: 30px; padding: 0 8px; border: 1px solid transparent; border-radius: 6px;
    background: transparent; color: var(--muted); font: inherit; font-size: 12.5px; font-weight: 600;
    cursor: pointer; max-width: 130px;
}
.wh-cv-select:hover { background: var(--hover); color: var(--ink); }
.wh-cv-select:focus { outline: none; border-color: var(--primary); }
.wh-cv-tools .wh-depth-group { border-radius: 6px; }
.wh-cv-tools .wh-depth-btn { padding: 6px 10px; font-size: 12px; }

.wh-cv-save { font-size: 11.5px; font-weight: 600; color: var(--faint); padding: 0 4px; min-width: 58px; }
.wh-cv-save.is-saving { color: var(--muted); }
.wh-cv-save.is-saved  { color: var(--success-strong); }
.wh-cv-save.is-failed { color: var(--red-600); }

.wh-cv-legend { left: 14px; bottom: 14px; padding: 8px 12px; }
.wh-cv-legend .wh-legend { gap: 12px; }
/* Warehouse totals, in the toolbar. This is the whole of what the detail panel
   used to show when nothing was selected — one line instead of 272px of
   permanently-reserved width. */
.wh-cv-totals { display: inline-flex; align-items: center; gap: 7px; padding: 0 6px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.wh-cv-totals b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.wh-cv-totals i { width: 3px; height: 3px; border-radius: 50%; background: var(--slate-300); display: inline-block; }

/* Detail panel — present only while a box is selected (`.is-hidden` otherwise),
   and deliberately small: a title, the address, one slim meter and four
   numbers. The donut gauge it used to lead with was five times the height for
   the same one percentage. */
.wh-cv-panel { top: 14px; right: 14px; width: 232px; max-height: calc(100% - 150px); display: flex; flex-direction: column; }
.wh-cv-panel.is-hidden { display: none; }
.wh-cv-panel-body { padding: 12px 13px 13px; overflow-y: auto; }
.wh-cv-panel-btn { width: 100%; justify-content: center; margin-top: 12px; }

.wh-cv-p-head { display: flex; align-items: center; gap: 7px; }
.wh-cv-p-head::before { content: ''; width: 3px; height: 15px; border-radius: 6px; background: var(--wh-c, var(--slate-400)); flex: 0 0 auto; }
.wh-cv-p-code { font-size: 13.5px; font-weight: 800; color: var(--ink); }
.wh-cv-p-name { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wh-cv-p-close {
    margin-left: auto; flex: 0 0 auto; width: 22px; height: 22px; padding: 0; cursor: pointer;
    border: 0; border-radius: 6px; background: transparent; color: var(--faint);
    font-size: 17px; line-height: 1;
}
.wh-cv-p-close:hover { background: var(--hover); color: var(--ink); }
.wh-cv-p-path { font-size: 10.5px; color: var(--faint); margin: 3px 0 10px; overflow: hidden; text-overflow: ellipsis; }

.wh-cv-p-meter { display: flex; align-items: center; gap: 8px; }
.wh-cv-p-track { flex: 1 1 auto; height: 6px; border-radius: 6px; background: var(--line-soft); overflow: hidden; }
.wh-cv-p-track i { display: block; height: 100%; border-radius: 6px; background: var(--wh-c, var(--primary)); }
.wh-cv-p-pct { font-size: 12px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }

.wh-cv-p-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; margin-top: 11px; background: var(--line-soft); border: 1px solid var(--line-soft); border-radius: 6px; overflow: hidden; }
.wh-cv-p-stat { background: var(--surface); padding: 7px 9px; }
.wh-cv-p-stat b { display: block; font-size: 15px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.15; }
.wh-cv-p-stat span { font-size: 10px; color: var(--faint); text-transform: uppercase; letter-spacing: .03em; }

.wh-cv-p-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.wh-cv-p-skus { font-size: 12px; color: var(--muted); }
.wh-cv-p-skus b { color: var(--ink); font-weight: 800; font-variant-numeric: tabular-nums; }

/* Rubber-band selection. Screen-space sibling of .wh-cv-world, so it stays a
   crisp 1px rectangle whatever the zoom is. */
.wh-cv-marquee {
    position: absolute; z-index: 4; pointer-events: none;
    border: 1px solid var(--primary);
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    border-radius: 6px;
}
.wh-cv-marquee[hidden] { display: none; }

/* Multi-select: the panel's group summary + its hint line. */
.wh-cv-p-hint { font-size: 11.5px; line-height: 1.5; color: var(--faint); margin: 10px 0 0; }
.wh-cv-p-head .wh-cv-p-code { white-space: nowrap; }

.wh-cv-mini {
    right: 14px; bottom: 14px; width: 190px; height: 128px; padding: 6px;
    cursor: pointer; touch-action: none; overflow: hidden;
}
.wh-cv-mini-svg { display: block; width: 100%; height: 100%; }
.wh-cv-mini-view {
    position: absolute; border: 1.5px solid var(--primary); border-radius: 6px;
    background: color-mix(in srgb, var(--primary) 10%, transparent); pointer-events: none;
}

@media (max-width: 820px) {
    .wh-cv-panel { display: none; }
    .wh-cv-mini { display: none; }
    .wh-cv-tools { flex-wrap: wrap; max-width: calc(100% - 28px); }
}

/* Hallmark · field shell redesign · genre: utilitarian · macrostructure: Queue + Stop-detail (see design.md "Field shell system") */
/* ============================================================================
 | Field UI — mobile/tablet shell for preparator & driver roles. A slim
 | sticky top bar + centered main column, same as always; above 640px a
 | persistent sidebar sits alongside it (same .nav/.nav-item vocabulary the
 | admin shell uses), collapsing below that to the same off-canvas drawer
 | pattern the admin sidebar already has (.sidebar-backdrop/.is-open) — see
 | design.md "Field shell system" for why "no sidebar" was revisited.
 | Touch-first sizing throughout; base size bumped to 15px (vs. the admin
 | shell's 14px) — arm's-length outdoor reading, not a desk screen.
 ========================================================================== */
.m-body { background: var(--bg); font-size: 15px; }
.m-shell { min-height: 100vh; display: flex; }
.m-sidebar { width: 200px; flex: 0 0 200px; background: var(--surface); border-right: 1px solid var(--line); display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; transition: width .15s ease, flex-basis .15s ease; }
.m-content { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
@media (max-width: 640px) {
    .m-shell { flex-direction: column; }
    .m-sidebar {
        position: fixed; inset: 0 auto 0 0; z-index: 50; width: 240px;
        transform: translateX(-100%); transition: transform .22s cubic-bezier(.16, 1, .3, 1);
        box-shadow: 0 20px 40px -16px rgba(15, 23, 42, .35);
    }
    .m-sidebar.is-open { transform: translateX(0); }
}
.m-topbar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px; min-height: 56px;
    background: var(--surface); border-bottom: 1px solid var(--line);
    padding-top: max(10px, env(safe-area-inset-top));
    margin-bottom: 6px;
}
.m-back {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex: 0 0 auto; margin-left: -6px;
    border-radius: 6px; color: var(--ink);
}
.m-back:active { background: var(--slate-100); }
.m-topbar-title { flex: 1 1 auto; min-width: 0; }
.m-title { font-size: 17px; font-weight: 700; color: var(--ink); line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.m-sub { font-size: 12px; color: var(--muted); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.m-menu { position: relative; flex: 0 0 auto; }
.m-menu-btn { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 8px; color: var(--ink); }
.m-menu-btn:active { background: var(--slate-100); }
.m-menu-pop { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 200px; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; box-shadow: 0 12px 32px rgba(15,23,42,.18); padding: 6px; }
.m-menu-user { padding: 8px 12px; font-weight: 700; color: var(--ink); border-bottom: 1px solid var(--line-soft); margin-bottom: 4px; }
.m-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px 12px; border-radius: 6px; font-size: 15px; color: var(--ink); background: none; border: none; appearance: none; -webkit-appearance: none; font-family: inherit; text-align: left; }
.m-menu-item:hover, .m-menu-item:active { background: var(--slate-100); }
.m-menu-item:focus-visible { outline: none; }
.m-menu-item { cursor: pointer; }
.m-menu-item.is-danger { color: var(--red-600); }
.m-menu-item.is-danger:hover, .m-menu-item.is-danger:active { background: var(--red-50); }
.m-menu-sep { height: 1px; background: var(--line-soft); margin: 4px 2px; }
/* An entry that has nothing to open yet (a BC's BL / facture before upload): greyed, inert. */
.m-menu-item:disabled { color: var(--muted); opacity: .55; cursor: not-allowed; }
.m-menu-item:disabled:hover { background: none; }
.m-menu-count { margin-left: auto; min-width: 20px; padding: 1px 7px; border-radius: 999px; background: var(--primary-soft); color: var(--primary-strong); font-size: 12px; font-weight: 700; text-align: center; }
.m-menu-note { margin-left: auto; padding-left: 12px; font-size: 12px; color: var(--muted); white-space: nowrap; }

/* BC list — BL fournisseur / facture viewer (modules/supplier_orders/pages/index.php). */
.so-docs-modal { width: min(880px, 100%); }
.so-docs-head-text { min-width: 0; }
.so-docs-files { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.so-docs-file.is-active { border-color: var(--primary); color: var(--primary-strong); background: var(--primary-soft); }
.so-docs-preview {
  display: flex; align-items: center; justify-content: center;
  height: min(68vh, 640px); overflow: auto;
  border: 1px solid var(--line-soft); border-radius: 6px; background: var(--slate-50);
}
.so-docs-preview iframe { width: 100%; height: 100%; border: 0; background: var(--white); }
.so-docs-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.so-docs-nopreview { padding: 24px; text-align: center; }
@media (max-width: 720px) {
  .so-docs-preview { height: 58vh; }
  .so-docs-modal .wh-modal-actions { flex-wrap: wrap; }
  .so-docs-modal .wh-modal-actions .btn { flex: 1 1 auto; justify-content: center; }
}
.lv-offline-banner {
    display: flex; align-items: center; justify-content: center; gap: 7px;
    background: var(--amber-50); color: var(--amber-700);
    font-size: 12.5px; font-weight: 700;
    padding: 7px 14px; padding-top: max(7px, env(safe-area-inset-top));
    border-bottom: 1px solid var(--line);
}
.m-main { flex: 1 1 auto; padding: 14px; padding-bottom: max(14px, env(safe-area-inset-bottom)); max-width: 720px; width: 100%; margin: 0 auto; }
/* Above phone/small-tablet width there's real desk space to use — most
   screens here are lists/tables (order queue, pick list, receiving inbox)
   that read better with more room, not a form stretched thin. `.m-main--narrow`
   (set via layout_min_header(['narrow' => true])) opts a single sequential
   form or fixed-shape widget (scan card, stop detail, calendar) out of the
   widen, so it stays a comfortable reading column instead of sprawling. */
@media (min-width: 860px) {
    .m-main { max-width: 1120px; padding: 22px 32px; }
}
.m-main--narrow { max-width: 640px; }
@media (min-width: 860px) {
    .m-main--narrow { max-width: 640px; padding: 22px 28px; }
}

/* History (driver) — a plain table of one month's delivered stops, with a
   prev/next month nav bar sharing the card's own `.card-head`/`.card-title`. */
.lv-hist-nav-btn {
    display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
    border-radius: 8px; color: var(--muted); transition: background .15s, color .15s; flex: 0 0 auto;
}
.lv-hist-nav-btn:hover { background: var(--primary-soft); color: var(--primary-strong); }
.lv-hist-nav-btn:active { background: var(--emerald-100, #d1fae5); }
/* Force horizontal scroll (via .table-wrap) instead of the browser wrapping
   "Customer"/"Delivered at" onto 2-3 lines on a driver's narrow phone. */
.lv-hist-table { min-width: 520px; }
.lv-hist-table td, .lv-hist-table th { white-space: nowrap; }

/* Preparator board (pages/index.php) — a quiet inline stat, still not a
   dedicated big-number block (see the zone-page note below for why that
   was reverted), but now a pill integrated into the tab row instead of a
   stray line floating above it — a positioning fix, not a weight one. */
.ps-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.ps-toolbar .m-tabs { flex: 1 1 auto; min-width: 240px; margin-bottom: 0; }
.ps-today {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
    font-size: 12.5px; font-weight: 700; color: var(--primary-strong);
    background: var(--primary-soft); padding: 7px 12px; border-radius: 999px; white-space: nowrap;
}
.ps-today strong { font-weight: 800; font-variant-numeric: tabular-nums; }
.ps-actionbar { display: flex; gap: 10px; margin-bottom: 14px; }
.ps-actionbar .ps-search { flex: 1 1 auto; margin-bottom: 0; }
.ps-actionbar .ps-zone-btn { flex: 0 0 auto; width: auto; margin-bottom: 0; white-space: nowrap; }
@media (max-width: 560px) {
    .ps-actionbar { flex-direction: column; }
    .ps-actionbar .ps-zone-btn { width: 100%; justify-content: center; }
}

/* Tabs (field board) */
.m-tabs { display: flex; gap: 8px; margin-bottom: 14px; }
/* On a phone three tabs with their count pills don't fit one row (/receiving
   ran 191px past the screen) — let them wrap, each sized to its own label. */
@media (max-width: 560px) {
    .m-tabs { flex-wrap: wrap; }
    .m-tabs .m-tab { flex: 1 1 auto; }
}
.m-tab { flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 11px 10px; font-weight: 600; font-size: 14px; color: var(--muted); background: var(--surface); border: 1px solid var(--line); border-radius: 8px; }
.m-tab.active { color: var(--primary-strong); border-color: var(--primary); background: var(--primary-soft); }
.m-tab .count { font-size: 12px; background: rgba(100,116,139,.16); border-radius: 999px; padding: 1px 8px; }
.m-tab.active .count { background: rgba(16,185,129,.20); color: var(--primary-strong); }

.m-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; color: var(--muted); padding: 40px 16px; }
.m-empty[hidden] { display: none; }
.m-empty-ico { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; background: var(--slate-100); color: var(--faint); }
.m-empty-ico .icon { width: 18px; height: 18px; }

/* Pick view — sticky scan + counters */
.scan-msg { font-size: 12px; min-height: 16px; margin-top: 6px; }
.scan-msg.is-ok  { color: var(--success-600, #059669); }
.scan-msg.is-err { color: var(--red-600, #dc2626); }
.scan-msg.is-warn { color: var(--amber-700); }

/* Pick rows (mobile cards) */
.pick-qty { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); font-size: 15px; }

/* Pick — quantity controls (scan batch qty + per-line scanned count) */
.pick-qtybox { flex: 0 0 auto; display: inline-flex; align-items: baseline; gap: 4px; }

/* ----------------------------------------------------------------------
 | Scan console — admin-shell native (pick.php, zone.php after their rebuild
 | onto the admin shell). Deliberately its own component, not the field
 | shell's .scan-bar reused verbatim: same 1px border / 10px radius / 14px
 | type as .input and .catalog-combobox-control, so it reads as "a normal
 | field that happens to take a scan" rather than a mobile widget dropped
 | into a desktop card. The monospace type on the input is the one
 | deliberate difference — a code, not a word, so it earns tabular/mono
 | treatment the way a reference or SKU does elsewhere in this app.
 |--------------------------------------------------------------------- */
.scan-console { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.scan-input {
    flex: 1 1 320px; min-width: 240px; display: flex; align-items: center; gap: 10px;
    border: 1px solid var(--slate-300); border-radius: 6px; background: var(--surface);
    padding: 0 14px; transition: border-color .15s ease, box-shadow .15s ease;
}
.scan-input:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.scan-input.is-ok  { border-color: var(--success); box-shadow: 0 0 0 3px var(--success-soft); }
.scan-input.is-err { border-color: var(--red-400); box-shadow: 0 0 0 3px rgba(239, 68, 68, .1); }
.scan-input.is-warn { border-color: var(--amber-600); box-shadow: 0 0 0 3px var(--amber-50); }
.scan-input > .icon { color: var(--faint); flex: 0 0 auto; transition: color .15s ease; }
.scan-input:focus-within > .icon { color: var(--primary); }
.scan-input input {
    flex: 1 1 auto; min-width: 0; border: 0; outline: 0; background: none;
    padding: 10px 0; font-size: 14px; color: var(--text);
    font-family: var(--font-mono); letter-spacing: .02em;
}
.scan-input input::placeholder {
    font-family: var(--font-sans);
    letter-spacing: normal; color: var(--faint);
}
.scan-cam-toggle {
    flex: 0 0 auto; width: 40px; height: 40px; border-radius: 8px;
    border: 1px solid var(--slate-300); background: var(--surface); color: var(--slate-600);
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.scan-cam-toggle:hover { background: var(--hover); border-color: var(--slate-400); color: var(--ink); }
.scan-cam-toggle.is-on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-strong); }
/* A short wide strip, not a tall portrait box: a barcode is wide, and the
   pick list must stay visible under the camera on a phone. */
.scan-cam-wrap { max-width: 320px; aspect-ratio: 2 / 1; margin-top: 10px; }
.scan-cam-wrap .lv-scan-reticle { inset: 22% 10%; }
/* The table row a scan just landed on — a brief emerald wash, so the eye
   catches which line moved without the row itself changing shape. */
@keyframes scan-hit { from { background: var(--primary-soft); } to { background: transparent; } }
tr.is-scan-hit > td { animation: scan-hit 1s ease both; }

/* ----------------------------------------------------------------------
 | Pick screen camera + found sheet (pick.php, 2026-10-13). The camera is
 | the main way in on a phone, so its button carries a word, not just an
 | icon. The view stays open from one product to the next: white corner
 | brackets + a sweeping red line while it looks (the scanner-gun laser
 | every preparator knows), green brackets while the sheet is up. Messages
 | sit on the picture itself, where the eyes are.
 |--------------------------------------------------------------------- */
/* Phone only — see the 900px block (pick.php's progress strip). */
.pick-progress { display: none; }
.pk-cam-btn {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    min-height: 42px; padding: 0 16px; border-radius: 8px; cursor: pointer;
    border: 1px solid var(--primary); background: var(--primary); color: var(--white);
    font: inherit; font-size: 14px; font-weight: 600; transition: background .15s ease, color .15s ease;
}
.pk-cam-btn:hover { background: var(--primary-strong); border-color: var(--primary-strong); }
.pk-cam-btn.is-on, .pk-cam-btn.is-on:hover { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-strong); }
.pk-cam {
    --pk-bracket: #fff;
    position: relative; width: 100%; max-width: 560px; aspect-ratio: 4 / 3; max-height: 58vh;
    margin-top: 12px; border-radius: 10px; overflow: hidden; background: var(--slate-900);
}
.pk-cam.is-paused { --pk-bracket: var(--success); }
.pk-cam video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.pk-cam-frame {
    position: absolute; inset: 26% 8%; border-radius: 4px; pointer-events: none;
    box-shadow: 0 0 0 999px rgba(15, 23, 42, .42);
    background:
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) top left / 30px 4px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) top left / 4px 30px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) top right / 30px 4px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) top right / 4px 30px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) bottom left / 30px 4px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) bottom left / 4px 30px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) bottom right / 30px 4px no-repeat,
        linear-gradient(var(--pk-bracket), var(--pk-bracket)) bottom right / 4px 30px no-repeat;
}
.pk-cam-laser {
    position: absolute; left: 5%; right: 5%; top: 50%; height: 2px; border-radius: 2px;
    background: #ef4444; box-shadow: 0 0 10px 1px rgba(239, 68, 68, .75);
}
.pk-cam.is-paused .pk-cam-laser { display: none; }
@media (prefers-reduced-motion: no-preference) {
    .pk-cam-laser { animation: pk-laser 1.6s ease-in-out infinite alternate; }
}
@keyframes pk-laser { from { top: 14%; } to { top: 86%; } }
.pk-cam-close {
    position: absolute; top: 10px; inset-inline-end: 10px; width: 42px; height: 42px; border: 0; border-radius: 999px;
    display: grid; place-items: center; cursor: pointer; background: rgba(15, 23, 42, .6); color: #fff;
}
.pk-cam-close .icon { width: 20px; height: 20px; }
.pk-cam-hint {
    position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
    width: max-content; max-width: calc(100% - 24px); padding: 6px 12px; border-radius: 12px;
    background: rgba(15, 23, 42, .66); color: #fff; font-size: 13px; font-weight: 600; line-height: 1.35; text-align: center;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.pk-cam-hint.is-ok   { background: var(--success); }
.pk-cam-hint.is-err  { background: var(--red-600); }
.pk-cam-hint.is-warn { background: var(--amber-600); }

/* The found sheet — slides up from the bottom on a phone, a centred card
   on a computer. Big − / + and a big number: counted with a thumb, read
   at arm's length. Continue is green when everything was found (the line
   is done — green means done), red when it reports a shortage. */
.pk-sheet-backdrop {
    position: fixed; inset: 0; z-index: 150; display: flex; align-items: flex-end; justify-content: center;
    background: rgba(15, 23, 42, .45); opacity: 0; transition: opacity .18s ease;
}
.pk-sheet-backdrop.is-open { opacity: 1; }
.pk-sheet {
    display: flex; flex-direction: column; gap: 12px;
    width: min(460px, 100%); max-height: 92vh; overflow-y: auto;
    padding: 10px 18px calc(18px + env(safe-area-inset-bottom));
    background: var(--surface); border-radius: 16px 16px 0 0; box-shadow: 0 -12px 40px rgba(15, 23, 42, .25);
    transform: translateY(28px); transition: transform .2s ease;
}
.pk-sheet::before { content: ''; align-self: center; width: 40px; height: 4px; border-radius: 999px; background: var(--slate-300); }
.pk-sheet-backdrop.is-open .pk-sheet { transform: none; }
@media (min-width: 901px) {
    .pk-sheet-backdrop { align-items: center; padding: 20px; }
    .pk-sheet { border-radius: 12px; padding-top: 18px; }
    .pk-sheet::before { display: none; }
}
/* "Big photo" (picked 2026-10-13): photo, name and location centred on
   top; then one count — found / needed, always in that left-to-right
   order like the lines' 3/5 — between big − / + buttons. The found number
   turns green when it covers the need, red when something is missing. */
.pk-sheet { align-items: stretch; text-align: center; }
.pk-sheet-photo {
    align-self: center; width: 148px; height: 148px; border-radius: 16px; object-fit: contain;
    border: 1px solid var(--line); background: var(--white); padding: 6px;
}
.pk-sheet-name { font-size: 18px; font-weight: 700; line-height: 1.3; color: var(--ink); overflow-wrap: anywhere; margin-top: 2px; text-wrap: balance; }
.pk-sheet-loc { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; margin-top: -4px; }
.pk-sheet-loc .pill::before { display: none; }   /* a place, not a status */
.pk-sheet-already { font-size: 13px; color: var(--muted); margin-top: -4px; }
.pk-sheet-already:empty { display: none; }
.pk-found-stepper { display: grid; grid-template-columns: 76px minmax(0, 1fr) 76px; align-items: center; gap: 8px; margin-top: 4px; }
.pk-found-step {
    height: 76px; border-radius: 16px; border: 1px solid var(--slate-300); background: var(--surface); color: var(--ink);
    display: grid; place-items: center; cursor: pointer; touch-action: manipulation;
}
.pk-found-step .icon { width: 30px; height: 30px; stroke-width: 2.5; }
.pk-found-step:active:not(:disabled) { background: var(--hover); }
.pk-found-step:disabled { opacity: .3; cursor: not-allowed; }
.pick-found-count {
    direction: ltr; min-width: 0; display: grid; grid-template-columns: auto auto auto;
    justify-content: center; align-items: baseline; column-gap: 8px; row-gap: 4px;
}
.pick-found-count .pick-found-input {
    width: 2.6ch; min-width: 0; padding: 0 2px; text-align: center; background: none;
    border: 0; border-bottom: 3px solid var(--slate-300); border-radius: 0;
    font-size: 46px; font-weight: 800; line-height: 1.05; color: var(--ink); font-variant-numeric: tabular-nums;
    -moz-appearance: textfield; appearance: textfield;
}
.pick-found-count .pick-found-input::-webkit-outer-spin-button, .pick-found-count .pick-found-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pick-found-count .pick-found-input:focus { outline: 0; border-bottom-color: var(--primary); }
.pick-found-count.is-full .pick-found-input { color: var(--success); border-bottom-color: var(--success); }
.pick-found-count.is-short .pick-found-input { color: var(--red-600); border-bottom-color: var(--red-600); }
.pick-found-sep, .pick-found-need { font-size: 34px; font-weight: 700; color: var(--faint); font-variant-numeric: tabular-nums; }
.pick-found-label { text-align: center; font-size: 12.5px; font-weight: 600; color: var(--muted); white-space: nowrap; }
@media (max-height: 700px) {
    .pk-sheet-photo { width: 104px; height: 104px; }
    .pk-found-step { height: 64px; }
}
.pk-sheet-warn {
    display: flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 12px; border-radius: 8px;
    background: var(--red-50); color: var(--red-600); font-size: 14px; font-weight: 600;
}
.pk-sheet-warn .icon { flex: 0 0 auto; width: 18px; height: 18px; }
.btn.btn-primary.pk-continue {
    min-height: 56px; justify-content: center; font-size: 16px; font-weight: 700;
    background: var(--success); border-color: var(--success); color: var(--white);
}
.btn.btn-primary.pk-continue:hover { background: var(--success-strong); border-color: var(--success-strong); color: var(--white); }
.btn.btn-primary.pk-continue.is-short,
.btn.btn-primary.pk-continue.is-short:hover { background: var(--red-600); border-color: var(--red-600); color: var(--white); }
.btn.btn-primary.pk-continue.is-short:hover { filter: brightness(.93); }
.btn.btn-ghost.pk-cancel { min-height: 44px; justify-content: center; color: var(--muted); }

/* Pick screen — per-line "Confirm" (server-persisted, see pick.php + migration 0079). */
/* .btn prefix so these outweigh the skin's `body.skin-shadcn .btn-*` (shadcn.css):
   "Valider" solid green, the confirmed line a soft green. */
.btn.btn-primary.pick-confirm-btn { background: var(--success); border-color: var(--success); color: var(--white); }
.btn.btn-primary.pick-confirm-btn:hover { background: var(--success-strong); border-color: var(--success-strong); color: var(--white); }
.btn.pick-confirm-btn.is-confirmed,
.btn.pick-confirm-btn.is-confirmed:hover { background: var(--success-soft); border-color: var(--success); color: var(--success-strong); }
/* One clear action per card (pick.php's actions cell): "Rupture de stock" is
   red so it never reads as the go-ahead; a finished line shows a green "Done"
   badge, a reported shortage a red "N missing" one — nothing to tap but Undo. */
.btn.btn-outline.pick-short-btn { color: var(--red-600); border-color: var(--red-400); }
.btn.btn-outline.pick-short-btn:hover { background: var(--red-50); color: var(--red-600); border-color: var(--red-400); }
.pick-state {
    display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 999px;
    font-size: 14px; font-weight: 700; white-space: nowrap;
}
.pick-state.is-done  { background: var(--success-soft); color: var(--success-strong); }
.pick-state.is-short { background: var(--red-50); color: var(--red-600); }
tr.is-confirmed > td,
.table-wrap .table tbody tr.is-confirmed > td:last-child { background: var(--success-soft); }
/* Pick screen — the line's "scanned/ordered" count (0/5 → 1/5 …), no input
   box: the scanned part goes amber while partial, green once complete, and
   bumps on each scan. */
.pl-count { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink); }
.pl-count .pl-done { color: var(--faint); }
.pl-count.is-part .pl-done { color: var(--amber-600); }
.pl-count.is-full, .pl-count.is-full .pl-done { color: var(--success-600); }
@media (prefers-reduced-motion: no-preference) {
    .pl-count { display: inline-block; }
    tr.is-scan-hit .pl-count { animation: pl-count-bump .35s ease; }
}
@keyframes pl-count-bump { 40% { transform: scale(1.25); } }
/* The location is a place, not a status: pin + code, no dot. */
.pick-lines .pl-loc .pill::before { display: none; }

/* Pick lines on a laptop (2026-10-15 redesign): plain white rows — no
   zebra, no green "done" tint, no start-edge bar (removed on request); the
   "Terminé" chip and the green count say a line is done. A reported
   shortage keeps its light red. The colour sits on the row, not the cells,
   so the scan flash (a td animation) fades back into it. The count is a
   small box, centred; photo and name sit together. Phones keep their cards. */
@media (min-width: 901px) {
  body .table-wrap .table.pick-lines > tbody > tr[data-line] > td,
  body .table-wrap .table.pick-lines > tbody > tr[data-line]:hover > td { background: transparent; }
  body .table-wrap .table.pick-lines > tbody > tr[data-line] > td { padding-top: 12px; padding-bottom: 12px; }
  body .table-wrap .table.pick-lines > tbody > tr[data-line] { background: var(--surface); }
  body .table-wrap .table.pick-lines > tbody > tr[data-line]:hover { background: var(--slate-50); }
  body .table-wrap .table.pick-lines > tbody > tr[data-short="1"]:not(.is-confirmed) { background: var(--red-50); }
  /* The table fits its card: no pinned actions column (its shadow drew a line). */
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-actions,
  [dir="rtl"] body .table-wrap .table.pick-lines > tbody > tr > td.pl-actions,
  body .table-wrap .table.pick-lines:has(tbody td:last-child > .row-actions) > thead > tr > th:last-child,
  [dir="rtl"] body .table-wrap .table.pick-lines:has(tbody td:last-child > .row-actions) > thead > tr > th:last-child { position: static; box-shadow: none; }
  body .table.pick-lines > thead > tr > th.text-center { text-align: center; }

  body .table-wrap .table.pick-lines > tbody > tr > td.pl-photo { width: 1%; padding-inline-end: 4px; }
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-product {
    min-width: 220px; white-space: normal; text-wrap: balance;
    font-size: 14px; font-weight: 600; line-height: 1.35; color: var(--ink);
  }
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-product .pill { margin-top: 6px; font-weight: 500; }
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-qty { width: 140px; }
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-loc { width: 200px; }
  body .table-wrap .table.pick-lines > tbody > tr > td.pl-actions { width: 1%; }
  .pick-lines .pl-loc .pill { padding: 3px 10px; font-size: 12.5px; }

  /* Count: "0 / 5" in a small box — amber while partial, green when complete. */
  .table.pick-lines .pl-count {
    display: inline-flex; justify-content: center; min-width: 68px; padding: 5px 12px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface); font-size: 15px;
  }
  .table.pick-lines .pl-count.is-part { border-color: var(--rl-amber-line, #fcd34d); }
  .table.pick-lines .pl-count.is-full { border-color: var(--rl-green-line, var(--success-200)); }

  /* State chips sit white on the tinted row, so they still read as chips. */
  .pick-lines .pl-actions .pick-state { padding: 5px 11px; font-size: 13px; background: var(--surface); }
  .pick-lines .pl-actions .pick-state.is-done  { box-shadow: inset 0 0 0 1px var(--rl-green-line, var(--success-200)); }
  .pick-lines .pl-actions .pick-state.is-short { box-shadow: inset 0 0 0 1px var(--rl-red-line, var(--red-400)); }
  .pick-lines .pl-actions .btn.pick-edit-btn,
  .pick-lines .pl-actions .btn.pick-unshort-btn { color: var(--muted); }
  .pick-lines .pl-actions .btn.pick-edit-btn:hover,
  .pick-lines .pl-actions .btn.pick-unshort-btn:hover { color: var(--ink); background: var(--surface); }
}


/* Board filters (préparateur, expéditeur — board-filters.js): search full width on
   a phone, then the filters (each with its own small label), side by side when there's room. */
.board-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 10px; padding: 12px 14px; border-bottom: 1px solid var(--line-soft); }
.board-filter-search { flex: 2 1 220px; max-width: none; }
.board-filter { display: flex; flex-direction: column; gap: 3px; flex: 1 1 150px; min-width: 0; }
.board-filter-label { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: var(--muted); }
.board-filter-label .icon { width: 12px; height: 12px; }
.board-filter .filter-select { width: 100%; }
.board-filter .datepicker-wrap { width: 100%; }
.board-filter .datepicker-input { width: 100%; height: 38px; }
.board-filter-reset { flex: 0 0 auto; }
@media (max-width: 600px) {
  .board-filter-search { flex-basis: 100%; }
  .board-filter { flex-basis: calc(50% - 5px); }
}
/* Order boards (préparateur, expéditeur): .job-table is a plain table on a
   computer and, under 900px, one card per row (on a phone the table hid half
   its columns): reference + its state (.jr-tag) on top, the facts with their
   labels (.jr-label, phone only), then the full-width tone button (.jr-go).
   The row is the link (table-tools data-table-row-links). auto-fit cards, not
   auto-fill: a lone card takes the row (design.md "Card material"). CSS only —
   the same rows are filtered / paged by table-tools on both. `body` prefix: the
   shadcn skin's table rules load later with equal weight. */
.job-table .jr-label, .job-table .job-card-ico { display: none; }
.job-table .jr-value .exp-zone-tag { font-size: 12px; padding: 1px 7px; }
.job-card-ico {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 24px; height: 24px; border-radius: 6px; background: var(--primary-soft); color: var(--primary-strong);
}
.job-card-go { width: 100%; justify-content: center; min-height: 34px; padding-top: 6px; padding-bottom: 6px; font-size: 14px; }
.job-table .job-card-go { width: auto; min-height: 0; padding-top: 4px; padding-bottom: 4px; font-size: 13px; }
[dir="rtl"] .job-card-go .icon { transform: scaleX(-1); }
@media (max-width: 900px) {
  body .table.job-table > thead { display: none; }
  body .table.job-table { display: block; width: 100%; }
  body .table.job-table > tbody { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 8px; padding: 10px; }
  body .table.job-table > tbody > tr { display: block; }
  body .table.job-table > tbody > tr.job-row {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px 12px; align-items: center;
    padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px;
    background: var(--surface); box-shadow: var(--shadow-card); color: var(--ink);
    transition: border-color .15s ease, box-shadow .15s ease;
  }
  body .table.job-table > tbody > tr[hidden] { display: none; }
  body .table.job-table > tbody > tr.job-row:hover { border-color: var(--primary); box-shadow: 0 4px 14px rgba(15, 23, 42, .08); background: var(--surface); }
  body .table.job-table > tbody > tr.job-row.tmr-warn   { border-color: var(--amber-600); background: linear-gradient(var(--amber-50), var(--surface) 60%); }
  body .table.job-table > tbody > tr.job-row.tmr-danger { border-color: var(--red-400); background: linear-gradient(var(--red-50), var(--surface) 60%); }
  body .table.job-table > tbody > tr > td { display: block; padding: 0; border: 0; background: none; box-shadow: none; position: static; text-align: start; white-space: normal; font-size: 13px; min-width: 0; }
  body .table.job-table > tbody > tr > td[colspan] { padding: 24px 0; text-align: center; }
  /* Top line: reference (left) and its state — timer / progress / status (right). */
  /* The reference takes the whole top line; its state sits over the line's end,
     so the padding keeps the reference clear of it. */
  body .table.job-table > tbody > tr > td.jr-ref { grid-column: 1 / -1; grid-row: 1; display: flex; align-items: center; gap: 8px; padding-inline-end: 112px; }
  body .table.job-table > tbody > tr > td.jr-ref .job-card-ico { display: inline-flex; }
  body .table.job-table > tbody > tr > td.jr-ref a { font-size: 15px; font-weight: 800; letter-spacing: -.01em; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  body .table.job-table > tbody > tr > td.jr-tag { grid-column: 2; grid-row: 1; justify-self: end; display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }
  /* The facts: "label  value", two per line; a value too long drops under its label. */
  body .table.job-table > tbody > tr > td.jr-f { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; }
  body .table.job-table > tbody > tr > td.jr-f.is-wide { grid-column: 1 / -1; }
  body .table.job-table .jr-label { display: inline-flex; align-items: center; gap: 4px; flex: none; font-family: var(--font-sans); font-size: 12px; font-weight: 500; color: var(--muted); white-space: nowrap; }
  body .table.job-table .jr-label .icon { width: 13px; height: 13px; }
  body .table.job-table .jr-value { max-width: 100%; font-weight: 700; color: var(--ink); overflow-wrap: break-word; }
  /* The card's button takes the free half of the last fact line (right side)
     instead of a full-width bar of its own; an empty "—" fact is left out. */
  body .table.job-table > tbody > tr > td.jr-go { grid-column: 2; justify-self: end; align-self: end; }
  body .table.job-table .job-card-go { width: auto; min-height: 36px; padding: 6px 14px; font-size: 14px; }
  body .table.job-table > tbody > tr > td.jr-f.is-empty { display: none; }
  /* A finished order: just its total time, not the start → end range. */
  body .table.job-table .tl-done .tl-range { display: none; }
  body .table.job-table > tbody > tr.is-done .job-card-ico { background: var(--slate-100); color: var(--muted); }
}

/* The three states, three colours: blue À préparer, amber En cours, green Préparées.
   Tab text/icon carry the tone; the selected tab fills with it. */
.m-tab.job-tab.tone-blue  { color: var(--blue-700); }
.m-tab.job-tab.tone-amber { color: var(--amber-700); }
.m-tab.job-tab.tone-green { color: var(--success-700); }
.m-tab.job-tab.tone-blue.active  { background: var(--blue-50);    border-color: var(--blue-600);    color: var(--blue-700); }
.m-tab.job-tab.tone-amber.active { background: var(--amber-50);   border-color: var(--amber-600);   color: var(--amber-700); }
.m-tab.job-tab.tone-green.active { background: var(--success-50); border-color: var(--success-600); color: var(--success-700); }
/* The row / card button takes its tab's tone (also a table-cards row's phone-only button). */
.tone-blue  > .job-card-go { background: var(--blue-600);   border-color: var(--blue-600);    color: var(--white); }
.tone-amber > .job-card-go { background: var(--amber-600);  border-color: var(--amber-600);   color: var(--white); }
.tone-green > .job-card-go { background: var(--success-50); border-color: var(--success-600); color: var(--success-700); }
.tone-blue  > .job-card-go:hover { background: var(--blue-700); border-color: var(--blue-700); }
.tone-amber > .job-card-go:hover { background: var(--amber-700); border-color: var(--amber-700); }
.job-row.tone-blue  .job-card-ico { background: var(--blue-50);    color: var(--blue-700); }
.job-row.tone-amber .job-card-ico { background: var(--amber-50);   color: var(--amber-700); }
.job-row.tone-green .job-card-ico { background: var(--success-50); color: var(--success-700); }

/* Table → cards on a phone — the expéditeur's and réceptionnaire's screens
   (they work from a phone most of the time). Add .table-cards to a .table and
   data-label="…" to its cells: under 900px each row is a card whose cells read
   "Label …… value", one per line. Cell roles:
     .tc-title        the row's name (product / reference): bold, no label
     .tc-photo        a thumbnail, beside the title
     .tc-half         a short fact — two sit side by side
     .tc-tag          a status pill, moved up beside the title
     .tc-block        label above, content full width (inputs, pickers)
     .tc-actions      buttons / forms, full width
     .tc-mobile-only  shown only as a card (e.g. an "Open" button)
     .tc-desktop-only left out of the card (a detail only the table needs)
   A td[colspan] row (empty state) stays one plain block; a .table-filter-row
   in the head becomes a toolbar above the cards. CSS only — the markup stays a
   table, so every script keeps its tr/td lookups. `body` prefix: the shadcn
   skin's `body.skin-shadcn .table tbody td` loads later with equal weight. */
.table td.tc-mobile-only, .table th.tc-mobile-only { display: none; }
@media (max-width: 900px) {
  body .table.table-cards > thead > tr:not(.table-filter-row) { display: none; }
  body .table.table-cards, body .table.table-cards > thead, body .table.table-cards > tbody { display: block; width: 100%; }
  body .table.table-cards > tbody > tr {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
    padding: 12px 14px; border-bottom: 1px solid var(--line-soft);
  }
  body .table.table-cards > tbody > tr[hidden] { display: none; }
  /* Line break after the title line (photo · title · tag, ordered first below):
     the facts always start on their own line, never squeezed beside the name. */
  body .table.table-cards > tbody > tr::before { content: ""; order: 0; flex: 0 0 100%; height: 0; }
  body .table.table-cards > tbody > tr:not(:has(> .tc-title))::before { display: none; }
  body .table.table-cards > tbody > tr:has(> td[colspan]) { display: block; }
  body .table.table-cards > tbody > tr > td {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex: 1 1 100%; min-width: 0; position: static;
    padding: 0; border: 0; background: none; box-shadow: none;
    text-align: end; white-space: normal; overflow-wrap: anywhere; font-size: 14px;
  }
  body .table.table-cards > tbody > tr > td[colspan] { display: block; padding: 16px 0; text-align: center; }
  body .table.table-cards > tbody > tr > td[data-label]::before {
    content: attr(data-label); flex: none; max-width: 50%;
    font-family: var(--font-sans);
    font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--muted); text-align: start;
  }
  body .table.table-cards > tbody > tr > td.tc-title { flex: 1 1 0; justify-content: flex-start; text-align: start; font-size: 15px; font-weight: 700; color: var(--ink); }
  body .table.table-cards > tbody > tr > td.tc-photo { flex: 0 0 auto; order: -3; }
  body .table.table-cards > tbody > tr > td.tc-title { order: -2; }
  /* A reference in the title never breaks ("CF- / 2026- / 0085"): the pill
     or button beside it moves down a line instead. */
  body .table.table-cards > tbody > tr > td.tc-title { overflow-wrap: break-word; min-width: min-content; }
  body .table.table-cards > tbody > tr > td.tc-title .mono { white-space: nowrap; }
  /* .tc-tag: a status pill pulled up to the end of the title line, no label. */
  body .table.table-cards > tbody > tr > td.tc-tag { flex: 0 0 auto; order: -1; margin-inline-start: auto; }
  body .table.table-cards > tbody > tr > td.tc-photo .cell-thumb { width: 48px; height: 48px; }
  body .table.table-cards > tbody > tr > td.tc-half { flex: 1 1 calc(50% - 6px); }
  body .table.table-cards > tbody > tr > td.tc-actions { flex-wrap: wrap; justify-content: stretch; gap: 8px; }
  body .table.table-cards > tbody > tr > td.tc-actions > *,
  body .table.table-cards > tbody > tr > td.tc-actions .row-actions > *,
  body .table.table-cards > tbody > tr > td.tc-actions form .btn { flex: 1 1 auto; }
  body .table.table-cards > tbody > tr > td.tc-actions .row-actions { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; }
  body .table.table-cards > tbody > tr > td.tc-actions .btn { width: 100%; min-height: 40px; justify-content: center; }
  body .table.table-cards > tbody > tr > td.tc-mobile-only { display: flex; }
  body .table.table-cards > tbody > tr > td.tc-desktop-only,
  body .table.table-cards > tbody > tr > td .tc-desktop-only { display: none; }
  /* A cell whose content needs the full width (inputs + hints, a position picker):
     label on top, content below. */
  body .table.table-cards > tbody > tr > td.tc-block { flex-direction: column; align-items: stretch; text-align: start; gap: 4px; }
  body .table.table-cards > tbody > tr > td.tc-block .input { max-width: none; }
  body .table.table-cards > tbody > tr > td.tc-icons { flex: 0 0 auto; margin-inline-start: auto; }
  /* A labelled fact with nothing in it (no lot, no note) is left out of the card. */
  body .table.table-cards > tbody > tr > td[data-label]:empty { display: none; }
  /* A table foot (an order total) reads as one "label …… value" line. */
  body .table.table-cards > tfoot { display: block; }
  body .table.table-cards > tfoot > tr { display: flex; justify-content: space-between; padding: 12px 14px; }
  body .table.table-cards > tfoot > tr > td { display: block; padding: 0; border: 0; }
  body .table.table-cards > tfoot > tr > td:empty { display: none; }
  body .table.table-cards > tbody > tr > td .input,
  body .table.table-cards > tbody > tr > td select { max-width: 62%; }
  body .table.table-cards > tbody > tr:hover { background: none; }
  body .table.table-cards > tbody > tr.is-confirmed { background: var(--success-soft); }
  /* The filter row of a list's head becomes a toolbar above its cards. */
  body .table.table-cards > thead > tr.table-filter-row {
    display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--line);
  }
  body .table.table-cards > thead > tr.table-filter-row > th { display: block; flex: 1 1 140px; min-width: 0; padding: 0; border: 0; height: auto; }
  body .table.table-cards > thead > tr.table-filter-row > th:empty { display: none; }
  body .table.table-cards > thead > tr.table-filter-row > th:first-child { flex-basis: 100%; }
  /* A hidden tbody (a "no match" message) stays hidden — the display:block
     above would otherwise show it. */
  body .table.table-cards > tbody[hidden] { display: none; }
  /* The desktop's sticky actions column is not pinned on a card. */
  body .table-wrap .table.table-cards > tbody > tr > td:last-child:has(> .row-actions) { position: static; background: none; box-shadow: none; }
}

/* Any list table on a phone (table-cards.js gives it .table-cards .tc-auto and
   each cell its role + label; desktop untouched). Read for "easy for everyone":
   one card per row, the name bold on top with its status pill and its ⋮ beside
   it, then the facts two per line with the label above the value, long text
   and inputs full width, buttons full width at the bottom. */
@media (max-width: 900px) {
  body .table.tc-auto { min-width: 0; }
  body .table.tc-auto > tbody > tr { gap: 6px 8px; padding: 10px 12px; }
  /* Icon buttons a little smaller on the card, so a reference, its pill and
     its eye / pencil share the first line. */
  body .table.tc-auto > tbody > tr > td.tc-icons :is(.wh-icon-btn, .ra-toggle) { width: 32px; height: 32px; }
  body .table.tc-auto > tbody > tr > td.tc-icons .row-actions { gap: 4px; }
  body .table.tc-auto > tbody > tr > td { font-size: 13.5px; line-height: 1.3; color: var(--ink); }
  body .table.tc-auto > tbody > tr > td.tc-title { font-size: 14.5px; }
  /* Title, status, icons, buttons and photo carry no label, even when a page
     script labelled every cell. */
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-title, .tc-tag, .tc-icons, .tc-actions, .tc-photo, .tc-pick)[data-label]::before { display: none; }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td.tc-half { flex-basis: calc(50% - 6px); }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-empty, .tc-same) { display: none; }
  body .table.tc-auto > tbody > tr > td.tc-icons :is(.wh-icon-btn, .btn):is([disabled], .is-disabled, [aria-disabled="true"]) { display: none; }
  body .table.tc-auto > tbody > tr > td .pill { padding-top: 1px; padding-bottom: 1px; max-width: 100%; white-space: normal; }
  body .table.tc-auto > tbody > tr > td.tc-title :is(.muted, small) { font-weight: 400; }
  body .table.tc-auto > tbody > tr > td.tc-title :is(a, .mono) { font-weight: 700; color: inherit; font-size: inherit; }
  body .table.tc-auto > tbody > tr > td.tc-title a.link { color: var(--primary); }
  body .table.tc-auto > tbody > tr > td.tc-title { flex-wrap: wrap; gap: 2px 8px; }
  body .table.tc-auto > tbody > tr > td :is(.cell-thumb, .product-thumb) { flex: none; }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td.tc-icons { order: -1; flex: 0 0 auto; align-self: flex-start; }
  body .table.tc-auto > tbody > tr > td.tc-pick { order: -4; flex: 0 0 auto; align-self: flex-start; }
  body .table.tc-auto > tbody > tr > td.tc-pick input { width: 20px; height: 20px; }
  /* A fact is a block: its label on top, its value flowing below as written
     ("43 / pièce", "A1 → B2" stay on one line instead of a column each). */
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-half, .tc-wide):not(.tc-empty):not(.tc-same):not(.tc-desktop-only) { display: block; align-self: start; text-align: start; }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-half, .tc-wide)[data-label]::before { display: block; }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-half, .tc-wide) :is(div, p) { margin: 0; }
  body .table.tc-auto > tbody > tr > td.tc-wide { flex: 1 1 100%; }
  body .table:is(.tc-auto, .tc-lite) > tbody > tr > td:is(.tc-half, .tc-wide, .tc-block)[data-label]::before { max-width: none; font-size: 11.5px; font-weight: 500; line-height: 1.3; color: var(--muted); }
  /* The title keeps room: a long status pill or several icons wrap under it
     instead of squeezing the name to "pr / ep". */
  body .table.tc-auto > tbody > tr > td.tc-title { flex: 1 1 150px; }
  body .table.tc-auto > tbody > tr > td.tc-check { flex: 1 1 calc(50% - 6px); justify-content: flex-start; gap: 8px; }
  body .table.tc-auto > tbody > tr > td.tc-block.tc-num { flex: 1 1 calc(50% - 6px); }
  body .table.tc-auto > tbody > tr > td.tc-block :is(.input, input, select) { max-width: none; width: 100%; }
  body .table.tc-auto > tbody > tr > td.tc-check[data-label]::before { order: 2; max-width: none; font-size: 13px; font-weight: 500; color: var(--ink); }
  body .table.tc-auto > tbody > tr > td.tc-check input { width: 20px; height: 20px; flex: none; }
  body .table.tc-auto > tbody > tr > td.tc-actions { order: 9; justify-content: flex-end; }
  body .table.tc-auto > tbody > tr > td.tc-actions :is(.btn, form .btn) { width: auto; flex: 0 1 auto; min-height: 36px; }
  body .table.tc-auto > tbody > tr > td.tc-icons .btn { min-height: 34px; padding-inline: 12px; }
  /* A sortable list keeps its sort: the head row becomes a strip of chips
     that scrolls sideways (tr[data-sort-label], also set by hand on the
     product statistics tables). */
  body .table.table-cards > thead > tr[data-sort-label]:not(.table-filter-row) {
    display: flex; align-items: center; gap: 6px; padding: 8px 12px;
    overflow-x: auto; scrollbar-width: none; border-bottom: 1px solid var(--line-soft);
  }
  body .table.table-cards > thead > tr[data-sort-label]::before {
    content: attr(data-sort-label); flex: none; font-size: 12px; font-weight: 600; color: var(--muted);
  }
  body .table.table-cards > thead > tr[data-sort-label] > th {
    display: inline-flex; align-items: center; flex: none; height: auto; min-width: 0; padding: 5px 10px;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
    font-size: 12.5px; white-space: nowrap; position: static;
  }
  body .table.table-cards > thead > tr[data-sort-label] > th:not(.th-sortable) { display: none; }
  body .table.table-cards > thead > tr[data-sort-label] > th.is-sorted { border-color: var(--primary); color: var(--primary); }
  /* Filters: the search box stays, the rest open from one "Filtres" button,
     each with its column's name above it. */
  body .table:is(.tc-auto, .tc-lite) > thead > tr.table-filter-row.tc-filters:not(.is-open) > th:not(.tc-filters-first) { display: none; }
  body .table:is(.tc-auto, .tc-lite) > thead > tr.table-filter-row > th.tc-filters-first { display: flex; gap: 8px; align-items: center; flex-basis: 100%; }
  body .table:is(.tc-auto, .tc-lite) > thead > tr.table-filter-row > th.tc-filters-first > :first-child { flex: 1 1 auto; min-width: 0; }
  body .table:is(.tc-auto, .tc-lite) > thead > tr.table-filter-row > th[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 3px; font-size: 11.5px; font-weight: 600; color: var(--muted);
  }
  body .btn.tc-filters-btn { display: inline-flex; min-height: 36px; }
  body .table:is(.tc-auto, .tc-lite) > thead > tr.table-filter-row > th > .btn.tc-filters-btn { flex: 0 0 auto; width: auto; contain: none; }
  /* A hand-made card's one "Voir →" / "Réceptionner" button: compact, on the
     title line, not a full-width bar under every card. */
  body .table.tc-lite > tbody > tr > td.tc-actions.tc-mobile-only { order: -1; flex: 0 0 auto; width: auto; }
  body .table.tc-lite > tbody > tr > td.tc-actions.tc-mobile-only .btn { width: auto; min-height: 34px; padding: 4px 12px; font-size: 13px; }
  .tc-filters.is-open .tc-filters-btn { border-color: var(--primary); color: var(--primary); }
  /* A corner pill that is not a status ("0 j", "0 / 2", a shelf) shows its
     column's name in front of it; icons that would sit alone on a line go to
     the end of the card's last line. */
  body .table.tc-auto > tbody > tr > td.tc-tag.tc-tag-named { gap: 6px; }
  body .table.tc-auto > tbody > tr > td.tc-tag.tc-tag-named[data-label]::before { display: inline; max-width: none; font-size: 11.5px; font-weight: 500; color: var(--muted); }
  body .table.tc-auto > tbody > tr.tc-icons-end > td.tc-icons { order: 10; align-self: flex-end; margin-inline-start: auto; }
  /* A list's own search box in its card head takes the full width on a phone
     (the placeholder reads whole), the buttons beside it stay on its line. */
  .card-head:has(input[type="search"]) { flex-wrap: wrap; }
  .card-head > :has(input[type="search"]):not(.card-title) { flex: 1 1 100%; min-width: 0; }
  .card-head > * :is(.min-w-60, .min-w-55, .filter-search-wrap):not(.tc-fold-keep):has(> input[type="search"]) { flex: 1 1 0; min-width: 0; width: auto; }
  .card-head .pd-filter-search { flex-basis: 100%; }
  .card-head input[type="search"] { width: 100%; }
}
.btn.tc-filters-btn { display: none; }
/* /customers/bulk-create on a phone: each row a numbered block, the name on
   its own line, code · city and e-mail · phone two per line. */
@media (max-width: 900px) {
  body .table.bulk-cust-table > tbody { counter-reset: bulkrow; }
  body .table.bulk-cust-table > tbody > tr { counter-increment: bulkrow; }
  body .table.bulk-cust-table > tbody > tr::before { content: attr(data-row-label) " " counter(bulkrow); display: block !important; order: -5; flex: 1 1 auto; height: auto; font-size: 14px; font-weight: 700; color: var(--ink); }
  body .table.bulk-cust-table > tbody > tr > td.tc-block { flex: 1 1 calc(50% - 4px); }
  body .table.bulk-cust-table > tbody > tr > td:nth-child(2) { order: 1; flex-basis: 100%; }
  body .table.bulk-cust-table > tbody > tr > td:nth-child(1) { order: 2; }
  body .table.bulk-cust-table > tbody > tr > td:nth-child(5) { order: 3; }
  body .table.bulk-cust-table > tbody > tr > td:nth-child(3) { order: 4; }
  body .table.bulk-cust-table > tbody > tr > td:nth-child(4) { order: 5; }
}
/* Supervisor: prepared orders that can be reopened — 5 on a phone, the rest
   behind "Voir les N"; the reason box and its button on one line. */
.reopen-more-btn { display: none; }
@media (max-width: 900px) {
  .reopen-card:not(.is-all) tr.reopen-more { display: none !important; }
  .reopen-card .reopen-more-btn { display: flex; width: 100%; justify-content: center; border-top: 1px solid var(--line-soft); border-radius: 0; }
  .reopen-card .reopen-form { display: flex; gap: 6px; width: 100%; }
  .reopen-card .reopen-form .input { flex: 1 1 auto; min-width: 0; }
  body .reopen-card .table.table-cards > tbody > tr > td.tc-actions .reopen-form .btn { flex: 0 0 auto; width: auto; }
}
/* Filter bars above a list (table-cards.js foldBar): on a phone the search
   box and a "Filtres" button, the rest when it is opened. Tab rows (m-tabs,
   supplier-tabs) stay one line that scrolls sideways. */
@media (max-width: 900px) {
  .tc-fold { flex-wrap: wrap; }
  .tc-fold:not(.is-open) > :not(.tc-fold-keep):not(.tc-filters-btn):not(.btn-primary:not([type="submit"])) { display: none !important; }
  .tc-fold > .tc-fold-keep { flex: 1 1 160px; min-width: 150px; max-width: none; width: auto; }
  .tc-fold > .btn.tc-filters-btn { display: inline-flex; flex: 0 0 auto; min-height: 38px; contain: none; }
  .tc-fold.is-open > .btn.tc-filters-btn { border-color: var(--primary); color: var(--primary); }
  .tc-fold:not(.is-open) > .tc-fold-keep > .label { display: none; }
  .tc-fold.is-open > :not(.tc-fold-keep):not(.tc-filters-btn) { flex: 1 1 140px; }
  /* A folded bar with no search box is only its button, at the end, not a
     card around one button. */
  .tc-fold:not(.is-open):not(:has(.tc-fold-keep)) { justify-content: flex-end; padding: 0; border: 0; background: none; box-shadow: none; margin-bottom: 10px; }
  .m-tabs, .supplier-tabs { flex-wrap: nowrap !important; overflow-x: auto; scrollbar-width: none; max-width: 100%; }
  .m-tabs > *, .m-tabs .m-tab, .supplier-tabs > .supplier-tab { flex: 0 0 auto !important; width: auto; white-space: nowrap; }
}

/* /inventory on a phone (modules/stock/pages/index.php), kept short:
   checkbox · photo · name (two lines at most) with the on-hand pill at the
   end of that line, then Fournisseur · Emplacement and Stock recommandé ·
   Min / Max two per line, label above value (.tc-lite), "—" facts left out,
   the icons at the end of the last line. SKU / EAN and last movement are
   .tc-desktop-only; "Acheté" and "Couvert" are hidden too. Desktop unchanged. */
@media (max-width: 900px) {
  body .table.stock-table.table-cards > tbody > tr { padding: 10px 12px; gap: 6px 10px; }
  body .table.stock-table.table-cards > tbody > tr > td.tc-pick { order: -3; flex: 0 0 auto; width: auto; align-self: flex-start; padding-top: 9px; }
  body .table.stock-table.table-cards > tbody > tr > td.tc-pick input { width: 18px; height: 18px; }
  body .table.stock-table.table-cards > tbody > tr > td.tc-title { flex: 1 1 0; min-width: 45%; }
  body .table.stock-table.table-cards .cell-product { gap: 8px; min-width: 0; width: 100%; }
  body .table.stock-table.table-cards .cell-thumb { width: 36px; height: 36px; }
  body .table.stock-table.table-cards .stock-name { min-width: 0; max-width: none; flex: 1 1 0; }
  body .table.stock-table.table-cards .stock-name .link { font-size: 13.5px; }
  body .table.stock-table.table-cards > tbody > tr > td.tc-tag { flex-direction: column; align-items: flex-end; gap: 3px; align-self: flex-start; }
  body .table.stock-table.table-cards .stock-site-trigger { min-width: 0; white-space: nowrap; }
  body .table.stock-table.table-cards .stock-badges { margin-top: 0; }
  body .table.stock-table.table-cards > tbody > tr > td:is(.tc-half, .tc-wide) { font-size: 13px; }
  body .table.stock-table.table-cards .stock-bought,
  body .table.stock-table.table-cards .stock-covered { display: none; }
  body .table.stock-table.table-cards .stock-supplier { max-width: 100%; font-weight: 600; }
  body .table.stock-table.table-cards .pa-pos-list { display: flex; flex-wrap: wrap; }
  body .table.stock-table.table-cards > tbody > tr > td.tc-icons { order: 10; align-self: flex-end; margin-inline-start: auto; }
  body .table.stock-table.table-cards .wh-icon-btn { width: 32px; height: 32px; }
}

/* /products on a phone (modules/products/pages/index.php), kept short: a grid
   card — the photo beside the name / code and the stock pill, ⋮ at the end of
   the name line, then Catégorie · Marque and PA · PVP as "label value" pairs,
   two per line. The product cell and its .product-cell are display: contents
   so the photo and the text are grid items of the card. On the card: no EAN
   (still searchable), no pill dot (the icon says it), no "Non" promo pill.
   The head's buttons sit two by two and the three tiles share one row.
   Desktop unchanged. */
@media (max-width: 900px) {
  body .table.product-table.table-cards > tbody > tr.product-row {
    display: grid; grid-template-columns: 40px minmax(0, 1fr) minmax(0, 1fr) auto;
    grid-template-areas: "img name name menu" "img stock stock promo" "cat cat brand brand" "pa pa pvp pvp";
    align-items: center; gap: 4px 10px; padding: 10px 12px;
  }
  body .table.product-table.table-cards > tbody > tr.product-row::before,
  body .table.product-table.table-cards > tbody > tr.product-row::after { content: none; }
  body .table.product-table.table-cards > tbody > tr.product-row > td.pt-product,
  .product-table.table-cards .pt-product .product-cell { display: contents; }
  .product-table.table-cards .product-list-image { grid-area: img; align-self: start; width: 40px; height: 40px; }
  .product-table.table-cards .product-cell-text { grid-area: name; min-width: 0; }
  .product-table.table-cards .product-cell-name {
    white-space: normal; font-size: 14px; font-weight: 600; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }
  .product-table.table-cards .product-cell-code { margin-top: 1px; font-size: 11.5px; }
  .product-table.table-cards .product-cell-ean { display: none; }
  body .table.product-table.table-cards > tbody > tr > td.pt-menu { grid-area: menu; align-self: start; justify-content: flex-end; }
  .product-table.table-cards .pt-menu .wh-icon-btn { width: 28px; height: 28px; }
  body .table.product-table.table-cards > tbody > tr > td.pt-stock { grid-area: stock; justify-content: flex-start; flex-wrap: wrap; gap: 4px; }
  body .table.product-table.table-cards > tbody > tr > td.pt-promo { grid-area: promo; justify-content: flex-end; }
  .product-table.table-cards .pt-stock .pill,
  .product-table.table-cards .pt-promo .pill { gap: 4px; padding: 1px 8px; font-size: 11.5px; }
  .product-table.table-cards .pt-stock .pill::before,
  .product-table.table-cards .pt-promo .pill::before { display: none; }
  .product-table.table-cards .pt-stock .pill .icon { width: 12px; height: 12px; }
  .product-table.table-cards .pt-stock .pill.mt-1 { margin-top: 0; }
  /* A fact reads "Label value" on one line. */
  body .table.product-table.table-cards > tbody > tr > td.pt-fact {
    align-items: baseline; justify-content: flex-start; gap: 5px;
    text-align: start; font-size: 13px; color: var(--ink);
  }
  body .table.product-table.table-cards > tbody > tr > td.pt-fact[data-label]::before { max-width: none; font-size: 11.5px; font-weight: 500; }
  body .table.product-table.table-cards > tbody > tr > td.pt-fact.mono { font-size: 12.5px; }
  .product-table.table-cards .pt-fact > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .product-table.table-cards .pt-cat   { grid-area: cat; }
  .product-table.table-cards .pt-brand { grid-area: brand; }
  .product-table.table-cards .pt-pa    { grid-area: pa; }
  .product-table.table-cards .pt-pvp   { grid-area: pvp; }
  /* The filters all start on "Tout": each gets its name above it. */
  body .table.product-table.table-cards > thead > tr.table-filter-row > th[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 3px;
    font-size: 11.5px; font-weight: 600; color: var(--muted);
  }
}
/* /products/{id} and /products/{id}/stats on a phone: their history tables
   (.supplier-panel .table carries a 920px floor for the laptop) become
   .table-cards; the stats tables' head row is the shared "Trier par" chip strip
   (tr[data-sort-label], above). */
@media (max-width: 900px) {
  .supplier-panel .table.table-cards { min-width: 0; }
  /* Facts read label above value, two per line — "Fourchette de prix" and
     "Pièces / carton" no longer wrap beside a squeezed number. */
  body .supplier-panel .table.table-cards > tbody > tr > td.tc-half { flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 1px; text-align: start; }
  body .supplier-panel .table.table-cards > tbody > tr > td.tc-half[data-label]::before { max-width: none; font-size: 11.5px; font-weight: 500; }
  body .pstats-tabs-card .table.table-cards > tbody > tr { padding: 10px 12px; gap: 5px 12px; }
  body .pstats-tabs-card .table.table-cards > tbody > tr > td { font-size: 13px; }
  body .pstats-tabs-card .table.table-cards > tbody > tr > td.tc-title { font-size: 14px; }
  body .pstats-tabs-card .table.table-cards > tbody > tr > td.tc-title .muted { font-size: 11.5px; font-weight: 400; }
  body .pstats-tabs-card .table.table-cards > tbody > tr > td.pstats-note { flex-direction: column; align-items: flex-start; gap: 2px; text-align: start; }
}
@media (max-width: 640px) {
  /* Product page: the three buttons wrap (Edit full width) instead of running
     off the left edge; photo beside the name, the facts two by two. */
  .product-detail-page .supplier-hero-card { padding: 12px; }
  .product-detail-page .supplier-hero-actions { flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
  .product-detail-page .supplier-hero-actions > .btn { flex: 1 1 0; min-width: 0; justify-content: center; white-space: nowrap; }
  .product-detail-page .supplier-hero-actions > .btn-primary { flex-basis: 100%; }
  .product-detail-page .supplier-hero-actions > .btn-ghost { border: 1px solid var(--line); }
  .product-detail-page .supplier-hero-main,
  .product-detail-page .supplier-hero-side { padding: 10px; }
  .product-detail-page .supplier-hero-main { grid-template-columns: 64px minmax(0, 1fr); gap: 12px; align-items: center; }
  .product-detail-page .supplier-avatar { width: 64px; height: 64px; }
  .product-detail-page .supplier-hero-copy { display: contents; }
  .product-detail-page .supplier-title-row { gap: 6px; }
  .product-detail-page .supplier-title-row h2 { flex-basis: 100%; font-size: 19px; line-height: 1.25; }
  .product-detail-page .supplier-hero-copy > .sub { display: none; }
  .product-detail-page .supplier-contact-grid { grid-column: 1 / -1; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; margin-top: 2px; overflow-wrap: anywhere; }
  .product-detail-page .supplier-mini-stat { flex-basis: calc(50% - 3px); padding: 9px 10px; }
  .product-detail-page .supplier-stock-box { flex-basis: 100%; max-width: none; }
  /* The hero says the name whole; the top bar cut it to "A…". */
  body:has(.product-detail-page) .topbar h1 { display: none; }

  /* Stats page: the period buttons on one line; search + export, then the
     period + filter, instead of four full-width rows per tab. */
  .pstats-period { gap: 10px; }
  .pstats-period-left, .pstats-period-left .trend-range { width: 100%; }
  body .pstats-period .trend-range .trend-range-btn { flex: 1 1 auto; padding: 8px 6px; text-align: center; white-space: nowrap; }
  .pstats-period-form, .pstats-period-form > * { width: 100%; }
  .pstats-panel-head { padding: 10px 12px; }
  .pstats-panel-tools { flex-wrap: wrap; gap: 8px; width: 100%; }
  .pstats-panel-tools > .pstats-search { order: -2; flex: 1 1 calc(100% - 60px); min-width: 0; max-width: none; }
  .pstats-panel-tools > .pstats-export { order: -1; flex: 0 0 auto; padding: 0 11px; }
  .pstats-panel-tools > .pstats-export .pstats-export-label { display: none; }
  .pstats-panel-tools > .pstats-export .icon { width: 16px; height: 16px; }
  .pstats-panel-tools > :not(.pstats-search):not(.pstats-export) { flex: 1 1 40%; min-width: 0; }
  .pstats-panel-tools .pstats-filter { min-width: 0; width: 100%; }
  .pstats-panel-tools .pstats-drp { min-width: 0; }
}
/* Phones, every page: the tracking steppers (order, BC, reception, invoice)
   read top to bottom — bullet, then the step and its date — instead of a
   row of 4-6 steps scrolled sideways with most of them out of sight; the
   one-line stat tiles sit two per row; a segmented control never breaks a
   choice over two lines. */
@media (max-width: 640px) {
  :is(.order-tracking-scroll, .sup-track-scroll) { overflow: visible; padding: 12px 14px 2px; }
  .stepper:is(.order-status-stepper, .stepper--compact) { min-width: 0; flex-direction: column; align-items: stretch; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step {
    display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 12px; align-content: start;
    text-align: start; padding-bottom: 12px;
  }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step > .bullet { grid-row: 1 / span 2; width: 28px; height: 28px; margin: 0; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step > .bullet .icon { width: 14px; height: 14px; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step > :is(.s-title, .s-sub) { grid-column: 2; margin: 0; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step > .s-title { font-size: 14px; line-height: 28px; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step::before,
  .stepper:is(.order-status-stepper, .stepper--compact) > .step::after {
    display: block; top: 30px; bottom: 0; left: 13px; width: 2px; height: auto; transform: none; animation: none;
  }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step::after { opacity: 0; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step:has(+ .step:is(.done, .current))::after { opacity: 1; }
  /* A step not reached yet has no date: just its name, a shorter line. */
  .stepper:is(.order-status-stepper, .stepper--compact) > .step > .s-sub.is-empty { display: none; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step:has(> .s-sub.is-empty) { padding-bottom: 6px; }
  .stepper:is(.order-status-stepper, .stepper--compact) > .step:last-child::before,
  .stepper:is(.order-status-stepper, .stepper--compact) > .step:last-child::after { display: none; }

  .stats-compact:not(.stats-inline):not(.products-stats) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stats-compact:not(.stats-inline):not(.products-stats) > .stat-compact { flex-wrap: wrap; gap: 2px 8px; padding: 8px 10px; }
  .stats-compact:not(.stats-inline):not(.products-stats) > .stat-compact > .stat-compact-copy { flex: 1 1 100%; white-space: normal; }
  .stats-inline .stat-compact-label { white-space: normal; overflow: visible; }
  .stats-compact:not(.stats-inline):not(.products-stats) > .stat-compact:last-child:nth-child(odd) { grid-column: 1 / -1; }
  /* A tile holding a long amount ("6,121,056.53 MAD") takes the whole row. */
  .stats-compact:not(.stats-inline):not(.products-stats) > .stat-compact.is-wide { grid-column: 1 / -1; }
  .trend-range-btn { white-space: nowrap; }
  .card-head { flex-wrap: wrap; row-gap: 8px; }
  /* A profile's one-line KPI tiles (supplier page): 2 × 2 too, the label
     under the value instead of cut to "Comm…". */
  .kpi-grid--compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpi-grid--compact .kpi-card { flex-wrap: wrap; gap: 2px 8px; padding: 8px 10px; }
  .kpi-grid--compact .kpi-card-label { flex: 1 1 100%; white-space: normal; }
  .kpi-grid--compact .kpi-card-change, .kpi-grid--compact .kpi-card-sub { display: none; }
  .kpi-grid--compact > .kpi-card:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
/* Phones, every page (2): a supplier / customer / product profile card
   (.supplier-hero-*) — small photo or initials beside the name, facts two
   per line with the empty ones left out, the four tiles 2 × 2, no repeated
   top-bar title; header buttons share their rows evenly; a notes box is
   tall enough to read; the product-stats notes wrap instead of being cut. */
@media (max-width: 640px) {
  .supplier-hero-card { padding: 12px; }
  .supplier-hero-grid { gap: 10px; }
  .supplier-hero-main { grid-template-columns: 56px minmax(0, 1fr); gap: 10px 12px; padding: 12px; align-items: center; }
  .supplier-avatar { width: 56px; height: 56px; font-size: 20px; }
  .supplier-hero-copy { display: contents; }
  .supplier-hero-copy > .sub { display: none; }
  .supplier-title-row h2 { font-size: 19px; line-height: 1.25; }
  .supplier-contact-grid { grid-column: 1 / -1; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; margin-top: 2px; overflow-wrap: anywhere; }
  .supplier-contact-grid > .is-empty-fact { display: none; }
  .supplier-hero-side { padding: 10px; gap: 10px; }
  .supplier-hero-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
  .supplier-mini-stat { padding: 9px 10px; }
  body:has(.supplier-hero-card) .topbar h1 { display: none; }

  .page-head > :last-child:not(:first-child) { width: 100%; }
  /* A heading with pills beside it (order, BC): the pills go under it rather
     than breaking "CMD-2026- / 0086" in two. */
  .page-head .row:has(> h2) { flex-wrap: wrap; row-gap: 6px; }
  .page-head .row > h2 { flex-basis: 100%; }
  /* Order page: status and payment side by side, read from the left. */
  .order-status-aside { flex-direction: row; flex-wrap: wrap; align-items: flex-start; text-align: start; gap: 10px 14px; }
  .order-status-aside > * { flex: 1 1 140px; min-width: 0; }
  .order-status-aside select.input { width: 100%; }
  .page-head > :last-child:not(:first-child) > :is(.btn, .catalog-link) { flex: 1 1 auto; justify-content: center; }
  textarea.input { min-height: 84px; }
  .kpi-card-change-note { display: none; }
  .kv { gap: 12px; }
  .kv .v { min-width: 0; text-align: end; overflow-wrap: anywhere; }
  .kv.is-long { flex-direction: column; gap: 2px; }
  .kv.is-long .v { text-align: start; }
  /* A section head whose button sat beside a long help text (permissions,
     the product form's SKUs): the text gets the full width, the button under. */
  .product-section-head { flex-wrap: wrap; gap: 8px; }
  .product-section-head > :first-child { flex: 1 1 100%; min-width: 0; }
  /* Permission checkboxes: one wrapping row (Tout · Lecture · Création…). */
  body .table.tc-auto > tbody > tr > td.tc-check { flex: 0 1 auto; }
  .user-form + .page-head, form + .page-head { margin-top: 28px; }
}

/* /brands and /categories on a phone: one line per brand / category and per
   sub-row — arrow, logo, name, then the product count, the status and the
   actions; the "Marque / Sous-marque" column is left out (the indent says
   it), "+ Sous-marque" shows its "+" only. Own layout (data-cards="off"):
   a tree whose rows open and close, not a list for table-cards.js. */
@media (max-width: 900px) {
  .taxonomy-table-wrap { overflow: visible; }
  .taxonomy-card-head { flex-wrap: wrap; gap: 8px; }
  .taxonomy-card-actions { flex-wrap: wrap; width: 100%; justify-content: space-between; gap: 8px; }
  .taxonomy-table { min-width: 0; table-layout: auto; }
  .taxonomy-table, .taxonomy-table > tbody, .taxonomy-table > thead { display: block; width: 100%; }
  .taxonomy-table > colgroup, .taxonomy-table > thead > tr:not(.table-filter-row) { display: none; }
  .taxonomy-table > thead > tr.table-filter-row { display: block; padding: 10px 12px; }
  .taxonomy-table > thead > tr.table-filter-row > th { display: block; padding: 0; border: 0; }
  .taxonomy-table > thead > tr.table-filter-row > th:empty { display: none; }
  /* Name on the first line (with + / 🗑 at the end), product count and
     status just under the name. */
  body .table.taxonomy-table tr.taxonomy-row {
    display: grid; grid-template-columns: 72px auto auto minmax(0, 1fr) auto;
    grid-template-areas: "name name name name act" ". count status . act";
    align-items: center; gap: 2px 6px; padding: 8px 12px; border-bottom: 1px solid var(--line-soft);
  }
  body .table.taxonomy-table tr.taxonomy-row > td { display: block; padding: 0; border: 0; height: auto; background: none; box-shadow: none; position: static; }
  body .table.taxonomy-table .taxonomy-group.is-collapsed tr.taxonomy-row:is(.taxonomy-subbrand-row, .taxonomy-subcategory-row, .taxonomy-subsubcategory-row) { display: none; }
  body .table.taxonomy-table tr.taxonomy-subsubcategory-row > td.taxonomy-name-cell { padding-left: 24px; }
  body .table.taxonomy-table tr.taxonomy-row > td.taxonomy-name-cell { grid-area: name; min-width: 0; }
  body .table.taxonomy-table tr.taxonomy-row > td:nth-child(2) { display: none; }
  body .table.taxonomy-table tr.taxonomy-row > td:nth-child(3) { grid-area: count; }
  body .table.taxonomy-table tr.taxonomy-row > td:nth-child(4) { grid-area: status; }
  body .table.taxonomy-table tr.taxonomy-row > td:nth-child(5) { grid-area: act; }
  .taxonomy-table .taxonomy-identity { gap: 8px; }
  .taxonomy-table .taxonomy-image { width: 32px; height: 32px; flex-basis: 32px; }
  .taxonomy-table tr.taxonomy-row .pill { font-size: 11.5px; padding: 1px 8px; }
  .taxonomy-table tr:is(.taxonomy-subbrand-form-row, .taxonomy-subcategory-form-row, .taxonomy-subsubcategory-form-row):not([hidden]),
  .taxonomy-table tr:has(> td.taxonomy-empty-cell) { display: block; padding: 10px 12px; }
  .taxonomy-table tr:is(.taxonomy-subbrand-form-row, .taxonomy-subcategory-form-row, .taxonomy-subsubcategory-form-row) > td,
  .taxonomy-table td.taxonomy-empty-cell { display: block; padding: 0; border: 0; }
  .taxonomy-table .taxonomy-row-actions { gap: 6px; }
  .taxonomy-table :is(.taxonomy-add-sub, .taxonomy-add-subsub) {
    width: 34px; height: 34px; padding: 0 0 0 9px; gap: 24px; overflow: hidden;
    white-space: nowrap; justify-content: flex-start;
  }
  .taxonomy-table .taxonomy-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
@media (max-width: 640px) {
  .page-head > .products-head-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; gap: 8px; }
  .products-head-actions > .catalog-link,
  .products-head-actions > .btn { min-width: 0; width: 100%; min-height: 40px; justify-content: center; white-space: normal; text-align: center; line-height: 1.2; }
  .products-head-actions .catalog-link-arrow { display: none; }
  .stats-compact.products-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .products-stats .stat-compact { flex-wrap: wrap; gap: 4px 8px; padding: 8px 10px; }
  .products-stats .stat-compact-copy { flex: 1 1 100%; }
  .products-stats .stat-compact-sub { display: none; }
  /* Three to a row: the number over its label, no icon (no room for it beside them). */
  body.skin-readable .products-stats > .stat-compact { grid-template-columns: minmax(0, 1fr); column-gap: 0; padding: 10px 12px; }
  body.skin-readable .products-stats > .stat-compact .stat-compact-ico { display: none; }
  body.skin-readable .products-stats > .stat-compact .stat-compact-value { grid-column: 1; font-size: 18px; }
  body.skin-readable .products-stats > .stat-compact .stat-compact-copy { grid-column: 1; white-space: normal; }
}

/* Réceptionnaire workspace on a phone: the received input (and its carton
   twin) side by side; the step's main button full width. */
@media (max-width: 900px) {
  .rc-received { flex-direction: row !important; flex-wrap: wrap; align-items: center !important; }
  .rc-received::before { flex-basis: 100%; }
  .rc-received .rc-qty-input, .rc-received .rc-carton-qty { flex: 1 1 0; width: auto; margin-top: 0; min-height: 40px; }
  .rc-received > div { flex-basis: 100%; }
  .rc-main-btn { width: 100%; min-height: 44px; justify-content: center; }
  .rc-card .card-head { flex-wrap: wrap; gap: 8px; }
  .pa-bulk-bar { flex-wrap: wrap; }
  .pa-bulk-bar .btn { width: 100%; min-height: 40px; justify-content: center; }
  .rc-etage-head { flex-wrap: wrap; }
  .rc-etage-head .btn { width: 100%; min-height: 40px; justify-content: center; margin-left: 0; }
}

/* Réception workspace (/receiving/{id}) on a phone — the laptop keeps its tables
   and stepper untouched: .rc-progress and every .rc-m-only control exist only
   under 900px. Slim 3-bar progress instead of the tall stepper; each counted
   line a card of its own with a big − count + row and "Tout reçu"; lot and DLC
   side by side; PA / TVA / Total as three small figures; the step's main button
   in a bar stuck to the bottom of the screen. */
@media (min-width: 901px) {
  .rc-progress, .rc-m-only { display: none !important; }
}
@media (max-width: 900px) {
  /* html body: the shadcn skin's body.skin-shadcn .card-pad / .card-head load later. */
  html body .rc-card.card-pad, html body .rc-track { padding: 14px; }
  html body .rc-card .card-head { padding: 0 0 12px; border-bottom: 0; }
  .rc-card .card-head .muted { font-size: 13px; margin-top: 4px; }

  .rc-track .stepper { display: none; }
  .rc-progress-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
  .rc-progress-head strong { font-size: 15px; color: var(--ink); }
  .rc-progress-head .muted { order: 1; font-size: 12px; white-space: nowrap; }
  .rc-progress-bars { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin: 0; padding: 0; list-style: none; }
  .rc-progress-bars li { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
  .rc-progress-bar { height: 6px; border-radius: 999px; background: var(--slate-200); }
  .rc-progress-bars li.is-current .rc-progress-bar { background: var(--primary); }
  .rc-progress-bars li.is-done .rc-progress-bar { background: var(--success-500); }
  .rc-progress-label { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; font-weight: 500; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .rc-progress-bars li.is-current .rc-progress-label { color: var(--ink); font-weight: 600; }
  .rc-progress-bars li.is-done .rc-progress-label { color: var(--success-700); }

  /* Admin setup: destination picker full width; "no unplanned items" is one line. */
  .rc-card .dest-cell { min-width: 0; width: 100%; }
  .rc-card .dest-text { width: 100%; min-height: 44px; font-family: var(--font-sans); }
  body .rc-card .dest-edit select { max-width: none; width: 100%; min-height: 44px; }
  #dest-form > .btn { width: 100%; min-height: 44px; justify-content: center; }
  html body .rc-card--empty.card-pad { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 14px; }
  .rc-card--empty > .muted { margin: 0; font-size: 13px; }

  /* Count step (2026-10-27, picked from the phone designs): no card around the
     cards — the step's card and the progress go flat on the page — and each
     line is its own card: photo, name, "SKU · Commandé 90 · carton de 9" and a
     state pill (À compter / Complet / −9) on top; a "81 / 90" count box that
     turns green when all is there and amber on a gap; "Tout reçu" + Pièces |
     Cartons (− / + then move a whole carton); the gap in words; lot / DLC /
     note folded behind one summary line; PA × count and the line total on one
     line. */
  html body .card.rc-track { padding: 0 2px; background: none; border: 0; box-shadow: none; }
  html body .card.rc-card.card-pad:has(#rc-discharge-form) { margin-bottom: 0; padding: 0; background: none; border: 0; box-shadow: none; }
  html body .rc-card:has(#rc-discharge-form) > .card-head { padding: 0 2px 12px; }

  #rc-discharge-form .table-wrap { border: 0; overflow: visible; }
  body #rc-discharge-form .table.table-cards > tbody > tr {
    gap: 10px; margin-bottom: 10px; padding: 12px;
    border: 1px solid var(--line); border-radius: 14px; background: var(--surface);
  }
  body #rc-discharge-form .table.table-cards > tbody > tr.rc-is-gap { border-color: var(--rl-amber-line, var(--amber-600)); }
  body #rc-discharge-form .table.table-cards > tbody > tr::before { display: none; }
  body #rc-discharge-form .table.table-cards > tbody > tr > td.tc-title,
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-sku,
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-money { display: none; }
  body #rc-discharge-form .table.table-cards > tfoot { display: none; }

  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-head { order: -3; align-items: flex-start; justify-content: flex-start; gap: 11px; text-align: start; }
  .rc-thumb { flex: none; width: 52px; height: 52px; border: 1px solid var(--line-soft); border-radius: 10px; background: var(--white, #fff); object-fit: contain; }
  .rc-head-text { display: flex; flex: 1 1 auto; flex-direction: column; gap: 3px; min-width: 0; }
  .rc-name { font-size: 14.5px; font-weight: 700; line-height: 1.3; color: var(--ink); overflow-wrap: break-word; }
  .rc-head-sub { font-size: 12px; color: var(--muted); }
  .rc-head-sub strong { color: var(--ink); }
  .rc-head-sub > span { white-space: nowrap; }
  .rc-head .rc-state-pill { flex: none; }

  body #rc-discharge-form td.rc-received { flex-direction: column !important; align-items: stretch !important; gap: 8px; padding: 0; background: none; }
  body #rc-discharge-form td.rc-received::before { display: none; }
  /* "Reçu / sur 90" on the left, one joined [ − | 81 | + ] stepper on the right
     (44px high, like a shop cart's) — green when complete, amber on a gap. */
  .rc-count { display: flex; align-items: center; }
  .rc-count-label { display: flex; flex: 1 1 auto; flex-direction: column; gap: 1px; min-width: 0; text-align: start; line-height: 1.25; }
  .rc-count-label strong { font-size: 14px; color: var(--ink); }
  .rc-count-label .muted { font-size: 12.5px; }
  .rc-count-box {
    display: flex; align-items: center; flex: none; width: 76px; height: 44px; box-sizing: border-box;
    border-block: 1px solid var(--line); background: var(--surface);
  }
  #rc-discharge-form tr[data-measurable] .rc-count-box { width: 92px; }
  body #rc-discharge-form tr.rc-is-ok :is(.rc-count-box, .rc-step) { border-color: var(--rl-green-line, var(--success-200)); background: var(--rl-green-soft, var(--success-50)); }
  body #rc-discharge-form tr.rc-is-gap :is(.rc-count-box, .rc-step) { border-color: var(--rl-amber-line, var(--amber-600)); background: var(--rl-amber-soft, var(--amber-50)); }
  body #rc-discharge-form tr.rc-is-ok .rc-qty-input { color: var(--rl-green-ink, var(--success-700)); }
  .rc-step {
    display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0; cursor: pointer; touch-action: manipulation;
    border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  }
  #rc-discharge-form :is(.rc-step, .rc-fill, .rc-unit-seg button, .rc-more-btn) { -webkit-tap-highlight-color: transparent; }
  .rc-step[data-step="-1"] { border-radius: 10px 0 0 10px; }
  .rc-step[data-step="1"] { border-radius: 0 10px 10px 0; }
  .rc-step:active { background: var(--hover); }
  .rc-step .icon { width: 18px; height: 18px; stroke-width: 2.5; }
  body #rc-discharge-form .rc-count .rc-qty-input {
    flex: 1 1 0; width: 100%; min-width: 0; max-width: none; height: 42px; min-height: 0; margin: 0; padding: 0;
    border: 0; border-radius: 0; background: transparent; box-shadow: none;
    text-align: center; font-size: 19px; font-weight: 700; appearance: textfield; -moz-appearance: textfield;
  }
  body #rc-discharge-form .rc-count .rc-qty-input:focus { box-shadow: inset 0 -2px 0 var(--primary); outline: 0; }
  .rc-count .rc-qty-input::-webkit-inner-spin-button, .rc-count .rc-qty-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
  .rc-count-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  body #rc-discharge-form .rc-count-tools > * { flex: 0 0 auto; width: auto; max-width: none; min-height: 38px; margin: 0; }
  body #rc-discharge-form .rc-count-tools > .rc-carton-qty { display: none; }
  /* Same shape as the app's other buttons (.btn: 8px corners), just phone-tall. */
  body #rc-discharge-form .rc-count-tools > .rc-fill { justify-content: center; height: 40px; min-height: 40px; padding: 0 14px; border-radius: 8px; background: var(--surface); }
  body #rc-discharge-form .rc-count-tools > .rc-fill.is-on { border-color: var(--rl-green-line, var(--success-200)); background: var(--rl-green-soft, var(--success-50)); color: var(--rl-green-ink, var(--success-700)); }
  body #rc-discharge-form .rc-count-tools > .rc-unit-seg { display: inline-flex; height: 40px; margin-inline-start: auto; overflow: hidden; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
  .rc-unit-seg button { min-height: 38px; padding: 0 12px; border: 0; background: none; font: inherit; font-size: 12.5px; color: var(--muted); cursor: pointer; touch-action: manipulation; }
  .rc-unit-seg button.is-on { background: var(--primary-soft); color: var(--primary-strong); font-weight: 700; }
  .rc-gapline { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 10px; background: var(--rl-amber-soft, var(--amber-50)); color: var(--rl-amber-ink, var(--amber-700)); font-size: 13px; font-weight: 600; text-align: start; }
  .rc-gapline[hidden] { display: none; }

  /* Lot / DLC / note: one summary line ("Lot L2610-07 · DLC 15-04-2027 · …"); a tap opens the three fields. */
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-more { padding-top: 8px; border-top: 1px solid var(--line-soft); }
  .rc-more-btn {
    display: flex; align-items: center; gap: 8px; width: 100%; min-height: 36px; padding: 0; border: 0; background: none;
    font: inherit; font-size: 13px; color: var(--ink); text-align: start; cursor: pointer;
  }
  .rc-more-btn strong { flex: none; }
  .rc-more-sum { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .rc-more-sum.is-warn { color: var(--rl-amber-ink, var(--amber-700)); font-weight: 600; }
  .rc-more-chev { flex: none; transition: transform .15s; }
  tr.rc-open .rc-more-chev { transform: rotate(180deg); }
  body #rc-discharge-form .table.table-cards > tbody > tr:not(.rc-open) > td.rc-field { display: none; }

  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-field { flex-direction: column; align-items: stretch; gap: 4px; text-align: start; }
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-field::before { max-width: none; }
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-field .input { max-width: none; width: 100%; min-height: 42px; }
  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-field-half { flex: 1 1 calc(50% - 5px); }
  #rc-discharge-form td.rc-field .input::placeholder { font-family: var(--font-sans); font-size: 13px; }

  body #rc-discharge-form .table.table-cards > tbody > tr > td.rc-money-line { order: 2; gap: 8px; font-size: 12.5px; }
  .rc-money-calc { text-align: start; }
  .rc-money-line strong { color: var(--ink); white-space: nowrap; }

  /* The step's main button: a bar stuck to the bottom while the lines scroll. */
  body .rc-actions {
    position: sticky; bottom: 0; z-index: 6; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
    margin: 14px -14px -14px; padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line); border-radius: 0 0 12px 12px; background: var(--surface);
    box-shadow: 0 -10px 18px -14px rgb(15 23 42 / .35);
  }
  /* Putaway with lines still to shelve: "Ranger la sélection" is the step's action,
     so "Terminer la réception" stays in place at the end instead of riding along. */
  html body .rc-card:has(.pa-bulk-bar) .rc-actions { position: static; margin: 16px 0 0; padding: 0; border: 0; box-shadow: none; }
  .rc-card .card-head > .row { flex: 1 1 100%; justify-content: stretch; }
  .rc-card .card-head > .row > .btn { flex: 1 1 auto; justify-content: center; min-height: 40px; }
  .rc-actions > .muted { flex: 1 1 100%; font-size: 12px; }
  .rc-actions-total { display: flex; flex-direction: column; flex: 0 0 auto; font-size: 11.5px; line-height: 1.25; }
  .rc-actions-total strong { font-size: 16px; color: var(--ink); white-space: nowrap; }
  .rc-actions-total strong small { font-size: 11px; font-weight: 500; }
  .rc-count-sum.has-gap { color: var(--rl-amber-ink, var(--amber-700)); }
  /* The count step's card is flat, so its bar reaches the screen edges (main's 18px gutter). */
  html body .rc-card:has(#rc-discharge-form) .rc-actions { margin: 14px -18px -18px; border-radius: 0; }
  body .rc-actions .rc-main-btn { flex: 1 1 0; width: auto; min-height: 48px; white-space: nowrap; }

  /* Documents step: type full width, then two big tiles — photo / file. */
  #rc-doc-form { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  #rc-doc-form select { grid-column: 1 / -1; max-width: none; width: 100%; min-height: 44px; }
  #rc-doc-form .btn { flex-direction: column; gap: 6px; min-height: 76px; justify-content: center; font-size: 13px; }
  #rc-doc-form .btn .icon { width: 22px; height: 22px; }
}

/* Réception board (/receiving, modules/receiving/pages/index.php) on a phone.
   The four status tabs are one slim switch — a short word and its count, each
   in its own colour, the chosen one filled — so none hides off-screen. Each order is a compact card:
   reference + open button, the status word under it, the supplier in bold,
   then the dates and lines as small tiles (grey capitals for the label, dark
   bold for the value). #rc-body / #rc-status-tabs ids: the generic .tc-lite
   card rules carry more classes. */
@media (max-width: 900px) {
  html body #rc-status-tabs {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2px; overflow: visible;
    padding: 3px; border: 1px solid var(--line); border-radius: 10px; background: var(--slate-100);
  }
  html body #rc-status-tabs > .m-tab {
    gap: 4px; min-width: 0; min-height: 36px; padding: 6px 2px;
    border: 0; border-radius: 8px; background: none; box-shadow: none;
    font-size: 12px; font-weight: 600; white-space: nowrap; color: var(--muted);
  }
  html body #rc-status-tabs > .m-tab.active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 3px rgb(15 23 42 / .14); }
  html body #rc-status-tabs .rc-tab-long { display: none; }
  html body #rc-status-tabs > .m-tab > .pill {
    padding: 0; border: 0; background: none; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
  }
  html body #rc-status-tabs > .m-tab > .pill::before { display: none; }
  /* Each status tab in its own colour (2026-10-27): a soft tint with the word and
     count in the tone — blue À recevoir, amber En cours, green Reçu — and the
     chosen one filled with that colour. "Tout" stays neutral (ink when chosen). */
  html body #rc-status-tabs > .m-tab > .pill { color: inherit; }
  html body #rc-status-tabs > .m-tab.tone-blue  { background: var(--blue-50); color: var(--blue-700); }
  html body #rc-status-tabs > .m-tab.tone-amber { background: var(--amber-50); color: var(--amber-700); }
  html body #rc-status-tabs > .m-tab.tone-green { background: var(--success-50); color: var(--success-700); }
  html body #rc-status-tabs > .m-tab.active { color: var(--white, #fff); box-shadow: 0 1px 3px rgb(15 23 42 / .18); }
  html body #rc-status-tabs > .m-tab.active:not([class*="tone-"]) { background: var(--ink); }
  html body #rc-status-tabs > .m-tab.tone-blue.active  { background: var(--blue-600); }
  html body #rc-status-tabs > .m-tab.tone-amber.active { background: var(--amber-600); }
  html body #rc-status-tabs > .m-tab.tone-green.active { background: var(--success-600); }

  .card:has(> .table-wrap > .rc-inbox) { background: none; border: 0; box-shadow: none; }
  .card:has(> .table-wrap > .rc-inbox) > .card-head { padding: 0 2px 6px; border: 0; }
  .card:has(> .table-wrap > .rc-inbox) > .table-wrap { border: 0; overflow: visible; }
  html body .table.rc-inbox > thead > tr.table-filter-row { padding: 0 0 8px; border: 0; background: none; }

  html body #rc-body > tr[data-status] {
    gap: 6px 8px; margin-bottom: 8px; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  }
  html body #rc-body > tr > td.tc-title a { color: var(--ink); font-size: 14px; font-weight: 700; }
  html body #rc-body > tr > td.tc-actions .btn.job-card-go { height: 30px; min-height: 30px; padding: 0 10px; font-size: 12.5px; }
  /* Status word · supplier on one line under the reference (order 0 / 1, right
     after the line break), then the tiles. */
  html body #rc-body > tr > td.tc-tag { order: 0; flex: 0 0 auto; justify-content: flex-start; margin: -10px 0 0; }
  html body #rc-body > tr > td.tc-tag .pill { font-size: 12px; }
  html body #rc-body > tr > td.rc-supplier { order: 1; }
  html body #rc-body > tr::after { content: ""; order: 1; flex: 0 0 100%; height: 0; }
  html body #rc-body > tr > td.rc-fact { order: 2; margin-top: -6px; }

  html body #rc-body > tr > td.rc-supplier {
    display: flex; flex: 1 1 0; justify-content: flex-start; gap: 5px; min-width: 0; margin-top: -10px;
    font-size: 13.5px; font-weight: 600; color: var(--ink); text-align: start;
  }
  html body #rc-body > tr > td.rc-supplier::before { content: "·"; flex: none; max-width: none; font-size: 14px; color: var(--muted); }
  html body #rc-body > tr > td.rc-supplier svg { flex: none; width: 14px; height: 14px; color: var(--muted); }

  html body #rc-body > tr > td.rc-fact {
    display: flex; flex: 1 1 0; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 1px;
    min-width: 0; padding: 5px 8px; border-radius: 6px; background: var(--slate-50);
    font-size: 13px; font-weight: 600; color: var(--ink); text-align: start; font-variant-numeric: tabular-nums;
  }
  html body #rc-body > tr > td.rc-fact::before {
    display: block; max-width: none; font-size: 10px; font-weight: 600; line-height: 1.2;
    letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
  }
  html body #rc-body > tr > td.rc-fact.rc-fact-lines { flex: 0 0 auto; min-width: 56px; }
  html body #rc-body > tr > td.rc-fact.rc-fact-wide { flex: 1 1 100%; }
  html body #rc-body > tr > td.rc-fact:has(.pill-red) { background: var(--red-50); }
  html body #rc-body > tr > td.rc-fact .pill-red { margin-top: 1px; font-size: 11px; }
  html body #rc-body > tr > td.rc-fact:is(.tc-empty, .tc-same) { display: none; }
}

/* Préparateur board (/preparator, modules/preparator/pages/index.php) on a
   phone — same look as the réception board above: the three tabs one slim
   switch (short word + count, the chosen one white), the two counters small,
   and each order a compact card: reference · timer · button on one line, then
   zone client / zone de livraison / lignes / date as small tiles (grey capitals
   for the label, dark bold for the value). Scoped to tbody#prep-body-* and
   (2026-10-27, same look asked for) the expéditeur board's tbody#ship-body-* /
   #ship-tabs — whose switch is coloured like the réception one. */
.pb-tab-short { display: none; }
@media (max-width: 900px) {
  html body .prep-stats { gap: 8px; margin-bottom: 12px; }
  html body .prep-stats > .stat-compact { display: flex; flex-wrap: nowrap; align-items: baseline; gap: 6px; padding: 8px 12px; }
  html body .prep-stats > .stat-compact .stat-compact-ico { display: none; }
  html body .prep-stats > .stat-compact .stat-compact-value { font-size: 18px; }
  html body .prep-stats > .stat-compact .stat-compact-copy { flex: 1 1 auto; min-width: 0; white-space: normal; font-size: 12px; line-height: 1.25; }
  html body .prep-stats > .stat-compact .stat-compact-sub { display: none; }

  .card:has(> :is([data-table^="prep-"], [data-table^="ship-"])) { background: none; border: 0; box-shadow: none; }
  .card:has(> :is([data-table^="prep-"], [data-table^="ship-"])) > .card-head { padding: 0 0 10px; border: 0; }
  .card:has(> :is([data-table^="prep-"], [data-table^="ship-"])) > .board-filters { padding: 0 0 8px; border: 0; background: none; }
  .card:has(> :is([data-table^="prep-"], [data-table^="ship-"])) .table-wrap { border: 0; overflow: visible; }

  html body :is(#prep-tabs, #ship-tabs) {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px; width: 100%; overflow: visible;
    padding: 3px; border: 1px solid var(--line); border-radius: 10px; background: var(--slate-100);
  }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab {
    gap: 4px; min-width: 0; min-height: 36px; padding: 6px 2px;
    border: 0; border-radius: 8px; background: none; box-shadow: none;
    font-size: 12.5px; font-weight: 600; white-space: nowrap; color: var(--muted);
  }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab.active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 3px rgb(15 23 42 / .14); }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab > svg, html body :is(#prep-tabs, #ship-tabs) .pb-tab-long { display: none; }
  html body :is(#prep-tabs, #ship-tabs) .pb-tab-short { display: inline; }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab > .pill { padding: 0; border: 0; background: none; font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab > .pill::before { display: none; }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab.tone-blue > .pill  { color: var(--blue-700); }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab.tone-amber > .pill { color: var(--amber-700); }
  html body :is(#prep-tabs, #ship-tabs) > .m-tab.tone-green > .pill { color: var(--success-700); }

  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) { display: block; padding: 0; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr.job-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin-bottom: 8px; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: 10px; box-shadow: none;
  }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr.job-row::before { content: ""; order: 0; flex: 0 0 100%; height: 0; }
  /* The reference never shrinks — it's what the card is about; the count and button wrap under it instead. */
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-ref { order: -3; flex: 1 0 auto; min-width: 0; padding: 0; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-ref .job-card-ico { display: none; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-ref :is(a, .mono) { font-size: 14px; font-weight: 700; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-tag { order: -2; flex: 0 0 auto; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-tag > .muted { display: none; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-go { order: -1; flex: 0 0 auto; align-self: center; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) .job-card-go { height: 30px; min-height: 30px; padding: 0 10px; font-size: 12.5px; }
  /* Shipping: "Vérifier" stays beside the reference, the "Lignes vérifiées" count goes under them. */
  html body .table.job-table > tbody[id^="ship-body-"] > tr > td.jr-go { order: -2; }
  html body .table.job-table > tbody[id^="ship-body-"] > tr > td.jr-tag { order: -1; flex: 1 1 100%; }

  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-f {
    order: 1; flex: 1 1 calc(33% - 6px); flex-direction: column; flex-wrap: nowrap; align-items: flex-start; gap: 1px;
    min-width: 0; padding: 5px 8px; border-radius: 6px; background: var(--slate-50);
  }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-f.jr-lines { flex: 0 0 auto; min-width: 56px; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) > tr > td.jr-f.is-empty { display: none; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) .jr-label {
    font-size: 10px; font-weight: 600; line-height: 1.2; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
  }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) .jr-label svg { display: none; }
  html body .table.job-table > tbody:is([id^="prep-body-"], [id^="ship-body-"]) .jr-value { font-size: 13px; font-weight: 600; color: var(--ink); }
  /* Expéditeur switch in colour (like the réception board): each tab tinted in
     its tone, the chosen one filled with it. */
  html body #ship-tabs > .m-tab.job-tab > .pill { color: inherit; }
  html body #ship-tabs > .m-tab.tone-blue  { background: var(--blue-50); color: var(--blue-700); }
  html body #ship-tabs > .m-tab.tone-amber { background: var(--amber-50); color: var(--amber-700); }
  html body #ship-tabs > .m-tab.tone-green { background: var(--success-50); color: var(--success-700); }
  html body #ship-tabs > .m-tab.active { color: var(--white, #fff); box-shadow: 0 1px 3px rgb(15 23 42 / .18); }
  html body #ship-tabs > .m-tab.tone-blue.active  { background: var(--blue-600); }
  html body #ship-tabs > .m-tab.tone-amber.active { background: var(--amber-600); }
  html body #ship-tabs > .m-tab.tone-green.active { background: var(--success-600); }
}

/* Expéditeur check screen on a phone: the OK / Problem / Undo buttons share the
   row, the validate / report buttons go full width. */
@media (max-width: 900px) {
  .ship-check { flex-direction: column; align-items: stretch !important; }
  .ship-check > .pill { align-self: flex-start; }
  .ship-check > .row { width: 100%; }
  .ship-check > .row > form, .ship-check > .row > .btn { flex: 1 1 0; }
  .ship-check > form.row > .input { flex: 1 1 auto; max-width: none; }
  .ship-check .ship-check-note { text-align: start; }
  .ship-actions > form, .ship-actions > .btn { flex: 1 1 100%; }
  .ship-actions .btn { width: 100%; min-height: 44px; justify-content: center; }
}

/* Pick screen on a phone — picked part by part from a gallery, 2026-10-27
   (A1 · B2 · C1 · D2 · E1 · F1 · G1 · H1 · I1):
     A1 the zone to drop the order in as a light-blue banner (client zone on its right);
     B2 the two counters as two small tiles;
     C1 the scan field + an icon camera button, stuck under the top bar;
     D2 one compact row per product (photo · name + location · count);
     E1 the count coloured — amber partial, green complete, red short — its state in a word;
     F1 one action line per row: "Scannez chaque article" + a small Rupture, or
        ✓ Terminé · Modifier, or Rupture — N manquants · Annuler (rows stay white);
     G1 locations as blue pills;
     H1 a compact found sheet: photo beside the name, − count + on one line;
     I1 a bar stuck to the bottom: progress, "N à finir" / "Tout est prêt", Terminer
        (grey until nothing is left, never blocked — it still asks "finish anyway?").
   CSS only plus a few phone-only spans the laptop hides (.pick-drop, .pl-word,
   .pl-hint, .pick-finish-sum, .pick-finish-short). The markup stays a table on
   purpose: the scan / confirm script works off tr[data-line] / closest('td'). */
.pick-finish-short { display: none; }
.pl-nobarcode { display: flex; align-items: center; gap: 4px; margin-top: 3px; font-size: 12px; font-weight: 500; color: var(--muted); }
.pl-nobarcode .icon { width: 12px; height: 12px; }
.pl-btn-short { display: none; }
@media (min-width: 901px) {
  .pick-drop, .pl-word, .pl-hint, .pick-finish-sum { display: none; }
}
@media (max-width: 900px) {
  /* A1 — the banner stands in for the client-zone line and the "Déposer dans la zone" tag. */
  .page-head:has(.pick-drop) { margin-bottom: 12px; }
  .page-head:has(.pick-drop) > div { flex: 1 1 auto; min-width: 0; }
  .page-head:has(.pick-drop) h2 { font-size: 20px; }
  /* Reference and status on one line (the generic phone rule puts a title on its own line). */
  .page-head:has(.pick-drop) .row:has(> h2) { flex-wrap: nowrap; }
  .page-head:has(.pick-drop) .row > h2 { flex-basis: auto; }
  .page-head:has(.pick-drop) :is(.pick-client-zone, .pick-drop-line) { display: none; }
  .pick-drop { display: flex; align-items: center; gap: 10px; margin-top: 10px; padding: 10px 12px; border-radius: 12px; background: var(--primary-soft); color: var(--primary-strong); }
  .pick-drop > .icon { flex: none; width: 20px; height: 20px; }
  .pick-drop-main { display: flex; flex-direction: column; min-width: 0; }
  .pick-drop-k { font-size: 11.5px; color: var(--muted); }
  .pick-drop-main strong { font-size: 15px; }
  .pick-drop-client { margin-inline-start: auto; font-size: 12.5px; color: var(--muted); text-align: end; }

  /* B2 — two small tiles: "1/2" over "Lignes faites", "6/11" over "Articles scannés". */
  .pick-progress { display: none; }
  html body .stats-compact.pick-stats { grid-template-columns: 1fr 1fr; gap: 8px; margin: 0; }
  html body .stats-compact.pick-stats > .stat-compact {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 3px;
    padding: 8px 12px; border-radius: 12px; box-shadow: none; animation: none;
  }
  html body .pick-stats .stat-compact-ico { display: none; }
  html body .pick-stats .stat-compact-copy { display: contents; }
  html body .pick-stats .stat-compact-value { order: 1; font-family: var(--font-mono); font-size: 20px; font-weight: 800; line-height: 1.2; color: var(--ink); }
  html body .pick-stats .stat-compact-sub { order: 2; display: inline; font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--muted); }
  html body .pick-stats .stat-compact-label { order: 3; flex: 1 1 100%; font-size: 12px; font-weight: 500; color: var(--muted); }
  /* Arabic: "0 / 11" still reads left to right, like the lines' 0/9 — not "11 / 0". */
  html[dir="rtl"] body .stats-compact.pick-stats > .stat-compact { flex-direction: row-reverse; justify-content: flex-end; }
  html[dir="rtl"] body .pick-stats :is(.stat-compact-value, .stat-compact-sub) { direction: ltr; unicode-bidi: isolate; }

  /* C1 — the scan bar stuck under the top bar (64px); a plain block while the camera is open. */
  html body .card.pick-scan-card {
    position: sticky; top: 64px; z-index: 7; margin: 4px -18px 2px; padding: 8px 18px;
    border: 0; border-radius: 0; background: var(--bg); box-shadow: none;
  }
  html body .card.pick-scan-card:has(#pk-camwrap:not([hidden])) { position: static; }
  .pick-scan-card .card-title { display: none; }
  .pick-scan-card .scan-console { flex-direction: row; flex-wrap: nowrap; gap: 8px; margin-top: 0; }
  .pick-scan-card .scan-input { flex: 1 1 0; min-width: 0; height: 46px; border: 1.5px solid var(--primary); border-radius: 10px; }
  .pick-scan-card .pk-cam-btn:not([hidden]) { order: 1; flex: none; width: 46px; min-height: 46px; height: 46px; padding: 0; border-radius: 10px; }
  .pick-scan-card .pk-cam-btn .icon { width: 22px; height: 22px; }
  .pick-scan-card .pk-cam-btn > span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .pick-scan-card .pk-cam { max-width: none; }
  .scan-msg:empty { min-height: 0; margin-top: 0; }
  /* Other scan bars (the zone page): the camera button beside the scan field. */
  .scan-console { flex-wrap: nowrap; }
  .scan-input { flex: 1 1 0; min-width: 0; }
  .scan-cam-toggle { width: 48px; height: auto; }

  /* D2 — one compact row per product, white (no green tint: the count and its word say it). */
  .pick-lines thead, .pick-summary thead { display: none; }
  .pick-lines, .pick-lines tbody, .pick-summary, .pick-summary tbody { display: block; width: 100%; }
  html body .card:has(> .table-wrap > .pick-lines) { background: none; border: 0; box-shadow: none; }
  html body .card:has(> .table-wrap > .pick-lines) > .table-wrap { border: 0; overflow: visible; }
  .pick-lines tbody tr {
    display: grid; grid-template-columns: 44px minmax(0, 1fr) auto;
    grid-template-areas: "photo product qty" "photo loc qty" "actions actions actions";
    align-items: center; gap: 4px 11px; margin-top: 8px; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  }
  .pick-lines tbody tr:has(> .table-empty), .pick-summary tbody tr:has(> .table-empty) { display: block; }
  .pick-lines tbody td, .pick-summary tbody td { display: block; padding: 0 !important; border: 0 !important; text-align: start !important; background: none !important; white-space: normal; min-width: 0; overflow-wrap: anywhere; }
  /* The table's sticky actions column (and its edge shadow) means nothing on a row card. */
  .table-wrap .table.pick-lines tbody td.pl-actions { position: static !important; box-shadow: none !important; }
  /* .table prefix: the skin's `body.skin-shadcn .table tbody tr:hover` would otherwise win. */
  .table.pick-lines tbody tr, .table.pick-lines tbody tr:hover,
  .table.pick-lines tbody tr.is-confirmed, .table.pick-lines tbody tr.is-confirmed:hover { background: var(--surface); }
  .table.pick-lines tbody tr[data-short="1"]:not(.is-confirmed) { border-color: var(--rl-red-line, var(--red-400)); }
  .pick-summary tbody tr:hover { background: none; }
  .pick-lines .pl-photo { grid-area: photo; align-self: start; }
  .pick-lines .pl-photo .cell-thumb { width: 44px; height: 44px; border-radius: 9px; }
  .table.pick-lines tbody td.pl-product { grid-area: product; align-self: end; font-size: 13.5px; font-weight: 700; line-height: 1.3; color: var(--ink); }
  .table.pick-lines tbody td.pl-product > div:first-child { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .pick-lines .pl-loc { grid-area: loc; align-self: start; display: flex !important; flex-wrap: wrap; gap: 4px; }

  /* E1 — "4" big and coloured, "/9" small; the state in a word under it. */
  .table.pick-lines tbody td.pl-qty { grid-area: qty; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; text-align: end !important; }
  .pick-lines .pl-count { font-family: var(--font-mono); font-size: 14px; font-weight: 600; line-height: 1; color: var(--muted); white-space: nowrap; }
  .pick-lines .pl-count .pl-done { font-size: 22px; font-weight: 800; color: var(--ink); }
  .pick-lines .pl-count.is-part .pl-done { color: var(--rl-amber-ink, var(--amber-700)); }
  .pick-lines .pl-count.is-full .pl-done { color: var(--rl-green-ink, var(--success-700)); }
  .pick-lines tr[data-short="1"] .pl-count:not(.is-full) .pl-done { color: var(--rl-red-ink, var(--red-600)); }
  .pl-word > span { display: none; align-items: center; gap: 5px; font-family: var(--font-sans); font-size: 12px; font-weight: 600; white-space: nowrap; }
  .pl-word > span::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
  .pl-word .w-todo { color: var(--rl-slate-ink, var(--slate-600)); }
  .pl-word .w-part { color: var(--rl-amber-ink, var(--amber-700)); }
  .pl-word .w-done { color: var(--rl-green-ink, var(--success-700)); }
  .pl-word .w-short { color: var(--rl-red-ink, var(--red-600)); }
  .pick-lines tr:not([data-short="1"]) .pl-count:not(.is-part, .is-full) + .pl-word .w-todo,
  .pick-lines tr:not([data-short="1"]) .pl-count.is-part + .pl-word .w-part,
  .pick-lines .pl-count.is-full + .pl-word .w-done,
  .pick-lines tr[data-short="1"] .pl-count:not(.is-full) + .pl-word .w-short { display: inline-flex; }

  /* F1 — one action line under each row. */
  .pick-lines .pl-actions { grid-area: actions; margin-top: 6px; padding-top: 8px !important; border-top: 1px solid var(--line-soft) !important; }
  .pick-lines .pl-actions .row-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }
  /* "Rupture" on a phone ("Rupture de stock" on a laptop), so Valider · Quantité · Rupture fit one line. */
  .pick-lines .pl-btn-long { display: none; }
  .pick-lines .pl-btn-short { display: inline; }
  .pl-hint { display: inline-flex; flex: 1 1 auto; align-items: center; gap: 6px; min-width: 0; font-size: 12.5px; font-weight: 500; color: var(--muted); }
  .pick-lines .row-actions:has(.pick-confirm-btn:not([hidden])) .pl-hint { display: none; }
  .pick-lines .pl-actions .btn:not([hidden]) { flex: 0 0 auto; height: 36px; min-height: 36px; padding: 0 12px; border-radius: 8px; font-size: 13px; }
  .pick-lines .pl-actions .pick-confirm-btn:not([hidden]) { margin-inline-end: auto; }
  /* Rupture: a soft red button (no outline), the same on every line. */
  .pick-lines .pl-actions .btn.pick-short-btn { border-color: transparent; background: var(--rl-red-soft, var(--red-50)); color: var(--rl-red-ink, var(--red-600)); }
  /* No barcode (2026-10-27, redesigned on request): Valider · Quantité · Rupture as three
     equal buttons filling the line — Valider solid green (the one-tap "all there"),
     Quantité soft blue (type the count), Rupture soft red. A hidden one leaves no gap. */
  .pick-lines .row-actions:has(.pick-qty-btn) { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; }
  .pick-lines .row-actions:has(.pick-qty-btn) > .btn:not([hidden]) {
    width: 100%; height: 44px; min-height: 44px; margin: 0; padding: 0 6px; justify-content: center; gap: 6px;
    border-radius: 10px; font-size: 13.5px; font-weight: 600;
  }
  .pick-lines .row-actions:has(.pick-qty-btn) > .btn .icon { width: 16px; height: 16px; }
  .pick-lines .pl-actions .btn.pick-qty-btn { border-color: transparent; background: var(--primary-soft); color: var(--primary-strong); }
  .pick-lines .pl-actions .pick-state { flex: 1 1 auto; min-width: 0; min-height: 0; padding: 0; background: none; box-shadow: none; font-size: 13.5px; font-weight: 700; }
  .pick-lines .pl-actions .pick-state.is-done { color: var(--rl-green-ink, var(--success-700)); }
  .pick-lines .pl-actions .pick-state.is-short { color: var(--rl-red-ink, var(--red-600)); font-size: 13px; }
  .pick-lines .pl-actions .btn.pick-unshort-btn:not([hidden]),
  .pick-lines .pl-actions .btn.pick-edit-btn:not([hidden]) { padding-inline: 8px; color: var(--muted); }

  /* G1 — locations as blue pills (the skin's dot + word pill otherwise); "no location" amber. */
  html body .table.pick-lines .pl-loc .pill, html body .pk-sheet .pk-sheet-loc .pill {
    gap: 4px; padding: 2px 7px; border: 0; border-radius: 6px;
    background: var(--rl-blue-soft, var(--blue-50)); color: var(--rl-blue-ink, var(--blue-700));
    font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  }
  html body .table.pick-lines .pl-loc .pill.pill-amber, html body .pk-sheet .pk-sheet-loc .pill.pill-amber { background: var(--rl-amber-soft, var(--amber-50)); color: var(--rl-amber-ink, var(--amber-700)); font-family: var(--font-sans); }
  html body .table.pick-lines .pl-loc .pill::before { display: none; }
  .pick-lines .pl-loc .pill .icon { width: 12px; height: 12px; }

  /* H1 — compact found sheet: photo beside the name and location, then − count + on one line. */
  .pk-sheet:not(.pk-reason) { display: grid; grid-template-columns: 72px minmax(0, 1fr); align-items: start; column-gap: 12px; row-gap: 10px; text-align: start; }
  .pk-sheet:not(.pk-reason)::before { grid-column: 1 / -1; justify-self: center; }
  .pk-sheet:not(.pk-reason) > * { grid-column: 1 / -1; }
  .pk-sheet:not(.pk-reason) > .pk-sheet-photo { grid-column: 1; grid-row: 2 / span 3; width: 72px; height: 72px; padding: 4px; border-radius: 12px; align-self: start; }
  .pk-sheet:not(.pk-reason) > :is(.pk-sheet-name, .pk-sheet-loc, .pk-sheet-already) { grid-column: 2; margin: 0; text-align: start; justify-content: flex-start; }
  .pk-sheet:not(.pk-reason):has(> .pk-sheet-photo[hidden]) > :is(.pk-sheet-name, .pk-sheet-loc, .pk-sheet-already) { grid-column: 1 / -1; }
  .pk-sheet:not(.pk-reason) > .pk-sheet-name { grid-row: 2; font-size: 15px; }
  .pk-sheet:not(.pk-reason) > .pk-sheet-loc { grid-row: 3; }
  .pk-sheet:not(.pk-reason) > .pk-sheet-already { grid-row: 4; }
  .pk-sheet:not(.pk-reason) .pk-found-stepper { grid-template-columns: 52px minmax(0, 1fr) 52px; margin-top: 2px; }
  .pk-sheet:not(.pk-reason) .pk-found-step { height: 52px; border-radius: 12px; }
  .pk-sheet:not(.pk-reason) .pk-found-step .icon { width: 24px; height: 24px; }
  .pk-sheet:not(.pk-reason) .pick-found-count .pick-found-input { font-size: 32px; }
  .pk-sheet:not(.pk-reason) :is(.pick-found-sep, .pick-found-need) { font-size: 24px; }

  /* I1 — the bar fixed to the bottom of the screen, even under a short list; room left for it. */
  main:has(#pk-finish-row) { padding-bottom: 96px; }
  .pick-finish-row {
    position: fixed; inset-inline: 0; bottom: 0; z-index: 40; display: flex; align-items: center; gap: 12px;
    margin: 0; padding: 10px 18px calc(10px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line); background: var(--surface); box-shadow: 0 -6px 18px rgb(15 23 42 / .07);
  }
  .pick-finish-sum { display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; line-height: 1.3; }
  .pick-finish-k { font-size: 11.5px; color: var(--muted); }
  .pick-finish-k b { font-family: var(--font-mono); color: var(--ink); }
  .pick-finish-sum strong { font-size: 15px; color: var(--ink); }
  .pick-finish-row.is-ready .pick-finish-sum strong { color: var(--rl-green-ink, var(--success-700)); }
  .pick-finish { flex: none; width: auto; min-height: 46px; justify-content: center; font-size: 15px; }
  .pick-finish-row:not(.is-ready) .pick-finish { border-color: var(--slate-300); background: var(--slate-300); color: var(--white); }
  .pick-finish-long { display: none; }
  .pick-finish-short { display: inline; }

  /* The read-only summary (a prepared order). */
  .pick-lines td[data-label]::before, .pick-summary td[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 1px;
    font-family: var(--font-sans); font-size: 11px; font-weight: 600; color: var(--muted); letter-spacing: 0;
  }
  .pick-summary tbody tr { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); }
  .table.pick-summary tbody td:first-child { grid-column: 1 / -1; font-weight: 700; font-size: 15px; color: var(--ink); }
}

/* Receiving — putaway as mobile cards (no overflowing table). */
/* Location shown below as a big grey selector (editable — change where it goes). */
.pa-dest { display: flex; align-items: center; gap: 8px; width: 100%; padding: 4px 12px; border-radius: 6px; background: var(--slate-100); border: 1px solid var(--line); color: var(--slate-500); }
.pa-dest svg { flex: 0 0 auto; }
.pa-dest select { flex: 1 1 auto; min-width: 0; border: 0; background: none; padding: 10px 0; font-size: 15px; font-weight: 700; color: var(--ink); }
.pa-dest select:focus { outline: 0; }
.pa-dest.is-done { font-weight: 700; color: var(--ink); padding: 10px 12px; }
/* Warehouse positions summary (all spots an item lives in — multi-position aware). */
.pa-pos-list { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pa-pos-list .pill svg { width: 13px; height: 13px; }

/* /inventory — compact stock table. Scope selects sit in the page head and
   the search in the card head, so the thead is a single row; every cell is
   one line (long names ellipsize, full name in the title tooltip) so a row
   stays about one thumbnail high. */
.stock-scope .filter-select { width: auto; min-width: 190px; }
.stock-card-tools { gap: 14px; }
.stock-search { width: 280px; }
.stock-table thead th { white-space: nowrap; }
/* The two long heads take two lines so the whole table fits a 1440px laptop. */
body .table.stock-table thead th.th-wrap { white-space: normal; min-width: 90px; }
/* Names are long ALL-CAPS labels (~30 chars): up to two lines instead of
   cut at ~22 characters; the full name stays in the title tooltip. */
.stock-name { min-width: 190px; max-width: 270px; }
.stock-name-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.stock-supplier { display: block; max-width: 160px; overflow: hidden; text-overflow: ellipsis; }
.stock-name .link {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; white-space: normal; line-height: 1.3; font-weight: 600; color: var(--ink);
}
.stock-table td.right { font-variant-numeric: tabular-nums; }
.stock-table td.mono { font-size: 12px; }
.stock-table .stock-site-trigger { min-width: 64px; justify-content: flex-end; }
/* The stock as a plain number (no pill): coloured only where it means something
   — red negative, amber to order, grey zero, ink otherwise; the unit muted. */
.stock-qty {
    display: inline-flex; align-items: baseline; gap: 4px; padding: 2px 0; border: 0; background: none;
    font: inherit; color: var(--ink); cursor: pointer; white-space: nowrap;
}
.stock-qty b { font-family: var(--font-mono); font-size: 14px; font-weight: 600; }
.stock-qty span { font-family: var(--font-sans); font-size: 12px; color: var(--muted); }
.stock-qty .icon { align-self: center; color: var(--red-600); }
.stock-qty-neg b { color: var(--red-600); }
.stock-qty-order b { color: var(--amber-700); }
.stock-qty-zero b { color: var(--faint); font-weight: 500; }
.stock-qty:hover b { text-decoration: underline; text-underline-offset: 3px; }
/* A row opens its product sheet (the panel on the right). */
.stock-table tbody tr[data-sku] { cursor: pointer; }
.stock-table tbody tr.is-sheet-open > td { background: var(--primary-soft); }
.stock-sheet .filter-drawer-panel { width: min(440px, 100%); }
.stock-sheet-body { display: flex; flex-direction: column; gap: 14px; padding-top: 16px; }
.stock-sheet-hero { display: flex; gap: 14px; align-items: center; }
.stock-sheet-photo { width: 76px; height: 76px; flex: none; border-radius: 12px; object-fit: contain; padding: 6px; background: var(--slate-50); border: 1px solid var(--line-soft); }
.stock-sheet-who { min-width: 0; }
.stock-sheet-name { display: block; font-size: 15px; font-weight: 600; line-height: 1.3; color: var(--ink); }
.stock-sheet-big { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.stock-sheet-qty { display: flex; align-items: baseline; gap: 6px; }
.stock-sheet-qty b { font-family: var(--font-sans); font-size: 34px; font-weight: 700; letter-spacing: -.02em; line-height: 1; color: var(--ink); }
.stock-sheet-qty span { font-size: 14px; color: var(--muted); }
.stock-sheet-qty .icon { width: 20px; height: 20px; align-self: center; color: var(--red-600); }
.stock-sheet-qty.stock-qty-neg b { color: var(--red-600); }
.stock-sheet-qty.stock-qty-order b { color: var(--amber-700); }
.stock-sheet-qty.stock-qty-zero b { color: var(--faint); }
.stock-sheet-state { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
.stock-sheet-word { font-size: 13px; font-weight: 600; color: var(--muted); }
.stock-sheet-word.is-neg { color: var(--red-600); }
.stock-sheet-word.is-order { color: var(--amber-700); }
.stock-sheet-word.is-ok { color: var(--success-700, var(--emerald-700)); }
.stock-sheet-facts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.stock-sheet-fact { display: flex; flex-direction: column; gap: 2px; padding: 9px 12px; border-radius: 10px; background: var(--slate-50); font-size: 13px; }
.stock-sheet-fact > span { font-size: 12px; color: var(--muted); }
.stock-sheet-fact > div { color: var(--ink); font-weight: 500; }
.stock-sheet-fact .text-xs { font-weight: 400; }
#inv-sheet-sites .stock-sheet-fact { flex-direction: row; justify-content: space-between; background: none; padding: 4px 0; border-bottom: 1px solid var(--line-soft); border-radius: 0; }
.stock-sheet-box { border: 1px solid var(--line-soft); border-radius: 12px; padding: 12px 14px; }
.stock-sheet-box-title { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.stock-sheet-box-title .mono { text-transform: none; letter-spacing: 0; color: var(--ink); }
.stock-sheet-box .stock-supplier { max-width: none; }
.stock-sheet-box .pa-pos-list { display: flex; flex-wrap: wrap; gap: 6px; }
.stock-sheet-box .pa-pos-list .pill::before { display: none; }
.stock-sheet-chart { min-height: 110px; }
.stock-sheet-svg { display: block; width: 100%; height: auto; overflow: visible; }
.stock-sheet-svg polygon { fill: var(--primary); opacity: .1; }
.stock-sheet-svg polyline { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.stock-sheet-svg circle { fill: var(--primary); }
.stock-sheet-svg.is-neg polygon, .stock-sheet-svg.is-neg circle { fill: var(--red-600); }
.stock-sheet-svg.is-neg polyline { stroke: var(--red-600); }
.stock-sheet-svg.is-order polygon, .stock-sheet-svg.is-order circle { fill: var(--amber-700); }
.stock-sheet-svg.is-order polyline { stroke: var(--amber-700); }
.stock-sheet-zero { stroke: var(--line); stroke-dasharray: 4 4; }
.stock-sheet-weeks { display: flex; justify-content: space-between; margin-top: 4px; font-size: 11px; color: var(--faint); }
.stock-sheet .filter-drawer-foot { justify-content: space-between; }
.stock-table .pa-pos-list { gap: 4px; }
/* Create a BC fournisseur from ticked /inventory rows (modules/stock/pages/index.php). */
.stock-table .stock-pick-col { width: 32px; padding-right: 0; }
.stock-pick-col input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--primary); }
.stock-pick-col input:disabled { cursor: not-allowed; opacity: .35; }
.stock-so-modal { width: min(820px, 100%); }
.stock-so-help { margin: -8px 0 14px; }
.stock-so-qty { width: 96px; text-align: right; margin-left: auto; }
.stock-so-hint { margin-top: 3px; color: var(--muted); white-space: nowrap; }
.stock-so-hint.is-error { color: var(--red-600, #dc2626); }
.stock-table .pa-pos-list .pill { padding: 2px 8px; font-size: 11px; }
.stock-table .pa-pos-list .pill::before { display: none; }
.stock-badges { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; margin-top: 4px; }   /* wraps so the column stays narrow enough for the table to fit a laptop */
.stock-badge {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 6px; border-radius: 999px; white-space: nowrap;
  font-size: 11px; font-weight: 600; background: var(--blue-50); color: var(--blue-700);
}
.stock-badge .icon { width: 11px; height: 11px; }
.stock-at-min { color: var(--amber-700); font-weight: 600; }
.stock-reco-need { color: var(--amber-700); font-weight: 600; }
/* Same idea on /products and /incoming-orders: a second line instead of a table cut at 1440px. */
body .table.product-table td.pt-stock > div.pill { display: flex; width: fit-content; }
body .table td .io-refused-note { display: inline-block; min-width: 150px; white-space: normal; text-align: right; }
body .table.stock-table .stock-reco-need { white-space: normal; }   /* "Commander 2 seaux" on two lines rather than widening the table */
/* /inventory tiles: one short row each — icon, number, label; the longer
   explanation is the tile's tooltip. */
.stats-inline { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); margin-bottom: 16px; }
.stats-inline .stat-compact { display: flex; align-items: center; gap: 8px; padding: 8px 12px; }
.stats-inline .stat-compact[title] { cursor: help; }
.stats-inline .stat-compact-value { flex: none; font-size: 17px; line-height: 1; overflow-wrap: normal; white-space: nowrap; }
.stats-inline .stat-compact-copy { min-width: 0; }
.stats-inline .stat-compact-label { display: block; overflow: hidden; text-overflow: ellipsis; color: var(--muted); }
@media (max-width: 900px) {
  .stats-inline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* A long value (money + unit) gets the whole row instead of overflowing. */
  .stats-inline .stat-compact.is-wide { grid-column: 1 / -1; }
}
@media (max-width: 720px) {
  .stock-card-tools { flex-wrap: wrap; justify-content: flex-end; }
  .stock-search { width: 100%; }
}
/* Inline assign (étage select + button) inside the positions table. */
.pa-assign { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.pa-assign .pa-dest { width: auto; min-width: 200px; flex: 1 1 200px; }
.pa-assign .btn { flex: 0 0 auto; }
/* Bulk bar above the putaway table — one étage picker + submit applied to every checked row. */
.pa-bulk-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 14px; padding: 10px 12px; border-radius: 8px; background: var(--slate-50); border: 1px solid var(--line-soft); }
.pa-bulk-bar .pa-dest { width: auto; min-width: 220px; flex: 1 1 220px; }
.pa-bulk-bar .btn { flex: 0 0 auto; }

/* ==== App-wide admin refinement — see design.md.
   This used to be two opt-in wrapper passes (.dash-v2 on the dashboard,
   .inv-v2 on invoices). Now that the same treatment is the standing system
   for every admin-shell page (design.md), the shared parts were promoted
   into the base .stat/.page-head/.table/.kv/.step rules above — no wrapper,
   no per-page opt-in, every page using those classes gets it for free.
   What's left here is genuinely page-specific: document-summary components
   (invoices' hero strip / progress bar / grand-total row — reusable by any
   future billing page) and the dashboard's own decorative hero layer (kept
   opt-in on purpose — it's the one showcase/overview page, see
   modules/dashboard/CLAUDE.md; the same flourish on a dense CRUD table would
   be noise, not polish). */

/* ---- Document-summary components (invoices; reusable by any billing page) */
.inv-collect {
    background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
    padding: 15px 18px; box-shadow: var(--shadow-card); margin-bottom: 24px;
    animation: rise .4s ease both .05s;
}
.inv-collect-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.inv-collect-label { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--slate-600); }
.inv-collect-label .icon { width: 16px; height: 16px; color: var(--primary); }
.inv-collect-pct { font-size: 20px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.inv-collect-track { position: relative; height: 8px; border-radius: 999px; background: var(--slate-100); margin-top: 12px; overflow: hidden; }
.inv-collect-fill { height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--emerald-600), var(--emerald-700)); transition: width .6s cubic-bezier(.16, 1, .3, 1); }
.inv-collect-foot { display: flex; justify-content: space-between; margin-top: 8px; font-size: 12px; color: var(--faint); }

/* .inv-summary is the outer card (border/shadow/radius); .inv-summary-row
   holds the flex row of stat items, and .inv-progress is a second row
   below it inside the SAME card (border-top separator) — kept as one
   visual block instead of a bordered card with a borderless progress
   strip floating in the page gutter underneath it. */
.inv-summary {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 6px; box-shadow: var(--shadow-card); margin-bottom: 16px; overflow: hidden;
    animation: rise .4s ease both;
}
.inv-summary-row { display: flex; align-items: stretch; }
.inv-summary-item { flex: 1 1 0; padding: 13px 16px; position: relative; min-width: 0; }
.inv-summary-item + .inv-summary-item::before { content: ""; position: absolute; left: 0; top: 13px; bottom: 13px; width: 1px; background: var(--line-soft); }
.inv-summary-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--faint); }
.inv-summary-value { font-size: 19px; font-weight: 800; margin-top: 4px; font-variant-numeric: tabular-nums; letter-spacing: -.01em; color: var(--ink); overflow-wrap: anywhere; }
.inv-summary-value.is-balance { color: var(--amber-700); }
.inv-summary-value.is-balance.is-clear { color: var(--primary-strong); }

.inv-progress { padding: 12px 16px 14px; border-top: 1px solid var(--line-soft); }
.inv-progress-track { height: 6px; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.inv-progress-fill { height: 100%; border-radius: inherit; transition: width .6s cubic-bezier(.16, 1, .3, 1); }
.inv-progress-fill.status-paid { background: var(--primary); }
.inv-progress-fill.status-partial { background: var(--amber-600); }
.inv-progress-fill.status-unpaid { background: var(--slate-300); }

/* Grand-total row — any table's <tfoot> can opt in with class="grand-total" */
.table tfoot .grand-total td { background: var(--primary-soft); font-size: 16px; padding-top: 16px; padding-bottom: 16px; }
.table tfoot .grand-total td strong { color: var(--primary-strong); }

@media (max-width: 640px) {
    .inv-summary-row { flex-direction: column; }
    .inv-summary-item + .inv-summary-item::before { top: 0; left: 16px; right: 16px; bottom: auto; width: auto; height: 1px; }
    .inv-collect-top { align-items: flex-start; }
}

/* ---- Dashboard-only decorative hero layer (opt-in via .hero-panel — the
   one overview/showcase page; see modules/dashboard/CLAUDE.md). Everything
   this used to share with the base pass (page-head weight, stat hover,
   tabular-nums, stepper current-ring) now lives in the base rules above. */
.hero-panel {
    position: relative;
    border-radius: 6px;
    background-image:
        radial-gradient(480px 260px at 0% 0%, var(--emerald-50) 0%, transparent 60%),
        radial-gradient(420px 240px at 100% 0%, var(--blue-50) 0%, transparent 55%);
    background-repeat: no-repeat;
}
.hero-panel .page-head h2 { position: relative; display: inline-block; }
.hero-panel .page-head h2::after {
    content: "";
    position: absolute;
    left: 1px; bottom: -6px;
    width: 34px; height: 3px;
    border-radius: 6px;
    background: linear-gradient(90deg, var(--primary), var(--blue-600));
}
.hero-panel .page-head .sub { margin-top: 12px; }

/* Recent orders / Recent activity — .dash-equal-row stretches both cards to
   match the taller one, and each card's body (.dash-scroll-body) caps at a
   fixed height with its own scrollbar, so neither card's height depends on
   how many rows it happens to have. Active delivery moved below, full width.
   Entrance animation + hover lift are still opt-in for the dashboard's own
   cards. */
.hero-panel .card { border-radius: 6px; }
.hero-panel .grid-2 > .card { animation: rise .5s ease both; transition: box-shadow .2s ease; }
.hero-panel .grid-2 > .card:nth-child(1) { animation-delay: .22s; }
.hero-panel .grid-2 > .card:nth-child(2) { animation-delay: .28s; }
.hero-panel .grid-2 > .card:hover { box-shadow: 0 18px 36px -18px rgba(15, 23, 42, .2); }

.dash-equal-row { align-items: stretch; }
.dash-scroll-card { display: flex; flex-direction: column; }
.dash-scroll-body {
    height: 360px;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--slate-300) transparent;
}
.dash-scroll-body thead th { position: sticky; top: 0; z-index: 1; }
/* On a phone the rows are cards, so a fixed-height box cut the last one in
   half: the boxes fit their rows instead, five at most ("Voir tout" for the
   rest), and the to-do list shows every line. */
@media (max-width: 900px) {
  .dash-scroll-body { height: auto; overflow: visible; }
  .dash-scroll-body .table > tbody > tr:nth-child(n+6),
  .dash-scroll-body .activity-item:nth-child(n+6) { display: none; }
  .alerts-card .alerts-list { max-height: none; overflow: visible; }
}

/* Dashboard parts picked from the gallery 2026-10-28 (case 196). */
.card-icon-chip.red { background: var(--red-50); color: var(--red-600); }

/* B4 "À traiter": the count big in its severity colour, the label over its
   note, an "Ouvrir" drawn as a button. The row stays one <a>. */
.alerts-card.alerts-card--b4 { max-width: none; }
.alerts-card--b4 .alerts-card-head { padding: 10px 14px; gap: 10px; }
.alerts-card--b4 .alerts-card-title { font-size: 14px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink); }
.alerts-card--b4 .alerts-list { padding: 0; max-height: 272px; }
.alerts-card--b4 .alerts-list li + li { border-top: 1px solid var(--line-soft); }
.alerts-card--b4 .alerts-row { height: auto; padding: 10px 14px; gap: 14px; border-radius: 0; }
.alerts-card--b4 .alerts-row-count { width: 56px; text-align: right; font-size: 22px; font-weight: 600; }
.alerts-card--b4 .alerts-row-copy { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.alerts-card--b4 .alerts-row-label { flex: none; font-size: 14px; font-weight: 600; color: var(--ink); white-space: normal; }
.alerts-card--b4 .alerts-row-note { font-size: 13px; color: var(--muted); }
.alerts-card--b4 .alerts-row-go { opacity: 1; color: var(--text); }
.alerts-card--b4 .alerts-row:hover .alerts-row-go { border-color: var(--primary); color: var(--primary); }

/* D1: five tiles in one row on a laptop, two per row on a phone. */
.kpi-grid--5 { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.kpi-grid--5 .kpi-card--k5 .kpi-card-main { padding: 12px; gap: 10px; }
.kpi-grid--5 .kpi-card--k5 .kpi-card-ico { width: 36px; height: 36px; }
.kpi-grid--5 .kpi-card--k5 .kpi-card-ico .icon { width: 18px; height: 18px; }
body .kpi-grid--5 .kpi-card-value { font-size: 19px; white-space: nowrap; }
/* The foot's fact wraps under itself instead of being cut with "…". */
.kpi-grid--5 .kpi-card--k5 .kpi-card-foot { padding: 7px 12px; gap: 2px 8px; flex-wrap: wrap; }
.kpi-grid--5 .kpi-card--k5 .kpi-card-foot :is(.kpi-card-change, .kpi-card-live) { white-space: normal; overflow: visible; }
@media (max-width: 640px) {
  .kpi-grid--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* E2: revenue per day as bars, today's in full colour. */
.rev-bars { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; height: 200px; }
.rev-bars.is-dense { gap: 2px; }
.rev-bar { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.rev-bar-track { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; }
.rev-bar-fill { display: block; border-radius: 4px 4px 0 0; background: var(--rl-primary-line, var(--primary-soft)); transition: background-color .12s ease; }
.rev-bar.is-today .rev-bar-fill, .rev-bar:hover .rev-bar-fill { background: var(--primary); }
.rev-bar-value { margin-bottom: 3px; font-size: 11px; text-align: center; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.rev-bar-label { height: 14px; font-size: 11px; line-height: 14px; text-align: center; color: var(--faint); white-space: nowrap; }
.rev-bar.is-today .rev-bar-label { color: var(--ink); font-weight: 600; }
/* Past 7 bars "jeu. 15" is wider than its bar: the day goes under its name. */
.rev-bars.has-many .rev-bar-label { width: min-content; height: 28px; margin: 0 auto; white-space: normal; }
.rev-empty { margin: 8px 0 0; font-size: 13px; }
/* A phone has room for every third date and no amounts on 14 bars. */
@media (max-width: 640px) {
  .rev-bars.has-many .rev-bar-value { display: none; }
  .rev-bar--minor .rev-bar-label { visibility: hidden; }
}

/* G2: the orders card's tabs are buttons switched in the page. */
.dash-tabs { margin: 0; padding: 0 16px; gap: 18px; }
.dash-tabs .list-tab { padding: 10px 0; background: none; border: 0; border-bottom: 2px solid transparent; font: inherit; font-size: 13.5px; cursor: pointer; }
.dash-tabs .list-tab.is-active { border-bottom-color: var(--primary); }
/* The head and the tab bar keep their size whatever the list's length (a long
   list used to squash the tabs), and every tab puts its columns in the same place. */
.dash-scroll-card > .card-head, .dash-tabs { flex: none; }
@media (min-width: 901px) {
  .dash-orders-table { table-layout: fixed; width: 100%; }
  .dash-orders-table th:nth-child(1) { width: 21%; }
  .dash-orders-table th:nth-child(3) { width: 21%; }
  .dash-orders-table th:nth-child(4) { width: 20%; }
  .dash-orders-table th:nth-child(5) { width: 15%; }
}
.dash-orders-table td { white-space: normal; }
.dash-orders-table td.mono, .dash-orders-table td.dash-when { white-space: nowrap; }
.dash-orders-table .pill { white-space: normal; }
.dash-orders-table td.dash-when { font-size: 12.5px; color: var(--muted); }
.dash-orders-table td.dash-late { color: var(--red-600); }

/* The two cards side by side keep one height; the orders card spends some of it on its tabs. */
.dash-equal-row > .dash-scroll-card { height: 430px; }
.dash-equal-row .dash-scroll-body { flex: 1 1 0; height: auto; min-height: 0; }
@media (max-width: 900px) {
  .dash-equal-row > .dash-scroll-card { height: auto; }
  .dash-equal-row .dash-scroll-body { flex: none; }
  .dash-scroll-body .dash-who:nth-child(n+6) { display: none; }
}

/* H1 "Qui a fait quoi". */
.dash-who-list { display: flex; flex-direction: column; }
.dash-who { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 12px; align-items: center; padding: 10px 16px; border-top: 1px solid var(--line-soft); }
.dash-who:first-child { border-top: 0; }
.dash-who-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; font-size: 13px; }
.dash-who-copy b { color: var(--ink); font-weight: 600; }
.dash-who-time { font-size: 12.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* J1 "L'équipe maintenant": one column per job. */
.dash-team { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.dash-team-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 12px 16px; border-left: 1px solid var(--line-soft); }
.dash-team-col:first-child { border-left: 0; }
.dash-team-head { display: flex; justify-content: space-between; align-items: center; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.dash-team-head b { font-size: 13px; letter-spacing: 0; color: var(--ink); font-variant-numeric: tabular-nums; }
.dash-person { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; align-items: center; }
.dash-person-copy { display: flex; flex-direction: column; min-width: 0; font-size: 12.5px; }
.dash-person-copy b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; color: var(--ink); }
.dash-person-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--slate-300); }
.dash-person-dot.is-busy { background: var(--primary); }
.dash-team-none { font-size: 12.5px; }
@media (max-width: 900px) {
  .dash-team { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-team-col:nth-child(odd) { border-left: 0; }
  .dash-team-col:nth-child(n+3) { border-top: 1px solid var(--line-soft); }
}
@media (max-width: 560px) {
  .dash-team { grid-template-columns: 1fr; }
  .dash-team-col { border-left: 0; }
  .dash-team-col + .dash-team-col { border-top: 1px solid var(--line-soft); }
}

.hero-panel .table tbody tr { animation: rise .4s ease both; }
.hero-panel .table tbody tr:nth-child(1) { animation-delay: .3s; }
.hero-panel .table tbody tr:nth-child(2) { animation-delay: .34s; }
.hero-panel .table tbody tr:nth-child(3) { animation-delay: .38s; }
.hero-panel .table tbody tr:nth-child(4) { animation-delay: .42s; }
.hero-panel .table tbody tr:nth-child(5) { animation-delay: .46s; }

.hero-panel .pill-blue { animation: hero-pulse-ring 2.4s ease-in-out infinite; }
@keyframes hero-pulse-ring {
    0%, 100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, .25); }
    50% { box-shadow: 0 0 0 5px rgba(37, 99, 235, 0); }
}

/* Icon tiles get a little life on hover — a small overshoot pop instead of
   the base .kpi-card-ico's plain color swap. */
.hero-panel .kpi-card-ico { transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), background .15s ease, color .15s ease; }
.hero-panel .kpi-card:hover .kpi-card-ico { transform: scale(1.1) rotate(-4deg); }

/* "Open orders" gets a live pulse dot next to its badge when anything is
   actually on the road — absent (not just hidden) otherwise, so it never
   implies live movement that isn't happening. Reused by .kpi-card-live-dot. */
@keyframes live-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(217, 119, 6, .45); }
    50% { box-shadow: 0 0 0 4px rgba(217, 119, 6, 0); }
}

/* Trend chart draws itself in on load rather than just appearing — pathLength="100"
   on the polyline (set in pages/index.php) makes the dasharray unit-independent
   of the actual data range. */
.hero-panel .trend-svg .trend-line {
    stroke-dasharray: 100; stroke-dashoffset: 100;
    animation: trend-draw 1.1s cubic-bezier(.16, 1, .3, 1) .15s forwards;
}
.hero-panel .trend-svg .trend-area { opacity: 0; animation: trend-area-fade .6s ease .5s forwards; }
@keyframes trend-draw { to { stroke-dashoffset: 0; } }
@keyframes trend-area-fade { to { opacity: .1; } }

@media (prefers-reduced-motion: reduce) {
    .main, .stat, .stats .stat, .hero-panel, .hero-panel *,
    .inv-collect, .inv-summary, .step.current .bullet,
    .trend-guide, .trend-tooltip, .pipe-legend-row,
    .nav-item, .nav-item .icon, .nav-group-label, .sidebar, .m-sidebar, .sidebar-backdrop,
    .nav-foot-btn,
    .auth-card, .auth-aside-logo, .auth-aside-copy, .auth-aside-tag, .auth-submit::after,
    .lv-nav-btn, .lv-photo-btn,
    .product-media-card, .product-editor-fields, .product-sku-card,
    .sku-row, .catalog-options,
    .confirm-modal-backdrop, .confirm-modal,
    tr.is-scan-hit > td {
        animation: none !important; transition: none !important;
    }
    /* These two hold a mid-animation (invisible) state as their plain CSS
       starting point and rely on the animation's "forwards" fill to reach
       the visible end state — cancelling the animation above would freeze
       them hidden, so the reduced-motion end state has to be set explicitly. */
    .hero-panel .trend-svg .trend-line { stroke-dashoffset: 0; }
    .hero-panel .trend-svg .trend-area { opacity: .1; }
}

/* ----------------------------------------------------------------------
 | Client Portal shell — third shell, external B2B self-service. Simple
 | top-nav (not the admin sidebar, not the field shell's hamburger menu).
 | See resources/views/partials/header_portal.php / footer_portal.php.
 |--------------------------------------------------------------------- */
/* Built like a mobile app: phones get a slim sticky top bar, a floating
   bottom tab bar (raised "order" button in the middle) and a "More" bottom
   sheet; from 1024px a light left sidebar replaces tab bar + sheet. */
.portal-body { background: var(--bg); -webkit-tap-highlight-color: transparent; }
:where(.portal-app) button { border: 0; background: none; padding: 0; color: inherit; font: inherit; cursor: pointer; }
.portal-app { min-height: 100vh; display: flex; }
.portal-col { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.portal-main {
    flex: 1 1 auto; max-width: 1180px; width: 100%; margin: 0 auto;
    padding: 18px 16px calc(104px + env(safe-area-inset-bottom));
}

.portal-brand { display: flex; align-items: center; gap: 9px; font-weight: 800; color: var(--ink); flex: 0 0 auto; min-width: 0; }
.portal-brand .brand-mark { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 10px; background: var(--ink); color: var(--white); }
.portal-brand .brand-mark .icon { width: 18px; height: 18px; }
.portal-user-avatar {
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 34px; height: 34px; border-radius: 999px;
    background: var(--primary); color: var(--white); font-weight: 700; font-size: 13.5px;
}
.portal-user-avatar.is-lg { width: 46px; height: 46px; font-size: 18px; }
.portal-user-copy { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.portal-user-copy strong { font-size: 14px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-user-copy span { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Top bar */
.portal-topbar {
    position: sticky; top: 0; z-index: 40;
    display: flex; align-items: center; gap: 10px;
    height: calc(60px + env(safe-area-inset-top)); padding: env(safe-area-inset-top) 16px 0;
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    -webkit-backdrop-filter: saturate(1.6) blur(12px); backdrop-filter: saturate(1.6) blur(12px);
    border-bottom: 1px solid var(--line);
}
.portal-topbar-back {
    display: inline-grid; place-items: center; flex: 0 0 auto; width: 38px; height: 38px;
    border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.portal-topbar-back:hover { background: var(--hover); }
.portal-topbar-title { display: none; flex: 1 1 auto; min-width: 0; font-size: 17px; font-weight: 700; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.portal-topbar-brand { flex: 1 1 auto; }
.portal-topbar-brand > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-topbar .notif { margin-left: auto; }
.portal-topbar-avatar { display: inline-flex; border-radius: 999px; }

/* Bottom tab bar (phones/tablets) */
.portal-tabbar {
    position: fixed; z-index: 45; left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
    display: flex; align-items: stretch; justify-content: space-around; gap: 2px;
    height: 66px; padding: 0 6px; max-width: 520px; margin: 0 auto;
    background: var(--surface); border: 1px solid var(--line); border-radius: 24px;
    box-shadow: 0 12px 34px rgba(15, 23, 42, .16);
}
.portal-tab {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    color: var(--muted); font-size: 11px; font-weight: 600;
}
.portal-tab-icon { display: grid; place-items: center; width: 46px; height: 30px; border-radius: 999px; transition: background .15s, color .15s; }
.portal-tab-icon .icon { width: 21px; height: 21px; }
.portal-tab-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-tab.is-active { color: var(--ink); }
.portal-tab.is-active .portal-tab-icon { background: var(--primary-soft); color: var(--primary-strong); }
.portal-tab.is-cta .portal-tab-icon {
    width: 54px; height: 54px; margin-top: -30px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white); border: 4px solid var(--bg);
    box-shadow: 0 8px 20px rgba(15, 23, 42, .25);
}
.portal-tab.is-cta .portal-tab-icon .icon { width: 22px; height: 22px; }
.portal-tab.is-cta.is-active .portal-tab-icon { background: var(--emerald-900); }
.portal-tab.is-cta { color: var(--ink); }

/* "More" bottom sheet */
.portal-sheet-backdrop { position: fixed; inset: 0; z-index: 70; background: rgba(15, 23, 42, .45); }
.portal-sheet {
    position: fixed; z-index: 71; left: 0; right: 0; bottom: 0; max-width: 560px; margin: 0 auto;
    max-height: 86vh; overflow-y: auto; padding: 8px 14px calc(18px + env(safe-area-inset-bottom));
    background: var(--surface); border-radius: 22px 22px 0 0; box-shadow: 0 -12px 32px rgba(15, 23, 42, .18);
    transition: transform .22s ease;
}
.portal-sheet.is-off { transform: translateY(100%); }
.portal-sheet::before { content: ''; display: block; width: 40px; height: 4px; margin: 2px auto 10px; border-radius: 999px; background: var(--slate-300); }
.portal-sheet-head { display: flex; align-items: center; gap: 12px; padding: 6px 4px 14px; border-bottom: 1px solid var(--line-soft); }
.portal-sheet-head .portal-user-copy { flex: 1 1 auto; }
.portal-sheet-close { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 999px; color: var(--muted); background: var(--slate-100) !important; }
.portal-sheet-nav { display: flex; flex-direction: column; padding-top: 8px; }
.portal-sheet-nav form { margin: 0; }
.portal-sheet-link {
    display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px 6px; border-radius: 12px;
    font-size: 15px; font-weight: 600; color: var(--ink); text-align: left;
}
.portal-sheet-link:hover, .portal-sheet-link.is-active { background: var(--hover); }
.portal-sheet-link > .icon { color: var(--faint); }
.portal-sheet-ico { display: grid; place-items: center; flex: 0 0 auto; width: 38px; height: 38px; border-radius: 12px; background: var(--slate-100); color: var(--slate-700); }
.portal-sheet-text { flex: 1 1 auto; }
.portal-sheet-value { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; color: var(--muted); }
.portal-sheet-link.is-danger { color: var(--red-600); }
.portal-sheet-link.is-danger .portal-sheet-ico { background: var(--red-50); color: var(--red-600); }

/* Sidebar (desktop) */
.portal-side { display: none; }
@media (min-width: 1024px) {
    .portal-side {
        position: sticky; top: 0; height: 100vh; flex: 0 0 250px; width: 250px;
        display: flex; flex-direction: column; gap: 18px; padding: 20px 14px 16px;
        background: var(--surface); border-right: 1px solid var(--line); overflow-y: auto;
    }
    .portal-side > .portal-brand { padding: 0 8px; font-size: 16px; }
    .portal-side-cta {
        display: flex; align-items: center; justify-content: center; gap: 8px; height: 44px; border-radius: 12px;
        background: var(--emerald-800); color: var(--white); font-size: 14px; font-weight: 700;
    }
    .portal-side-cta:hover, .portal-side-cta.is-active { background: var(--emerald-900); }
    .portal-side-nav { display: flex; flex-direction: column; gap: 2px; }
    .portal-side-link {
        display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; border-radius: 10px;
        font-size: 14px; font-weight: 600; color: var(--slate-600); text-align: left;
    }
    .portal-side-link:hover { background: var(--hover); color: var(--ink); }
    .portal-side-link.is-active { background: var(--primary-soft); color: var(--primary-strong); }
    .portal-side-foot { margin-top: auto; display: flex; flex-direction: column; gap: 2px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
    .portal-side-foot form { margin: 0; }
    .portal-side-user { display: flex; align-items: center; gap: 10px; padding: 6px 8px 10px; }

    .portal-topbar { padding: 0 28px; height: 64px; }
    .portal-topbar-brand, .portal-topbar-avatar { display: none; }
    .portal-topbar-title { display: block; }
    .portal-tabbar, .portal-sheet, .portal-sheet-backdrop { display: none !important; }
    .portal-main { padding: 26px 28px 48px; }
}

/* Phone-width content: page heads stack, action buttons go full width, and
   tables labelled by footer_portal.php's script turn into one card per row. */
@media (max-width: 720px) {
    .portal-main .page-head, .portal-main .portal-hero { flex-direction: column; align-items: stretch; gap: 12px; margin-bottom: 16px; }
    .portal-main .page-head h2 { font-size: 22px; }
    .portal-main .page-head > .row, .portal-main .page-head > div:last-child:not(:first-child) { flex-wrap: wrap; }
    .portal-main .page-head > .btn, .portal-main .portal-hero > .btn,
    .portal-main .page-head > .row > .btn { justify-content: center; flex: 1 1 auto; }
    .portal-main .card { border-radius: 14px; }
    .portal-main .table-wrap { overflow: visible; }
    .portal-main .table.is-stacked, .portal-main .table.is-stacked tbody, .portal-main .table.is-stacked tfoot,
    .portal-main .table.is-stacked tr, .portal-main .table.is-stacked td { display: block; width: 100%; min-width: 0; }
    .portal-main .table.is-stacked thead { display: block; }
    .portal-main .table.is-stacked thead tr:not(.table-filter-row) { display: none; }
    .portal-main .table.is-stacked .table-filter-row { display: block; }
    .portal-main .table.is-stacked .table-filter-row th { display: block; border: 0; padding: 10px 14px; }
    .portal-main .table.is-stacked .table-filter-row th:empty { display: none; }
    .portal-main .table.is-stacked tr { padding: 12px 16px; border-bottom: 1px solid var(--line-soft); }
    .portal-main .table.is-stacked tbody tr:last-child { border-bottom: 0; }
    .portal-main .table.is-stacked tr[hidden], .portal-main .table.is-stacked tbody[hidden] { display: none; }
    /* block + floated label (not flex): a cell's own inline parts — "W_Ma_0319 / pièce",
       a pill next to a date — stay together on the right instead of being spread apart */
    .portal-main .table.is-stacked td {
        padding: 5px 0; border: 0; text-align: right; font-size: 13.5px; line-height: 1.45;
        white-space: normal; overflow-wrap: anywhere;
    }
    .portal-main .table.is-stacked td::after { content: ''; display: table; clear: both; }
    .portal-main .table.is-stacked td::before {
        content: attr(data-label); float: left; max-width: 48%; margin-right: 12px;
        color: var(--muted); font: 500 12.5px/1.6 var(--font-sans);
        text-align: left; letter-spacing: 0;
    }
    .portal-main .table.is-stacked td[data-label=""]::before { display: none; }
    .portal-main .table.is-stacked tbody td:first-child {
        display: block; text-align: left; font-size: 14.5px; font-weight: 600; color: var(--ink); padding: 0 0 6px;
    }
    .portal-main .table.is-stacked tbody td:first-child::before { display: none; }
    .portal-main .table.is-stacked td[colspan] { display: block; text-align: center; }
    .portal-main .table.is-stacked tfoot tr { display: flex; justify-content: space-between; align-items: center; background: var(--hover); }
    .portal-main .table.is-stacked tfoot td { width: auto; display: block; padding: 0; }
    .portal-main .table.is-stacked tfoot td:empty { display: none; }
    .portal-main .table.is-stacked td .input { max-width: 120px; }
    .portal-main .stats-compact { grid-template-columns: 1fr; }
}

.portal-hero {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px;
    margin-bottom: 20px;
}

/* ----------------------------------------------------------------------
 | Portal order tracking (client_portal_order_tracking()): a headline for
 | the current step, then a 5-step line. Vertical like parcel tracking on
 | phones, horizontal from 720px. The filled part of the line is the ol's
 | --progress (0..1, current step / 4).
 |--------------------------------------------------------------------- */
.track-card { margin-bottom: 16px; padding: 20px; border-radius: 14px; }
.track-card-title { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 12px; margin-bottom: 14px; font-size: 14px; }

/* ----------------------------------------------------------------------
 | Client dashboard (modules/client_portal/pages/index.php): the
 | order-in-progress note and the two side-by-side list cards (invoices to
 | pay · usual products).
 |--------------------------------------------------------------------- */
.portal-more-open { margin: -6px 0 16px; font-size: 13px; color: var(--muted); }
.portal-dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-bottom: 16px; }
.portal-dash-grid > .card { margin: 0; display: flex; flex-direction: column; }
.portal-list { list-style: none; margin: 0; padding: 0; }
.portal-list li { display: flex; align-items: center; gap: 12px; padding: 12px 18px; border-bottom: 1px solid var(--line-soft); }
.portal-list li:last-child { border-bottom: 0; }
.portal-list-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; font-size: 13px; }
.portal-list-name { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.portal-list-side { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; font-size: 12px; white-space: nowrap; }
.portal-list-side strong { font-size: 14px; color: var(--ink); }
.portal-thumb { display: grid; place-items: center; width: 44px; height: 44px; flex: 0 0 auto; border: 1px solid var(--line-soft); border-radius: 10px; background: #fff; color: var(--faint); overflow: hidden; }
.portal-thumb img { width: 100%; height: 100%; object-fit: contain; padding: 3px; }
.portal-usual-add { white-space: nowrap; }
.portal-usual-add.is-added { border-color: var(--success); color: var(--success-700); }
.portal-empty { display: flex; align-items: center; gap: 10px; padding: 22px 18px; font-size: 13px; color: var(--muted); }
.portal-card-foot { margin-top: auto; padding: 12px 18px 16px; border-top: 1px solid var(--line-soft); }
.portal-card-foot .btn { width: 100%; height: auto; min-height: 40px; padding: 9px 14px; justify-content: center; white-space: normal; text-align: center; line-height: 1.3; }
.track-head { display: flex; align-items: center; gap: 14px; }
.track-head-ico {
    display: grid; place-items: center; flex: 0 0 auto; width: 48px; height: 48px; border-radius: 14px;
    background: var(--blue-50); color: var(--blue-600);
}
.track-head.is-done .track-head-ico { background: var(--success-soft); color: var(--success); }
.track-head.is-cancelled .track-head-ico { background: var(--red-50); color: var(--red-600); }
.track-head-title { font-size: 18px; font-weight: 800; color: var(--ink); line-height: 1.25; }
.track-head-sub { font-size: 13.5px; color: var(--muted); margin-top: 2px; }
.track-head-sub strong { color: var(--ink); }
.track-flash { margin: 14px 0 0; }

.track { --dot: 36px; position: relative; list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-direction: column; }
.track::before, .track::after {
    content: ''; position: absolute; left: calc(var(--dot) / 2 - 1.5px); top: calc(var(--dot) / 2); width: 3px; border-radius: 3px;
}
.track::before { bottom: calc(var(--dot) / 2); background: var(--line); }
.track::after { height: calc((100% - var(--dot)) * var(--progress, 0)); background: var(--success); transition: height .4s ease; }
.track-step { position: relative; z-index: 1; display: flex; align-items: flex-start; gap: 14px; min-height: 60px; }
.track-step:last-child { min-height: var(--dot); }
.track-dot {
    display: grid; place-items: center; flex: 0 0 auto; width: var(--dot); height: var(--dot); border-radius: 999px;
    background: var(--surface); border: 2px solid var(--line); color: var(--faint);
}
.track-step.is-done .track-dot { background: var(--success); border-color: var(--success); color: var(--white); }
.track-step.is-current .track-dot {
    background: var(--blue-600); border-color: var(--blue-600); color: var(--white);
    animation: track-pulse 1.8s ease-out infinite;
}
@keyframes track-pulse { 0% { box-shadow: 0 0 0 0 rgba(37, 99, 235, .35); } 70%, 100% { box-shadow: 0 0 0 10px rgba(37, 99, 235, 0); } }
@media (prefers-reduced-motion: reduce) { .track-step.is-current .track-dot { animation: none; box-shadow: 0 0 0 5px var(--blue-50); } }
.track-copy { display: flex; flex-direction: column; padding-top: 7px; min-width: 0; }
.track-copy strong { font-size: 14px; font-weight: 700; color: var(--ink); line-height: 1.3; }
.track-step.is-todo .track-copy strong { color: var(--faint); font-weight: 600; }
.track-date { font-size: 12.5px; color: var(--muted); }
.track-step.is-current .track-date { color: var(--blue-600); font-weight: 600; }

@media (min-width: 720px) {
    .track-card { padding: 22px 24px 24px; }
    .track { --dot: 40px; flex-direction: row; margin-top: 26px; }
    .track::before, .track::after { top: calc(var(--dot) / 2 - 1.5px); left: 10%; width: auto; height: 3px; bottom: auto; }
    .track::before { right: 10%; }
    .track::after { width: calc(80% * var(--progress, 0)); height: 3px; transition: width .4s ease; }
    .track-step, .track-step:last-child { flex: 1 1 0; flex-direction: column; align-items: center; text-align: center; gap: 10px; min-height: 0; }
    .track-copy { padding-top: 0; align-items: center; }
}

/* Mini 5-segment version for order lists (pages/_track_mini.php). */
/* Named 4-step tracker in the portal's order lists (pages/_track_mini.php):
   dot + label per step, a short connector between them, green up to the step
   the order is at. */
.track-mini { display: inline-flex; align-items: center; flex-wrap: nowrap; vertical-align: middle; }
.track-mini-step { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--faint); white-space: nowrap; }
.track-mini-step + .track-mini-step::before { content: ''; width: 14px; height: 2px; margin: 0 6px; border-radius: 1px; background: var(--line); }
.track-mini-step i { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 999px; background: var(--line); }
.track-mini-step.is-done, .track-mini-step.is-current { color: var(--slate-700); }
.track-mini-step.is-done i, .track-mini-step.is-current i { background: var(--success); }
.track-mini-step.is-done::before, .track-mini-step.is-current::before { background: var(--success) !important; }
.track-mini-step.is-current { font-weight: 700; color: var(--ink); }
.track-mini-step.is-current i { box-shadow: 0 0 0 3px var(--success-50); }
.portal-reorder { white-space: nowrap; }
/* Portal orders on a phone: date · lines · total on one line, the tracker
   under them with no label, "Commander à nouveau" an icon in the corner. */
@media (max-width: 900px) {
  body .table.portal-orders > tbody > tr > td.po-third { flex: 1 1 calc(33.33% - 6px); }
  body .table.portal-orders > tbody > tr > td.po-track { order: 5; }
  body .table.portal-orders > tbody > tr > td.po-track[data-label]::before { display: none; }
  body .table.portal-orders .portal-reorder { width: 34px; height: 34px; min-height: 34px; padding: 0; justify-content: center; }
  body .table.portal-orders .portal-reorder-label { display: none; }
}

/* ----------------------------------------------------------------------
 | Portal shop — the client's order page (/portal/orders/create), laid out
 | like a grocery app: location + search, a hero, round category chips,
 | "order again" / special-price rails, a product-card grid with an
 | add-to-cart stepper, and a cart that is a sticky right column on desktop
 | and a bottom sheet behind a floating cart bar on phones.
 | Product/category tiles are white; one without a photo shows a monogram
 | whose colour comes from the category name via the inline --tint property.
 |--------------------------------------------------------------------- */
.shop [hidden] { display: none !important; }
:where(.shop) button { border: 0; background: none; padding: 0; color: inherit; font: inherit; cursor: pointer; }
.shop .icon-xs { width: 14px; height: 14px; flex: 0 0 auto; }
.shop { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; padding-bottom: 64px; }
.shop-main { min-width: 0; display: flex; flex-direction: column; gap: 22px; }
.shop-main > .flash { margin: 0; }

.shop-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.shop-where { display: flex; align-items: center; gap: 12px; min-width: 0; }
.shop-where-pin, .shop-iconlink {
    display: inline-grid; place-items: center; flex: 0 0 auto; width: 44px; height: 44px; border-radius: 999px;
    background: var(--surface); border: 1px solid var(--line); color: var(--red-500);
}
.shop-iconlink { color: var(--slate-700); }
.shop-iconlink:hover { background: var(--hover); }
.shop-where-label { font-size: 12px; color: var(--muted); }
.shop-where-value { font-size: 15px; font-weight: 700; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shop-where-value .muted { font-weight: 500; }

.shop-search {
    display: flex; align-items: center; gap: 10px; height: 50px; padding: 0 8px 0 18px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 999px; color: var(--faint);
    transition: border-color .15s, box-shadow .15s;
}
.shop-search:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.shop-search input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; font-size: 15px; color: var(--ink); }
.shop-search input::-webkit-search-cancel-button { display: none; }
.shop-search button { display: inline-grid; place-items: center; width: 34px; height: 34px; border-radius: 999px; color: var(--muted); }
.shop-search button:hover { background: var(--hover); }

.shop-strip {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 10px 14px;
    border: 1px solid var(--emerald-200); border-radius: 12px; background: var(--emerald-50);
    font-size: 13.5px; color: var(--emerald-900);
}
.shop-strip-ico { display: inline-grid; place-items: center; width: 28px; height: 28px; flex: 0 0 auto; border-radius: 999px; background: var(--surface); color: var(--emerald-700); }
.shop-strip-text { font-weight: 600; }
.shop-strip-stat { margin-left: auto; color: var(--emerald-800); font-size: 13px; }
.shop-where-missing { color: var(--amber-700); }

.shop-section { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.shop-section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; }
.shop-section-head h2 { margin: 0; font-size: 18px; font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.shop-seeall { font-size: 13.5px; font-weight: 600; color: var(--emerald-700); }
.shop-seeall:hover { text-decoration: underline; }
.shop-count {
    display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 22px; padding: 0 8px;
    border-radius: 999px; background: var(--slate-200); color: var(--slate-700); font-size: 12px; font-weight: 700;
}

.shop-cats { display: flex; gap: 10px; overflow-x: auto; padding: 4px 2px 8px; scrollbar-width: thin; scroll-snap-type: x proximity; }
.shop-cat {
    flex: 0 0 auto; width: 84px; display: flex; flex-direction: column; align-items: center; gap: 7px;
    padding: 0; background: none; border: 0; cursor: pointer; scroll-snap-align: start;
}
.shop-cat-icon {
    display: grid; place-items: center; width: 64px; height: 64px; border-radius: 999px;
    background: #fff; color: hsl(var(--tint, 150) 45% 30%);
    font-size: 18px; font-weight: 800; letter-spacing: .02em;
    border: 2px solid var(--line); transition: transform .15s, border-color .15s, box-shadow .15s;
}
.shop-cat-icon.is-all { background: var(--primary-soft); color: var(--primary-strong); }
.shop-cat-icon .icon { width: 26px; height: 26px; }
.shop-cat:hover .shop-cat-icon { transform: translateY(-2px); }
.shop-cat.is-active .shop-cat-icon { border-color: var(--emerald-600); box-shadow: var(--ring-primary); }
.shop-cat-name {
    font-size: 12.5px; line-height: 1.25; color: var(--slate-700); text-align: center;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.shop-cat.is-active .shop-cat-name { color: var(--emerald-800); font-weight: 700; }
/* Under a picked category: its sous-catégories, then the picked one's sous-sous-catégories. */
.shop-subcats { display: flex; flex-wrap: wrap; gap: 8px; margin-top: -4px; }
.shop-subcat {
    display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid var(--line);
    border-radius: 999px; background: var(--surface); color: var(--slate-700); font-size: 13px; font-weight: 600; cursor: pointer;
}
.shop-subcat:hover { border-color: var(--emerald-600); }
.shop-subcat.is-active { border-color: var(--emerald-600); background: var(--emerald-50); color: var(--emerald-800); }
.shop-subcat.is-third { padding: 4px 10px; font-size: 12.5px; font-weight: 500; }
.shop-subcat-count { color: var(--muted); font-size: 11.5px; font-weight: 600; }

.shop-rail {
    display: grid; grid-auto-flow: column; grid-auto-columns: 136px; gap: 10px;
    overflow-x: auto; padding: 2px 2px 10px; scroll-snap-type: x proximity; scrollbar-width: thin;
}
.shop-rail .shop-card { scroll-snap-align: start; }
/* Rail cards ("Commander à nouveau", "Vos prix spéciaux") are a compact version of the grid card. */
.shop-card.is-rail { gap: 6px; padding: 6px; border-radius: 12px; }
.shop-card.is-rail .shop-card-media { border-radius: 9px; }
.shop-card.is-rail .shop-card-media img { padding: 6px; }
.shop-card.is-rail .shop-card-mono { font-size: 26px; }
.shop-card.is-rail .shop-badge { top: 5px; left: 5px; padding: 2px 7px; font-size: 10.5px; }
.shop-card.is-rail .shop-badge.is-again { top: auto; bottom: 5px; width: 22px; height: 22px; padding: 0; }
.shop-card.is-rail .shop-card-media .shop-fav { top: 5px; right: 5px; width: 26px; height: 26px; }
.shop-card.is-rail .shop-card-body { gap: 1px; padding: 0 2px 2px; }
.shop-card.is-rail .shop-card-name { font-size: 12.5px; }
.shop-card.is-rail .shop-card-meta { font-size: 11px; }
.shop-card.is-rail .shop-card-foot { gap: 4px; padding-top: 4px; min-height: 36px; }
.shop-card.is-rail .shop-card-price strong { font-size: 14px; }
.shop-card.is-rail .shop-card-price strong small { display: block; font-size: 10px; }
.shop-card.is-rail .shop-add { width: 32px; height: 32px; }
.shop-card.is-rail .shop-stepper button { width: 24px; height: 24px; }
.shop-card.is-rail .shop-stepper input { font-size: 13px; }

.shop-filters { flex-wrap: wrap; align-items: center; }
.shop-filter-row { display: flex; flex-wrap: wrap; gap: 8px; }
.shop-pill-combo { width: 210px; }
.shop-pill-combo .catalog-combobox-control { height: 40px; border-radius: 999px; border-color: var(--line); background: var(--surface); padding-left: 14px; }
.shop-pill-combo .catalog-combobox-control input { font-size: 13.5px; font-weight: 600; }
.shop-pill-combo.is-set .catalog-combobox-control { border-color: var(--emerald-600); background: var(--primary-soft); }
.shop-pill-combo .catalog-options { min-width: 240px; }
.shop-pill-select {
    position: relative; display: inline-flex; align-items: center; gap: 6px; height: 40px; padding: 0 12px 0 14px;
    border: 1px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--slate-600);
}
.shop-pill-select:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.shop-pill-select select {
    appearance: none; -webkit-appearance: none; border: 0; outline: 0; background: transparent;
    font: inherit; font-size: 13.5px; font-weight: 600; color: var(--ink); padding-right: 2px; cursor: pointer;
}

.shop-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); gap: 14px; }
/* Client-side pager under the grid (the shared .table-pagination look, minus the table's border/padding). */
.shop-pager { padding: 6px 0 0; border-top: 0; }
.shop-page-gap { color: var(--muted); padding: 0 2px; }
#shop-all { scroll-margin-top: 80px; }
.shop-card {
    display: flex; flex-direction: column; gap: 10px; padding: 8px; min-width: 0;
    background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
    transition: box-shadow .15s, transform .15s, border-color .15s;
}
.shop-card:hover { box-shadow: 0 10px 26px rgba(15, 23, 42, .08); transform: translateY(-2px); }
.shop-card.is-in-cart { border-color: var(--emerald-200); }
.shop-card-media {
    position: relative; display: grid; place-items: center; aspect-ratio: 1 / 1; border-radius: 12px; overflow: hidden;
    background: #fff;
}
.shop-card-media img { width: 100%; height: 100%; object-fit: contain; padding: 10px; mix-blend-mode: multiply; }
.shop-card-mono { font-size: 36px; font-weight: 800; letter-spacing: .02em; color: hsl(var(--tint, 150) 40% 40%); opacity: .8; }
.shop-badge {
    position: absolute; top: 8px; left: 8px; padding: 3px 9px; border-radius: 999px;
    background: var(--red-500); color: var(--white); font-size: 11.5px; font-weight: 700;
}
.shop-badge.is-again {
    top: auto; bottom: 8px; left: 8px; display: grid; place-items: center; width: 26px; height: 26px; padding: 0;
    background: var(--surface); color: var(--emerald-700); box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
}
/* Favourite heart: top-right of the photo (and next to the brand in the quick view). */
.shop-fav {
    display: grid; place-items: center; width: 32px; height: 32px; border-radius: 999px;
    background: var(--surface); color: var(--slate-400); box-shadow: 0 1px 3px rgba(15, 23, 42, .14);
    transition: color .15s, transform .1s;
}
.shop-card-media .shop-fav { position: absolute; top: 8px; right: 8px; z-index: 1; }
.shop-fav:hover { color: var(--red-500); }
.shop-fav:active { transform: scale(.88); }
.shop-fav[aria-pressed="true"] { color: var(--red-500); }
.shop-fav[aria-pressed="true"] svg { fill: currentColor; }
.shop-fav-filter {
    display: inline-flex; align-items: center; gap: 6px; height: 40px; padding: 0 14px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--surface); color: var(--slate-600);
    font-size: 13.5px; font-weight: 600;
}
.shop-fav-filter .icon { color: var(--red-500); }
.shop-fav-filter.is-on { border-color: var(--red-500); background: var(--red-50); color: var(--ink); }
.shop-fav-filter.is-on .icon { fill: currentColor; }
.shop-fav-count { min-width: 20px; padding: 1px 6px; border-radius: 999px; background: var(--slate-100); font-size: 12px; text-align: center; }
.shop-usual-btn {
    display: inline-flex; align-items: center; gap: 8px; margin-top: 10px; padding: 10px 16px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-size: 13px; font-weight: 700;
}
.shop-usual-btn:hover { background: var(--hover); }
.shop-seeall .icon { vertical-align: -2px; }
.shop-card-body { display: flex; flex-direction: column; gap: 3px; padding: 0 4px 4px; flex: 1; }
.shop-card-name {
    margin: 0; font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--ink); min-height: 2.6em;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.shop-card-meta { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shop-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-top: auto; padding-top: 8px; min-height: 46px; }
.shop-card-price { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.shop-card-price strong { font-size: 16px; font-weight: 800; color: var(--ink); white-space: nowrap; }
.shop-card-price strong small { font-size: 11px; font-weight: 600; color: var(--muted); }
.shop-card-price s { font-size: 12px; color: var(--red-500); }
.shop-card-price .muted { font-size: 12px; }
.shop-add {
    display: grid; place-items: center; flex: 0 0 auto; width: 40px; height: 40px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white); transition: background .15s, transform .1s;
}
.shop-add:hover { background: var(--emerald-900); }
.shop-add:active { transform: scale(.92); }

.shop-stepper {
    display: inline-flex; align-items: center; flex: 0 0 auto; gap: 2px; padding: 3px;
    border-radius: 999px; background: var(--primary-soft);
}
.shop-stepper button {
    display: grid; place-items: center; width: 30px; height: 30px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white);
}
.shop-stepper button:hover { background: var(--emerald-900); }
.shop-stepper input {
    width: 34px; border: 0; outline: 0; background: transparent; text-align: center;
    font: inherit; font-size: 14px; font-weight: 700; color: var(--ink); -moz-appearance: textfield;
}
.shop-stepper input::-webkit-outer-spin-button, .shop-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* In a card, the stepper takes its own full-width row under the price — beside it, a
   172px card has no room for both. */
.shop-card.is-in-cart .shop-card-foot { flex-wrap: wrap; }
.shop-card-foot .shop-stepper { flex: 1 1 100%; justify-content: space-between; }
.shop-card-foot .shop-stepper input { flex: 1; }
.shop-stepper.is-sm button { width: 26px; height: 26px; }
.shop-stepper.is-sm input { width: 30px; font-size: 13px; }

.shop-empty {
    display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 40px 20px;
    border: 1px dashed var(--slate-300); border-radius: 16px; color: var(--muted); text-align: center;
}
.shop-empty > .icon { width: 28px; height: 28px; }
.shop-empty strong { color: var(--slate-700); }

/* Cart — bottom sheet by default (phones), sticky column from 1024px. */
.shop-cart {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60; max-height: 88vh;
    transform: translateY(105%); visibility: hidden; transition: transform .25s ease, visibility .25s;
}
.shop-cart.is-open { transform: none; visibility: visible; }
.shop-cart-backdrop { position: fixed; inset: 0; z-index: 55; background: rgba(15, 23, 42, .45); }
body.shop-lock { overflow: hidden; }
.shop-cart-inner {
    display: flex; flex-direction: column; max-height: 88vh; margin: 0;
    background: var(--surface); border: 1px solid var(--line); border-radius: 22px 22px 0 0;
    box-shadow: 0 -12px 32px rgba(15, 23, 42, .14);
}
.shop-cart-head { display: flex; align-items: center; gap: 10px; padding: 16px 18px; border-bottom: 1px solid var(--line-soft); }
.shop-cart-head h2 { flex: 1; margin: 0; display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 700; color: var(--ink); }
.shop-cart-head h2 > .icon { color: var(--emerald-700); }
.shop-cart-clear { font-size: 13px; font-weight: 600; color: var(--red-600); }
.shop-cart-clear:hover { text-decoration: underline; }
.shop-cart-close { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 999px; color: var(--muted); }
.shop-cart-close:hover { background: var(--hover); }
.shop-cart-lines { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 4px 12px; }
.shop-line { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 10px 4px; border-bottom: 1px solid var(--line-soft); }
.shop-line:last-child { border-bottom: 0; }
.shop-line-thumb {
    display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px; overflow: hidden;
    background: #fff; border: 1px solid var(--line-soft); color: hsl(var(--tint, 150) 40% 38%); font-size: 13px; font-weight: 800;
}
.shop-line-thumb img { width: 100%; height: 100%; object-fit: contain; padding: 3px; mix-blend-mode: multiply; }
.shop-line-name {
    font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--ink);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.shop-line-price { font-size: 12px; color: var(--muted); margin-top: 2px; }
.shop-line-side { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.shop-line-total { font-size: 13px; color: var(--ink); white-space: nowrap; }
.shop-cart-empty { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 30px 20px; text-align: center; font-size: 13px; color: var(--muted); }
.shop-cart-empty strong { font-size: 14px; color: var(--slate-700); }
.shop-cart-empty-icon { display: grid; place-items: center; width: 56px; height: 56px; margin-bottom: 4px; border-radius: 999px; background: var(--slate-100); color: var(--faint); }
.shop-cart-foot { display: flex; flex-direction: column; gap: 10px; padding: 14px 18px 18px; border-top: 1px solid var(--line-soft); }
.shop-note summary { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--slate-600); cursor: pointer; list-style: none; }
.shop-note summary::-webkit-details-marker { display: none; }
.shop-note textarea { margin-top: 8px; width: 100%; }
.shop-cart-total { display: flex; align-items: baseline; justify-content: space-between; font-size: 14px; color: var(--slate-600); }
.shop-cart-total strong { font-size: 20px; font-weight: 800; color: var(--ink); }
.shop-cart-hint { margin: 0; font-size: 11.5px; color: var(--faint); }
/* Cart foot: the client's wished delivery date, then HT / VAT / TTC. */
.shop-delivery { display: flex; flex-direction: column; gap: 6px; }
.shop-delivery label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--slate-600); }
.shop-delivery label .muted { font-weight: 400; }
.shop-cart-totals { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.shop-cart-totals > div { display: flex; align-items: baseline; justify-content: space-between; font-size: 13px; color: var(--slate-600); }
.shop-cart-totals dt, .shop-cart-totals dd { margin: 0; }
.shop-cart-totals dd { font-variant-numeric: tabular-nums; }
.shop-cart-totals > .shop-cart-total { margin-top: 2px; padding-top: 6px; border-top: 1px dashed var(--line); font-size: 14px; }
.shop-cartbar-total small { font-size: 11px; font-weight: 600; opacity: .8; }

/* Photos and names open the quick view. */
.shop [data-qv] { cursor: pointer; }
.shop-card-media[data-qv]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Product quick view — a native <dialog>, outside .shop, so it resets its own buttons. */
.shop-qv {
    width: min(760px, calc(100vw - 32px)); max-height: calc(100vh - 48px); overflow: auto; padding: 0; border: 0;
    border-radius: 20px; background: var(--surface); color: var(--ink); box-shadow: 0 24px 60px rgba(15, 23, 42, .25);
}
.shop-qv::backdrop { background: rgba(15, 23, 42, .45); }
.shop-qv [hidden] { display: none !important; }
:where(.shop-qv) button { border: 0; background: none; padding: 0; color: inherit; font: inherit; cursor: pointer; }
.shop-qv-inner { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; padding: 24px; }
.shop-qv-close {
    position: absolute; top: 12px; right: 12px; z-index: 1; display: grid; place-items: center;
    width: 36px; height: 36px; border-radius: 999px; color: var(--muted);
}
.shop-qv-close:hover { background: var(--hover); color: var(--ink); }
.shop-qv-media { position: relative; display: grid; place-items: center; aspect-ratio: 1 / 1; border: 1px solid var(--line-soft); border-radius: 16px; background: #fff; overflow: hidden; }
.shop-qv-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 16px; }
.shop-qv .icon-xs { width: 14px; height: 14px; flex: 0 0 auto; }
.shop-qv-media .shop-card-mono { font-size: 64px; }
.shop-qv-body { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding-top: 8px; }
.shop-qv-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-right: 40px; }
.shop-qv-brand { font-size: 12.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--emerald-700); }
.shop-qv-name { margin: 0; font-size: 20px; font-weight: 700; line-height: 1.3; }
.shop-qv-facts { display: grid; gap: 6px; margin: 4px 0 0; }
.shop-qv-facts > div { display: flex; justify-content: space-between; gap: 12px; padding-bottom: 6px; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.shop-qv-facts dt { color: var(--muted); }
.shop-qv-facts dd { margin: 0; text-align: right; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.shop-qv-price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-top: 6px; }
.shop-qv-price strong { font-size: 24px; font-weight: 800; }
.shop-qv-price strong small { font-size: 12px; font-weight: 600; color: var(--muted); }
.shop-qv-price s { font-size: 13px; color: var(--red-500); }
.shop-qv-ttc { flex-basis: 100%; font-size: 13px; color: var(--slate-600); }
.shop-qv-onrequest { margin-top: 6px; font-size: 15px; font-weight: 600; }
.shop-qv-actions { margin-top: auto; padding-top: 8px; }
.shop-qv-add {
    display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; height: 48px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white); font-size: 15px; font-weight: 700;
}
.shop-qv-add:hover { background: var(--emerald-900); }
.shop-stepper.is-lg { display: flex; width: 100%; justify-content: space-between; padding: 4px; }
.shop-stepper.is-lg button { width: 40px; height: 40px; }
.shop-stepper.is-lg input { flex: 1; font-size: 16px; }
@media (max-width: 640px) {
    .shop-qv { width: 100%; max-width: 100%; max-height: 92vh; margin: auto 0 0; border-radius: 20px 20px 0 0; }
    .shop-qv-inner { grid-template-columns: minmax(0, 1fr); gap: 14px; padding: 18px 16px 20px; }
    .shop-qv-media { aspect-ratio: auto; height: 220px; }
    .shop-qv-name { font-size: 18px; }
}
.shop-submit {
    display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; height: 50px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white); font-size: 15px; font-weight: 700; transition: background .15s;
}
.shop-submit:hover:not(:disabled) { background: var(--emerald-900); }
.shop-submit:disabled { opacity: .45; cursor: not-allowed; }

.shop-cartbar {
    /* floats just above the portal's bottom tab bar (66px tall, 12px up) */
    position: fixed; left: 50%; bottom: calc(88px + env(safe-area-inset-bottom)); z-index: 50; transform: translateX(-50%);
    width: calc(100% - 32px); max-width: 480px; height: 58px; padding: 0 8px 0 10px;
    display: flex; align-items: center; gap: 12px; border-radius: 999px;
    background: var(--emerald-800); color: var(--white); box-shadow: 0 12px 30px rgba(15, 23, 42, .3);
}
.shop-cartbar-icon { position: relative; display: grid; place-items: center; width: 42px; height: 42px; border-radius: 999px; background: rgba(255, 255, 255, .14); }
.shop-cartbar-icon > span {
    position: absolute; top: -4px; right: -4px; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px;
    display: grid; place-items: center; background: var(--amber-600); font-size: 11px; font-weight: 800;
}
.shop-cartbar-total { flex: 1; text-align: left; font-size: 16px; font-weight: 800; }
.shop-cartbar-cta { display: inline-flex; align-items: center; gap: 4px; height: 42px; padding: 0 16px; border-radius: 999px; background: var(--surface); color: var(--emerald-800); font-size: 13.5px; font-weight: 700; }
.shop-cartbar.is-bump .shop-cartbar-icon { animation: shop-bump .35s ease; }
@keyframes shop-bump { 40% { transform: scale(1.18); } }

@media (max-width: 560px) {
    .shop-strip-stat { margin-left: 0; }
    .shop-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
    /* Smaller tiles: the photo capped at 96px instead of a square half the tile. */
    .shop-grid .shop-card-media { aspect-ratio: auto; height: 96px; }
    .shop-grid .shop-card-media img { padding: 6px; }
    .shop-rail { grid-auto-columns: 124px; }
    .shop-pill-combo { width: auto; flex: 1 1 150px; }
    .shop-filter-row { width: 100%; }
}
@media (min-width: 1024px) {
    .shop { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; padding-bottom: 0; }
    .shop-cart { position: sticky; top: 84px; left: auto; right: auto; bottom: auto; z-index: 1; transform: none; visibility: visible; transition: none; }
    .shop-cart-inner { max-height: calc(100vh - 108px); border-radius: 18px; box-shadow: var(--shadow-card); }
    .shop-cart-close, .shop-cart-backdrop, .shop-cartbar { display: none !important; }
}

/* ----------------------------------------------------------------------
 | Leaflet maps (customer page, supervisor dispatch board) — Leaflet
 | requires an explicit pixel height on its container or it renders 0px
 | tall. Rounded to match .card's own radius so the map sits flush inside.
 |--------------------------------------------------------------------- */
.leaflet-map { height: 320px; border-radius: 0 0 6px 6px; }
/* Leaflet's panes/controls carry z-index 400–1000; isolate keeps them inside
   the map so the phone sidebar (z-index 50) and topbar still cover it. */
.leaflet-container { isolation: isolate; }
.dispatch-map { height: 220px; border-radius: 6px; margin-bottom: 12px; }

/* ----------------------------------------------------------------------
 | Error pages (404 / 500 / 503) — resources/views/errors/page.php.
 | One centered card; the status numeral sits behind the icon as a large
 | translucent watermark so the page reads as "a dead end", not as a table
 | that failed to load. Rendered inside the normal app shell, so it stays
 | narrow rather than filling the main column.
 |--------------------------------------------------------------------- */
.errpage { display: flex; justify-content: center; padding: 32px 0 48px; }
.errpage-card {
    background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
    box-shadow: var(--shadow-card);
    width: 100%; max-width: 480px; padding: 36px 32px 30px; text-align: center;
}

/* Numeral watermark + icon chip stacked in the same box. */
.errpage-mark { position: relative; display: grid; place-items: center; height: 92px; margin-bottom: 6px; }
.errpage-code {
    position: absolute; inset: 0; display: grid; place-items: center;
    font-size: 88px; font-weight: 800; line-height: 1; letter-spacing: -.04em;
    color: var(--slate-100); font-variant-numeric: tabular-nums; user-select: none;
}
.errpage-ic {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 56px; height: 56px; border-radius: 6px;
    background: var(--primary-soft); color: var(--primary-strong);
}
.errpage-ic svg { width: 26px; height: 26px; }

.errpage-title { font-size: 18px; font-weight: 700; color: var(--ink); margin: 0; }
.errpage-text { font-size: 13.5px; line-height: 1.6; color: var(--muted); margin: 8px 0 0; }

/* The URL that missed. Breaks anywhere — a pasted path can be arbitrarily long. */
.errpage-path {
    display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
    margin-top: 14px; padding: 6px 10px; border-radius: 6px;
    background: var(--slate-100); border: 1px solid var(--line);
    font-family: var(--font-mono);
    font-size: 12px; color: var(--slate-600);
}
.errpage-path svg { flex: 0 0 auto; color: var(--faint); }
.errpage-path span { overflow-wrap: anywhere; text-align: left; }

/* APP_DEBUG-only diagnostics (500 / 503). */
.errpage-detail {
    margin: 14px 0 0; padding: 10px 12px; border-radius: 6px;
    background: var(--slate-100); color: var(--slate-700);
    font-size: 12px; line-height: 1.5; text-align: left;
    white-space: pre-wrap; overflow-wrap: anywhere; max-height: 220px; overflow: auto;
}

.errpage-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 22px; }

/* The 404's carton that fell off the belt (Three.js, modules/scene3d/, case 178).
   It replaces the numeral + icon once drawn; click it to play it again. */
.errpage-scene { position: relative; height: 210px; margin: -18px -12px 8px; cursor: pointer; }
.errpage-card.has-scene .errpage-mark { display: none; }
.errpage-scene-code {
    position: absolute; top: 4px; left: 8px; z-index: 1;
    font-family: var(--font-mono); font-size: 26px; font-weight: 600; color: var(--slate-300);
}
.errpage-scene-caption {
    position: absolute; left: 0; right: 0; bottom: 2px; z-index: 1;
    font-size: 12.5px; color: var(--muted); pointer-events: none;
}

@media (max-width: 520px) {
    .errpage { padding: 16px 0 32px; }
    .errpage-card { padding: 28px 20px 24px; }
    .errpage-code { font-size: 68px; }
    .errpage-actions .btn { flex: 1 1 100%; justify-content: center; }
}

/* ---- Notifications: topbar bell panel + /notifications page ------------- */
/* The panel and the full page share .notif-list / .notif-item so a
   notification looks like the same object in both places; only the
   wrappers differ. */

.notif { position: relative; }

/* Replaces the old decorative .dot with a real count. Sits on the bell
   button, which is already position:relative (.iconbtn). */
.notif-badge {
    position: absolute; top: 3px; right: 2px; min-width: 16px; height: 16px;
    padding: 0 4px; border-radius: 999px; background: var(--red-500);
    border: 2px solid var(--surface); color: var(--white);
    font-size: 10px; font-weight: 700; line-height: 12px; text-align: center;
}
.notif.is-open > .iconbtn { background: var(--slate-100); color: var(--ink); }

.notif-panel {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 100;
    width: 360px; max-width: calc(100vw - 32px);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 6px; box-shadow: 0 12px 32px rgba(15, 23, 42, .16);
    overflow: hidden;
}
.notif-panel-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 12px 14px; border-bottom: 1px solid var(--line-soft);
}
.notif-panel-title { font-size: 13px; font-weight: 700; color: var(--ink); }
.notif-panel-action {
    border: 0; background: transparent; padding: 0; cursor: pointer;
    font-size: 12px; font-weight: 600; color: var(--primary);
}
.notif-panel-action:hover { text-decoration: underline; }
.notif-panel-more {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    padding: 11px 14px; border-top: 1px solid var(--line-soft);
    font-size: 12px; font-weight: 600; color: var(--primary); text-decoration: none;
}
.notif-panel-more:hover { background: var(--slate-50); }

.notif-list { list-style: none; margin: 0; padding: 0; }
/* Panel only — "To do" and the list scroll together; the full page scrolls
   with the document instead. */
.notif-panel-body { max-height: 440px; overflow-y: auto; }

/* "To do" heads the panel (design gallery 2026-10-29, P4): unread rows that ask
   for an action, each with its button. Not a single submit button like the
   rows below — it holds two forms. */
.notif-todo { background: var(--amber-50); border-bottom: 1px solid var(--line-soft); }
.notif-todo-label {
    padding: 10px 14px 2px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--amber-700);
}
.notif-todo .notif-item { border-bottom: 0; }
.notif-item-todo { display: flex; align-items: flex-start; gap: 10px; padding: 10px 14px; }
.notif-item-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.notif-item-actions .btn,
.notif-todo-btn { height: 28px; padding: 0 10px; font-size: 12px; }

.notif-item { border-bottom: 1px solid var(--line-soft); }
.notif-item:last-child { border-bottom: 0; }
.notif-item.is-unread { background: var(--emerald-50); }

/* The whole row is the submit button of its own form — opening a
   notification marks it read, which is a write (see modules/notifications/). */
.notif-item-btn {
    display: flex; align-items: flex-start; gap: 10px; width: 100%;
    padding: 11px 14px; border: 0; background: transparent; cursor: pointer;
    text-align: left; font: inherit; color: inherit;
}
.notif-item-btn:hover { background: var(--hover); }
.notif-item-icon {
    flex: none; display: grid; place-items: center;
    width: 30px; height: 30px; border-radius: 6px;
}
.notif-item-copy { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.notif-item-title { font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.notif-item-body { font-size: 12px; color: var(--muted); line-height: 1.4; }
.notif-item-meta { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.notif-item-dot {
    flex: none; width: 8px; height: 8px; margin-top: 6px;
    border-radius: 50%; background: var(--primary);
}

.notif-empty {
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    padding: 30px 14px; color: var(--muted); font-size: 13px; text-align: center;
}
/* The search empty-state ships with the `hidden` attribute and is revealed by
   notifFilter(). A class-level `display: flex` outranks the UA's
   [hidden] { display: none }, so without this it would render permanently —
   stacked under the server-side "no match for these filters" message. Same
   guard as .m-empty[hidden] / .table-pagination[hidden] above. */
.notif-empty[hidden] { display: none; }
.notif-empty svg { color: var(--faint); }

/* ---- /notifications page ----------------------------------------------- */
.notif-page-card { overflow: visible; }   /* the date pickers pop out of it */
.notif-filter-dates { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 8px 10px; }
.notif-filter-label { font-size: 12px; font-weight: 600; color: var(--muted); }
.notif-filter-apply { margin-top: 10px; }
/* The table (design gallery 2026-10-29, N4): ticked rows feed the bulk bar. */
.notif-bulk {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    padding: 8px 16px; border-bottom: 1px solid var(--line-soft); background: var(--slate-50);
}
.notif-bulk.has-selection { background: var(--primary-soft); }
.notif-bulk-count { font-size: 12px; font-weight: 600; color: var(--muted); margin-right: 4px; }
.notif-bulk.has-selection .notif-bulk-count { color: var(--primary-strong); }
.notif-bulk .btn { height: 30px; padding: 0 10px; font-size: 12px; }
.notif-table .notif-col-check { width: 36px; }
.notif-table tr.is-unread > td { background: var(--emerald-50); }
.notif-table tr.is-unread .notif-item-title { font-weight: 700; }
.notif-table tr.is-selected > td { background: var(--primary-soft); }
.notif-open-form { display: flex; align-items: center; gap: 10px; }
.notif-open {
    flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px;
    border: 0; background: transparent; padding: 0; cursor: pointer;
    text-align: left; font: inherit; color: inherit;
}
.notif-open:hover .notif-item-title { color: var(--primary); text-decoration: underline; }
.notif-open .notif-item-title { font-weight: 500; }
.notif-when { white-space: nowrap; color: var(--muted); font-variant-numeric: tabular-nums; }
.notif-when .notif-item-dot { display: inline-block; margin: 0 6px 1px 0; vertical-align: middle; }
@media (max-width: 899px) {
    /* As a card, the tick box leads the title's line instead of trailing at the bottom. */
    body .table.tc-auto.notif-table > tbody > tr > td.notif-col-check { order: -3; flex: 0 0 auto; width: auto; }
    .notif-bulk-count { flex: 1 1 100%; white-space: nowrap; }
    .notif-bulk .btn { flex: 1 1 0; min-width: 0; padding: 0 6px; white-space: nowrap; }
    .notif-bulk .btn svg { display: none; }
}

@media (max-width: 720px) {
    /* Anchored to .notif (the bell button) via right:0, .notif-panel used to
       just get wider here without moving — since the bell sits well short of
       the topbar's own right edge (the user chip follows it), a ~full-viewport
       panel anchored that way ran off the LEFT edge of the screen instead of
       centering under the bell. Pinning it to the viewport instead of the
       icon fixes that; --app-banner-h (already measured by footer.php for the
       sidebar) keeps it below the preview/offline banners when either shows. */
    .notif-panel {
        position: fixed; top: calc(var(--app-banner-h, 0px) + 72px);
        left: 12px; right: 12px; width: auto; max-width: none;
    }
}

/* ---- Dashboard: urgent-action card ------------------------------------- */
/* Hallmark · component: alert card · genre: modern-minimal · theme: design.md (locked)
 * states: default · hover · focus-visible · active · scrolled
 *
 * One fixed-size card holding a scrollable list, replacing the grid of
 * severity-washed tiles that grew with the alert count and pushed the KPIs
 * below the fold. Card material is the shared one (--surface, --line,
 * --shadow-card, 12px radius) — this is a normal card of the app, not a
 * special red panel, and it stays the same height whether two alerts fire or ten.
 *
 * Severity is a 7px dot plus the count's colour on an otherwise plain row.
 * In a single column that is instantly scannable, and it keeps the block from
 * reading as a wall of tint the way the per-tile border + gradient did.
 */
.alerts-card {
    max-width: 560px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: var(--shadow-card);
    overflow: hidden;
    margin: 0 0 20px;
    animation: rise .4s ease both;
}

.alerts-card-head {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px 8px 12px;
    border-bottom: 1px solid var(--line-soft);
}
.alerts-card-title {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--muted);
}
.alerts-card-title svg { color: var(--faint); }
/* The count lives in the header so the card can be short without hiding how
   much is behind the scroll. */
.alerts-card-count {
    min-width: 18px; padding: 0 5px; height: 18px; border-radius: 6px;
    display: inline-grid; place-items: center;
    background: var(--slate-100); color: var(--slate-700);
    font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.alerts-card-head form { margin-left: auto; display: flex; }

/* ~4 rows visible (4 × 38px), then it scrolls. Fixed ceiling is the whole
   point: the card's height stops depending on how bad the day is. */
.alerts-list {
    list-style: none; margin: 0; padding: 4px;
    max-height: 160px; overflow-y: auto; overscroll-behavior: contain;
    scrollbar-width: thin; scrollbar-color: var(--slate-300) transparent;
}
.alerts-list::-webkit-scrollbar { width: 8px; }
.alerts-list::-webkit-scrollbar-thumb { background: var(--slate-300); border: 2px solid var(--surface); border-radius: 6px; }
.alerts-list::-webkit-scrollbar-track { background: transparent; }

.alerts-row {
    display: flex; align-items: center; gap: 9px;
    height: 34px; padding: 0 8px; border-radius: 6px;
    text-decoration: none; color: inherit;
    transition: background-color .12s ease;
}
.alerts-row:hover { background: var(--hover); }
.alerts-row:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.alerts-row-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.alerts-row.is-critical .alerts-row-dot { background: var(--red-500); }
.alerts-row.is-warning  .alerts-row-dot { background: var(--amber-600); }
/* One line, ellipsised — the full phrase plus its explanation is one hover
   away in the row's title attribute. */
.alerts-row-label {
    flex: 1 1 auto; min-width: 0;
    font-size: 13px; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.alerts-row-count {
    flex: none; font-size: 13px; font-weight: 700;
    font-variant-numeric: tabular-nums; color: var(--ink);
}
.alerts-row.is-critical .alerts-row-count { color: var(--red-600); }
.alerts-row.is-warning  .alerts-row-count { color: var(--amber-700); }
/* Affordance only — appears on hover so ten chevrons don't stripe the card. */
.alerts-row-go { flex: none; display: flex; color: var(--faint); opacity: 0; transition: opacity .12s ease; }
.alerts-row:hover .alerts-row-go, .alerts-row:focus-visible .alerts-row-go { opacity: 1; }

/* Only rendered when something is hidden. */
.alerts-card-foot {
    display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
    padding: 7px 12px; border-top: 1px solid var(--line-soft);
    background: var(--slate-50);
    font-size: 12px; color: var(--muted);
}

.alerts-card-btn {
    display: inline-flex; align-items: center; gap: 5px;
    border: 0; background: transparent; padding: 2px; cursor: pointer;
    font: inherit; font-size: 12px; font-weight: 600; color: var(--muted);
    border-radius: 8px;
    transition: color .15s ease, background-color .15s ease;
}
.alerts-card-btn svg { flex: none; color: var(--faint); transition: color .15s ease; }
.alerts-card-btn:hover, .alerts-card-btn:hover svg { color: var(--slate-700); background: var(--hover); }
.alerts-card-btn:focus-visible { outline: none; box-shadow: var(--ring-primary); }
/* The way back is the affirmative action once things are hidden. */
.alerts-card-btn.is-link, .alerts-card-btn.is-link svg { color: var(--primary); }
.alerts-card-btn.is-link:hover, .alerts-card-btn.is-link:hover svg { color: var(--primary-strong); }

@media (max-width: 720px) {
    .alerts-card { max-width: none; }
}

/* All-clear: one quiet line, no card, no green panel. Nothing to act on
   means nothing should compete with the KPI row for attention. */
.alert-clear {
    display: flex; align-items: center; gap: 7px;
    margin: 0 0 20px; color: var(--muted); font-size: 13px;
}
.alert-clear svg { flex: none; color: var(--primary); }

/* Pricing page — headline "was → now" card for the promo currently live (or
   next up) on a SKU, so the effect of a promo reads at a glance instead of
   only as a dated row in the table below it. */
.promo-highlight {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 10px 16px; padding: 12px 14px; border-radius: 6px; margin-bottom: 14px;
    border: 1px solid var(--red-50); background: var(--red-50);
}
.promo-highlight.is-upcoming { border-color: var(--blue-50); background: var(--blue-50); }
.promo-highlight-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.promo-highlight-row > svg { color: var(--faint); flex: none; }
.promo-highlight-price { display: flex; flex-direction: column; gap: 2px; line-height: 1.15; }
.promo-highlight-label {
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--faint);
}
.promo-highlight-was .mono { font-size: 13px; color: var(--muted); text-decoration: line-through; }
.promo-highlight-now .mono { font-size: 19px; font-weight: 800; letter-spacing: -.01em; color: var(--red-600); }
.promo-highlight.is-upcoming .promo-highlight-now .mono { color: var(--blue-700); }
.promo-highlight-pct { flex: none; }
.promo-highlight-meta {
    display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); white-space: nowrap;
}
.promo-highlight-meta svg { color: var(--faint); flex: none; }

.price-was { color: var(--faint); text-decoration: line-through; }
.price-now { font-weight: 700; color: var(--ink); }

/* Pricing page — "price ladder" list, replacing the old tier/promo <table>s.
   Two dense tables side by side read as a spreadsheet to non-technical
   warehouse/distribution staff; a plain stack of rows (threshold → price →
   discount, no header row, no gridlines) reads like a simple price list
   instead. Shared by the piece-tier and carton-tier ladders. */
.price-ladder { display: flex; flex-direction: column; }
.price-ladder-row {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 0; border-bottom: 1px solid var(--line-soft);
}
.price-ladder-row:first-child { padding-top: 0; }
.price-ladder-row:last-child { border-bottom: 0; padding-bottom: 0; }
.price-ladder-qty {
    flex: none; min-width: 46px; padding: 3px 10px; border-radius: 999px;
    background: var(--slate-100); color: var(--slate-700);
    font-size: 12.5px; font-weight: 700; text-align: center;
}
.price-ladder-row > svg { flex: none; color: var(--faint); }
.price-ladder-price { flex: 1 1 auto; font-size: 14.5px; font-weight: 700; color: var(--ink); }
.price-ladder-pct { flex: none; }
.price-ladder-del { flex: none; }
.price-ladder-empty,
.promo-list-empty {
    padding: 18px 14px; text-align: center; color: var(--muted); font-size: 13px;
    border: 1px dashed var(--line); border-radius: 8px;
}

/* Promotions list — same "no table" logic: one row per promo carrying a
   status dot (reusing .activity-dot's colour set), the date range + status
   pill, the was→now price change, and delete — instead of a 6-column table
   row. The headline "was → now" banner (.promo-highlight above) still
   covers the live/upcoming promo at a glance; this list is the full history
   underneath it. */
.promo-list { display: flex; flex-direction: column; }
.promo-row {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 12px 0; border-bottom: 1px solid var(--line-soft);
}
.promo-row:first-child { padding-top: 0; }
.promo-row:last-child { border-bottom: 0; padding-bottom: 0; }
.promo-row .activity-dot { margin-top: 7px; }
.promo-row-body { flex: 1 1 auto; min-width: 0; }
.promo-row-period { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; font-weight: 600; color: var(--ink); }
.promo-row-prices { margin-top: 4px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13px; }
.promo-row-prices svg { color: var(--faint); }
.promo-row-del { flex: none; margin-top: 1px; }

/* Pricing page — "Add tier" / "Add promo" mini forms. The submit button
   always sits on its own row below the fields (never inline beside them),
   so the two forms — one with 2 fields, the other with 3 — line up at the
   same heights next to each other instead of one wrapping its button to a
   second line while the other doesn't. */
.pricing-add-form { margin-top: 10px; }
.pricing-add-fields { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
.pricing-add-fields .field { flex: 1 1 110px; margin: 0; }
.pricing-add-form > .btn { width: 100%; justify-content: center; }

/* Pricing page — product-wide Promotion switch (own card at the top of the
   page), same switch styling as the product form's Availability toggle
   (.product-switch) but with the type <select> kept narrow instead of
   spanning the full card width. */
.pricing-promo-card { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; }
.pricing-promo-type { margin: 14px 0 0; padding-top: 14px; border-top: 1px solid var(--line-soft); max-width: 260px; }

/* Compact-density pass, matching the products create/edit form and the
   product detail page — every input and button here was still at the app's
   default (roomier) .input/.btn scale, which read oddly bigger next to the
   rest of the now-tightened product pages. Page-scoped (not a change to
   .input/.btn themselves) since both are used all over the app. */
.pricing-page .input { padding: 9px 10px; font-size: 13px; border-radius: 6px; }
.pricing-page .btn { border-radius: 8px; }

/* ---- Product statistics (/products/{id}/stats) --------------------------
   Period bar, monthly sales-vs-purchases bars, per-customer share bars and
   price-move arrows. All hand-drawn with plain CSS — the page has no chart
   library and doesn't need one, the series is at most 24 month pairs. */
.pstats-period { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.pstats-period-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pstats-period-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pstats-period-now { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.pstats-search { max-width: 330px; }

/* Series colours, one place: sold = the brand petrol (its brighter 500 step —
   the 600 button fill reads grey as a chart mark), received = amber (the
   colour of the "Achats & stock" heading), stock = ink, a negative stock in
   red; PIVAP / Woliz on the channel card, Woliz raspberry. All three hues
   checked together for colour-blind separation, light and dark (dataviz
   validate_palette.js --pairs all, 2026-10-19; dark steps in dark.css). */
.pstats-page {
    --pstats-sold: var(--emerald-500);
    --pstats-received: #c8800d;
    --pstats-stock: var(--slate-700);
    --pstats-neg: var(--red-500);
    --pstats-pivap: var(--emerald-500);
    --pstats-woliz: #b8487a;
}

/* "En bref": the page's story in a few sentences, two columns on a wide
   screen, each sentence behind a small icon; numbers in bold. */
.pstats-brief { margin-bottom: 14px; padding: 14px 18px 16px; }
.pstats-brief-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.pstats-brief-title {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.pstats-brief-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 32px; }
.pstats-brief-item { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.pstats-brief-ico {
    flex: none; width: 26px; height: 26px; border-radius: 7px; display: inline-flex; align-items: center; justify-content: center;
    background: var(--slate-100); color: var(--slate-600);
}
.pstats-brief-item p { margin: 3px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--text); }
.pstats-brief-item strong { font-weight: 600; color: var(--ink); }
.pstats-brief-item.is-warn {
    grid-column: 1 / -1; padding: 8px 10px; border-radius: 8px;
    background: color-mix(in srgb, var(--amber-50) 75%, var(--surface));
}
.pstats-brief-item.is-warn .pstats-brief-ico { background: var(--amber-50); color: var(--amber-700); }
@media (max-width: 900px) { .pstats-brief-list { grid-template-columns: minmax(0, 1fr); } }

/* Eight number cards in two groups — "Ventes" and "Achats & stock" — side
   by side on a wide screen, stacked below 1100px. Each card reads top-down:
   label (icon, name, "?" tip, ↑/↓ against the previous period), the figure,
   then what it is made of. Doubled .kpi-grid.pstats-kpis so it outranks
   shadcn.css's body.skin-shadcn sizes. */
.pstats-kpi-groups { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
@media (max-width: 1100px) { .pstats-kpi-groups { grid-template-columns: minmax(0, 1fr); } }
.pstats-kpi-group { min-width: 0; }
.pstats-kpi-group-title {
    display: flex; align-items: center; gap: 7px; margin: 0 0 7px 2px;
    font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.pstats-kpi-group-title::before { content: ""; width: 8px; height: 8px; border-radius: 2px; background: var(--slate-400); }
.pstats-kpi-group-title.is-sales::before { background: var(--pstats-sold); }
.pstats-kpi-group-title.is-buying::before { background: var(--pstats-received); }
.kpi-grid.pstats-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 0; }
.kpi-grid.pstats-kpis .kpi-card { display: flex; flex-direction: column; gap: 7px; min-width: 0; padding: 12px 14px 13px; }
.kpi-grid.pstats-kpis .kpi-card-label {
    display: flex; align-items: center; gap: 7px; min-width: 0; margin: 0;
    font-size: 12.5px; font-weight: 500; color: var(--muted); white-space: nowrap; overflow: visible;
}
.kpi-grid.pstats-kpis .kpi-card-ico { flex: none; width: 24px; height: 24px; border-radius: 7px; }
.kpi-grid.pstats-kpis .kpi-card-ico .icon { width: 13px; height: 13px; }
.pstats-kpi-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.kpi-grid.pstats-kpis .kpi-card-value {
    margin: 0; font-size: 22px; font-weight: 600; line-height: 1.15; letter-spacing: -.01em;
    white-space: nowrap; font-variant-numeric: proportional-nums;
}
.kpi-grid.pstats-kpis .kpi-card-value .unit { margin-left: 2px; font-size: 12px; font-weight: 500; letter-spacing: 0; color: var(--faint); }
.kpi-grid.pstats-kpis .kpi-card-change { margin: 0; font-size: 12px; line-height: 1.4; white-space: normal; }
@media (max-width: 480px) {
    .kpi-grid.pstats-kpis .kpi-card { padding: 10px 11px; }
    .kpi-grid.pstats-kpis .kpi-card-value { font-size: 18px; }
    .kpi-grid.pstats-kpis .kpi-card-label { font-size: 12px; white-space: normal; }
}
/* "↑ 12 %" against the previous period, pushed to the right of the label. */
.pstats-delta {
    margin-left: auto; flex: none; padding: 0 7px; border-radius: 999px; cursor: default;
    font-size: 11px; font-weight: 700; line-height: 19px; font-variant-numeric: tabular-nums;
}
.pstats-delta.is-up { color: var(--success-strong); background: var(--success-soft); }
.pstats-delta.is-down { color: var(--red-600); background: var(--red-50); }
.pstats-delta.is-flat { color: var(--muted); background: var(--slate-100); }
/* "?" help tip beside a term (tooltip.js reads its data-tip). */
.kpi-card-change .tip-i { color: inherit; }

/* Tabbed detail card: the six history tables live one per panel rather than
   stacked, each with its own head row (title + copy on the left, totals and
   the search box on the right) above the table. The card head carries nothing
   but the tab row — the panel each tab opens names and describes itself, so a
   card title above them only repeated whichever tab was open.

   Scoped overrides of the shared .supplier-tabs / .supplier-tab shell, not
   edits to it: the suppliers and customers detail pages use the same classes
   and keep their wrapping pill tabs. Here the six tabs stay on ONE line
   (scrolling sideways on a narrow screen rather than stacking into a second
   row that pushes the tables down) and take the app's standard 6px button
   radius instead of the pill shape. */
.pstats-tabs-card .card-head { padding: 10px 12px; min-height: 0; }
.pstats-tabs-card .supplier-tabs {
  display: flex; flex-wrap: nowrap; justify-content: flex-start;
  width: 100%; overflow-x: auto; scrollbar-width: thin;
}
.pstats-tabs-card .supplier-tab { border-radius: 8px; white-space: nowrap; flex: 0 0 auto; }
.pstats-tab-count {
  margin-left: 7px; padding: 1px 6px; border-radius: 999px;
  background: var(--slate-100); color: var(--slate-600);
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.supplier-tab.is-active .pstats-tab-count { background: rgba(255, 255, 255, .18); color: var(--white); }
/* Reports: a card whose tables are tabs (reports_tabs_head()), the date range kept at the end of the head. */
.report-tabs-card .card-head { align-items: center; }
.report-tabs-card .supplier-tabs { width: auto; flex: 1 1 auto; min-width: 0; }
.report-tabs-card .card-head > .muted { flex: none; white-space: nowrap; font-size: 12px; }
.pstats-panel-head {
  display: flex; align-items: center; justify-content: flex-end; gap: 12px; flex-wrap: wrap;
  padding: 12px 20px; border-bottom: 1px solid var(--line-soft);
}
.pstats-panel-head:empty { display: none; }
.pstats-panel-head .muted { font-size: 12px; max-width: 70ch; }
/* Search box and dropdown filters stay on one line with the panel title; they
   only drop to their own row once the whole head can no longer fit. */
.pstats-panel-tools { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; margin-left: auto; }
.pstats-panel-tools .muted { white-space: nowrap; }
.pstats-empty { text-align: center; padding: 28px; }

.pstats-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; color: var(--muted); flex: none; }
/* Narrow: the legend drops onto its own line rather than being squeezed against
   the end of the card's description. */
@media (max-width: 720px) {
  .pstats-page .card-head { flex-wrap: wrap; gap: 8px; }
  .pstats-legend { flex-basis: 100%; }
}
.pstats-legend span { display: inline-flex; align-items: center; gap: 6px; }
.pstats-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: none; }
.pstats-swatch-sold { background: var(--pstats-sold); }
.pstats-swatch-bought { background: var(--pstats-received); }
.pstats-swatch-stock { width: 14px; height: 0; border-radius: 0; border-top: 2px solid var(--pstats-stock); background: none; }
.pstats-swatch-pivap { background: var(--pstats-pivap); }
.pstats-swatch-woliz { background: var(--pstats-woliz); }

/* The chart: a bars band (sold vs received, gridlines, scale on the left)
   and, under it, a stock band (the level at the end of each column, one line
   drawn by an SVG across the whole band). Both bands share the columns, so a
   column's bars and its stock dot line up. Columns are equal-width with no
   gap — the SVG puts each point at (i + ½) / n of its width — and scroll
   sideways together when there are more than fit (31 days on a phone).
   --pstats-top leaves room for the value printed over the tallest bar. */
.pstats-plot {
    --pstats-h: 150px; --pstats-stock-h: 64px; --pstats-band-gap: 16px; --pstats-top: 18px;
    display: flex; align-items: flex-start; gap: 10px;
}
.pstats-yaxes { flex: none; display: flex; flex-direction: column; }
.pstats-yaxis {
  margin-top: var(--pstats-top); height: var(--pstats-h); display: flex; flex-direction: column;
  justify-content: space-between; align-items: flex-end;
  font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums;
}
.pstats-yaxis span { height: 0; display: flex; align-items: center; white-space: nowrap; }
.pstats-yaxis.is-stock { position: relative; display: block; margin-top: var(--pstats-band-gap); height: var(--pstats-stock-h); }
.pstats-yaxis.is-stock span { position: absolute; right: 0; height: auto; transform: translateY(50%); }
.pstats-yaxis.is-stock .is-neg { color: var(--red-600); }
.pstats-plot-area { position: relative; flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: thin; }
.pstats-chart {
  --pstats-col-min: 40px; --pstats-bar-w: 14px;
  position: relative; z-index: 0; display: flex; align-items: stretch;
  padding-top: var(--pstats-top); min-width: calc(var(--pstats-n, 12) * var(--pstats-col-min));
}
.pstats-chart[data-grain="day"] { --pstats-col-min: 26px; --pstats-bar-w: 8px; }
/* Gridlines of the bars band, and the stock band's own tinted strip. */
.pstats-chart::before {
  content: ''; position: absolute; left: 0; right: 0; top: var(--pstats-top); height: var(--pstats-h); z-index: -1;
  background: repeating-linear-gradient(to bottom, var(--line-soft) 0 1px, transparent 1px calc(var(--pstats-h) / 4));
  border-bottom: 1px solid var(--line); pointer-events: none;
}
.pstats-plot.has-stock .pstats-chart::after {
  content: ''; position: absolute; left: 0; right: 0; z-index: -1; border-radius: 6px; pointer-events: none;
  top: calc(var(--pstats-top) + var(--pstats-h) + var(--pstats-band-gap)); height: var(--pstats-stock-h);
  background: var(--slate-50);
}

/* A column: its bars, its stock dot, its label. With lines behind it, it is
   a button — tinted on hover, held tinted while its lines are open. */
.pstats-chart-col {
  position: relative; flex: 1 1 0; min-width: var(--pstats-col-min);
  display: flex; flex-direction: column; align-items: stretch;
  margin: 0; padding: 0; border: 0; border-radius: 6px; background: none;
  font: inherit; color: inherit; text-align: center; cursor: default;
}
button.pstats-chart-col { cursor: pointer; }
button.pstats-chart-col:hover { background: color-mix(in srgb, var(--primary) 6%, transparent); }
button.pstats-chart-col:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--primary); }
.pstats-chart-col.is-selected { background: color-mix(in srgb, var(--primary) 11%, transparent); }
.pstats-bars { position: relative; display: flex; align-items: flex-end; justify-content: center; gap: 2px; height: var(--pstats-h); }
.pstats-bar { position: relative; display: block; width: var(--pstats-bar-w); border-radius: 4px 4px 0 0; transition: opacity .15s ease; }
.pstats-chart[data-grain="day"] .pstats-bar { border-radius: 3px 3px 0 0; }
.pstats-bar-sold { background: var(--pstats-sold); }
.pstats-bar-bought { background: var(--pstats-received); }
.pstats-chart-col:hover .pstats-bar { opacity: .85; }
/* Value over its bar; only printed when the bar is not zero. A day column is
   too narrow for two, so it keeps the sold one (the tip and the click give
   the rest). */
.pstats-bar-val {
    position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 2px;
    font-size: 10px; font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums;
    white-space: nowrap; pointer-events: none; color: var(--ink);
}
.pstats-chart[data-grain="day"] .pstats-bar-bought .pstats-bar-val { display: none; }
/* Same month last year: a dashed level across the month's bar pair. */
.pstats-ly {
    position: absolute; left: 50%; width: calc(var(--pstats-bar-w) * 2 + 14px); transform: translate(-50%, 1px); height: 0;
    border-top: 2px dashed var(--slate-400); pointer-events: none; z-index: 1;
}
.pstats-swatch.pstats-swatch-ly { height: 0; width: 14px; border-radius: 0; border-top: 2px dashed var(--slate-400); background: none; }

/* Stock band: one dot per column (shown on hover, while open, and always on
   the last one, which also prints its level), over the SVG line. */
.pstats-stock-cell { position: relative; display: block; height: var(--pstats-stock-h); margin-top: var(--pstats-band-gap); }
.pstats-stock-hit { position: absolute; inset: 0; display: block; }
.pstats-stock-dot {
    position: absolute; left: 50%; z-index: 3; width: 8px; height: 8px; margin: 0 0 -4px -4px; border-radius: 50%;
    background: var(--pstats-stock); box-shadow: 0 0 0 2px var(--surface); opacity: 0; transition: opacity .12s ease;
}
.pstats-stock-dot.is-neg { background: var(--pstats-neg); }
.pstats-stock-dot.is-last,
.pstats-chart-col:hover .pstats-stock-dot,
.pstats-chart-col.is-selected .pstats-stock-dot { opacity: 1; }
.pstats-stock-val {
    position: absolute; right: calc(50% - 4px); z-index: 3; margin-bottom: 7px; padding: 0 3px; border-radius: 3px;
    font-size: 11px; font-style: normal; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap;
    color: var(--ink); background: color-mix(in srgb, var(--surface) 85%, transparent);
}
.pstats-stock-val.is-neg { color: var(--red-600); }
.pstats-stock-svg {
    position: absolute; left: 0; z-index: 2; width: 100%; overflow: visible; pointer-events: none;
    top: calc(var(--pstats-top) + var(--pstats-h) + var(--pstats-band-gap)); height: var(--pstats-stock-h);
}
.pstats-stock-line { fill: none; stroke: var(--pstats-stock); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.pstats-stock-line.is-neg { stroke: var(--pstats-neg); }
.pstats-stock-area { fill: var(--pstats-stock); opacity: .1; }
.pstats-stock-area.is-neg { fill: var(--pstats-neg); opacity: .16; }
.pstats-stock-zero { stroke: var(--slate-300); stroke-width: 1; vector-effect: non-scaling-stroke; }

.pstats-chart-label { display: block; margin-top: 8px; padding-bottom: 2px; font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pstats-chart-label.is-minor { visibility: hidden; }
.pstats-chart-col.is-future .pstats-chart-label { opacity: .5; }

/* The clicked column's lines, under the chart: what moved, then its sales
   and its purchases side by side (stacked on a narrow screen). */
.pstats-drill-block {
    margin-top: 16px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); overflow: hidden;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05); animation: pstats-drill-in .16s ease-out;
}
@keyframes pstats-drill-in { from { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) { .pstats-drill-block { animation: none; } }
.pstats-drill-head {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
    padding: 11px 14px; border-bottom: 1px solid var(--line-soft); background: var(--slate-50);
}
.pstats-drill-title { font-size: 14px; font-weight: 600; color: var(--ink); }
.pstats-drill-sub { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 3px; font-size: 12px; color: var(--muted); }
.pstats-drill-sub span { display: inline-flex; align-items: center; gap: 6px; }
.pstats-drill-sub strong { font-weight: 600; color: var(--ink); }
.pstats-drill-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.pstats-drill-part { min-width: 0; padding: 12px 14px 14px; }
.pstats-drill-part + .pstats-drill-part { border-left: 1px solid var(--line-soft); }
.pstats-drill-part-title {
    display: flex; align-items: center; gap: 2px; margin-bottom: 8px;
    font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.pstats-drill-table th, .pstats-drill-table td { padding-left: 10px; padding-right: 10px; white-space: nowrap; }
.pstats-drill-none { padding: 6px 0; font-size: 13px; }
@media (max-width: 1000px) {
    .pstats-drill-grid { grid-template-columns: minmax(0, 1fr); }
    .pstats-drill-part + .pstats-drill-part { border-left: 0; border-top: 1px solid var(--line-soft); }
}

/* Three small cards under the chart: where the stock is, PIVAP vs Woliz,
   the biggest customers. Each row: a name and its figure, a thin bar for its
   share of the card's total. */
.pstats-insights { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin: 16px 0; }
@media (max-width: 1100px) { .pstats-insights { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .pstats-insights { grid-template-columns: minmax(0, 1fr); } }
.pstats-insights > .card { min-width: 0; margin: 0; padding: 14px 16px 16px; }
.pstats-insight-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.pstats-insight-sub { margin-top: 2px; font-size: 12px; }
.pstats-insight-total { flex: none; font-size: 15px; font-weight: 600; white-space: nowrap; color: var(--ink); }
.pstats-insight-empty { padding: 6px 0; font-size: 13px; }
.pstats-insight-more { margin-top: 12px; font-size: 12px; }
.pstats-bar-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 13px; }
.pstats-bar-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 13px; }
.pstats-bar-name { display: inline-flex; align-items: center; gap: 7px; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.pstats-bar-name .link, .pstats-bar-name > span:not(.pill):not(.pstats-top-rank) { overflow: hidden; text-overflow: ellipsis; }
.pstats-bar-fig { flex: none; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pstats-bar-fig strong { font-weight: 600; color: var(--ink); }
.pstats-bar-meta { margin-top: 1px; font-size: 11.5px; }
.pstats-meter { display: block; height: 6px; margin-top: 6px; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.pstats-meter i { display: block; height: 100%; border-radius: inherit; background: var(--pstats-stock); }
.pstats-meter.is-primary i { background: var(--pstats-sold); }
.pstats-where-row.is-unplaced { padding-top: 10px; border-top: 1px dashed var(--line); }
.pstats-split { display: flex; gap: 2px; height: 10px; margin-bottom: 16px; border-radius: 999px; overflow: hidden; }
.pstats-split i { display: block; flex-basis: 0; min-width: 3px; }
.pstats-split .is-pivap { background: var(--pstats-pivap); }
.pstats-split .is-woliz { background: var(--pstats-woliz); }
.pstats-top-rank {
    flex: none; display: inline-flex; align-items: center; justify-content: center; width: 19px; height: 19px; border-radius: 50%;
    background: var(--slate-100); color: var(--slate-600); font-size: 10.5px; font-weight: 700;
}


/* Seasonality facts — a row of small tiles under the month-by-month chart. */
.pstats-season-facts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.pstats-fact { flex: 1 1 auto; min-width: 140px; border: 1px solid var(--line); border-radius: 6px; padding: 7px 11px; background: var(--bg); }
.pstats-fact-label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .03em; }
.pstats-fact-value { font-size: 14px; font-weight: 700; margin-top: 2px; white-space: nowrap; }

/* Sortable column headers (same affordance as modules/suppliers/pages/show.php). */
.th-sortable { cursor: pointer; user-select: none; }
.th-sortable svg { width: 12px; height: 12px; margin-left: 3px; vertical-align: -1px; opacity: .45; transition: opacity .12s, transform .12s; }
.th-sortable:hover svg { opacity: .8; }
.th-sortable.is-sorted svg { opacity: 1; color: var(--primary); }
.th-sortable.is-sorted-desc svg { transform: rotate(180deg); }

/* Per-table dropdown filters sitting next to a panel's search box. Chromium
   under-measures a select's intrinsic width and clips the selected option
   ("Tous les types de mouvement" showed as "Tous les types d"), so the floor
   is set from the longest label rather than left to the browser. */
.pstats-filter { width: auto; min-width: 230px; }
/* A table's own date-range button (stats.php $rangePicker). */
.pstats-drp { flex: none; min-width: 150px; }
.pstats-export { flex: none; }

/* "Données à vérifier": prices that cannot be right, above the cards. */
.pstats-alerts {
    margin-bottom: 14px; padding: 10px 14px; border-left: 3px solid var(--amber-600);
    background: color-mix(in srgb, var(--amber-50) 70%, var(--surface));
}
.pstats-alerts-head {
    display: flex; align-items: center; gap: 6px; margin-bottom: 4px;
    font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--amber-700);
}
.pstats-alert { font-size: 13px; color: var(--text); }
.pstats-alert + .pstats-alert { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line); }
.pstats-alert p { margin: 0; }
.pstats-alert ul { margin: 3px 0 0; padding-left: 18px; }
.pstats-alert li { margin: 1px 0; }

/* The history tables carry up to nine columns; at the app's default cell
   padding the widest of them overflowed its card and cut the last column
   off. Compact rows plus a floor under the name column fit them all. */
.pstats-tabs-card .table th,
.pstats-tabs-card .table td { padding-left: 12px; padding-right: 12px; }
.pstats-tabs-card .table td { white-space: nowrap; }
.pstats-col-name { min-width: 158px; }
.pstats-tabs-card .table td:first-child { white-space: normal; }
@media (max-width: 560px) {
  .pstats-panel-tools { flex-wrap: wrap; }
  .pstats-search { max-width: none; }
}

.price-up { color: var(--red-600); font-weight: 600; }
.price-down { color: var(--success-strong); font-weight: 600; }

/* ---- Bon de commande post-save modal (supplier order show) --------------
   Reuses .order-confirm-backdrop / .order-confirm-modal for the shell; the
   form inside is laid out as a mail composer (label-in-line To/Cc/Subject
   rows over a borderless body, attachments as chips) rather than as a stack
   of ordinary .input fields, so it reads as the email it is about to send. */
.bc-send-modal {
  width: min(100%, 620px);
  max-height: calc(100vh - 40px);
  padding: 20px 22px;
  overflow-y: auto;
}
.bc-send-head { display: flex; align-items: flex-start; gap: 12px; justify-content: space-between; }
.bc-send-head .order-confirm-copy { margin-bottom: 14px; }
.bc-send-head .order-confirm-copy strong { display: block; margin-top: 4px; }
.bc-send-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
/* Where the email actually stands (envoyé / à envoyer) — the send is a
   separate operation from the save, so the modal states it outright. */
.bc-send-state { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.bc-send-state .pill { display: inline-flex; align-items: center; gap: 5px; }

.bc-send-form {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  overflow: hidden;
}
.bc-compose-row { display: flex; align-items: center; gap: 8px; padding: 0 12px; border-bottom: 1px solid var(--line); }
.bc-compose-label { flex: 0 0 auto; min-width: 54px; color: var(--muted); font-size: 12px; }
.bc-compose-input {
  flex: 1;
  min-width: 0;
  padding: 10px 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}
.bc-compose-input:focus { outline: none; }
.bc-compose-cc {
  flex: 0 0 auto;
  padding: 2px 6px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
}
.bc-compose-cc:hover { color: var(--primary); }
.bc-compose-body {
  min-height: 150px;
  padding: 12px;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.55;
  resize: vertical;
}
.bc-compose-body:focus { outline: none; }

.bc-attachments { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--line); }
.bc-attachment,
.bc-attachment-add {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 5px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--slate-50, #f8fafc);
  color: var(--ink);
  font-size: 12px;
  cursor: pointer;
}
.bc-attachment.is-off { opacity: .5; text-decoration: line-through; }
.bc-attachment-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.bc-attachment-size { color: var(--muted); }
.bc-attachment-remove { display: inline-flex; padding: 0; border: 0; background: transparent; color: var(--muted); cursor: pointer; }
.bc-attachment-remove:hover { color: var(--red-600, #dc2626); }
.bc-attachment-add { border-style: dashed; background: transparent; color: var(--muted); }
.bc-attachment-add:hover { border-color: var(--primary); color: var(--primary); }
.bc-attachment-add input[type="file"] { display: none; }

.bc-send-warning { display: flex; align-items: center; gap: 6px; padding: 0 12px 10px; color: var(--amber-600, #d97706); }
.bc-send-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  justify-content: flex-end;
  padding: 10px 12px;
  border-top: 1px solid var(--line);
  background: var(--slate-50, #f8fafc);
}
.bc-send-hint { flex: 1 1 120px; min-width: 0; margin-right: auto; line-height: 1.35; }

@media (max-width: 560px) {
  .bc-compose-row { align-items: flex-start; }
  .bc-send-hint { width: 100%; margin-bottom: 4px; }
}

/* ---------------------------------------------------------------------------
   Prix de revient (/cost-prices) — "Tout ouvert", picked from a gallery of
   whole-page designs 2026-10-23: one product = one card, already open. Inside
   it, per variant: the batches table and the written-out sum on the left, the
   price tiles (calculated / manual / final / PA) and "Save and apply" on the
   right; they stack under 1100px.
--------------------------------------------------------------------------- */
.cost-scope { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--surface); }
.cost-scope a { padding: 8px 12px; font-size: 13.5px; color: var(--muted); text-decoration: none; border-left: 1px solid var(--line); white-space: nowrap; }
.cost-scope a:first-child { border-left: 0; }
.cost-scope a span { margin-left: 3px; font-weight: 700; color: var(--ink); }
.cost-scope a:hover { background: var(--hover); }
.cost-scope a.is-on { background: var(--primary-soft); color: var(--ink); font-weight: 600; }
.cost-numline { display: flex; flex-wrap: wrap; gap: 6px 22px; margin: -4px 0 14px; font-size: 14px; color: var(--muted); }
.cost-numline b { color: var(--ink); font-size: 15px; }
.cost-numline i { font-style: normal; font-size: 12.5px; }
.cost-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.cost-toolbar .filter-select { width: auto; }
.cost-search { flex: 0 1 340px; }
.cost-search .filter-search { width: 100%; }
.cost-count { margin-left: auto; font-size: 13px; }

.cost-card { padding: 18px 20px; margin-bottom: 14px; }
.cost-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.cost-card-head { cursor: pointer; align-items: center; }
.cost-card-title { flex: 1; min-width: 0; }
.cost-card-head h3 { margin: 0 0 2px; font-size: 18px; }
.cost-card-toggle {
    flex: none; width: 30px; height: 30px; display: grid; place-items: center; padding: 0;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: var(--muted); cursor: pointer;
    transition: transform .15s ease, background .12s ease;
}
.cost-card-toggle:hover { background: var(--hover); color: var(--ink); }
.cost-card.is-closed .cost-card-toggle { transform: rotate(-90deg); }
/* The one-line summary only shows on a closed card — open, the tiles say it all. */
.cost-card-summary { display: none; gap: 16px; font-size: 13px; color: var(--muted); white-space: nowrap; }
.cost-card-summary b { color: var(--ink); font-size: 14px; }
.cost-card.is-closed .cost-card-summary { display: inline-flex; }
.cost-card.is-closed { padding-top: 12px; padding-bottom: 12px; }
@media (max-width: 700px) { .cost-card.is-closed .cost-card-summary { display: none; } }
.cost-card-head h3 .link { color: var(--ink); }
.cost-sub { font-size: 13px; color: var(--muted); line-height: 1.35; }
.cost-sub-dot { margin: 0 5px; opacity: .6; }
.cost-sku { margin-top: 14px; }
.cost-sku + .cost-sku { padding-top: 14px; border-top: 1px dashed var(--line); }
.cost-sku-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.cost-sku-grid { display: grid; gap: 18px; align-items: start; }
@media (min-width: 1100px) { .cost-sku-grid { grid-template-columns: minmax(0, 1fr) 370px; } }
.cost-sku-main, .cost-sku-side { min-width: 0; }

.cost-panel-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; font-size: 13px; }
.cost-picked { margin-left: auto; font-weight: 600; color: var(--primary); font-variant-numeric: tabular-nums; }
.cost-batches-scroll { max-height: 360px; overflow: auto; border: 1px solid var(--line-soft); border-radius: 10px; }
.cost-batches { margin: 0; width: 100%; }
.cost-batches tbody tr.is-picked { background: color-mix(in srgb, var(--primary) 5%, var(--surface)); }
.cost-batches tbody tr.is-picked td:first-child { box-shadow: inset 2px 0 0 var(--primary); }
.cost-batches input[type=checkbox] { width: 16px; height: 16px; cursor: pointer; accent-color: var(--primary); }
.cost-batch-empty { opacity: .5; }
.cost-formula {
    margin-top: 10px; padding: 10px 14px; border-radius: 10px; background: var(--slate-50);
    font: 13px var(--font-mono); color: var(--muted); overflow-wrap: anywhere;
}

.cost-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cost-tile { display: flex; flex-direction: column; gap: 4px; padding: 12px; border: 1px solid var(--line-soft); border-radius: 10px; min-width: 0; }
.cost-tile .k { font-size: 12px; color: var(--muted); }
/* span.v.mono outranks the readable skin's body.skin-readable .mono (12.5px). */
.cost-tile span.v.mono { font-size: 19px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.cost-tile .s { font-size: 12px; color: var(--muted); }
.cost-tile.is-final { border-color: var(--primary); background: var(--primary-soft); }
.cost-tile .cost-manual { width: 100%; max-width: 120px; text-align: right; padding: 5px 8px; font-size: 14px; }
.cost-manual-row { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.cost-manual-row .cost-manual { max-width: 92px; font-size: 13px; }
.cost-save { width: 100%; justify-content: center; margin-top: 12px; }
/* A card on another page (costPaginate()). Printing shows them all. */
.cost-card[data-page-off] { display: none; }
@media print { .cost-card[data-page-off]:not([hidden]) { display: block; } }
.cost-pager .table-pagination { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }

/* ---------------------------------------------------------------------------
   Purchasing needs (/purchase-needs) — "reliquats client".
   The page is one pipeline in three stages (à valider -> à acheter ->
   commandé), so the KPI row IS the tab bar: each stage card is a button that
   swaps the single table below it. It used to stack every stage's table on
   one scroll, with the "sent to purchasing" rows appearing twice for admins.
   --------------------------------------------------------------------------- */
.pn-stages {
    display: flex;
    align-items: stretch;
    gap: 8px;
    margin-bottom: 16px;
}
.pn-stage {
    flex: 1 1 0;
    min-width: 0;
    display: grid;
    grid-template-columns: 28px 1fr auto;
    grid-template-areas: "icon label value" "icon sub value";
    align-items: center;
    column-gap: 10px;
    row-gap: 1px;
    padding: 12px 14px;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.pn-stage:hover { border-color: var(--slate-300); background: var(--hover); }
.pn-stage:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.pn-stage.is-active {
    border-color: var(--primary);
    background: var(--primary-soft);
    box-shadow: 0 6px 16px -12px rgba(15, 23, 42, .3);
}
.pn-stage-ico {
    grid-area: icon;
    width: 28px; height: 28px;
    border-radius: 8px;
    display: grid; place-items: center;
}
.pn-stage-ico .icon { width: 15px; height: 15px; }
.pn-stage-ico.red { background: var(--red-50); color: var(--red-600); }
.pn-stage-ico.blue { background: var(--blue-50); color: var(--blue-600); }
.pn-stage-ico.green { background: var(--primary-soft); color: var(--primary); }
.pn-stage-label { grid-area: label; font-size: 13px; font-weight: 700; color: var(--ink); white-space: nowrap; }
.pn-stage-sub {
    grid-area: sub;
    font-size: 11.5px; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pn-stage-value {
    grid-area: value;
    font-size: 22px; font-weight: 800; letter-spacing: -.02em;
    color: var(--ink); font-variant-numeric: tabular-nums;
}
.pn-stage.is-active .pn-stage-value { color: var(--primary-strong); }
/* The arrows between the cards say "same thing, one step later" — decoration
   only, and the first thing to drop when the row has to stack. */
.pn-stage-arrow { align-self: center; color: var(--slate-300); flex: 0 0 auto; }

.pn-panel-card { overflow: hidden; }
.pn-panel-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    flex-wrap: wrap; gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--line-soft);
}
.pn-panel-title { font-size: 15px; font-weight: 700; color: var(--ink); }
.pn-panel-hint { margin-top: 3px; font-size: 12.5px; color: var(--muted); }
.pn-search { flex: 0 1 280px; min-width: 200px; }

/* Bulk bar for the "à valider" stage: select-all, live count, and the one
   action — disabled until something is actually ticked, so the button says
   whether the form is ready instead of failing on submit. */
.pn-actionbar {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    padding: 10px 20px;
    background: var(--slate-50);
    border-bottom: 1px solid var(--line-soft);
}
.pn-selectall { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--slate-700); cursor: pointer; }
.pn-actionbar .btn { margin-left: auto; }

.pn-cell-main { font-weight: 600; color: var(--ink); }
.pn-cell-sub { margin-top: 2px; font-size: 12px; color: var(--muted); }

/* "3 commandes" is a disclosure, not a label: the buying decision is the
   pooled quantity, the client orders behind it are the justification you
   open when you want it. They used to be permanent extra rows in the table. */
.pn-expand {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 9px 4px 6px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--surface);
    font-size: 12.5px; font-weight: 600; color: var(--slate-600);
    cursor: pointer;
}
.pn-expand:hover { border-color: var(--slate-300); color: var(--ink); background: var(--hover); }
.pn-expand-chev { display: inline-flex; transition: transform .15s ease; }
.pn-expand-chev.is-open { transform: rotate(90deg); }
.pn-detail-row td { background: var(--slate-50); padding-top: 10px; padding-bottom: 12px; }
.pn-detail-client { color: var(--ink); font-weight: 600; }
.pn-detail-qty { color: var(--muted); font-variant-numeric: tabular-nums; }
/* "To buy" detail: one card per waiting client order. */
.pn-orders { display: flex; flex-direction: column; gap: 8px; }
.pn-order { padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); font-size: 12.5px; color: var(--slate-600); }
.pn-order-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
.pn-order-qty { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 18px; margin-top: 6px; }
.pn-order-qty strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.pn-order-others-title { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line); font-size: 11.5px; font-weight: 600; color: var(--muted); }
.pn-order-others { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.pn-order-others li { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; }
.pn-order-other-name { color: var(--ink); }

.pn-empty { padding: 44px 20px; text-align: center; color: var(--muted); font-size: 13.5px; }
.pn-empty .icon { width: 26px; height: 26px; color: var(--slate-300); margin-bottom: 8px; }

@media (max-width: 900px) {
    .pn-stages { flex-wrap: wrap; }
    .pn-stage { flex: 1 1 200px; }
    .pn-stage-arrow { display: none; }
    .pn-panel-head { flex-direction: column; }
    .pn-search { flex: 1 1 auto; width: 100%; }
    .pn-actionbar .btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* ---- COD review queue (modules/cod/pages/index.php) ---------------------
   A pending collection is a *decision*, not a form to fill in: the row puts
   the driver's claim (who, when, how much) on the left, the money on the
   right, and the decision buttons next to the money. The happy path —
   "confirm exactly what the driver reported" — is one click on a button
   that states the amount, so nothing has to be typed or re-read to take it.
   The two exceptions (confirm a smaller amount, reject with a reason) are
   disclosures that open under the row, which is also what replaces the old
   native confirm() dialog: opening the panel IS the deliberate step, and it
   spells out the consequence instead of a browser alert. */
.cod-queue { display: flex; flex-direction: column; }
.cod-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "main amount actions" "panel panel panel";
  align-items: center;
  gap: 0 22px;
  padding: 14px 18px;
  border-top: 1px solid var(--line-soft);
  transition: background .15s ease;
}
/* The card head above already draws a line — without this the first row
   would sit under a double border. */
.cod-row:first-child { border-top: 0; }
.cod-row:hover { background: var(--hover); }
.cod-row.is-open { background: var(--hover); }

.cod-row-main { grid-area: main; min-width: 0; }
.cod-ident { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cod-customer { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.cod-meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-top: 5px; font-size: 12px; color: var(--muted); }
.cod-meta .avatar-sm { width: 22px; height: 22px; font-size: 10px; }
.cod-driver { display: inline-flex; align-items: center; gap: 6px; }
.cod-driver-name { font-weight: 600; color: var(--slate-600); }
.cod-sep { color: var(--slate-300); }
/* The driver's own words, quoted rather than run into the metadata line —
   it is the one free-text field on the row and usually the reason a
   reviewer hesitates. */
.cod-note {
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 7px; padding: 6px 10px;
  border-left: 2px solid var(--slate-200);
  background: var(--slate-50);
  border-radius: 0 6px 6px 0;
  font-size: 12.5px; color: var(--slate-600); overflow-wrap: anywhere;
}
.cod-note .icon { color: var(--faint); margin-top: 1px; }

.cod-amount-cell { grid-area: amount; text-align: right; }
.cod-amount {
  font-family: var(--font-mono);
  font-size: 19px; font-weight: 800; letter-spacing: -.01em;
  color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.cod-amount-unit { font-size: 12px; font-weight: 700; color: var(--muted); }
/* How the collected cash compares to what the order is worth. Silent when
   they match to the centime (the common case — a line saying "this is
   normal" on every row is noise); loud only when they don't. */
.cod-compare { margin-top: 2px; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.cod-compare.is-match { color: var(--faint); font-weight: 500; }
.cod-compare.is-short { color: var(--amber-700); }
.cod-compare.is-over  { color: var(--red-600); }

.cod-actions { grid-area: actions; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.cod-actions form { margin: 0; }
.cod-actions .btn-primary { white-space: nowrap; }

/* Disclosures. Hidden with the `hidden` attribute (the app's [hidden] reset
   makes that authoritative), so no JS is needed for the closed state. */
.cod-panel {
  grid-area: panel;
  margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-left-width: 3px; border-radius: 6px;
  background: var(--surface);
  animation: cod-panel-in .16s ease both;
}
.cod-panel-confirm { border-left-color: var(--primary); }
.cod-panel-reject  { border-left-color: var(--red-400); }
@keyframes cod-panel-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.cod-panel-form { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; margin: 0; }
.cod-panel-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.cod-panel-label { font-size: 12px; font-weight: 600; color: var(--slate-600); }
.cod-panel-field .input-affix { width: 150px; }
.cod-panel-note-field { flex: 1 1 260px; max-width: 420px; }
.cod-panel-hint { flex: 1 1 100%; margin: 8px 0 0; font-size: 12px; color: var(--muted); }

/* Reviewed (audit) table — the review note and who signed it off live under
   the status pill, so "why was this refused, and by whom" is answerable
   without opening anything. */
.cod-reviewed-head { display: flex; align-items: center; gap: 12px; }
.cod-status-cell { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.cod-status-pills { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cod-review-note {
  max-width: 260px; font-size: 11.5px; color: var(--muted); font-style: italic;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cod-reviewed-by { display: block; margin-top: 2px; font-size: 11px; color: var(--faint); }
.cod-posted { color: var(--amber-700); font-weight: 700; }
.cod-resubmit-field { width: 160px; }
/* The resubmit control makes the last column wide; without these the
   date and the amount are the cells that give way and wrap mid-value. */
.cod-when, .cod-amount-col { white-space: nowrap; }

/* Empty state — a queue with nothing in it is good news, so it reads as a
   clean bill rather than as a missing table. */
.cod-empty { padding: 44px 20px; text-align: center; }
.cod-empty-ico {
  display: inline-grid; place-items: center; width: 46px; height: 46px;
  margin-bottom: 12px; border-radius: 999px;
  background: var(--success-soft); color: var(--success-600);
}
.cod-empty-title { font-size: 15px; font-weight: 600; color: var(--ink); }
.cod-empty-copy { margin-top: 4px; font-size: 13px; color: var(--muted); }

@media (max-width: 900px) {
  .cod-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "main amount" "actions actions" "panel panel";
    align-items: start;
    gap: 12px 16px;
  }
  .cod-actions { justify-content: flex-start; }
  .cod-review-note { max-width: 180px; }
}
@media (max-width: 560px) {
  .cod-actions .btn { flex: 1 1 auto; justify-content: center; }
  .cod-panel-field, .cod-panel-field .input-affix { width: 100%; }
  .cod-panel-form > .btn { flex: 1 1 auto; justify-content: center; }
}

/* Record-page overflow menu (customers/show): a "⋯" button holding the
   secondary actions, reusing the .m-menu-pop dropdown the products row menu
   already uses. The popup needs a positioned parent. */
.action-menu { position: relative; display: inline-flex; }
.action-menu .m-menu-pop { min-width: 190px; }
.action-menu .m-menu-pop form { margin: 0; }
.action-menu .m-menu-item { font-size: 13px; padding: 8px 10px; }

/* Empty-state message in a table cell or card body ("Aucun … pour le
   moment"). !important because it replaces inline styles whose whole job was
   to beat the table's own cell padding (and the skin's, which is more
   specific than any single class). */
.table-empty { text-align: center !important; padding: 28px !important; }
/* Compact pill for dense cells and popups. */
.pill.pill-sm, body.skin-shadcn .pill.pill-sm { font-size: 11px; padding: 2px 7px; }

/* ---- round2 pages ----------------------------------------------------------
   Static inline styles and page <style> blocks moved out of module pages
   (everything outside the pager/table pages). Grouped by module, each rule
   scoped as tightly as where it came from. */

/* purchase_documents/pages/_filters.php */
.pd-filter-search { max-width: 240px; }

/* products/pages/_form.php — list inside the "fix the SKU rows" error flash */
.product-sku-errors { margin: 6px 0 0; padding-left: 18px; }
/* ---- end round2 pages ---- */

/* ---- round2 tables1 (list pages on table-tools.js) ----
   Classes that replaced the inline styles of the batch-1 list pages. */

/* A status pill rendered as a toggle <button> (customers, suppliers): no button
   chrome. Two classes so it beats the shadcn skin's `body.skin-shadcn .pill`
   border, which the old inline style used to override. */
.pill.pill-toggle { border: 0; cursor: pointer; }

/* supplier_orders/pages/index.php — the status multiselect button while a
   status or variance filter is applied, and the rule between the two groups
   of options in its popup. */
.filter-drp-btn.is-active { border-color: var(--primary); color: var(--primary); }
.multisel-divider { border-top: 1px solid var(--line-soft); margin: 4px 2px; }
/* The over-delivery detail lines under a supplier order's status pill. */
.so-over-label { margin-top: 3px; color: var(--blue-700); }
/* ---- end round2 tables1 ---- */

/* ---- Driver tracking (modules/tracking/) + sync conflicts (supervisor/pages/sync_conflicts.php) ---- */
.trk-layout { display: grid; grid-template-columns: minmax(260px, 340px) 1fr; gap: 16px; align-items: start; }
.trk-list { max-height: 72vh; overflow: auto; }
.trk-map-card { overflow: hidden; }
.trk-map { height: 72vh; min-height: 420px; }
.trk-empty { padding: 28px 16px; text-align: center; }
.trk-stop { display: flex !important; align-items: center; justify-content: center; border-radius: 50%; border: 2px solid #fff; color: #fff; font-size: 12px; font-weight: 700; background: var(--slate-600); box-shadow: 0 1px 3px rgba(0, 0, 0, .3); }
.trk-stop--delivered { background: var(--emerald-600); }
.trk-stop--failed { background: var(--red-600); }
.trk-replay-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; padding: 14px 16px; margin-bottom: 16px; }
.trk-field { display: flex; flex-direction: column; min-width: 200px; }
.trk-field .label { margin-bottom: 4px; }
.trk-replay-controls { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
.trk-replay-controls input[type="range"] { flex: 1; accent-color: var(--primary); }
.trk-timeline { display: flex; flex-direction: column; }
.trk-timeline-row { display: flex; gap: 12px; padding: 9px 16px; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.trk-timeline-row .mono { color: var(--muted); flex: none; }
.trk-timeline-row.is-alert { color: var(--red-600); }
/* Live map (/tracking, pages/index.php): number tiles, a driver list beside the
   map, a summary card over the map for the driver picked. One colour per state,
   taken from the palette: live / signal delayed / no signal / location off. */
.trk-layout--live, .trk-live {
    --trk-live: var(--rl-green-dot, var(--emerald-600));
    --trk-recent: var(--rl-amber-dot, var(--amber-600));
    --trk-offline: var(--rl-slate-dot, var(--slate-400));
    --trk-alert: var(--rl-red-dot, var(--red-600));
}
.trk-layout--live { --trk-h: max(520px, calc(100vh - 290px)); grid-template-columns: minmax(300px, 360px) 1fr; align-items: stretch; }
.trk-live { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.trk-live-dot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: var(--trk-live); flex: none; }
.trk-live-dot::after { content: ''; position: absolute; inset: 0; border-radius: 50%; background: inherit; animation: trk-ping 2s ease-out infinite; }
.trk-live.is-lost { color: var(--rl-red-ink, var(--red-600)); border-color: var(--rl-red-line, var(--red-400)); background: var(--rl-red-soft, var(--red-50)); }
.trk-live.is-lost .trk-live-dot { background: var(--trk-alert); }
.trk-live.is-lost .trk-live-dot::after { animation: none; }
@keyframes trk-ping { 0% { transform: scale(1); opacity: .55; } 80%, 100% { transform: scale(2.8); opacity: 0; } }
.trk-stats { margin-bottom: 16px; }
.trk-stat-btn { font: inherit; text-align: start; cursor: pointer; width: 100%; }
.trk-stat-btn[aria-pressed="true"] { border-color: var(--trk-alert, var(--red-400)); box-shadow: 0 0 0 3px var(--rl-red-soft, var(--red-50)); }
.stat-compact-value .trk-k-num { font-size: inherit; font-weight: inherit; color: inherit; letter-spacing: inherit; }
.stat-compact-value .trk-k-of { font-size: 14px; font-weight: 600; }
.stat-compact-value .trk-k-of span { font-size: inherit; }

.trk-panel { display: flex; flex-direction: column; height: var(--trk-h); min-height: 0; overflow: hidden; }
.trk-panel-head { display: flex; flex-direction: column; gap: 10px; padding: 14px 14px 12px; border-bottom: 1px solid var(--line-soft); }
.trk-panel-title { display: flex; align-items: center; gap: 8px; }
.trk-count { display: inline-grid; place-items: center; min-width: 22px; height: 22px; padding: 0 7px; border-radius: 999px; background: var(--slate-100); color: var(--ink); font-size: 12px; font-weight: 600; }
.trk-seg { display: flex; gap: 2px; padding: 3px; border-radius: 8px; background: var(--slate-100); }
.trk-seg-btn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 6px 10px; border: 0; border-radius: 6px; background: none; font: inherit; font-size: 13px; color: var(--muted); cursor: pointer; }
.trk-seg-btn:hover { color: var(--ink); }
.trk-seg-btn.is-active { background: var(--surface); color: var(--ink); font-weight: 600; box-shadow: 0 1px 2px rgba(15, 23, 42, .1); }
.trk-seg-btn:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.trk-seg-count { display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--rl-red-soft, var(--red-50)); color: var(--rl-red-ink, var(--red-600)); font-size: 11px; font-weight: 600; }
.trk-seg-count.is-zero { background: var(--surface); color: var(--muted); }
.trk-search { display: flex; align-items: center; gap: 8px; padding: 0 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.trk-search:focus-within { border-color: var(--primary); box-shadow: var(--ring-primary); }
.trk-search .icon { width: 15px; height: 15px; color: var(--muted); flex: none; }
.trk-search input { width: 100%; padding: 8px 0; border: 0; outline: 0; background: none; font: inherit; font-size: 13.5px; color: var(--ink); }

.trk-drivers { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; padding: 8px; }
.trk-driver { display: flex; align-items: flex-start; gap: 12px; width: 100%; padding: 12px; border: 1px solid var(--line-soft); border-radius: 10px; background: var(--surface); font: inherit; color: inherit; text-align: start; cursor: pointer; transition: border-color .15s, box-shadow .15s, background .15s; }
.trk-driver:hover { border-color: var(--line); box-shadow: 0 4px 14px -8px rgba(15, 23, 42, .3); }
.trk-driver:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.trk-driver.is-active { border-color: var(--primary); background: var(--primary-soft); }
.trk-avatar { --trk-c: var(--trk-offline); flex: none; display: grid; place-items: center; width: 36px; height: 36px; margin: 2px; border-radius: 50%; background: var(--slate-100); color: var(--ink); font-size: 12.5px; font-weight: 600; letter-spacing: .02em; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--trk-c); }
.trk-avatar--live, .trk-pin--live { --trk-c: var(--trk-live); }
.trk-avatar--recent, .trk-pin--recent { --trk-c: var(--trk-recent); }
.trk-avatar--alert, .trk-pin--alert { --trk-c: var(--trk-alert); }
.trk-driver-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.trk-driver-top { display: flex; align-items: center; gap: 8px; }
.trk-driver-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: var(--ink); }
.trk-ico { display: inline-flex; }
.trk-batt { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.trk-batt .icon { width: 17px; height: 17px; }
.trk-batt.is-low { color: var(--rl-red-ink, var(--red-600)); font-weight: 600; }
.trk-driver-state { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 12.5px; }
.trk-driver-seen { color: var(--muted); }
.trk-progress { display: flex; align-items: center; gap: 10px; }
.trk-progress-bar { flex: 1; display: flex; height: 6px; overflow: hidden; border-radius: 999px; background: var(--slate-100); }
.trk-progress-bar i { display: block; height: 100%; }
.trk-progress-done { background: var(--trk-live); }
.trk-progress-failed { background: var(--trk-alert); }
.trk-progress-num { font-size: 12px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.trk-driver-meta { font-size: 12px; color: var(--muted); }
.trk-none { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 44px 20px; text-align: center; }
.trk-none-ico { display: grid; place-items: center; width: 48px; height: 48px; margin-bottom: 6px; border-radius: 50%; background: var(--slate-100); color: var(--muted); }
.trk-none-ico .icon { width: 22px; height: 22px; }
.trk-none-title { font-weight: 600; color: var(--ink); }
.trk-none-hint { max-width: 260px; font-size: 13px; color: var(--muted); }

.trk-map-card--live { position: relative; height: var(--trk-h); }
.trk-map-card--live .trk-map { position: absolute; inset: 0; z-index: 0; height: auto; min-height: 0; }
.trk-legend { position: absolute; z-index: 2; top: 12px; inset-inline-start: 12px; display: flex; flex-wrap: wrap; gap: 4px 12px; max-width: calc(100% - 84px); padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); box-shadow: 0 2px 8px -2px rgba(15, 23, 42, .18); font-size: 12px; color: var(--ink); }
.trk-legend span { display: inline-flex; align-items: center; gap: 6px; }
.trk-legend-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--trk-offline); }
.trk-legend-dot--live { background: var(--trk-live); }
.trk-legend-dot--recent { background: var(--trk-recent); }
.trk-legend-dot--alert { background: var(--trk-alert); }
.trk-tool { position: absolute; z-index: 2; top: 12px; inset-inline-end: 12px; display: grid; place-items: center; width: 36px; height: 36px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); color: var(--ink); box-shadow: 0 2px 8px -2px rgba(15, 23, 42, .18); cursor: pointer; }
.trk-tool:hover { background: var(--hover); }
.trk-tool:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.trk-tool .icon { width: 16px; height: 16px; }
.trk-focus { position: absolute; z-index: 2; bottom: 12px; inset-inline-start: 12px; width: min(340px, calc(100% - 24px)); display: flex; flex-direction: column; gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); box-shadow: 0 14px 34px -14px rgba(15, 23, 42, .4); animation: rise .2s ease both; }
.trk-focus-head { display: flex; align-items: flex-start; gap: 12px; }
.trk-focus-who { flex: 1; min-width: 0; }
.trk-focus-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; font-weight: 600; color: var(--ink); }
.trk-focus-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; margin-top: 3px; font-size: 12.5px; color: var(--muted); }
.trk-focus-close { display: grid; place-items: center; width: 30px; height: 30px; flex: none; border: 0; border-radius: 6px; background: none; color: var(--muted); cursor: pointer; }
.trk-focus-close:hover { background: var(--hover); color: var(--ink); }
.trk-focus-close .icon { width: 16px; height: 16px; }
.trk-focus-facts { display: grid; grid-template-columns: repeat(3, 1fr); border: 1px solid var(--line-soft); border-radius: 8px; }
.trk-fact { min-width: 0; padding: 8px 10px; }
.trk-fact + .trk-fact { border-inline-start: 1px solid var(--line-soft); }
.trk-fact-value { font-size: 15px; font-weight: 600; color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; }
.trk-fact-label { font-size: 11.5px; color: var(--muted); }
.trk-focus-foot .btn { width: 100%; justify-content: center; }

/* Map pins: the driver's initials on their state colour, name below; a soft
   pulse while live. The depot is a square petrol tile. */
.trk-pin-wrap { background: none; border: 0; }
.trk-pin { position: relative; width: 36px; height: 36px; }
.trk-pin--live::before { content: ''; position: absolute; inset: 0; border-radius: 50%; background: var(--trk-c, var(--trk-offline)); animation: trk-ping 2.2s ease-out infinite; }
.trk-pin-badge { position: absolute; inset: 0; display: grid; place-items: center; border: 3px solid #fff; border-radius: 50%; background: var(--trk-c, var(--trk-offline)); color: #fff; font: 600 12px/1 var(--font-sans); box-shadow: 0 2px 6px rgba(15, 23, 42, .35); transition: transform .15s; }
.trk-pin-name { position: absolute; top: calc(100% + 4px); left: 50%; transform: translateX(-50%); padding: 4px 7px; border-radius: 6px; background: var(--surface); color: var(--ink); font: 600 11.5px/1 var(--font-sans); white-space: nowrap; box-shadow: 0 1px 4px rgba(15, 23, 42, .28); }
.trk-pin.is-selected .trk-pin-badge { transform: scale(1.15); box-shadow: 0 0 0 3px var(--primary), 0 4px 12px rgba(15, 23, 42, .35); }
.trk-pin.is-selected .trk-pin-name { background: var(--primary); color: #fff; }
.trk-depot { display: grid; place-items: center; width: 30px; height: 30px; border: 2px solid #fff; border-radius: 9px; background: var(--primary); color: #fff; box-shadow: 0 2px 6px rgba(15, 23, 42, .35); }
.trk-depot .icon { width: 16px; height: 16px; }
@media (prefers-reduced-motion: reduce) {
    .trk-live-dot::after, .trk-pin--live::before { animation: none; }
}
@media (max-width: 900px) {
    .trk-layout--live { grid-template-columns: 1fr; }
    .trk-map-card--live { order: -1; height: 62vh; min-height: 360px; }
    .trk-panel { height: auto; }
    .trk-legend { font-size: 11px; gap: 2px 8px; padding: 5px 10px; border-radius: 10px; }
    .trk-focus { bottom: 8px; inset-inline-start: 8px; width: calc(100% - 16px); }
}
.sync-conflict-body { display: grid; grid-template-columns: minmax(240px, 1fr) minmax(240px, 1fr); gap: 16px; padding: 16px; }
.sync-conflict-facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0; font-size: 13px; }
.sync-conflict-facts dt { color: var(--muted); }
.sync-conflict-facts dd { margin: 0; color: var(--ink); }
.sync-conflict-proof { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
.sync-conflict-proof img { width: 120px; height: 120px; object-fit: cover; border: 1px solid var(--line-soft); border-radius: 6px; background: var(--surface); }
.sync-conflict-map { width: 220px; height: 120px; border-radius: 6px; overflow: hidden; }
.sync-conflict-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
.sync-conflict-reject { display: flex; gap: 8px; flex: 1; min-width: 260px; }
.sync-conflict-reject .input { flex: 1; }
@media (max-width: 860px) {
    .trk-layout, .sync-conflict-body { grid-template-columns: 1fr; }
    .trk-list { max-height: none; }
    .trk-map { height: 60vh; }
}

/* Right-to-left — Arabic, the preparator's language (core/i18n.php). Flex and
   grid already mirror under <html dir="rtl">; these undo the rules written
   with a physical left/right. */
/* Arabic glyphs in IBM Plex Sans Arabic (vendored, OFL — public/assets/fonts/):
   unicode-range limits it to Arabic letters, so references, names and digits
   keep the app's own font. */
@font-face {
  font-family: "IBM Plex Sans Arabic"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../fonts/ibm-plex-sans-arabic-400.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}
@font-face {
  font-family: "IBM Plex Sans Arabic"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("../fonts/ibm-plex-sans-arabic-500.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}
@font-face {
  font-family: "IBM Plex Sans Arabic"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../fonts/ibm-plex-sans-arabic-600.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}
@font-face {
  font-family: "IBM Plex Sans Arabic"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../fonts/ibm-plex-sans-arabic-700.woff2") format("woff2");
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}
[lang="ar"] body {
  font-family: "IBM Plex Sans Arabic", var(--font-sans);
}
[dir="rtl"] .sidebar { border-right: 0; border-left: 1px solid var(--line); }
[dir="rtl"] .nav-item.active::before { left: auto; right: -12px; border-radius: 6px 0 0 6px; }
[dir="rtl"] .nav-sub-items .nav-item.active::before { left: auto; right: -9px; }
[dir="rtl"] .table th { text-align: right; }
[dir="rtl"] .table .right, [dir="rtl"] .text-right { text-align: left; }
[dir="rtl"] .text-left { text-align: right; }
[dir="rtl"] .table-wrap .table tbody td:last-child:has(> .row-actions),
[dir="rtl"] .table-wrap .table:has(tbody td:last-child > .row-actions) thead th:last-child {
  right: auto; left: 0; box-shadow: 8px 0 8px -8px rgba(15, 23, 42, .18);
}
/* Arrows and chevrons point the reading direction (icon() tags them). */
[dir="rtl"] .icon-flip-rtl { transform: scaleX(-1); }
/* Driver screens (livreur/): urgent bar on the reading-start edge, the
   signature pad's Clear button and the fail toggle's chevron mirrored. */
[dir="rtl"] .lv-stop.is-urgent > td:first-child { box-shadow: inset -3px 0 0 var(--red-500); }
[dir="rtl"] .lv-sign-clear { right: auto; left: 12px; }
[dir="rtl"] .lv-fail-chevron { margin-left: 0; margin-right: auto; }
@media (max-width: 720px) {
  [dir="rtl"] .lv-stop.is-urgent { box-shadow: inset -3px 0 0 var(--red-500); }
  [dir="rtl"] .lv-stop.is-urgent > td:first-child { box-shadow: none; }
}
@media (min-width: 721px) {
  [dir="rtl"] .notif-panel { right: auto; left: 0; }
}
@media (max-width: 640px) {
  [dir="rtl"] .sidebar { inset: 0 0 0 auto; transform: translateX(100%); }
  [dir="rtl"] .sidebar.is-open { transform: translateX(0); }
}

/* Shipping check screen (shipping/show.php): line-by-line check progress,
   the per-line check cell, and a flagged line's tint. An OK line reuses the
   pick screen's tr.is-confirmed. */
.ship-progress { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.ship-progress-label { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
.ship-check { min-width: 220px; }
.ship-check > .pill { margin-bottom: 6px; }
.ship-check-note { font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
tr.is-flagged > td { background: var(--red-50); }
.ship-flagged { margin: 0 0 12px; padding-left: 18px; }
/* Its line table on a laptop (2026-10-15, same look as the pick table):
   plain white rows — no zebra, no green tint on a checked line (its
   "Vérifiée" pill says it); a flagged line keeps its light red, on the row
   so it reaches every cell. Photo and name sit together; the check cell
   puts its pill, note and buttons on one line. Scoped by :has() — case 96
   pins the table's class list. Phones keep their cards. */
@media (min-width: 901px) {
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id] > td,
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id]:hover > td { background: transparent; }
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id] > td { padding-top: 12px; padding-bottom: 12px; }
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id] { background: var(--surface); }
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id]:hover { background: var(--slate-50); }
  body .table-wrap .table:has(td.ship-check) > tbody > tr[id].is-flagged { background: var(--red-50); }

  body .table-wrap .table:has(td.ship-check) > tbody > tr > td.tc-photo { width: 1%; padding-inline-end: 4px; }
  body .table-wrap .table:has(td.ship-check) > tbody > tr > td.tc-title {
    min-width: 220px; white-space: normal; text-wrap: balance;
    font-size: 14px; font-weight: 600; line-height: 1.35; color: var(--ink);
  }
  body .table-wrap .table:has(td.ship-check) > tbody > tr > td.mono:nth-child(3) { color: var(--muted); }
  body .table-wrap .table:has(td.ship-check) > tbody > tr > td.right { width: 110px; font-size: 14px; font-weight: 600; }

  body .table-wrap .table:has(td.ship-check) > tbody > tr > td.ship-check { width: 1%; min-width: 260px; white-space: normal; }
  .ship-check > .pill { margin: 0; vertical-align: middle; }
  .ship-check > .pill::before { display: none; }   /* the pill carries its own icon */
  .ship-check > .ship-check-note { display: inline; margin: 0; margin-inline-start: 8px; vertical-align: middle; }
  .ship-check > .row { display: inline-flex; vertical-align: middle; }
  .ship-check > :is(.pill, .ship-check-note) + .row { margin-inline-start: 6px; }
  .ship-check > .row .btn-ghost { color: var(--muted); }
  .ship-check > .row .btn-ghost:hover { color: var(--ink); }
}
/* A checked line: only its "Vérifiée" is green (word + tick) — the row stays
   white on a phone too, like on a laptop (2026-10-27, on request: a whole list
   of green cards read as noise). */
/* html body: readable.css's dot + word pill (body.skin-readable .pill:is(…)) loads later. */
html body .ship-check > .pill.pill-green { color: var(--rl-green-ink, var(--success-700)); font-weight: 600; }
html body .ship-check > .pill.pill-green::before { display: none; }
@media (max-width: 900px) {
  body .table.table-cards:has(td.ship-check) > tbody > tr.is-confirmed { background: var(--surface); }
  body .table.table-cards:has(td.ship-check) > tbody > tr.is-confirmed > td { background: none; }
}

/* Zones d'expédition (modules/expedition/) — zone cards, the zone tag shown
   wherever an order's drop zone appears, and the send bar under a zone. */
.exp-zones { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; margin-bottom: 20px; }
.exp-zone-card {
    display: flex; flex-direction: column; gap: 8px; background: var(--surface); border: 1px solid var(--line);
    border-radius: 6px; padding: 12px 14px; box-shadow: var(--shadow-card); color: inherit; text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}
.exp-zone-card:hover { border-color: var(--primary); transform: translateY(-2px); }
.exp-zone-card:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.exp-zone-card.is-ready { border-color: var(--primary); }
.exp-zone-card.is-none { border-style: dashed; }
.exp-zone-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.exp-zone-name { font-size: 16px; font-weight: 700; color: var(--ink); }
.exp-zone-quartiers { font-size: 12px; color: var(--muted); }
.exp-zone-counts { display: flex; flex-wrap: wrap; gap: 6px; }
.exp-zone-tag {
    display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px;
    background: var(--primary-soft); color: var(--primary-strong); font-weight: 700; font-size: 12px; white-space: nowrap; text-decoration: none;
}
.exp-zone-tag.is-big { font-size: 15px; padding: 4px 12px; }
/* One zone's page (/expedition/zones/{id}): the state tabs are buttons that
   switch panels in place, and the "Prêtes" bar carries the running count +
   amount, the driver and the send button. */
.exp-tabs { margin: 0 0 12px; }
.exp-tabs .list-tab { background: none; border: 0; border-bottom: 2px solid transparent; font: inherit; cursor: pointer; }
.exp-tabs .list-tab.is-active { border-bottom-color: var(--primary); }
.exp-toolbar { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.exp-picked { font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
.exp-grow { flex: 1; }
.exp-driver { width: auto; min-width: 220px; }
.exp-dispatch-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
/* Zone assignment page: a filter row (search + selects + result count), then
   the action bar for the ticked rows. */
.exp-filters { flex-wrap: wrap; justify-content: flex-start; gap: 8px; }
.exp-filters .filter-search-wrap { flex: 1 1 240px; max-width: 340px; }
.exp-filters > .filter-select { width: auto; flex: 0 1 190px; min-width: 150px; }
.exp-filter-count { margin-left: auto; font-size: 13px; color: var(--muted); white-space: nowrap; }
.exp-assign-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding: 10px 16px; background: var(--slate-50); border-bottom: 1px solid var(--line-soft);
}
.exp-assign-count { margin-right: auto; font-size: 13px; font-weight: 600; color: var(--muted); white-space: nowrap; }
.exp-assign-count.is-active { color: var(--primary-strong); }
.exp-assign-label { font-size: 13px; color: var(--muted); white-space: nowrap; }
.exp-assign-bar > .filter-select { width: auto; flex: 0 1 260px; min-width: 200px; }
@media (max-width: 900px) {
    .exp-filters .filter-search-wrap, .exp-filters > .filter-select { flex: 1 1 100%; max-width: none; }
    .exp-filter-count { margin-left: 0; }
    .exp-assign-count { flex: 1 1 100%; }
    .exp-assign-bar > .filter-select { flex: 1 1 auto; min-width: 0; }
}

/* ======================================================================
   "Ajouter des produits" picker — customer and supplier order forms
   (modules/orders/pages/_form.php, modules/supplier_orders/pages/_form.php;
   rows built by public/assets/js/product-picker.js). A fixed-height dialog:
   header, a toolbar (search first and widest, then brand / category),
   a scrolling list of product rows, and a footer with what is already on
   the order and a Done button. One row = photo, name + codes, price,
   Pièce/Carton switch + − qty + stepper, Add.
   ====================================================================== */
.order-picker-backdrop {
    position: fixed; inset: 0; z-index: 80; display: none;
    align-items: flex-start; justify-content: center; padding: 32px 16px;
    background: rgba(15, 23, 42, .5); backdrop-filter: blur(2px);
}
.order-picker-backdrop:not([hidden]) { display: flex; }
.pk-modal {
    width: min(1080px, 100%); height: min(780px, calc(100vh - 64px));
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
    box-shadow: 0 30px 70px -20px rgba(15, 23, 42, .45);
    animation: rise .2s ease both;
}
.pk-head { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line-soft); }
.pk-head-text { min-width: 0; }
.pk-title { font-size: 16px; font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.pk-sub { font-size: 12.5px; }
.pk-close {
    margin-left: auto; flex: none; width: 34px; height: 34px; display: grid; place-items: center;
    border: 0; border-radius: 6px; background: transparent; color: var(--muted); cursor: pointer;
}
.pk-close:hover { background: var(--hover); color: var(--ink); }
.pk-close .icon { width: 18px; height: 18px; }

.pk-modal .pk-toolbar {
    display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr); gap: 10px;
    margin: 0; padding: 12px 18px; background: var(--bg); border-bottom: 1px solid var(--line-soft);
}
.pk-modal .pk-toolbar .field { margin: 0; }
.pk-search {
    display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 12px; min-width: 0;
    background: var(--surface); border: 1px solid var(--line); border-radius: 6px; color: var(--faint);
    transition: border-color .15s, box-shadow .15s;
}
.pk-search:focus-within { border-color: var(--slate-400); box-shadow: var(--ring-primary); color: var(--ink); }
.pk-search input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.pk-search-clear {
    flex: none; width: 24px; height: 24px; display: grid; place-items: center; border: 0; border-radius: 50%;
    background: var(--slate-100); color: var(--muted); cursor: pointer;
}
.pk-search-clear:hover { background: var(--slate-200); color: var(--ink); }
.pk-search-clear[hidden] { display: none; }
.pk-search input { flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent; font: inherit; font-size: 14px; color: var(--ink); }
.pk-modal .pk-toolbar .catalog-combobox-control { height: 38px; background: var(--surface); }
.pk-modal .pk-toolbar .catalog-combobox-control input { padding: 0; height: 36px; }

.pk-results { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 10px 10px; scrollbar-width: thin; }
.pk-group {
    padding: 14px 8px 6px; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--faint);
}
.pk-row {
    display: grid; grid-template-columns: 48px minmax(0, 1fr) 104px 262px 92px; align-items: center; gap: 14px;
    padding: 10px 8px; border-bottom: 1px solid var(--line-soft); border-radius: 6px;
    transition: background .12s ease;
}
.pk-row:hover { background: var(--hover); }
.pk-row.is-added { background: color-mix(in srgb, var(--success-soft) 55%, transparent); }
.pk-thumb {
    width: 48px; height: 48px; object-fit: contain; border-radius: 6px;
    background: var(--white, #fff); border: 1px solid var(--line-soft);
}
.pk-main { min-width: 0; }
.pk-name {
    font-size: 13.5px; font-weight: 600; color: var(--ink); line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pk-mark { background: color-mix(in srgb, var(--amber-600) 22%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }
.pk-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 5px; margin-top: 3px; font-size: 11.5px; color: var(--muted); }
.pk-meta .mono { font-size: 11px; }
.pk-dot { color: var(--faint); }
.pk-price { text-align: right; line-height: 1.2; }
.pk-price-value { font-size: 15px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.pk-price-cur { font-size: 11px; font-weight: 600; color: var(--muted); }
.pk-price-sub { margin-top: 2px; font-size: 11px; color: var(--muted); }
.pk-price-sub .price-hint:empty { display: none; }
.pk-price-sub .price-hint:not(:empty) + .pk-price-per { display: none; }
/* Switch and stepper side by side so every row keeps one height; the
   "= N pièces" line only appears in carton mode (the switch already says ×N). */
.pk-qty { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px 8px; }
.pk-unit { display: inline-flex; gap: 2px; padding: 2px; background: var(--slate-100); border-radius: 6px; }
.pk-unit button {
    border: 0; background: transparent; color: var(--muted); cursor: pointer; white-space: nowrap;
    font: inherit; font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 4px;
}
.pk-unit button.is-active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(15, 23, 42, .12); }
.pk-unit button:disabled { cursor: default; }
.pk-stepper { display: inline-flex; align-items: stretch; height: 30px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); overflow: hidden; }
.pk-stepper:focus-within { border-color: var(--slate-400); box-shadow: var(--ring-primary); }
.pk-step { width: 32px; border: 0; background: transparent; color: var(--slate-600); cursor: pointer; display: grid; place-items: center; }
.pk-step:hover:not(:disabled) { background: var(--hover); color: var(--ink); }
.pk-step:disabled { color: var(--faint); cursor: default; }
.pk-stepper input {
    width: 54px; border: 0; border-left: 1px solid var(--line-soft); border-right: 1px solid var(--line-soft);
    text-align: center; font: inherit; font-size: 14px; font-weight: 700; color: var(--ink);
    background: transparent; outline: 0; -moz-appearance: textfield; appearance: textfield;
}
.pk-stepper input::-webkit-outer-spin-button,
.pk-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pk-stepper input:disabled { color: var(--muted); }
.pk-qty .qty-carton-info { flex-basis: 100%; text-align: right; font-size: 11px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.pk-row:not(.is-carton) .qty-carton-info { display: none; }
/* Compact and light (user, 2026-10-27: the solid full-size "Ajouter" on every
   row was too big): the same height as the quantity box, a soft tint that
   turns solid on hover, so the column no longer outweighs the products. */
.pk-add { height: 30px; padding: 0 10px; justify-content: center; gap: 4px; font-size: 12.5px; white-space: nowrap; }
.pk-add svg { width: 14px; height: 14px; }
.pk-row .pk-add.btn-primary { background: var(--primary-soft); color: var(--primary); border-color: transparent; box-shadow: none; }
.pk-row .pk-add.btn-primary:hover { background: var(--primary); color: var(--white); }
.pk-add.is-done, .pk-add.is-done:disabled {
    opacity: 1; cursor: default; background: var(--success-soft); color: var(--success-strong);
    border-color: transparent; box-shadow: none;
}
.pk-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 64px 16px; text-align: center; color: var(--muted); font-size: 13.5px; }
.pk-empty-icon { width: 40px; height: 40px; color: var(--slate-300); }
.pk-foot {
    display: flex; align-items: center; gap: 14px; padding: 12px 18px;
    border-top: 1px solid var(--line-soft); background: var(--bg);
}
.pk-count { font-size: 12.5px; }
.pk-summary { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); }
.pk-summary b { color: var(--ink); }
.pk-summary.is-bump { animation: pk-bump .35s ease; }
@keyframes pk-bump { 40% { transform: scale(1.06); } }

@media (max-width: 860px) {
    .pk-modal .pk-toolbar { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .pk-modal .pk-toolbar .pk-search { grid-column: 1 / -1; }
    /* Photo + name on the first line; price, quantity and Add wrap below. */
    .pk-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
    .pk-thumb { width: 44px; height: 44px; }
    .pk-main { flex: 1 1 calc(100% - 60px); }
    .pk-price { text-align: left; }
    .pk-qty { margin-left: auto; flex-direction: column; align-items: flex-end; }
    .pk-stepper, .pk-add { height: 34px; }
}
@media (max-width: 640px) {
    .order-picker-backdrop { padding: 0; }
    .pk-modal { height: 100vh; width: 100%; border-radius: 0; border: 0; }
    .pk-count { display: none; }
}

/* Guided tour (public/assets/js/tour.js) — dims the page, cuts the current
   element out of the dim (.tour-spot's huge box-shadow) and puts a card next
   to it. The layer catches every click, so the page can't be used mid-tour. */
.tour-layer { position: fixed; inset: 0; z-index: 300; opacity: 0; transition: opacity .2s ease; }
.tour-layer.is-open { opacity: 1; }
.tour-spot {
  position: fixed; border-radius: 8px; pointer-events: none;
  box-shadow: 0 0 0 3px var(--primary), 0 0 0 9999px rgba(15, 23, 42, .6);
  transition: left .25s ease, top .25s ease, width .25s ease, height .25s ease;
}
.tour-layer.is-center .tour-spot { left: 50% !important; top: 50% !important; width: 0 !important; height: 0 !important; box-shadow: 0 0 0 9999px rgba(15, 23, 42, .6); }
.tour-pop {
  position: fixed; width: min(340px, calc(100vw - 24px));
  background: var(--surface); border-radius: 8px; padding: 16px 18px 14px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, .3);
  transition: left .25s ease, top .25s ease;
}
.tour-step { font-size: 11.5px; font-weight: 600; color: var(--primary); text-transform: uppercase; letter-spacing: .04em; }
.tour-title { margin-top: 4px; font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.tour-text { margin-top: 6px; font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.tour-dots { display: flex; gap: 5px; margin-top: 14px; }
.tour-dots span { width: 6px; height: 6px; border-radius: 999px; background: var(--slate-200); }
.tour-dots span.is-on { width: 18px; background: var(--primary); }
.tour-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.tour-actions .tour-next { min-width: 92px; justify-content: center; }
/* Topbar "Guide" button that replays the tour — same chip as .topbar-back. */
.topbar-guide {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; cursor: pointer;
  height: 32px; padding: 0 10px; border-radius: 6px; font: inherit;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); font-size: 13px; font-weight: 600; white-space: nowrap;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.topbar-guide:hover { background: var(--slate-100); color: var(--ink); border-color: var(--slate-300); }
@media (max-width: 520px) {
  .topbar-guide { padding: 0 8px; }
  .topbar-guide-label { display: none; }
}

/* Formation card on a user's edit page (admin, modules/tours/). */
.formation-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce) { .tour-spot, .tour-pop, .tour-layer { transition: none; } }

/* "Next step" banner at the top of an order / BC (modules/orders/pages/_next_step.php):
   who has to act now and for how long. Tone = blue (someone acts), amber
   (waiting on a problem), red (failed), green (done), slate (closed). */
.next-step { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 12px 14px; border-radius: 8px; border: 1px solid var(--line); background: var(--surface); }
.next-step-ico { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 8px; flex: 0 0 auto; }
.next-step-body { min-width: 0; flex: 1 1 auto; }
.next-step-title { font-weight: 600; color: var(--ink); line-height: 1.35; }
.next-step-text { margin-top: 2px; font-size: 13px; color: var(--muted); line-height: 1.45; }
.next-step-since { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; font-size: 12.5px; font-weight: 600; color: var(--muted); white-space: nowrap; }
.next-step.tone-blue  { border-color: var(--blue-50);  background: var(--blue-50); }
.next-step.tone-blue  .next-step-ico { background: var(--surface); color: var(--blue-600); }
.next-step.tone-amber { border-color: var(--amber-50); background: var(--amber-50); }
.next-step.tone-amber .next-step-ico { background: var(--surface); color: var(--amber-600); }
.next-step.tone-red   { border-color: var(--red-50);   background: var(--red-50); }
.next-step.tone-red   .next-step-ico { background: var(--surface); color: var(--red-600); }
.next-step.tone-green { border-color: var(--success-50); background: var(--success-50); }
.next-step.tone-green .next-step-ico { background: var(--surface); color: var(--success-600); }
.next-step.tone-slate .next-step-ico { background: var(--slate-100); color: var(--slate-600); }
@media (max-width: 520px) {
  .next-step { flex-wrap: wrap; }
  .next-step-since { width: 100%; padding-inline-start: 48px; }
}

/* Page changes cross-fade: cross-document view transitions (Chrome / Edge
   126+, Safari 18.2+ — other browsers simply load the next page as before).
   The sidebar and top bar carry their own names so they hold still while the
   content fades; never when the visitor asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
.sidebar, .portal-side { view-transition-name: app-sidebar; }
.topbar, .portal-topbar { view-transition-name: app-topbar; }
::view-transition-group(*) { animation-duration: .2s; }
::view-transition-old(root) { animation: vt-fade-out .12s ease-in both; }
::view-transition-new(root) { animation: vt-rise-in .22s cubic-bezier(.2, .7, .2, 1) both; }
@keyframes vt-fade-out { to { opacity: 0; } }
@keyframes vt-rise-in { from { opacity: 0; transform: translateY(6px); } }

/* ======================================================================
   Phones — found on the 2026-10-13 tour of every role's screens:
   - every control a thumb hits is ≥ 34px (menu group heads were 22px,
     row icons 30px, the calendar icon 16px, the logout button 28px);
   - a page with its own heading drops the top-bar title, which the
     crowded bar cut to "Réce…" / "Expé…" — the heading below says it whole.
   ====================================================================== */
@media (max-width: 640px) {
  body:has(.page-head) .topbar h1 { display: none; }
  .nav-group-head { min-height: 36px; padding-top: 4px; padding-bottom: 4px; }
  .lang-switch { height: 40px; }
  .nav-foot-btn { width: 36px; height: 36px; }
  .wh-icon-btn { width: 36px; height: 36px; }
  .trend-range-btn { padding: 8px 12px; }
  .alerts-card-btn { min-width: 34px; min-height: 34px; }
  /* Bigger hit area, same look: the icon stays where it is. */
  .datepicker-cal-btn::after { content: ""; position: absolute; inset: -10px; }
}

/* ---- Woliz shortage decision (orders/pages/show.php #shortage) ----------
   One choice per short line, all visible at once — the admin compares
   "send the 5 found" with "remove it" side by side, nothing hides in a select. */
.shortage-card { border-color: var(--rl-amber-line, var(--line)); overflow: hidden; }
.shortage-head {
  display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
  padding: 14px 16px; background: var(--rl-amber-soft, var(--amber-50)); border-bottom: 1px solid var(--rl-amber-line, var(--line));
}
.shortage-head-icon {
  flex: none; display: inline-grid; place-items: center; width: 34px; height: 34px;
  border-radius: 8px; background: var(--surface); border: 1px solid var(--rl-amber-line, var(--line)); color: var(--amber-700);
}
.shortage-head-text { flex: 1 1 240px; min-width: 0; }
.shortage-head .card-title { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 2px; }
.shortage-bulk { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; align-self: center; }
/* `body .table` prefix: the skins' `.table tbody td` colour loads later. */
body .table .shortage-found { color: var(--red-600); font-weight: 700; }
/* An even segmented row: 2 or 3 tiles, same width, never wrapping 2 + 1. */
.shortage-choices { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; min-width: 300px; }
.shortage-choice {
  position: relative; display: flex; flex-direction: column; justify-content: center; gap: 1px;
  min-height: 44px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--ink); cursor: pointer; line-height: 1.25;
}
.shortage-choice input { position: absolute; opacity: 0; pointer-events: none; }
.shortage-choice-main { font-size: 13px; font-weight: 600; white-space: nowrap; }
.shortage-choice-sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shortage-choice:hover { background: var(--hover); border-color: var(--slate-300); }
.shortage-choice:has(input:checked) { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-strong); box-shadow: inset 0 0 0 1px var(--primary); }
.shortage-choice.is-remove:has(input:checked) { border-color: var(--red-600); background: var(--rl-red-soft, var(--red-50)); color: var(--red-600); box-shadow: inset 0 0 0 1px var(--red-600); }
.shortage-choice:has(input:checked) .shortage-choice-sub { color: inherit; opacity: .8; }
.shortage-choice:has(input:focus-visible) { box-shadow: var(--ring-primary); }
.shortage-foot {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 16px;
  padding: 12px 16px; border-top: 1px solid var(--line-soft); background: var(--slate-50);
}
.shortage-cancel { border-top: 1px solid var(--line-soft); }
.shortage-cancel > summary {
  display: inline-flex; align-items: center; gap: 6px; padding: 12px 16px;
  font-size: 13px; color: var(--red-600); cursor: pointer; list-style: none;
}
.shortage-cancel > summary::-webkit-details-marker { display: none; }
.shortage-cancel > summary:hover { text-decoration: underline; }
.shortage-cancel > form { padding: 0 16px 16px; }
.shortage-cancel-row { display: flex; flex-wrap: wrap; gap: 8px; }
.shortage-cancel-row .input { flex: 1 1 260px; }
@media (max-width: 900px) {
  .shortage-choices { min-width: 0; }
  .shortage-choice { min-height: 48px; }
  .shortage-choice-sub { white-space: normal; }
  .shortage-bulk { width: 100%; }
  .shortage-bulk > span { flex-basis: 100%; }
  .shortage-bulk .btn { flex: 1 1 0; }
}

/* A product taken off a Woliz order (orders/pages/show.php) — still listed,
   in red and struck through, never counted in the total. */
/* `body .table` prefix: the skins' `.table tbody td` colour loads later. */
body .table tbody tr.ol-row-removed > td,
body .table tbody tr.ol-row-removed > td .muted { color: var(--red-600); }
body .table tbody tr.ol-row-removed .ol-struck { text-decoration: line-through; text-decoration-thickness: 1.5px; }
body .table tbody tr.ol-row-removed .cell-thumb { opacity: .5; }

/* What Woliz was told about a shortage (orders/pages/show.php, Woliz card). */
.woliz-told {
  display: flex; align-items: flex-start; gap: 8px; margin-top: 12px; padding: 10px 12px;
  border-radius: 8px; border: 1px solid var(--line-soft); background: var(--slate-50); font-size: 13px; color: var(--ink);
}
.woliz-told > svg { flex: none; margin-top: 2px; color: var(--success); }
.woliz-told.is-held { border-color: var(--amber-600); background: var(--amber-50); }
.woliz-told.is-held > svg { color: var(--amber-700); }
.woliz-told-list { margin: 4px 0 0; padding-inline-start: 18px; color: var(--muted); }
.woliz-told-list li { margin: 1px 0; }
.shortage-why-label { font-weight: 600; color: var(--ink); }

/* The reason chooser on the pick screen (#pk-reason) — the found sheet's
   frame, above it when both are open. Big tap targets: picked with a thumb. */
.pk-reason-backdrop { z-index: 160; }
.pk-reason-title { font-size: 16px; font-weight: 700; color: var(--ink); line-height: 1.35; overflow-wrap: anywhere; text-wrap: balance; }
.pk-reason-ask { font-size: 14px; color: var(--muted); margin-top: -6px; }
.pk-reason-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pk-reason-opt {
    min-height: 48px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
    background: var(--surface); color: var(--ink); font-size: 15px; font-weight: 600; cursor: pointer;
}
.pk-reason-opt:hover { background: var(--hover); }
.pk-reason-opt.is-picked { border-color: var(--red-600); background: var(--red-50); color: var(--red-600); }
.pk-reason-opt:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.pk-reason-opt:last-child:nth-child(odd) { grid-column: 1 / -1; }
.pk-reason-go { min-height: 48px; justify-content: center; }

/* Supervisor board: reopen a prepared customer order (supervisor/pages/supplier_orders.php). */
.reopen-card .card-title { display: flex; align-items: center; gap: 6px; }
.reopen-form { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.reopen-form .input { width: 180px; }

/* Échéancier fournisseurs (/supplier-payments, supplier_payments/pages/index.php):
   due tabs + search + supplier filter above the To pay list, a "1–10 of 25"
   range in each card head, share / paid bars (<progress>, no inline widths),
   and a red edge on overdue rows and on the Overdue tile when there is any. */
.spay-head-sub { margin-top: 2px; font-size: 12px; color: var(--muted); }
.spay-range { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.spay-stat-late { box-shadow: inset 3px 0 0 var(--red-600), var(--shadow-card); }
.spay-stat-late .stat-compact-value { color: var(--red-600); }

.spay-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 12px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.spay-tabs { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; border: 1px solid var(--line); border-radius: 8px; background: var(--slate-50); }
.spay-tab { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border: 0; border-radius: 6px; background: none; font: inherit; font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer; }
.spay-tab:hover { color: var(--ink); }
.spay-tab.active { background: var(--surface); color: var(--ink); box-shadow: 0 0 0 1px var(--line), 0 1px 2px rgba(15, 23, 42, .06); }
.spay-tab-count { min-width: 20px; padding: 0 6px; border-radius: 999px; background: var(--slate-100); color: var(--slate-600); font-size: 11px; line-height: 18px; text-align: center; font-variant-numeric: tabular-nums; }
.spay-tab.active .spay-tab-count { background: var(--primary-soft); color: var(--primary-strong); }
.spay-tab.is-late .spay-tab-count { background: var(--red-50); color: var(--red-600); }
.spay-filters { display: flex; flex-wrap: wrap; gap: 8px; }
.spay-filters .filter-search-wrap { width: 240px; }
.spay-filters select.filter-select { width: 210px; }

.spay-supplier-link { padding: 0; border: 0; background: none; font: inherit; font-weight: 600; color: var(--ink); text-align: start; cursor: pointer; }
.spay-supplier-link:hover, tr:hover > td > .spay-supplier-link { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
.spay-meta { margin-top: 1px; font-size: 12px; color: var(--muted); }

.spay-bar { -webkit-appearance: none; appearance: none; display: block; width: 100%; max-width: 200px; height: 6px; border: 0; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.spay-bar::-webkit-progress-bar { background: var(--slate-100); border-radius: 999px; }
.spay-bar::-webkit-progress-value { background: var(--primary); border-radius: 999px; }
.spay-bar::-moz-progress-bar { background: var(--primary); border-radius: 999px; }
.spay-bar.spay-bar-paid { width: 72px; height: 4px; margin: 5px 0 0 auto; }
.spay-bar-paid::-webkit-progress-value { background: var(--success-600); }
.spay-bar-paid::-moz-progress-bar { background: var(--success-600); }

.spay-when { margin-top: 1px; font-size: 12px; color: var(--muted); }
.spay-when.is-soon { color: var(--amber-700); font-weight: 600; }
.spay-when.is-late { color: var(--red-600); font-weight: 600; }
body .spay-table tbody tr.spay-late > td:first-child { box-shadow: inset 3px 0 0 var(--red-600); }

@media (max-width: 900px) {
  .spay-tools { padding: 10px 12px; }
  .spay-tabs { width: 100%; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .spay-tab { flex: 0 0 auto; }
  .spay-filters, .spay-filters .filter-search-wrap, .spay-filters select.filter-select { width: 100%; min-width: 0; }
}

/* Extra module access grid (roles/pages/_grid.php — /roles/{role} and a user's
   edit page). A shadcn-style box per capability, a "Tout" switch (the
   .product-switch look), an open module's row tinted with a primary edge and
   its access in words, the price switches in their own block, and a save bar
   that sticks to the bottom and says when something is unsaved. Phones: each
   module is a card — name, then the four boxes in a row, labelled. */
.role-perm-card { overflow: clip; }
.role-perm-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 14px 24px; padding: 16px 18px 14px; border-bottom: 1px solid var(--line-soft); }
.role-perm-head-main { flex: 1 1 360px; min-width: 0; }
.role-perm-copy { margin-top: 4px; max-width: 72ch; font-size: 13px; line-height: 1.5; color: var(--muted); }
.role-perm-progress { flex: 0 0 200px; display: grid; gap: 6px; }
.role-perm-progress-text { display: flex; align-items: baseline; gap: 6px; font-size: 13px; }
.role-perm-total-pill { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.role-perm-meter { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border: 0; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.role-perm-meter::-webkit-progress-bar { background: var(--slate-100); border-radius: 999px; }
.role-perm-meter::-webkit-progress-value { background: var(--primary); border-radius: 999px; transition: width .2s ease; }
.role-perm-meter::-moz-progress-bar { background: var(--primary); border-radius: 999px; }

.role-perm-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; padding: 10px 18px; border-bottom: 1px solid var(--line-soft); background: var(--slate-50); }
.role-perm-search { flex: 0 1 260px; }
.role-perm-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; flex: 1 1 auto; font-size: 12px; color: var(--muted); }
.role-perm-legend span { display: inline-flex; align-items: center; gap: 5px; }
.role-perm-legend .icon { width: 13px; height: 13px; }
.role-perm-collapse-icon { display: inline-flex; transition: transform .15s ease; }
.role-perm-collapse-toggle.is-folded .role-perm-collapse-icon { transform: rotate(-90deg); }

body .table.role-perm-table { border-collapse: separate; border-spacing: 0; }
body .table.role-perm-table th.center, body .table.role-perm-table td.center { text-align: center; width: 112px; }
body .table.role-perm-table thead th { position: sticky; top: 0; z-index: 1; background: var(--surface); }
.role-perm-section-row td { padding: 0 !important; background: var(--slate-50); border-top: 1px solid var(--line-soft); }
.role-perm-group-toggle { display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 18px; border: 0; background: transparent; cursor: pointer; font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); text-align: start; }
.role-perm-group-toggle:hover, .role-perm-group-toggle:focus-visible { color: var(--ink); outline: none; }
.role-perm-group-toggle-icon { display: inline-flex; flex: none; transition: transform .15s ease; }
.role-perm-group.is-collapsed .role-perm-group-toggle-icon { transform: rotate(-90deg); }
.role-perm-group.is-collapsed .role-perm-row { display: none; }
.role-perm-section-pill { margin-inline-start: auto; padding: 1px 8px; border-radius: 999px; background: var(--slate-100); color: var(--slate-600); font-size: 11px; letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums; }
.role-perm-section-pill.is-on { background: var(--primary-soft); color: var(--primary-strong); }

.role-perm-row > td { transition: background-color .15s ease; }
.role-perm-row:hover > td { background: var(--slate-50); }
body .table.role-perm-table tr.role-perm-row.is-granted > td { background: color-mix(in srgb, var(--primary-soft) 60%, var(--surface)); }
body .table.role-perm-table tr.role-perm-row.is-granted > td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
.role-perm-module { display: flex; align-items: center; gap: 10px; min-width: 0; }
.role-perm-module-ico { flex: none; width: 30px; height: 30px; display: grid; place-items: center; border-radius: 7px; background: var(--slate-100); color: var(--slate-600); transition: background .15s, color .15s; }
.role-perm-module-ico .icon { width: 15px; height: 15px; }
.role-perm-row.is-granted .role-perm-module-ico { background: var(--primary); color: var(--white); }
.role-perm-module-copy { display: grid; min-width: 0; }
.role-perm-module-label { font-weight: 600; color: var(--ink); }
.role-perm-summary { font-size: 12px; color: var(--muted); }
.role-perm-row.is-granted .role-perm-summary { color: var(--primary-strong); font-weight: 600; }

.role-perm-check { -webkit-appearance: none; appearance: none; display: inline-grid; place-content: center; width: 20px; height: 20px; margin: 0; border: 1.5px solid var(--slate-300); border-radius: 5px; background: var(--surface); cursor: pointer; vertical-align: middle; transition: background .12s, border-color .12s, box-shadow .12s; }
.role-perm-check::after { content: ""; width: 10px; height: 5px; border-left: 2px solid var(--white); border-bottom: 2px solid var(--white); transform: rotate(-45deg) translate(1px, -1px) scale(0); transition: transform .12s ease; }
.role-perm-check:hover:not(:disabled) { border-color: var(--primary); }
.role-perm-check:checked { background: var(--primary); border-color: var(--primary); }
.role-perm-check:checked::after { transform: rotate(-45deg) translate(1px, -1px) scale(1); }
.role-perm-check:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.role-perm-check:disabled { cursor: not-allowed; border-style: dashed; background: var(--slate-50); opacity: .55; }
.role-perm-na { display: inline-block; width: 10px; height: 2px; border-radius: 2px; background: var(--slate-200); vertical-align: middle; }
.role-perm-switch { justify-content: center; }
.product-switch input:indeterminate + .product-switch-track { background: color-mix(in srgb, var(--primary) 45%, var(--slate-300)); }
.product-switch input:indeterminate + .product-switch-track::after { transform: translateX(7px); }

.role-perm-prices { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; padding: 14px 18px; border-top: 1px solid var(--line); }
.role-perm-prices-head { flex: 1 1 260px; }
.role-perm-prices-title { display: flex; align-items: center; gap: 6px; font-weight: 700; color: var(--ink); }
.role-perm-prices-list { display: flex; flex-wrap: wrap; gap: 10px; }
.role-perm-price { display: flex; align-items: center; gap: 10px; min-width: 230px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); cursor: pointer; transition: border-color .15s, background .15s; }
.role-perm-price:hover { border-color: var(--slate-300); }
.role-perm-price .product-switch { margin-inline-start: auto; }
.role-perm-price.is-hidden-price { background: var(--slate-50); }
.role-perm-price.is-hidden-price .role-perm-summary { color: var(--amber-700); font-weight: 600; }

.role-perm-savebar { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; justify-content: flex-end; gap: 14px; padding: 12px 18px; border-top: 1px solid var(--line); background: var(--surface); }
.role-perm-savebar.is-dirty { background: var(--amber-50); }
.role-perm-dirty { display: inline-flex; align-items: center; gap: 6px; margin-inline-end: auto; font-size: 13px; font-weight: 600; color: var(--amber-700); }

@media (max-width: 900px) {
  .role-perm-head, .role-perm-toolbar, .role-perm-prices, .role-perm-savebar { padding-left: 12px; padding-right: 12px; }
  .role-perm-progress { flex-basis: 100%; }
  .role-perm-search { flex-basis: 100%; }
  .role-perm-legend { display: none; }
  body .table.role-perm-table thead { display: none; }
  body .table.role-perm-table, body .table.role-perm-table tbody, body .table.role-perm-table tr, body .table.role-perm-table td { display: block; width: auto; }
  body .table.role-perm-table tr.role-perm-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px 4px; padding: 12px; border-top: 1px solid var(--line-soft); }
  body .table.role-perm-table tr.role-perm-row > td { padding: 0; border: 0; background: none; box-shadow: none; }
  body .table.role-perm-table tr.role-perm-row > td:first-child { grid-column: 1 / -1; }
  body .table.role-perm-table tr.role-perm-row.is-granted { background: color-mix(in srgb, var(--primary-soft) 60%, var(--surface)); box-shadow: inset 3px 0 0 var(--primary); }
  body .table.role-perm-table tr.role-perm-row > td.center { display: flex; flex-direction: column; align-items: center; gap: 6px; width: auto; }
  body .table.role-perm-table tr.role-perm-row > td.center::before { content: attr(data-label); font-size: 10.5px; color: var(--muted); }
  body .table.role-perm-table tr.role-perm-row.is-granted > td:first-child { background: none; box-shadow: none; }
  .role-perm-group.is-collapsed .role-perm-row { display: none !important; }
  .role-perm-price { flex: 1 1 100%; }
  .role-perm-savebar { gap: 10px; }
  .role-perm-savebar .btn { white-space: nowrap; }
  .role-perm-dirty { font-size: 12px; }
}

/* Stuck orders (modules/order_waits/pages/_panel.php) — the admin dashboard
   only. Head: count + a legend of how many are red (≥ 2× the limit) / amber.
   Then the team flow — one tile per team in the order an order meets them,
   with its count, its longest wait and a bar sized against the busiest team,
   so the bottleneck shows at a glance; a tile filters the rows. Rows line up
   as columns on a laptop (order · step · client · team · wait over limit);
   under 900px each row stacks into ref + wait / step / client · team. */
.waits-card {
    background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
    box-shadow: var(--shadow-card);
    overflow: hidden; margin: 0 0 20px; animation: rise .4s ease both;
}
.waits-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 14px 16px 12px; }
.waits-ico { flex: none; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 8px; background: var(--rl-red-soft); color: var(--red-600); box-shadow: inset 0 0 0 1px var(--rl-red-line); }
.waits-ico .icon { width: 17px; height: 17px; }
.waits-head-copy { flex: 1 1 240px; min-width: 0; }
.waits-title { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; font-weight: 700; line-height: 1.3; color: var(--ink); }
.waits-count { min-width: 22px; height: 20px; padding: 0 7px; display: inline-grid; place-items: center; border-radius: 999px; background: var(--red-600); color: var(--white); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.waits-sub { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }
.waits-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0; padding: 0; list-style: none; font-size: 12px; color: var(--muted); }
.waits-legend li { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.waits-legend b { color: var(--ink); font-variant-numeric: tabular-nums; }

/* Dot: amber past the limit, red from twice it (rows + legend). */
.waits-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--rl-amber-dot); box-shadow: 0 0 0 3px var(--rl-amber-soft); }
.is-red > .waits-dot { background: var(--red-500); box-shadow: 0 0 0 3px var(--rl-red-soft); }

/* Team flow — tiles joined by a chevron, Ventes › … › Finance. */
.waits-flow {
    display: grid; grid-template-columns: minmax(96px, .8fr) repeat(6, minmax(116px, 1fr)); gap: 8px;
    padding: 12px 16px; border-block: 1px solid var(--line-soft); background: var(--surface-2, var(--slate-50));
}
.waits-tile {
    position: relative; display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 2px 8px;
    padding: 9px 11px 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--surface);
    font: inherit; text-align: start; color: var(--text); cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease, background-color .12s ease;
}
.waits-tile + .waits-tile:not(.is-all)::before {
    content: ""; position: absolute; inset-inline-start: -7px; top: 50%; width: 6px; height: 6px; margin-top: -3px;
    border-top: 1.5px solid var(--slate-300); border-inline-end: 1.5px solid var(--slate-300); transform: rotate(45deg);
}
.waits-tile.is-all + .waits-tile::before { display: none; }
[dir="rtl"] .waits-tile + .waits-tile:not(.is-all)::before { transform: rotate(-45deg); }
.waits-tile:hover:not(:disabled) { border-color: var(--slate-300); box-shadow: 0 1px 2px rgb(15 23 42 / .06); }
.waits-tile:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.waits-tile[aria-pressed="true"] { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); background: var(--primary-soft); }
.waits-tile-label { font-size: 12px; font-weight: 600; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.waits-tile-count { grid-row: span 2; align-self: center; font-size: 22px; font-weight: 700; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.waits-tile.is-red .waits-tile-count { color: var(--red-600); }
.waits-tile.is-amber .waits-tile-count { color: var(--amber-700); }
.waits-tile-worst { font-size: 11px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.waits-tile-bar { grid-column: 1 / -1; height: 4px; margin-top: 6px; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.waits-tile-bar i { display: block; width: var(--share, 0%); height: 100%; border-radius: inherit; background: var(--rl-amber-dot); }
.waits-tile.is-red .waits-tile-bar i { background: var(--red-500); }
.waits-tile.is-all { grid-template-columns: 1fr; align-content: center; }
.waits-tile.is-all .waits-tile-count { grid-row: auto; font-size: 20px; }
.waits-tile.is-empty { cursor: default; background: transparent; border-style: dashed; }
.waits-tile.is-empty .waits-tile-count { color: var(--faint); }
.waits-tile.is-empty .waits-tile-worst { color: var(--success-700); }

/* Rows — one grid shared by the column header and every row. */
.waits-cols, .waits-row {
    display: grid; align-items: center; column-gap: 14px;
    grid-template-columns: 8px minmax(128px, 150px) minmax(0, 2fr) minmax(0, 1.2fr) 104px 118px 16px;
}
.waits-cols { padding: 9px 26px 7px; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--faint); border-bottom: 1px solid var(--line-soft); }
.waits-cols .is-end { text-align: end; }
.waits-list { list-style: none; margin: 0; padding: 4px 10px 6px; }
.waits-card.is-open .waits-list { max-height: 460px; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--slate-300) transparent; }
.waits-list > li + li { border-top: 1px solid var(--line-soft); }
.waits-row { min-height: 48px; padding: 7px 6px 7px 10px; border-radius: 6px; text-decoration: none; color: inherit; transition: background-color .12s ease; }
a.waits-row:hover { background: var(--hover); }
.waits-row:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.waits-ref { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.waits-what { min-width: 0; font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.waits-client { min-width: 0; font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.waits-team-tag { justify-self: start; max-width: 100%; padding: 1px 8px; border-radius: 999px; background: var(--rl-slate-soft); box-shadow: inset 0 0 0 1px var(--rl-slate-line); color: var(--rl-slate-ink); font-size: 11.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.waits-time { display: grid; justify-items: end; text-align: end; line-height: 1.25; }
.waits-time strong { font-size: 13px; font-weight: 700; color: var(--amber-700); font-variant-numeric: tabular-nums; white-space: nowrap; }
.waits-row.is-red .waits-time strong { color: var(--red-600); }
.waits-time small { font-size: 11px; color: var(--faint); white-space: nowrap; }
.waits-go { display: flex; color: var(--faint); opacity: 0; transition: opacity .12s ease, transform .12s ease; }
a.waits-row:hover .waits-go, .waits-row:focus-visible .waits-go { opacity: 1; transform: translateX(2px); }

.waits-more {
    display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%;
    padding: 10px 16px; border: 0; border-top: 1px solid var(--line-soft); background: var(--surface);
    font: inherit; font-size: 12.5px; font-weight: 600; color: var(--primary-strong, var(--primary)); cursor: pointer;
}
.waits-more:hover { background: var(--hover); }
.waits-more:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.waits-more .icon { transition: transform .15s ease; }
.waits-more.is-open .icon { transform: rotate(180deg); }
.waits-more[hidden] { display: none; }

@media (max-width: 1200px) {
  .waits-flow { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .waits-tile::before { display: none; }
}
@media (max-width: 900px) {
  .waits-head { padding: 12px; }
  .waits-legend { flex-basis: 100%; }
  .waits-flow { display: flex; gap: 8px; padding: 10px 12px; overflow-x: auto; scroll-snap-type: x proximity; scroll-padding-inline: 12px; scrollbar-width: none; }
  .waits-flow::-webkit-scrollbar { display: none; }
  .waits-tile { flex: 0 0 auto; min-width: 118px; scroll-snap-align: start; }
  .waits-tile.is-all { min-width: 84px; }
  .waits-cols { display: none; }
  .waits-list { padding: 2px 6px 4px; }
  .waits-row {
      grid-template-columns: 8px minmax(0, 1fr) auto;
      grid-template-areas: "dot ref time" ". what time" ". meta meta";
      row-gap: 2px; column-gap: 10px; padding: 9px 6px;
  }
  .waits-row > .waits-dot { grid-area: dot; }
  .waits-ref { grid-area: ref; }
  .waits-time { grid-area: time; align-self: start; }
  .waits-what { grid-area: what; white-space: normal; }
  .waits-client { grid-area: meta; }
  .waits-team-tag, .waits-go { display: none; }
}

/* ---- Delivery issues (modules/delivery_issues/) — the urgent home card's rows,
   the report photo, the count fields, the driver's waiting card ---- */
.di-panel-list { list-style: none; margin: 0; padding: 0 10px 10px; }
.di-panel-list > li + li { border-top: 1px solid var(--line-soft); }
.di-panel-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 48px; padding: 7px 6px 7px 10px; border-radius: 6px; text-decoration: none; color: inherit; transition: background-color .12s ease; }
a.di-panel-row:hover { background: var(--hover); }
.di-panel-row:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.di-panel-who { flex: 1 1 180px; min-width: 0; font-size: 13px; }
.di-panel-time { font-size: 12px; }
.di-panel-go { display: inline-flex; color: var(--muted); }
.di-photo { display: block; max-width: min(100%, 280px); max-height: 280px; border-radius: 8px; border: 1px solid var(--line); object-fit: cover; }
.di-row-reported td { background: var(--rl-red-soft, var(--red-50)); }
/* The report page's lines (2026-10-15 redesign): the SKU small under the
   name; each count is the office's number (an input while it waits) with
   the driver's beside it. */
/* Not `.mono`: the skin's `.mono` (dark, 12.5px) and the phone card's bold
   `.tc-title .mono` would win. */
.di-sku { margin-top: 2px; font-family: var(--font-mono); font-size: 12px; font-weight: 400; color: var(--muted); }
.di-count-cell { display: inline-flex; align-items: center; gap: 8px; }
.di-count { width: 64px; text-align: center; }

/* Camions (/trucks/{id}) — gallery pick A1 · B4 · C2 · D1 · E2 · F1 · G1 · L1:
   driver card, compact "À décharger" table with one "Trouvé" stepper, a bar
   stuck to the bottom, orders still to deliver + unloads in a right column. */
.truck-head { display: flex; align-items: center; justify-content: space-between; gap: 16px 24px; flex-wrap: wrap; padding: 18px 20px; margin-bottom: 16px; }
.truck-driver { display: flex; align-items: center; gap: 16px; min-width: 0; }
.truck-avatar { width: 52px; height: 52px; flex: none; border-radius: 50%; display: grid; place-items: center; background: var(--primary-soft); color: var(--primary-strong, var(--primary)); font-weight: 700; font-size: 17px; }
.truck-title { margin: 0; font-size: 21px; font-weight: 700; color: var(--ink); }
.truck-facts { display: flex; flex-wrap: wrap; gap: 4px 18px; margin-top: 5px; font-size: 13.5px; color: var(--muted); }
.truck-facts b { color: var(--ink); font-weight: 600; }
.truck-facts b.text-red-600 { color: var(--rl-red-ink, var(--red-600)); }
.truck-head-cta { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.truck-head-count { font-size: 15px; font-weight: 600; color: var(--ink); }
.truck-head-count b { font-size: 24px; font-variant-numeric: tabular-nums; }
.truck-cols { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; align-items: start; }
.truck-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.truck-min { min-width: 0; }
.truck-lines .cell-product { gap: 10px; }
.truck-name { font-weight: 600; color: var(--ink); line-height: 1.3; }
body .card .table-wrap .table.truck-lines > tbody > tr > td.tc-title { white-space: normal; min-width: 200px; }
.truck-lines .truck-name { white-space: normal; overflow-wrap: anywhere; }
.truck-why { display: flex; flex-wrap: wrap; gap: 6px 12px; }
.truck-why-item { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.truck-why-item .pill { white-space: nowrap; flex: none; }
.truck-ref { font-family: var(--font-mono); font-size: 12px; padding: 2px 7px; border: 1px solid var(--line); border-radius: 6px; color: var(--ink); background: var(--surface); text-decoration: none; }
a.truck-ref:hover { border-color: var(--primary); color: var(--primary); }
.truck-qty { white-space: nowrap; color: var(--muted); }
.truck-qty b { font-family: var(--font-mono); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.truck-lines .line-qty-stepper .line-qty { width: 58px; max-width: none; height: auto; padding: 0 4px; border: 0; border-radius: 0; box-shadow: none; text-align: center; font-weight: 700; color: var(--ink); background: transparent; -moz-appearance: textfield; appearance: textfield; }
.truck-missing { margin-top: 4px; max-width: 150px; white-space: normal; font-size: 12px; font-weight: 600; line-height: 1.35; color: var(--rl-red-ink, var(--red-600)); }
body .card .table-wrap .table.truck-lines > tbody > tr.is-missing > td { background: var(--rl-red-soft, var(--red-50)); }
.truck-bar { position: sticky; bottom: 12px; z-index: 5; display: flex; align-items: center; gap: 12px 24px; flex-wrap: wrap; margin: 0 12px 12px; padding: 12px 14px 12px 18px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 10px 28px -14px rgba(15, 23, 42, .4); }
.truck-bar-fig { display: flex; flex-direction: column; gap: 1px; }
.truck-bar-fig span { font-size: 11.5px; color: var(--muted); }
.truck-bar-fig b { font-size: 18px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.truck-bar-fig b.is-back, .truck-timeline b.is-back { color: var(--rl-green-ink, var(--emerald-700)); }
.truck-bar-fig b.is-missing { color: var(--rl-red-ink, var(--red-600)); }
.truck-bar-note { flex: 1 1 200px; max-width: 320px; margin-left: auto; }
.truck-orders { display: flex; flex-direction: column; gap: 10px; padding: 12px; }
.truck-order { border: 1px solid var(--line-soft); border-radius: 10px; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
.truck-order-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.truck-order-ref { font-weight: 600; font-size: 13px; }
.truck-order-items { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.truck-order-items li { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.truck-order-items .cell-thumb { width: 30px; height: 30px; }
.truck-order-name { flex: 1; min-width: 0; color: var(--ink); line-height: 1.3; }
.truck-timeline { list-style: none; margin: 0; padding: 14px 16px; }
.truck-timeline li { display: grid; grid-template-columns: 14px minmax(0, 1fr); gap: 12px; padding-bottom: 14px; position: relative; }
.truck-timeline li:last-child { padding-bottom: 0; }
.truck-timeline li:not(:last-child)::before { content: ""; position: absolute; left: 6px; top: 17px; bottom: 0; width: 2px; background: var(--line); }
.truck-timeline-dot { width: 14px; height: 14px; margin-top: 3px; border-radius: 50%; border: 3px solid var(--primary); background: var(--surface); box-sizing: border-box; }
.truck-timeline-head { font-weight: 600; font-size: 13.5px; color: var(--ink); }
@media (max-width: 1100px) {
  .truck-cols { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 899px) {
  .truck-head-cta { align-items: flex-start; }
  .truck-bar { margin: 0; border-radius: 0; border-left: 0; border-right: 0; bottom: 0; }
  .truck-bar-note { max-width: none; margin-left: 0; flex-basis: 100%; order: 3; }
  .truck-bar .btn { margin-left: auto; }
}

/* Remise en stock (modules/preparator/pages/putback.php) — a cancelled order's
   picked products going back on the shelf; same bar and "missing" line as the
   truck unload. */
.pb-count { font-family: var(--font-mono); font-size: 15px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.pb-count strong { font-size: 18px; color: var(--ink); }
.pb-lines td.right .btn { margin-inline-start: 8px; }
.pb-lines .pb-nobarcode { justify-content: flex-end; margin: 0 0 6px; }
.pb-lines .line-qty-stepper .line-qty { width: 58px; max-width: none; height: auto; padding: 0 4px; border: 0; border-radius: 0; box-shadow: none; text-align: center; font-weight: 700; color: var(--ink); background: transparent; -moz-appearance: textfield; appearance: textfield; }
.pb-missing { margin-top: 4px; font-size: 12px; font-weight: 600; color: var(--rl-red-ink, var(--red-600)); }
body .card .table-wrap .table.pb-lines > tbody > tr.is-short > td { background: var(--rl-red-soft, var(--red-50)); }
.pb-bar { position: sticky; bottom: 12px; z-index: 5; display: flex; align-items: center; justify-content: space-between; gap: 12px 24px; flex-wrap: wrap; margin: 0 12px 12px; padding: 12px 14px 12px 18px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 10px 28px -14px rgba(15, 23, 42, .4); color: var(--muted); }
.pb-bar strong { font-size: 18px; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 899px) {
  .pb-bar { margin: 0; border-radius: 0; border-left: 0; border-right: 0; bottom: 0; }
}
.di-kept { min-width: 24px; font-size: 14px; font-weight: 600; color: var(--ink); }
.di-driver { font-size: 12px; color: var(--muted); white-space: nowrap; }
/* Totals under the table, at the end; the one after the driver's counts in bold. */
.di-totals { display: grid; gap: 8px; justify-items: end; padding: 14px 20px 18px; border-top: 1px solid var(--line); }
.di-total-row { display: flex; align-items: baseline; justify-content: space-between; gap: 24px; width: min(100%, 400px); font-size: 13px; color: var(--muted); }
.di-total-row .num { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--slate-700); white-space: nowrap; }
.di-total-row.di-total-after { padding-top: 8px; border-top: 1px dashed var(--line); font-size: 14px; font-weight: 700; color: var(--ink); }
.di-total-row.di-total-after .num { font-size: 16px; font-weight: 700; color: var(--ink); }
/* The answer: refuse (reason + button) at the start, Confirm at the end;
   on a phone stacked, Confirm first, everything full width. */
.di-decide { display: flex; align-items: center; justify-content: space-between; gap: 16px 24px; flex-wrap: wrap; }
.di-refuse { flex: 1 1 360px; max-width: 580px; }
.di-refuse-row { display: flex; gap: 8px; }
.di-refuse-row .input { flex: 1 1 auto; min-width: 0; }
.di-refuse-row .btn { flex: none; white-space: nowrap; }
.di-confirm-btn { flex: none; }
@media (min-width: 901px) {
  /* Plain white rows (no zebra — it hid every other reported row's tint),
     the tint on the row so it reaches every cell. */
  body .card .table-wrap .table.di-lines > tbody > tr > td,
  body .card .table-wrap .table.di-lines > tbody > tr:hover > td { background: transparent; }
  body .card .table-wrap .table.di-lines > tbody > tr > td { padding-top: 12px; padding-bottom: 12px; }
  body .card .table-wrap .table.di-lines > tbody > tr { background: var(--surface); }
  body .card .table-wrap .table.di-lines > tbody > tr:hover { background: var(--slate-50); }
  body .card .table-wrap .table.di-lines > tbody > tr.di-row-reported { background: var(--rl-red-soft, var(--red-50)); }
  body .card .table-wrap .table.di-lines > tbody > tr > td.tc-photo { width: 1%; padding-inline-end: 4px; }
  body .card .table-wrap .table.di-lines > tbody > tr > td.tc-title {
    white-space: normal; text-wrap: balance; font-size: 14px; font-weight: 600; line-height: 1.35; color: var(--ink);
  }
  body .card .table-wrap .table.di-lines > tbody > tr > td.di-hand { font-size: 14px; font-weight: 600; color: var(--ink); }
  body .card .table-wrap .table.di-lines > tbody > tr > td.text-center { width: 170px; }
  body .table.di-lines > thead > tr > th.text-center { text-align: center; }
}
@media (max-width: 900px) {
  .di-count-cell { display: flex; width: 100%; }
  .di-count-cell .di-count { flex: 1 1 auto; width: auto; }
  .di-totals { padding: 12px 16px 16px; }
  .di-total-row { width: 100%; }
  .di-decide { flex-direction: column-reverse; align-items: stretch; }
  .di-refuse { flex: none; max-width: none; }
  .di-refuse-row { flex-direction: column; }
  .di-decide .btn { width: 100%; min-height: 44px; justify-content: center; }
}
.lv-issue-lines { display: grid; gap: 10px; }
.lv-issue-line { padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; }
.lv-issue-name { margin-bottom: 8px; font-weight: 600; }
.lv-issue-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.lv-issue-field { display: grid; gap: 4px; font-size: 12.5px; color: var(--muted); }
.lv-issue-count { width: 100%; }
.lv-issue-wait { border-inline-start: 3px solid var(--amber-600); }
.lv-issue-wait-list { margin: 0; padding-inline-start: 18px; }
.lv-issue-wait-list li + li { margin-top: 4px; }

/* Team performance (/reports?tab=staff, modules/reports/pages/_staff.php):
   a person per row with a tinted avatar, the main counter in bold, problem
   counters as an amber pill when not zero (a plain faint 0 when all is well). Tones follow the team tile: green (primary) / blue / slate. */
.staff-teams { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 14px; }
.staff-team {
    display: flex; flex-direction: column; gap: 10px; min-width: 0;
    background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
    padding: 14px 16px; text-decoration: none; color: inherit; box-shadow: var(--shadow-card);
    transition: border-color .15s ease, box-shadow .15s ease;
}
.staff-team:hover { border-color: var(--slate-300); box-shadow: 0 6px 16px -10px rgba(15, 23, 42, .16); }
.staff-team:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.staff-team-head { display: flex; align-items: center; min-width: 0; }
.staff-team-head .card-icon-chip { flex: none; }
.staff-team-name { font-size: 13px; font-weight: 700; color: var(--ink); }
.staff-team-people { margin-inline-start: auto; display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.staff-team-people .icon { width: 12px; height: 12px; }
.staff-team-value { display: flex; align-items: baseline; gap: 8px; font-size: 26px; font-weight: 800; line-height: 1.1; letter-spacing: -.01em; color: var(--ink); font-variant-numeric: tabular-nums; }
.staff-team-unit { font-size: 12.5px; font-weight: 600; letter-spacing: 0; color: var(--text); }
.staff-team-facts { display: grid; gap: 5px; padding-top: 10px; border-top: 1px solid var(--line-soft); }
.staff-team-fact { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 12.5px; color: var(--muted); }
.staff-team-fact b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.staff-team-fact.is-warn b { color: var(--amber-700); }
.staff-team-fact.is-warn b::before { content: ""; display: inline-block; width: 6px; height: 6px; margin-inline-end: 6px; border-radius: 50%; background: var(--amber-600); vertical-align: middle; }
.staff-card { scroll-margin-top: 80px; }
.staff-note { display: flex; align-items: center; gap: 6px; margin: 0 0 16px; font-size: 12px; color: var(--muted); }
.staff-note .icon { width: 13px; height: 13px; flex: none; color: var(--faint); }
.staff-range { font-size: 12px; font-variant-numeric: tabular-nums; }
.staff-who { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.staff-avatar { flex: none; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; font-size: 11px; font-weight: 700; letter-spacing: .02em; }
.staff-avatar.green { background: var(--primary-soft); color: var(--primary); }
.staff-avatar.blue  { background: var(--blue-50); color: var(--blue-600); }
.staff-avatar.slate { background: var(--slate-100); color: var(--slate-600); }
.staff-name { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; }
.staff-issue { display: inline-flex; align-items: center; gap: 4px; font-variant-numeric: tabular-nums; }
.staff-issue-none { font-family: var(--font-mono); color: var(--faint); }
.staff-table td.staff-main { font-weight: 700; color: var(--ink); }
.staff-empty { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); }
.staff-empty .icon { width: 15px; height: 15px; color: var(--faint); }

/* PDF template picker (/settings, admins — modules/pdf_templates/). Each card
   holds a small sketch of its A4 page: the sheet stays white in dark mode on
   purpose (it is paper), and its ink colours are the PDF's own, not tokens. */
.pdf-tpl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: 14px; }
.pdf-tpl-option {
    display: flex; flex-direction: column; gap: 6px; padding: 14px; cursor: pointer;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
    transition: border-color .15s ease, box-shadow .15s ease;
}
.pdf-tpl-option:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--line)); }
.pdf-tpl-option:has(input:checked) { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.pdf-tpl-option:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 2px; }
.pdf-tpl-name { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--ink); }
.pdf-tpl-hint { font-size: 12.5px; line-height: 1.45; }
.pdf-tpl-open { display: inline-flex; align-items: center; gap: 5px; margin-top: auto; padding-top: 4px; font-size: 12.5px; font-weight: 600; color: var(--primary); text-decoration: none; }
.pdf-tpl-open:hover { text-decoration: underline; }
.pdf-tpl-open .icon { width: 13px; height: 13px; }
.pdf-tpl-frame { display: block; width: 100%; height: 760px; margin-top: 16px; border: 1px solid var(--line); border-radius: 8px; background: var(--slate-100); }
@media (max-width: 900px) { .pdf-tpl-frame { display: none; } }

.pdf-tpl-paper {
    position: relative; display: block; width: 100%; max-width: 170px; aspect-ratio: 210 / 297; margin: 0 auto 8px;
    background: #fff; border: 1px solid #e2e8f0; border-radius: 3px; overflow: hidden;
    box-shadow: 0 8px 18px -12px rgba(15, 23, 42, .45);
}
.pdf-tpl-paper i { position: absolute; display: none; left: 8%; }
.pdf-tpl-paper .m-logo { display: block; top: 6%; width: 24%; height: 3%; background: #0f172a; border-radius: 1px; }
.pdf-tpl-paper .m-co { display: block; top: 11%; width: 26%; height: 1.4%; background: #64748b; }
.pdf-tpl-paper .m-co2 { display: block; top: 13.5%; width: 34%; height: 1.2%; background: #cbd5e1; }
.pdf-tpl-paper .m-qr {
    display: block; left: auto; right: 8%; top: 5%; width: 15%; aspect-ratio: 1;
    background: repeating-conic-gradient(#0f172a 0 25%, #fff 0 50%) 0 0 / 34% 34%;
}
.pdf-tpl-paper .m-rows { display: block; width: 84%; }

/* Classique: QR top-right, title, info grid, grey head, zebra rows, grey total row. */
.is-classic .m-title { display: block; top: 19%; width: 42%; height: 3.4%; background: #0f172a; }
.is-classic .m-ref { display: block; top: 24%; width: 22%; height: 1.8%; background: #475569; }
.is-classic .m-rule { display: block; top: 29%; width: 84%; height: 1px; background: #e2e8f0; }
.is-classic .m-grid { display: block; top: 32%; width: 84%; height: 9%; background: repeating-linear-gradient(to bottom, #cbd5e1 0 2px, transparent 2px 8px); mask: linear-gradient(to right, #000 0 38%, transparent 38% 50%, #000 50% 88%, transparent 88%); }
.is-classic .m-thead { display: block; top: 45%; width: 84%; height: 3.4%; background: #f1f5f9; border-bottom: 1px solid #e2e8f0; }
.is-classic .m-rows { top: 48.4%; height: 22%; background: repeating-linear-gradient(to bottom, #fff 0 8px, #f8fafc 8px 16px); border-bottom: 1px solid #e2e8f0; }
.is-classic .m-total { display: block; top: 70.4%; width: 84%; height: 3.6%; background: linear-gradient(to right, transparent 70%, #0f172a 70% 92%, transparent 92%) center / 100% 40% no-repeat, #f1f5f9; }

/* Moderne: indigo strip + title band, tinted panels, indigo head and total, signature boxes, footer. */
.is-modern .m-strip { display: block; top: 0; left: 0; width: 100%; height: 1.4%; background: #006a88; }
.is-modern .m-band { display: block; top: 19%; width: 84%; height: 6%; background: linear-gradient(to right, transparent 4%, #fff 4% 40%, transparent 40%) center / 100% 26% no-repeat, #006a88; }
.is-modern .m-p1, .is-modern .m-p2 { display: block; top: 28%; width: 40.5%; height: 10%; background: #e3f9ff; border-left: 2px solid #006a88; }
.is-modern .m-p2 { left: 51.5%; }
.is-modern .m-thead { display: block; top: 41%; width: 84%; height: 3.4%; background: #006a88; }
.is-modern .m-rows { top: 44.4%; height: 21%; background: repeating-linear-gradient(to bottom, #fff 0 7px, #e2e8f0 7px 8px, #f8f9fe 8px 15px, #e2e8f0 15px 16px); }
.is-modern .m-total { display: block; left: auto; right: 8%; top: 67.5%; width: 42%; height: 4.4%; background: #006a88; border-radius: 1px; }
.is-modern .m-s1, .is-modern .m-s2 { display: block; top: 76%; width: 39%; height: 10%; border: 1px solid #cbd5e1; }
.is-modern .m-s2 { left: auto; right: 8%; }
.is-modern .m-foot { display: block; top: 93%; width: 84%; height: 1.4%; border-top: 1px solid #e2e8f0; background: linear-gradient(to right, #cbd5e1 0 55%, transparent 55% 86%, #006a88 86%) bottom / 100% 50% no-repeat; }

/* Épuré: issuer right-aligned, big title, hairlines only, double-ruled total, lines to sign. */
.is-clean .m-logo { width: 20%; height: 2.6%; }
.is-clean .m-co { left: auto; right: 8%; top: 6%; width: 24%; background: #0f172a; }
.is-clean .m-co2 { left: auto; right: 8%; top: 9%; width: 30%; }
.is-clean .m-qr { top: 17%; width: 12%; }
.is-clean .m-title { display: block; top: 18%; width: 54%; height: 4.4%; background: #0f172a; border-radius: 1px; }
.is-clean .m-ref { display: block; top: 24.5%; width: 24%; height: 1.6%; background: #475569; }
.is-clean .m-rule { display: block; top: 30%; width: 84%; height: 1px; background: #0f172a; }
.is-clean .m-facts { display: block; top: 33%; width: 84%; height: 1.2%; background: repeating-linear-gradient(to right, #94a3b8 0 17%, transparent 17% 25%); }
.is-clean .m-facts2 { display: block; top: 35.6%; width: 84%; height: 1.6%; background: repeating-linear-gradient(to right, #0f172a 0 20%, transparent 20% 25%); }
.is-clean .m-thead { display: block; top: 42%; width: 84%; height: 2.6%; border-bottom: 1px solid #0f172a; background: repeating-linear-gradient(to right, #94a3b8 0 14%, transparent 14% 22%) top / 100% 40% no-repeat; }
.is-clean .m-rows { top: 44.6%; height: 22%; background: repeating-linear-gradient(to bottom, transparent 0 8px, #e2e8f0 8px 9px); }
.is-clean .m-total { display: block; left: auto; right: 8%; top: 69%; width: 40%; height: 4%; border-top: 1px solid #0f172a; border-bottom: 3px double #0f172a; background: linear-gradient(to right, transparent 52%, #0f172a 52%) center / 100% 40% no-repeat; }
.is-clean .m-s1, .is-clean .m-s2 { display: block; top: 84%; width: 39%; height: 1px; background: #0f172a; }
.is-clean .m-s2 { left: auto; right: 8%; }
.is-clean .m-foot { display: block; top: 94%; left: 25%; width: 50%; height: 1.1%; background: #cbd5e1; }

/* 3D scenes (Three.js, public/assets/js/scene3d.js — modules/scene3d/, case 178).
   Each host is position:relative with a height; the script puts its canvas inside. */
.scene3d-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* Moments (modules/scene3d/): a 3D scene played once on the page a write
   lands on — K the carton closes after "Préparée" / "Vérifiée", the truck
   leaves after a zone is sent, a new product's label is printed. A click or
   Escape closes it early; it leaves by itself. */
.moment-scene {
  position: fixed; inset: 0; z-index: 300;
  display: grid; place-items: center; padding: 16px;
  background: rgba(15, 23, 42, .28);
  opacity: 0; transition: opacity .3s ease;
  cursor: pointer;
}
.moment-scene.is-in { opacity: 1; }
.moment-scene-card {
  width: min(340px, 100%);
  padding: 8px 8px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  box-shadow: 0 24px 60px -18px rgba(15, 23, 42, .45);
  text-align: center;
}
/* The truck and the label need a wider stage than the single carton. */
.moment-scene[data-scene="load"] .moment-scene-card,
.moment-scene[data-scene="label"] .moment-scene-card { width: min(480px, 100%); }
.moment-scene-stage { position: relative; height: 240px; }
.moment-scene[data-scene="load"] .moment-scene-stage,
.moment-scene[data-scene="label"] .moment-scene-stage { height: 270px; }
.moment-scene-caption { margin: 2px 0 0; padding: 0 8px; font-size: 15px; font-weight: 600; color: var(--ink); text-wrap: balance; }
.moment-scene-caption .mono { font-family: var(--font-mono); }

/* O — one SKU's stock as a pallet against its minimum (product page stock box,
   /inventory's sheet). The note under it explains the outlines or the scale. */
.pallet-scene { position: relative; height: 190px; margin: 4px 0 8px; }
.pallet-scene .scene3d-canvas { height: calc(100% - 18px); } /* the note's line stays under the drawing */
.pallet-scene-note {
  position: absolute; left: 0; bottom: 0;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--muted); pointer-events: none;
}
.pallet-scene-note.is-min::before {
  content: ""; width: 12px; height: 9px; border-radius: 2px;
  border: 1.5px dashed var(--amber-600);
}

/* Suggestions d'achat (/inventory/purchase-suggestions, design 5 of the gallery):
   the orders-list shape — tabs, a "Filtres" bar, one compact table — with the
   supplier's own colour as a dot (supplier_colors(); amber = no supplier yet),
   a −/+ quantity per line and a bar stuck to the bottom of the screen that
   shows what each supplier gets and "Créer les BC". */
.sug-product { display: flex; align-items: center; gap: 10px; min-width: 0; }
.sug-thumb { flex: none; width: 30px; height: 30px; border-radius: 6px; border: 1px solid var(--line-soft); background: var(--surface); display: grid; place-items: center; overflow: hidden; color: var(--faint); }
.sug-thumb img { width: 100%; height: 100%; object-fit: contain; }
.sug-thumb.is-empty { background: var(--slate-50); }
.sug-name { font-weight: 600; max-width: 230px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sug-supplier { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sug-supplier .pill { margin-left: 6px; }
.sug-dot { display: inline-block; width: 9px; height: 9px; margin-right: 7px; border-radius: 50%; vertical-align: 1px; background: var(--ref-l, var(--amber-600)); }
.sug-neg { color: var(--red-600); }
.sug-table tr.is-off td { color: var(--faint); }
.sug-table tr.is-off .sug-thumb img { opacity: .45; }
.sug-step { display: inline-flex; align-items: stretch; height: 30px; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; background: var(--surface); vertical-align: middle; }
.sug-step-btn { flex: none; width: 26px; display: grid; place-items: center; padding: 0; border: 0; background: var(--slate-50); color: var(--muted); cursor: pointer; }
.sug-step-btn:hover { background: var(--slate-100); color: var(--ink); }
.sug-qty { width: 44px; padding: 0 4px; border: 0; background: none; text-align: center; font-size: 13px; font-weight: 600; color: var(--ink); -moz-appearance: textfield; appearance: textfield; }
.sug-qty::-webkit-inner-spin-button, .sug-qty::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.sug-qty:disabled { color: var(--faint); }
.sug-min { font-size: 12px; white-space: nowrap; }
.sug-capped { display: inline-flex; margin-left: 4px; color: var(--amber-600); vertical-align: middle; }
.sug-bar { position: sticky; bottom: 12px; z-index: 5; display: flex; align-items: center; gap: 10px 16px; margin-top: 14px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); box-shadow: 0 -6px 22px rgba(15, 23, 42, .08); font-size: 14px; color: var(--muted); }
.sug-bar-count { white-space: nowrap; }
.sug-bar-chips { flex: 1; display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.sug-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border: 1px solid var(--line-soft); border-radius: 999px; font-size: 12.5px; color: var(--ink); white-space: nowrap; }
.sug-chip .sug-dot { margin-right: 0; }
.sug-chip b { font-weight: 600; color: var(--muted); }
.sug-bar-total { color: var(--ink); white-space: nowrap; }
@media (max-width: 900px) {
    .sug-bar { bottom: 8px; padding: 8px 10px; gap: 8px; font-size: 13px; }
    .sug-bar { flex-wrap: nowrap; }
    .sug-bar-chips { display: none; }
    .sug-bar .btn { margin-left: auto; white-space: nowrap; }
    .sug-bar-count { min-width: 0; white-space: normal; }
    .sug-name { white-space: normal; max-width: none; }
}

/* ---------------------------------------------------------------------------
 | Sales home (/sales, modules/sales_home/) — "Where my orders are": one tile
 | per step, draft to out for delivery, a chevron between them, the count over
 | a bar sized against the busiest step. Each tile opens that step's list.
 |--------------------------------------------------------------------------- */
.sales-flow { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 10px; padding: 14px 16px 16px; }
.sales-flow-tile {
    position: relative; display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 6px 8px;
    padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
    color: var(--text); text-decoration: none; transition: border-color .12s ease, box-shadow .12s ease;
}
.sales-flow-tile + .sales-flow-tile::before {
    content: ""; position: absolute; inset-inline-start: -8px; top: 50%; width: 6px; height: 6px; margin-top: -3px;
    border-top: 1.5px solid var(--slate-300); border-inline-end: 1.5px solid var(--slate-300); transform: rotate(45deg);
}
[dir="rtl"] .sales-flow-tile + .sales-flow-tile::before { transform: rotate(-45deg); }
.sales-flow-tile:hover { border-color: var(--slate-300); box-shadow: 0 1px 2px rgb(15 23 42 / .06); }
.sales-flow-tile:focus-visible { outline: none; box-shadow: var(--ring-primary); }
.sales-flow-label { font-size: 12px; font-weight: 600; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sales-flow-count { font-size: 22px; font-weight: 700; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.sales-flow-bar { grid-column: 1 / -1; height: 4px; border-radius: 999px; background: var(--slate-100); overflow: hidden; }
.sales-flow-bar i { display: block; width: var(--share, 0%); height: 100%; border-radius: inherit; background: var(--primary); }
.sales-flow-tile.is-empty { border-style: dashed; background: transparent; }
.sales-flow-tile.is-empty .sales-flow-count { color: var(--faint); }
@media (max-width: 1100px) {
    .sales-flow { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .sales-flow-tile:nth-child(3n + 1)::before { display: none; }
}
@media (max-width: 560px) {
    .sales-flow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sales-flow-tile:nth-child(3n + 1)::before { display: block; }
    .sales-flow-tile:nth-child(2n + 1)::before { display: none; }
}

/* ---------------------------------------------------------------------------
 | Line quantity − / + (.line-qty-stepper) — the "Lignes de commande" tables of
 | the BC fournisseur form and the customer order form (user, 2026-10-27: the
 | browser's own tiny up/down arrows replaced). The number stays typable; the
 | native arrows are hidden.
 |--------------------------------------------------------------------------- */
.line-qty-stepper { display: inline-flex; align-items: stretch; height: 35px; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; background: var(--surface); }
.line-qty-stepper:focus-within { border-color: var(--slate-400); box-shadow: var(--ring-primary); }
.order-lines-card .line-qty-stepper .line-qty { width: 54px; max-width: none; height: auto; padding: 0 4px; border: 0; border-radius: 0; box-shadow: none; text-align: center; font-weight: 700; color: var(--ink); background: transparent; -moz-appearance: textfield; appearance: textfield; }
.line-qty-stepper .line-qty::-webkit-inner-spin-button, .line-qty-stepper .line-qty::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.line-qty-step { flex: none; width: 30px; display: grid; place-items: center; padding: 0; border: 0; background: none; color: var(--muted); cursor: pointer; }
.line-qty-step:hover:not(:disabled) { background: var(--hover, var(--slate-50)); color: var(--ink); }
.line-qty-step:disabled { color: var(--faint); cursor: default; }
.line-qty-minus { border-inline-end: 1px solid var(--line); }
.line-qty-plus { border-inline-start: 1px solid var(--line); }

/* ---------------------------------------------------------------------------
 | Invoice page (/invoices/{id}) — picked from the gallery 2026-10-28 as
 | A3·B3·C2·D3·E3·F1·G3·H1·L3: the invoice as the printed sheet (company
 | header, Facturé à / Émise par, numbered lines, HT/TVA/TTC box) on the left,
 | the money on the right (settlement bar, collect button → right panel,
 | follow-up list, payments with a reconcile switch). Case 193.
 |--------------------------------------------------------------------------- */
.inv-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; align-items: start; }
.inv-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.inv-label { font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.inv-amount { font-family: var(--font-mono); font-weight: 700; color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; }
.inv-amount small { font-family: var(--font-sans); font-size: 11px; font-weight: 600; color: var(--muted); }
.inv-count { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 20px; padding: 0 6px; margin-inline-start: 6px; border-radius: 999px; background: var(--slate-100); color: var(--muted); font-size: 12px; font-weight: 700; vertical-align: 2px; }

.inv-sheet { min-width: 0; display: flex; flex-direction: column; gap: 22px; padding: 28px 28px; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; box-shadow: 0 14px 34px -22px rgb(15 23 42 / .4); }
.inv-sheet-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 18px; }
.inv-company { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.inv-company-logo { align-self: flex-start; height: 34px; width: auto; max-width: 100%; }
.inv-company-text { font-size: 13px; line-height: 1.55; color: var(--text); }
.inv-company-text strong { display: block; font-size: 15px; color: var(--ink); }
.inv-doc { text-align: end; }
.inv-doc-word { font-size: 26px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--primary); line-height: 1.1; }
.inv-doc-ref { margin: 4px 0 0; font-family: var(--font-mono); font-size: 16px; font-weight: 600; color: var(--ink); }
.inv-doc .muted { font-size: 13px; }
.inv-sheet-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.inv-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.inv-party { padding: 14px 18px; border: 1px solid var(--line); border-radius: 12px; font-size: 13.5px; line-height: 1.6; color: var(--text); }
.inv-party .inv-label { margin-bottom: 6px; }
.inv-party strong { color: var(--ink); font-size: 14.5px; }

.inv-sheet .table-wrap { border: 0; border-radius: 0; box-shadow: none; }
.table.inv-lines th { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); border-bottom: 2px solid var(--ink); }
.table.inv-lines td { vertical-align: top; white-space: normal; }
.table.inv-lines td.right, .table.inv-lines th.right { white-space: nowrap; }
.table.inv-lines .inv-no { width: 36px; }
.inv-line-name { font-weight: 600; color: var(--ink); }
.inv-line-codes { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }

.inv-totals { align-self: flex-end; width: min(380px, 100%); padding: 14px 18px; border: 1px solid var(--line); border-radius: 12px; }
.inv-totals-ttc { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin: 4px 0; padding: 11px 0 9px; border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
.inv-totals-ttc strong { font-size: 15px; color: var(--ink); }
.inv-totals-ttc .inv-ttc-amount { font-family: var(--font-mono); font-size: 22px; font-weight: 700; color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; }
.inv-totals-left .k { font-weight: 600; color: var(--ink); }
.inv-totals-left.is-due .v { color: var(--rl-amber-ink, var(--amber-700)); }
.inv-totals-left.is-clear .v { color: var(--rl-green-ink, var(--success-strong)); }

.inv-settle-bar { display: flex; height: 24px; margin-top: 10px; border-radius: 7px; overflow: hidden; background: var(--slate-100); }
.inv-settle-bar i { display: flex; align-items: center; padding: 0 9px; font-style: normal; font-size: 12px; font-weight: 700; color: #fff; white-space: nowrap; overflow: hidden; }
.inv-settle-bar .is-paid { background: var(--rl-green-dot, var(--success)); }
.inv-settle-bar .is-credit { background: var(--rl-blue-dot, var(--blue-600)); }
.inv-settle-bar .is-left { flex: 1; color: var(--rl-amber-ink, var(--amber-700)); background: repeating-linear-gradient(45deg, var(--rl-amber-soft, var(--amber-50)), var(--rl-amber-soft, var(--amber-50)) 6px, var(--rl-amber-line, #f8dab9) 6px, var(--rl-amber-line, #f8dab9) 12px); }
.inv-legend { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; font-size: 13px; color: var(--muted); }
.inv-legend span { display: flex; align-items: center; gap: 7px; }
.inv-legend b { margin-inline-start: auto; font-family: var(--font-mono); color: var(--ink); }
.inv-legend i { flex: none; width: 10px; height: 10px; border-radius: 3px; }
.inv-legend .is-paid { background: var(--rl-green-dot, var(--success)); }
.inv-legend .is-credit { background: var(--rl-blue-dot, var(--blue-600)); }
.inv-legend .is-left { background: var(--rl-amber-line, #f8dab9); }
.inv-legend .is-refund { background: var(--rl-amber-dot, var(--amber-600)); }
.inv-legend .is-refunded { background: var(--slate-400); }

.inv-collect { display: flex; flex-direction: column; gap: 10px; font-size: 14px; color: var(--text); }
.inv-collect .btn { justify-content: center; }
.inv-settled { display: flex; gap: 10px; align-items: center; padding: 12px 16px; border-radius: 12px; font-size: 14px; background: var(--rl-green-soft, var(--success-soft)); border: 1px solid var(--rl-green-line, var(--success-200)); color: var(--rl-green-ink, var(--success-strong)); }
.inv-settled .icon { width: 18px; height: 18px; flex: none; }

.inv-story { list-style: none; margin: 0; padding: 0; }
.inv-story li { position: relative; display: flex; gap: 11px; padding-bottom: 14px; }
.inv-story li:last-child { padding-bottom: 0; }
.inv-story li::before { content: ""; position: absolute; inset-inline-start: 13px; top: 28px; bottom: 2px; width: 2px; background: var(--slate-200); }
.inv-story li:last-child::before { display: none; }
.inv-story-dot { flex: none; width: 28px; height: 28px; border-radius: 999px; display: grid; place-items: center; background: var(--rl-green-soft, var(--success-soft)); color: var(--rl-green-ink, var(--success-strong)); }
.inv-story-dot .icon { width: 14px; height: 14px; }
.inv-story li.is-credit .inv-story-dot { background: var(--rl-blue-soft, var(--blue-50)); color: var(--rl-blue-ink, var(--blue-700)); }
.inv-story li.is-refund .inv-story-dot { background: var(--rl-amber-soft, var(--amber-50)); color: var(--rl-amber-ink, var(--amber-700)); }
.inv-story li.is-now .inv-story-dot { background: var(--primary); color: #fff; }
.inv-story-text { min-width: 0; flex: 1; }
.inv-story-title { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.inv-story-meta { margin-top: 1px; font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
.inv-story-amount { flex: none; font-size: 13px; font-weight: 700; color: var(--ink); white-space: nowrap; }

.table.inv-money td { white-space: normal; vertical-align: top; }
.inv-money-amount { white-space: nowrap; }
.inv-money-amount .mono { display: block; font-weight: 700; color: var(--ink); }
.inv-money-amount form, .inv-money-amount .inv-switch.is-static { margin-top: 6px; }
.inv-money-ref { color: var(--ink); }
.inv-switch { display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; background: none; font: inherit; font-size: 12.5px; color: var(--muted); cursor: pointer; white-space: nowrap; }
.inv-switch.is-static { cursor: default; }
.inv-switch-track { position: relative; flex: none; width: 32px; height: 18px; border-radius: 999px; background: var(--slate-300); transition: background .15s; }
.inv-switch-track::after { content: ""; position: absolute; top: 2px; inset-inline-start: 2px; width: 14px; height: 14px; border-radius: 50%; background: var(--white); box-shadow: 0 1px 3px rgba(15, 23, 42, .25); transition: transform .15s; }
.inv-switch.is-on { color: var(--rl-green-ink, var(--success-strong)); }
.inv-switch.is-on .inv-switch-track { background: var(--rl-green-dot, var(--success)); }
.inv-switch.is-on .inv-switch-track::after { transform: translateX(14px); }
[dir="rtl"] .inv-switch.is-on .inv-switch-track::after { transform: translateX(-14px); }
.inv-switch:focus-visible .inv-switch-track { box-shadow: var(--ring-primary); }
.inv-drawer .filter-drawer-panel { width: min(400px, 100%); }
.inv-drawer-form { padding-top: 14px; }

@media (max-width: 1180px) {
    .inv-layout { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
    .inv-sheet { padding: 18px 16px; }
    .inv-sheet-head, .inv-parties { grid-template-columns: minmax(0, 1fr); }
    .inv-doc { text-align: start; }
    .inv-sheet-actions { justify-content: flex-start; }
}

/* Customer detail page (modules/customers/pages/show.php) — design A picked
   from the design gallery 2026-10-29: one page, every section stacked under a
   jump bar (no hidden tabs). Tokens only, so dark.css follows. */
.customer-page { display: flex; flex-direction: column; gap: 16px; }
.customer-page > .card { margin: 0; }
.customer-hero { padding: 18px; display: flex; flex-direction: column; gap: 16px; }
.customer-hero-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; }
.customer-hero-id { display: flex; align-items: center; gap: 14px; min-width: 0; }
.customer-hero-name { margin: 0; font-size: 22px; font-weight: 600; color: var(--ink); }
.customer-hero-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 4px; font-size: 13px; color: var(--muted); }
.customer-hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.customer-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }
.customer-tile { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--line-soft); border-radius: 8px; min-width: 0; }
.customer-tile-ico { flex: none; width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; }
.customer-tile-ico .icon { width: 18px; height: 18px; }
.customer-tile-ico.is-primary { background: var(--primary-soft); color: var(--primary); }
.customer-tile-ico.is-amber { background: var(--amber-50); color: var(--amber-600); }
.customer-tile-ico.is-red { background: var(--red-50); color: var(--red-600); }
.customer-tile-ico.is-slate { background: var(--slate-100); color: var(--slate-600); }
.customer-tile-value { font-size: 19px; font-weight: 700; line-height: 1.2; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.customer-tile-label { font-size: 13px; color: var(--muted); }
.customer-jump { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px; }
.customer-jump a { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: 6px; font-size: 14px; font-weight: 500; color: var(--text); text-decoration: none; }
.customer-jump a:hover { background: var(--primary-soft); color: var(--primary); }
.customer-jump-count { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.customer-profile { display: flex; flex-wrap: wrap; gap: 16px; align-items: stretch; scroll-margin-top: 16px; }
.customer-contact { flex: 1 1 320px; padding: 16px; display: flex; flex-direction: column; gap: 14px; }
.customer-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin: 0; font-size: 14px; }
.customer-fields dt { font-size: 12px; color: var(--muted); margin-bottom: 2px; }
.customer-fields dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.customer-fields .is-wide { grid-column: 1 / -1; }
.customer-map-card { flex: 2 1 480px; min-width: 0; overflow: hidden; }
.customer-map-card .leaflet-map { height: 100%; min-height: 300px; border-radius: 6px; }
.customer-map-empty { height: 100%; min-height: 220px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--muted); font-size: 14px; }
.customer-section { scroll-margin-top: 16px; }
.customer-section .card-head { flex-wrap: wrap; gap: 10px; }
.customer-section .card-head .stock-summary { padding: 0; margin: 0; font-size: 13px; }
.customer-price-add { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.customer-statement-stats { padding: 14px 16px 0; }
@media (max-width: 640px) {
    .customer-fields { grid-template-columns: minmax(0, 1fr); }
}
