/*
 * /admin-mobile/assets/app.css
 * Home Épure — Administration mobile
 *
 * Identité reprise de /css/style.css (« Luxe Éditorial — Blanc, Crème & Or
 * Bruni ») : fond crème, surfaces blanches, texte brun-noir, noir éditorial
 * comme couleur signature, titres en serif. Les jetons de couleur en
 * reprennent les valeurs exactes — et, comme le site, l'application n'a
 * qu'une seule palette : pas de mode sombre.
 *
 * Aucune ressource externe : ni CDN, ni Google Fonts. Une police distante est
 * exactement ce qui manque quand le back-office est ouvert dans un entrepôt
 * sans réseau — et elle imposerait d'assouplir la CSP. La pile serif utilise
 * Playfair Display / Cormorant Garamond si le téléphone les possède déjà, et
 * retombe sinon sur les serif système, très proches visuellement.
 */

/* ─── Jetons ─────────────────────────────────────────────────────────────── */
:root {
  --bg:            #F5F3F0;
  --bg-2:          #EDE9E3;
  --bg-3:          #E4DED5;
  --surface:       #FFFFFF;
  --surface-2:     #FAF8F5;
  --border:        rgba(28, 22, 17, 0.10);
  --border-2:      rgba(28, 22, 17, 0.18);

  --text:          #1C1611;
  --text-muted:    #5A4F45;
  --text-dim:      #9A8E84;
  --text-inv:      #FFFFFF;

  --ink:           #1A1A1A;   /* noir éditorial — couleur signature */
  --ink-soft:      rgba(26, 26, 26, 0.06);
  --ink-border:    rgba(26, 26, 26, 0.15);

  --red:           #C0392B;
  --red-soft:      #FBEEEC;
  --green:         #27AE60;
  --green-soft:    #EAF7F0;
  --amber:         #B4762B;
  --amber-soft:    #FBF3E7;
  --blue:          #2C5F8A;
  --blue-soft:     #EBF2F8;
  --violet:        #6B4E8F;
  --violet-soft:   #F2EDF8;

  --font-ui:     'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
  --font-serif:  'Playfair Display', 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-mono:   ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --r-sm: 8px;
  --r:    12px;
  --r-lg: 18px;
  --r-xl: 24px;

  --sh-sm: 0 1px 2px rgba(28, 22, 17, .06), 0 1px 3px rgba(28, 22, 17, .04);
  --sh:    0 4px 16px rgba(28, 22, 17, .08);
  --sh-lg: 0 -8px 32px rgba(28, 22, 17, .14);

  --topbar-h: 56px;
  --nav-h:    58px;
  --safe-t:   env(safe-area-inset-top, 0px);
  --safe-b:   env(safe-area-inset-bottom, 0px);

  --tap: 44px;   /* cible tactile minimale */
}

/* Pas de mode sombre.
   /css/style.css n'en définit aucun : le site reste crème quel que soit le
   réglage du téléphone. Une version précédente transposait ici toute la
   palette sur un brun très sombre — ouverte le soir, l'application ne
   ressemblait alors plus du tout au site qu'elle administre. L'identité prime
   sur le confort nocturne : une seule palette, celle de la marque.

   `color-scheme: light` sur html n'est pas décoratif. Sans lui, un téléphone
   en thème sombre continue de rendre les contrôles natifs — champs, listes
   déroulantes, sélecteurs de date, barres de défilement — en sombre, sur nos
   fonds crème : c'est exactement l'incohérence que ce retrait vise à faire
   disparaître. */

/* ─── Base ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  background: var(--bg);
  color-scheme: light;
}

body {
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  /* Empêche la sélection accidentelle pendant un appui long sur les cartes ;
     les zones de texte réellement copiables la réactivent plus bas. */
  -webkit-tap-highlight-color: transparent;

  /* AUCUN zoom du navigateur : ni double-tapotement, ni pincement.
   *
   * Sur mobile, deux appuis rapprochés ou deux doigts qui se resserrent
   * déclenchent par défaut un zoom. Dans une application où l'on pointe des
   * pièces une par une, où l'on enchaîne les filtres et où l'on corrige un
   * stock au clavier numérique, ces gestes se produisent constamment sans être
   * voulus : l'écran saute, il faut le remettre d'aplomb, et on a perdu sa
   * place dans la liste.
   *
   * `pan-x pan-y` n'autorise que le DÉFILEMENT. Tout le reste des gestes de
   * zoom du navigateur est retiré. Le geste de traction pour rafraîchir et les
   * balayages continuent de fonctionner : ils reposent sur des écouteurs
   * `touchstart`/`touchmove` du code, que `touch-action` ne concerne pas — il
   * ne gouverne que les comportements par défaut du navigateur.
   *
   * Effet secondaire bienvenu : supprime aussi le délai de 300 ms que certains
   * navigateurs gardent avant de valider un appui, au cas où un second suivrait.
   * Les boutons répondent instantanément.
   *
   * Posé sur `body` : couvre l'application, l'écran de connexion et la page
   * hors-ligne, qui partagent cette feuille. */
  touch-action: pan-x pan-y;
}

/* ─── Coquille verrouillée : UN SEUL conteneur défile ─────────────────────
 *
 * C'est ce qui sépare une page web d'une application.
 *
 * Tant que c'est le DOCUMENT qui défile, trois défauts sont inévitables sur un
 * téléphone, et aucun réglage local ne les corrige :
 *   • le rebond élastique — on tire vers le bas et l'écran entier décolle,
 *     découvrant un vide sous la navigation ; on tire encore et le défilement
 *     paraît ne jamais s'arrêter ;
 *   • la dérive latérale — un seul élément trop large de quelques pixels (un
 *     bouton qui ne peut pas rétrécir, un code-barres sans espace) rend TOUTE
 *     la page glissante de gauche à droite ;
 *   • la barre d'adresse qui se replie et se déplie à chaque geste, si bien
 *     que la hauteur utile change en plein défilement.
 *
 * `position: fixed` sur le corps supprime le défilement du document. La vue
 * devient l'unique zone défilante, avec `overscroll-behavior: contain` pour
 * que son rebond ne se propage pas au document, et `overflow-x: hidden` pour
 * qu'aucun débordement horizontal ne soit possible.
 *
 * Portée volontairement limitée à `.is-app` : l'écran de connexion et la page
 * hors-ligne (`.is-login`) restent des pages ordinaires — clavier ouvert sur
 * un petit écran, le formulaire doit pouvoir remonter.
 */
html { height: 100%; }

body.is-app {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  overscroll-behavior: none;
}

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.sprite symbol { fill: none; stroke: currentColor; stroke-width: 1.7;
                 stroke-linecap: round; stroke-linejoin: round; }

.ico { width: 22px; height: 22px; flex: none; fill: none; stroke: currentColor;
       stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ico-sm { width: 16px; height: 16px; }
.ico-lg { width: 28px; height: 28px; }

a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }

/* L'attribut `hidden` doit l'emporter sur toute règle de disposition.
   Sans cela, `.tb-btn { display: grid }` (plus spécifique que la règle
   `[hidden]` de la feuille de l'agent utilisateur) rendait visibles la flèche
   de retour du tableau de bord, le bandeau hors-ligne et le bouton d'effacement
   de la recherche — des éléments explicitement masqués par le script. */
[hidden] { display: none !important; }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 4px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ═══ CONNEXION ══════════════════════════════════════════════════════════ */
.login {
  min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: calc(24px + var(--safe-t)) 20px calc(24px + var(--safe-b));
  background:
    radial-gradient(120% 80% at 50% 0%, var(--bg-2) 0%, var(--bg) 60%);
}
.login-card { width: 100%; max-width: 400px; }

.login-brand { text-align: center; margin-bottom: 32px; }
.login-mark {
  width: 68px; height: 68px; margin: 0 auto 18px;
  display: grid; place-items: center;
  border: 1px solid var(--ink-border);
  border-radius: 50%;
  font-family: var(--font-serif);
  font-size: 24px; letter-spacing: 1px; color: var(--ink);
  background: var(--surface);
  box-shadow: var(--sh-sm);
}
.login-title {
  font-family: var(--font-serif);
  font-size: 30px; font-weight: 500; letter-spacing: .5px; line-height: 1.15;
}
.login-sub {
  margin-top: 6px; font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; color: var(--text-dim);
}

.login-form { display: grid; gap: 16px; }

.field { display: block; }
.field-label {
  display: block; margin-bottom: 4px;
  /* Interlettrage ramené de 1.2 à 0.5 px : au-delà, une étiquette de deux mots
     passait à la ligne et ajoutait 17 px à chaque champ. */
  font-size: 11px; font-weight: 600; letter-spacing: .5px;
  text-transform: uppercase; color: var(--text-muted);
}
.input {
  width: 100%;
  min-height: var(--tap);
  padding: 10px 13px;
  /* 16px minimum : en dessous, iOS zoome à la mise au point du champ. */
  font-size: 16px;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  transition: border-color .16s, box-shadow .16s;
}
.input::placeholder { color: var(--text-dim); }
.input:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--ink-soft);
}
.input:disabled { opacity: .55; }

.field-pass { position: relative; display: block; }
.field-pass .input { padding-right: 48px; }
.pass-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; display: grid; place-items: center;
  color: var(--text-dim); border-radius: var(--r-sm);
}

.login-foot {
  margin-top: 26px; text-align: center;
  font-size: 12px; line-height: 1.9; color: var(--text-dim);
}
.link { color: var(--text-muted); text-decoration: underline;
        text-underline-offset: 3px; }

/* ═══ BOUTONS ════════════════════════════════════════════════════════════ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap); padding: 11px 18px;
  font-size: 14px; font-weight: 600; letter-spacing: .3px;
  border: 1px solid transparent; border-radius: var(--r);
  transition: transform .1s, opacity .15s, background .15s, border-color .15s;
  white-space: nowrap;
}
.btn:active { transform: scale(.985); }
.btn[disabled], .btn.is-busy { opacity: .6; pointer-events: none; }
.btn-block { width: 100%; }

.btn-primary   { background: var(--ink); color: var(--bg); }
.btn-secondary { background: var(--surface); color: var(--text); border-color: var(--border-2); }
.btn-ghost     { background: transparent; color: var(--text-muted); }
.btn-danger    { background: var(--red); color: #fff; }
.btn-sm        { min-height: 36px; padding: 7px 13px; font-size: 13px; }

/* Fil d'attente visuel sur un bouton occupé : indispensable sur mobile où
   l'utilisateur retape immédiatement s'il ne voit rien se passer. */
.btn.is-busy .btn-label { visibility: hidden; }
.btn.is-busy::after {
  content: ''; position: absolute;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: spin .7s linear infinite;
}
.btn { position: relative; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ═══ COQUILLE ═══════════════════════════════════════════════════════════ */
/* `min-height` sur l'écran de connexion (page ordinaire), `height` fixe dès
   que la coquille est verrouillée : la vue ne peut alors pas pousser la
   colonne au-delà de l'écran, c'est elle qui défile. */
.app { min-height: 100dvh; display: flex; flex-direction: column; }
body.is-app .app { height: 100%; min-height: 0; overflow: hidden; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  flex: none;
  display: flex; align-items: center; gap: 4px;
  height: calc(var(--topbar-h) + var(--safe-t));
  padding: var(--safe-t) 8px 0;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(1px)) { .topbar { background: var(--bg); } }

.tb-btn {
  width: var(--tap); height: var(--tap); flex: none;
  display: grid; place-items: center;
  color: var(--text); border-radius: var(--r-sm);
}
.tb-btn:active { background: var(--ink-soft); }

.tb-titles { flex: 1; min-width: 0; padding: 0 4px; }
.tb-title {
  font-family: var(--font-serif); font-size: 19px; font-weight: 500;
  letter-spacing: .2px; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-sub {
  font-size: 11px; color: var(--text-dim); letter-spacing: .3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.tb-avatar {
  width: 34px; height: 34px; flex: none; margin-left: 2px;
  display: grid; place-items: center;
  border-radius: 50%; border: 1px solid var(--ink-border);
  background: var(--surface); color: var(--ink);
  font-size: 12px; font-weight: 700; letter-spacing: .5px;
}

.offline-bar {
  position: sticky; top: calc(var(--topbar-h) + var(--safe-t)); z-index: 39;
  flex: none;
  padding: 7px 16px; font-size: 12px; font-weight: 600; text-align: center;
  background: var(--amber-soft); color: var(--amber);
  border-bottom: 1px solid var(--border);
}

/* L'unique zone défilante de l'application.
   `min-height: 0` est indispensable : sans lui, un enfant flex refuse de
   descendre sous sa hauteur de contenu et la vue ne défile jamais — c'est le
   document qui reprend la main, avec tous les défauts décrits plus haut.
   `overscroll-behavior` empêche le rebond de se propager (et, sur Android,
   coupe le rafraîchissement par traction natif du navigateur, qui entrait en
   conflit avec le nôtre). */
.view {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 14px 14px calc(var(--nav-h) + var(--safe-b) + 22px);
  outline: none;
  scrollbar-width: none;
}
.view::-webkit-scrollbar { width: 0; height: 0; }

/* Filet de sécurité contre la dérive horizontale : une chaîne sans espace
   (référence, code-barres, adresse e-mail) coupe plutôt qu'elle n'élargit la
   ligne. `overflow-x: hidden` ci-dessus la masquerait ; ici elle reste lisible.
   Les montants en sont exclus : un prix coupé au milieu de ses chiffres se
   lit de travers — ils rétrécissent au lieu de se couper (voir .kpi-value). */
.row-title, .insight-title, .empty-title, .sheet-title,
.product-name, .dl-val, .alias-code { overflow-wrap: anywhere; }

/* ─── Navigation basse ───────────────────────────────────────────────────── */
.bottomnav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  height: calc(var(--nav-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-top: 1px solid var(--border);
}
@supports not (backdrop-filter: blur(1px)) { .bottomnav { background: var(--surface); } }

.nav-item {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px;
  color: var(--text-dim); font-size: 10px; font-weight: 600;
  letter-spacing: .2px; transition: color .15s;
}
.nav-item.is-active { color: var(--ink); }
.nav-item .ico { width: 23px; height: 23px; }
.nav-badge {
  position: absolute; top: 5px; left: 50%; margin-left: 5px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: grid; place-items: center;
  background: var(--red); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 1;
  border-radius: 9px;
}

/* ═══ CARTES & LISTES ════════════════════════════════════════════════════ */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}
.card + .card, .stack > * + * { margin-top: 12px; }
.stack-sm > * + * { margin-top: 8px; }

.section { margin-top: 22px; }
.section:first-child { margin-top: 4px; }
.section-head {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 4px 10px;
}
.section-title {
  font-family: var(--font-serif); font-size: 17px; font-weight: 500;
  letter-spacing: .2px;
}
.section-action {
  margin-left: auto; font-size: 12px; font-weight: 600; color: var(--text-muted);
  display: inline-flex; align-items: center; gap: 3px;
}

/* Rangée de liste tactile — 60 px de haut minimum, séparateurs discrets. */
.row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 13px 14px; min-height: 62px;
  text-align: left; background: var(--surface);
  border: 0; border-bottom: 1px solid var(--border);
  transition: background .12s;
}
.row:last-child { border-bottom: 0; }
.row:active { background: var(--bg-2); }
.card > .row:first-child { border-radius: var(--r-lg) var(--r-lg) 0 0; }
.card > .row:last-child  { border-radius: 0 0 var(--r-lg) var(--r-lg); }
.card > .row:only-child  { border-radius: var(--r-lg); }

.row-main  { flex: 1; min-width: 0; }
.row-title {
  font-size: 14.5px; font-weight: 600; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.row-sub {
  margin-top: 3px; font-size: 12.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Le bloc de droite ne doit JAMAIS affamer le titre.
   En `flex: none` il gardait toute sa largeur naturelle : dans le journal WMS,
   où il portait « il y a 23 h · Mohammed Amine Benali », il ne laissait que
   trois caractères au libellé de l'action — la colonne de gauche affichait
   « Mi / s… » sur deux lignes. Il rétrécit désormais, et ne prend jamais plus
   de 42 % de la ligne ; c'est le titre qui a la priorité. */
.row-side { text-align: right; flex: 0 1 auto; min-width: 0; max-width: 42%; }
.row-amount { font-size: 14.5px; font-weight: 700; white-space: nowrap; }
.row-meta {
  margin-top: 3px; font-size: 11.5px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row-chev { color: var(--text-dim); }

.thumb {
  width: 46px; height: 46px; flex: none; border-radius: var(--r-sm);
  object-fit: cover; background: var(--bg-2);
  border: 1px solid var(--border);
}
.thumb-ph {
  display: grid; place-items: center;
  color: var(--text-dim); font-size: 11px; font-weight: 700;
  font-family: var(--font-serif);
}

/* ═══ KPI ════════════════════════════════════════════════════════════════ */
.kpi-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.kpi-grid.is-hero { grid-template-columns: 1fr; }

.kpi {
  padding: 15px 16px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm);
  text-align: left; width: 100%;
}
.kpi-label {
  font-size: 10.5px; font-weight: 700; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--text-dim);
}
.kpi-value {
  margin-top: 7px;
  font-family: var(--font-serif); font-size: 26px; font-weight: 500;
  letter-spacing: -.4px; line-height: 1.05;
}
.kpi.is-hero .kpi-value { font-size: 36px; }
/* Un montant complet (« 2 106,48 Dhs ») ne tient pas à 26 px dans une tuile de
   demi-largeur : il passait à la ligne et désalignait toute la grille, une
   tuile devenant plus haute que sa voisine. Il rétrécit au lieu de se plier —
   sur un tableau de bord, l'alignement se lit avant la taille du caractère. */
.kpi-grid:not(.is-hero) .kpi-value { font-size: clamp(18px, 5.6vw, 26px); }
@media (min-width: 640px) {
  .kpi-grid:not(.is-hero) .kpi-value { font-size: 26px; }
}
.kpi-unit { font-size: .5em; font-weight: 400; color: var(--text-muted);
            margin-left: 3px; letter-spacing: 0; }
.kpi-delta {
  margin-top: 7px; display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; font-weight: 600;
}
.kpi-delta.up   { color: var(--green); }
.kpi-delta.down { color: var(--red); }
.kpi-delta.flat { color: var(--text-dim); }

/* Sélecteur de période */
.periods {
  display: flex; gap: 6px; overflow-x: auto; padding: 2px 4px 8px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.periods::-webkit-scrollbar { display: none; }
.period {
  flex: none; min-height: 34px; padding: 7px 14px;
  font-size: 13px; font-weight: 600; color: var(--text-muted);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; transition: all .15s;
}
.period.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* ═══ FILTRES / ONGLETS ══════════════════════════════════════════════════ */
.chips {
  display: flex; gap: 7px; overflow-x: auto;
  margin: 0 -14px 8px; padding: 2px 14px 6px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  min-height: 36px; padding: 7px 14px;
  font-size: 13px; font-weight: 600; color: var(--text-muted);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; transition: all .15s;
}
.chip.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.chip-n {
  min-width: 20px; padding: 1px 6px; border-radius: 999px;
  background: var(--bg-2); color: var(--text-muted);
  font-size: 11px; font-weight: 700;
}
.chip.is-active .chip-n { background: rgba(255,255,255,.22); color: inherit; }

/* ═══ RECHERCHE ══════════════════════════════════════════════════════════ */
.searchbar { position: relative; margin-bottom: 12px; }
.searchbar .ico {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--text-dim); width: 19px; height: 19px; pointer-events: none;
}
.searchbar .input { padding-left: 42px; padding-right: 42px; }
.search-clear {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 38px; height: 38px; display: grid; place-items: center;
  color: var(--text-dim);
}

/* ═══ BADGES DE STATUT ═══════════════════════════════════════════════════ */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .2px;
  white-space: nowrap;
}
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%;
                 background: currentColor; flex: none; }
.badge.b-pending   { background: var(--amber-soft);  color: var(--amber); }
.badge.b-ready     { background: var(--blue-soft);   color: var(--blue); }
.badge.b-shipped   { background: var(--violet-soft); color: var(--violet); }
.badge.b-done      { background: var(--green-soft);  color: var(--green); }
.badge.b-cancelled { background: var(--bg-2);        color: var(--text-dim); }
.badge.b-refund    { background: var(--red-soft);    color: var(--red); }
.badge.b-neutral   { background: var(--bg-2);        color: var(--text-muted); }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--r-sm);
  background: var(--bg-2); color: var(--text-muted);
  font-size: 11.5px; font-weight: 600;
}
.pill.is-warn   { background: var(--amber-soft); color: var(--amber); }
.pill.is-bad    { background: var(--red-soft);   color: var(--red); }
.pill.is-good   { background: var(--green-soft); color: var(--green); }

/* ═══ ALERTES ════════════════════════════════════════════════════════════ */
.alert {
  padding: 12px 14px; margin-bottom: 14px;
  border-radius: var(--r); border: 1px solid transparent;
  font-size: 13.5px; line-height: 1.5;
}
.alert-error { background: var(--red-soft);   color: var(--red);
               border-color: color-mix(in srgb, var(--red) 25%, transparent); }
.alert-warn  { background: var(--amber-soft); color: var(--amber);
               border-color: color-mix(in srgb, var(--amber) 25%, transparent); }
.alert-info  { background: var(--blue-soft);  color: var(--blue);
               border-color: color-mix(in srgb, var(--blue) 25%, transparent); }

/* Cartes d'alerte du tableau de bord */
.insight {
  display: flex; gap: 11px; align-items: flex-start;
  width: 100%; padding: 13px 14px; text-align: left;
  border-bottom: 1px solid var(--border); background: var(--surface);
}
.insight:last-child { border-bottom: 0; }
.insight-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex: none; }
.insight-dot.warning { background: var(--amber); }
.insight-dot.danger  { background: var(--red); }
.insight-dot.success { background: var(--green); }
.insight-dot.info    { background: var(--blue); }
.insight-title { font-size: 13.5px; font-weight: 600; line-height: 1.4; }
.insight-body  { margin-top: 2px; font-size: 12px; color: var(--text-muted); }

/* ═══ ÉTATS VIDES & SQUELETTES ═══════════════════════════════════════════ */
.empty {
  padding: 54px 24px; text-align: center;
}
.empty-mark {
  width: 58px; height: 58px; margin: 0 auto 16px;
  display: grid; place-items: center;
  border: 1px solid var(--border-2); border-radius: 50%;
  color: var(--text-dim); background: var(--surface);
}
.empty-title {
  font-family: var(--font-serif); font-size: 18px; font-weight: 500;
  margin-bottom: 6px;
}
.empty-text { font-size: 13.5px; color: var(--text-muted); line-height: 1.6;
              max-width: 32ch; margin: 0 auto; }
.empty .btn { margin-top: 18px; }

.skel {
  background: linear-gradient(90deg,
    var(--bg-2) 25%, var(--bg-3) 50%, var(--bg-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
}
@keyframes shimmer { to { background-position: -200% 0; } }
.skel-line { height: 12px; margin-bottom: 8px; }
.skel-line.w-40 { width: 40%; } .skel-line.w-60 { width: 60%; }
.skel-line.w-80 { width: 80%; } .skel-line.w-30 { width: 30%; }
.skel-row {
  display: flex; gap: 12px; align-items: center;
  padding: 14px; border-bottom: 1px solid var(--border);
}
.skel-thumb { width: 46px; height: 46px; border-radius: var(--r-sm); flex: none; }
.skel-kpi { height: 92px; border-radius: var(--r-lg); }

/* ═══ FEUILLES (bottom sheets) ═══════════════════════════════════════════ */
.sheet-host {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
}

/*
 * Enveloppe d'UNE feuille (fond + panneau). Une pile de feuilles = plusieurs
 * `.sheet-wrap` empilées dans `.sheet-host`.
 *
 * Elle DOIT occuper tout le hôte. Sans règle, c'est un simple élément flex de
 * `.sheet-host` : sa largeur retombe alors sur celle de son contenu, et le
 * `width: 100%` de `.sheet` s'y adosse — mesuré à 240 px sur un écran de
 * 375 px, panneau flottant centré au lieu d'une feuille pleine largeur. Toutes
 * les feuilles étaient concernées : confirmations, actions produit, prix,
 * encaissement, facturation, formulaire de code promo.
 *
 * `position: absolute` sert aussi de référence au `.sheet-backdrop` (lui-même
 * en `position: absolute; inset: 0`), qui se calait jusqu'ici sur `.sheet-host`
 * par ricochet.
 */
.sheet-wrap {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet-backdrop {
  position: absolute; inset: 0;
  background: rgba(16, 12, 9, .48);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  animation: fade .18s ease-out;
}
@keyframes fade { from { opacity: 0; } }

.sheet {
  position: relative; width: 100%; max-width: 560px;
  max-height: 88dvh; display: flex; flex-direction: column;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--sh-lg);
  padding-bottom: var(--safe-b);
  animation: rise .22s cubic-bezier(.22, .8, .3, 1);
}
@keyframes rise { from { transform: translateY(18%); opacity: .4; } }

.sheet-grip {
  width: 38px; height: 4px; margin: 9px auto 2px;
  border-radius: 2px; background: var(--border-2); flex: none;
}
.sheet-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px 12px; border-bottom: 1px solid var(--border);
}
.sheet-title {
  flex: 1; font-family: var(--font-serif); font-size: 18px; font-weight: 500;
}
.sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch;
              padding: 8px 0; }
.sheet-body.is-padded { padding: 16px; }
.sheet-foot {
  display: flex; gap: 10px; padding: 12px 16px;
  border-top: 1px solid var(--border);
}
.sheet-foot .btn { flex: 1; }

.sheet-item {
  display: flex; align-items: center; gap: 13px;
  width: 100%; padding: 14px 18px; min-height: var(--tap);
  text-align: left; font-size: 14.5px; font-weight: 500;
}
.sheet-item:active { background: var(--bg-2); }
.sheet-item .ico { color: var(--text-muted); }
.sheet-item.is-danger { color: var(--red); }
.sheet-item.is-danger .ico { color: var(--red); }
.sheet-item-badge {
  margin-left: auto; min-width: 20px; padding: 1px 7px;
  background: var(--red); color: #fff; border-radius: 999px;
  font-size: 11px; font-weight: 700;
}

/* ═══ TOASTS ═════════════════════════════════════════════════════════════ */
.toast-host {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(var(--nav-h) + var(--safe-b) + 12px);
  z-index: 70; display: grid; gap: 8px; pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 15px; border-radius: var(--r);
  background: var(--text); color: var(--bg);
  font-size: 13.5px; font-weight: 500; line-height: 1.4;
  box-shadow: var(--sh); pointer-events: auto;
  animation: toast-in .2s cubic-bezier(.22, .8, .3, 1);
}
.toast.is-out { animation: toast-out .18s ease-in forwards; }
.toast.is-ok    { background: #1F7A47; color: #fff; }
.toast.is-error { background: #9E2F22; color: #fff; }
@keyframes toast-in  { from { transform: translateY(14px); opacity: 0; } }
@keyframes toast-out { to   { transform: translateY(8px);  opacity: 0; } }

/* ═══ FICHES DÉTAIL ══════════════════════════════════════════════════════ */
.detail-hero {
  padding: 18px 16px; text-align: center;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm);
}
.detail-amount {
  font-family: var(--font-serif); font-size: 34px; font-weight: 500;
  letter-spacing: -.6px; line-height: 1.05;
}
.detail-caption { margin-top: 6px; font-size: 12.5px; color: var(--text-muted); }
.detail-badges { margin-top: 12px; display: flex; gap: 7px;
                 justify-content: center; flex-wrap: wrap; }

.dl { }
.dl-item {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.dl-item:last-child { border-bottom: 0; }
.dl-key { flex: none; width: 108px; font-size: 12.5px; color: var(--text-dim); }
.dl-val { flex: 1; font-size: 13.5px; min-width: 0; word-break: break-word;
          -webkit-user-select: text; user-select: text; }

.product-hero { display: flex; gap: 14px; padding: 14px; align-items: center; }
.product-hero .thumb,
.product-hero .thumb-zoom .thumb { width: 78px; height: 78px; border-radius: var(--r); }
.product-name {
  font-family: var(--font-serif); font-size: 19px; font-weight: 500;
  line-height: 1.25;
}
.product-sku {
  margin-top: 4px; font-family: var(--font-mono); font-size: 12px;
  color: var(--text-dim); -webkit-user-select: text; user-select: text;
}
.price-row { margin-top: 8px; display: flex; align-items: baseline; gap: 8px; }
.price-now { font-size: 18px; font-weight: 700; }
.price-old { font-size: 13px; color: var(--text-dim); text-decoration: line-through; }

.quick-actions {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
  gap: 8px;
}
.qa {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 14px 8px; min-height: 76px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--sh-sm);
  font-size: 11.5px; font-weight: 600; color: var(--text-muted);
  text-align: center; line-height: 1.25;
}
.qa:active { background: var(--bg-2); }
.qa .ico { color: var(--ink); }

/* Graphique en barres (CA par jour), dessiné en CSS pur — pas de librairie. */
.chart {
  display: flex; align-items: flex-end; gap: 3px;
  height: 118px; padding: 14px 14px 8px;
}
.chart-col {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  justify-content: flex-end; gap: 2px; height: 100%;
}
.chart-bar { border-radius: 2px 2px 0 0; min-height: 2px; transition: height .3s; }
.chart-bar.pos { background: var(--ink); }
.chart-bar.web { background: color-mix(in srgb, var(--ink) 38%, transparent); }
.chart-legend {
  display: flex; gap: 16px; justify-content: center;
  padding: 0 14px 14px; font-size: 11.5px; color: var(--text-muted);
}
.chart-key { display: inline-flex; align-items: center; gap: 6px; }
.chart-key::before {
  content: ''; width: 10px; height: 10px; border-radius: 2px; background: var(--ink);
}
.chart-key.web::before { background: color-mix(in srgb, var(--ink) 38%, transparent); }
.chart-axis {
  display: flex; justify-content: space-between;
  padding: 0 14px 12px; font-size: 10.5px; color: var(--text-dim);
}

/* Barre d'action collante en bas d'une fiche */
/* Barre d'action épinglée au-dessus de la navigation basse.
   Le fond doit être OPAQUE sur toute la zone des boutons : un simple dégradé
   laissait apparaître les cartes blanches qui défilaient dessous, et les
   libellés devenaient illisibles. Le dégradé ne sert plus qu'à adoucir la
   jonction, sur les 18 px situés au-dessus des boutons. */
/* La barre d'action porte DÉJÀ la hauteur de la navigation dans son propre
   rembourrage bas : la vue ne doit pas la réserver une seconde fois. Elle le
   faisait, et l'écran continuait donc de défiler sur 80 px de vide après la
   barre — l'impression de défilement sans fin, tout en haut de la liste des
   griefs. Les deux valeurs se compensent exactement, quel que soit le
   téléphone : la même expression `--nav-h + --safe-b` des deux côtés.
   Sans `:has()` (navigateur ancien), on retombe simplement sur l'ancien
   comportement — de l'espace en trop, jamais de contenu masqué. */
.view:has(.actionbar) { padding-bottom: 0; }

.actionbar {
  position: sticky; bottom: 0; z-index: 30;
  display: flex; gap: 10px;
  margin: 22px -14px 0;
  padding: 12px 14px calc(12px + var(--nav-h) + var(--safe-b));
  background: var(--bg);
}
.actionbar::before {
  content: ''; position: absolute; left: 0; right: 0; top: -20px; height: 20px;
  background: linear-gradient(to top, var(--bg), transparent);
  pointer-events: none;
}
/* `min-width: 0` : sans lui, un bouton flex refuse de descendre sous la
   largeur de son libellé (`.btn { white-space: nowrap }`). Deux boutons dont
   les libellés sont longs débordaient alors de la barre — et, le document
   étant le conteneur défilant, rendaient l'écran entier glissant vers la
   droite. Le libellé s'abrège désormais plutôt que de pousser la mise en page. */
.actionbar .btn { flex: 1 1 0; min-width: 0; }
.actionbar .btn-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.more-btn { margin: 16px auto 0; display: block; }

.load-sentinel { height: 1px; }
.list-loading { padding: 16px; text-align: center;
                font-size: 12.5px; color: var(--text-dim); }

/* ═══ PAGE HORS LIGNE ════════════════════════════════════════════════════ */
.offline-card { text-align: center; }
.offline-card .login-mark { margin-bottom: 22px; }
.offline-title { font-size: 24px; }
.offline-text  { margin-top: 12px; }
.offline-action{ margin-top: 24px; }

/* Très petits écrans (iPhone SE et assimilés, 320 px de large).
   Le décor d'une ligne — vignette, gouttières, chevron — consomme 90 px avant
   le premier caractère : il ne restait qu'une centaine de pixels au titre, et
   « Ticket 100 · Fatima-Zahra Bennani » se lisait « Ticket 100 ·… ». On
   resserre le décor plutôt que de rogner l'information. */
@media (max-width: 360px) {
  .view { padding-left: 10px; padding-right: 10px; }
  .chips     { margin-left: -10px; margin-right: -10px;
               padding-left: 10px; padding-right: 10px; }
  .actionbar { margin-left: -10px; margin-right: -10px;
               padding-left: 10px; padding-right: 10px; }
  .row { padding: 12px 11px; gap: 10px; }
  .thumb { width: 40px; height: 40px; }
  .thumb-zoom-ico { width: 15px; height: 15px; }
  .row-amount { font-size: 13.5px; }
  .row-side { max-width: 46%; }
  .dl-key { width: 92px; }
}

/* Grands écrans (tablette / mode paysage) : on élargit sans casser le rythme. */
@media (min-width: 640px) {
  .view { max-width: 680px; margin: 0 auto; width: 100%; padding-left: 20px; padding-right: 20px; }
  .kpi-grid { grid-template-columns: repeat(3, 1fr); }
  .kpi-grid.is-hero { grid-template-columns: 1fr; }
  .chips { margin-left: -20px; margin-right: -20px; padding-left: 20px; padding-right: 20px; }
  .actionbar { margin-left: -20px; margin-right: -20px; padding-left: 20px; padding-right: 20px; }
}

/* ═══ FORMULAIRES DE FEUILLE ═════════════════════════════════════════════ */
.field-hint {
  margin-top: 4px; font-size: 11.5px; line-height: 1.45; color: var(--text-dim);
}
/* Rythme vertical resserré.
 *
 * Le formulaire « Nouveau produit » demandait 1448 px de contenu dans une
 * feuille qui en montre 563 : deux écrans et demi de défilement pour créer un
 * article. Chaque champ prenait 74 px pour un contrôle de 50 — le reste était
 * du blanc et une étiquette en capitales largement espacées. On garde la
 * hauteur de frappe (--tap, la cible tactile minimale, qui ne se négocie pas)
 * et on rend le reste. Profite à TOUTES les feuilles, pas au seul produit. */
.field + .field { margin-top: 10px; }

select.input {
  appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position: calc(100% - 19px) 50%, calc(100% - 13px) 50%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 38px;
}
textarea.input { resize: vertical; min-height: 76px; line-height: 1.5; }

/* Interrupteur — cible tactile pleine largeur, l'état est lisible d'un coup d'œil */
.switch {
  display: flex; align-items: center; gap: 14px;
  min-height: var(--tap); padding: 9px 0;
  border-bottom: 1px solid var(--border); cursor: pointer;
}
.switch:last-of-type { border-bottom: 0; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-main { flex: 1; min-width: 0; }
.switch-label { display: block; font-size: 14px; font-weight: 600; }
.switch-hint  { display: block; margin-top: 3px; font-size: 11.5px;
                line-height: 1.5; color: var(--text-dim); }
.switch-track {
  flex: none; width: 46px; height: 28px; padding: 3px;
  border-radius: 999px; background: var(--bg-3);
  transition: background .18s;
}
.switch-knob {
  display: block; width: 22px; height: 22px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--sh-sm);
  transition: transform .18s;
}
.switch input:checked ~ .switch-track { background: var(--ink); }
.switch input:checked ~ .switch-track .switch-knob { transform: translateX(18px); }
.switch input:focus-visible ~ .switch-track { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Groupe de champs dans une feuille */
.form-section {
  margin-top: 15px; padding-top: 13px; border-top: 1px solid var(--border);
}
.form-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.form-section-title {
  font-size: 11px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--text-dim); margin-bottom: 9px;
}
.form-row { display: flex; gap: 10px; }
.form-row > * { flex: 1; min-width: 0; }
.form-row .field + .field { margin-top: 0; }

/* Liste d'alias (codes-barres secondaires) */
.alias-item {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 0; border-bottom: 1px solid var(--border);
}
.alias-item:last-child { border-bottom: 0; }
.alias-code {
  flex: 1; font-family: var(--font-mono); font-size: 13.5px;
  -webkit-user-select: text; user-select: text; word-break: break-all;
}
.alias-del {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  color: var(--red); border-radius: var(--r-sm);
}
.alias-del:active { background: var(--red-soft); }

/* Barre de tri / compteur au-dessus d'une liste */
.list-head {
  display: flex; align-items: center; gap: 10px; margin: 0 4px 10px;
}
.list-count { font-size: 12.5px; color: var(--text-dim); }
.list-sort {
  margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 600; color: var(--text-muted);
  min-height: 34px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface);
}

/* ═══ PRÉPARATION DE COMMANDE (PICKING) ══════════════════════════════════ */
.progress {
  height: 6px; margin: 0 4px 10px; border-radius: 3px;
  background: var(--bg-3); overflow: hidden;
}
.progress-bar {
  height: 100%; background: var(--ink); border-radius: 3px;
  transition: width .3s ease-out;
}

.pick-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; min-height: 72px;
  background: var(--surface); border-bottom: 1px solid var(--border);
  transition: background .15s, opacity .15s;
}
.pick-row:last-child { border-bottom: 0; }
.pick-row.is-done { background: var(--green-soft); }
.pick-row.is-off  { opacity: .55; }
.pick-main { flex: 1; min-width: 0; }
.pick-count {
  display: inline-block; margin-top: 5px;
  font-family: var(--font-mono); font-size: 13px; font-weight: 700;
  color: var(--text-muted);
}
.pick-row.is-done .pick-count { color: var(--green); }

.pick-actions { display: flex; gap: 8px; flex: none; }
.pick-btn {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--border-2); border-radius: var(--r);
  background: var(--surface); color: var(--text-muted);
  transition: transform .1s, background .15s, opacity .15s;
}
.pick-btn:active { transform: scale(.94); }
.pick-btn.is-primary { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.pick-btn[disabled] { opacity: .32; pointer-events: none; }
.pick-btn.is-busy { opacity: .5; pointer-events: none; }

/* ═══ ÉCART DE STOCK ═════════════════════════════════════════════════════ */
.ecart {
  font-family: var(--font-mono); font-size: 15px; font-weight: 700;
}
.ecart.is-plus  { color: var(--amber); }
.ecart.is-minus { color: var(--red); }

/* Titre fort en tête d'une feuille (nom de l'article concerné) */
.sheet-strong { font-size: 15px; font-weight: 600; margin-bottom: 4px; }

/* Commentaire interne, en toutes lettres sur la fiche d'une vente.
   Sélectionnable : on recopie souvent un numéro de suivi ou un rappel noté là. */
.note-text {
  font-size: 13.5px; line-height: 1.6; white-space: pre-wrap;
  overflow-wrap: anywhere;
  -webkit-user-select: text; user-select: text;
}

.pick-count.is-warn { color: var(--amber); }
.pick-warn {
  margin-top: 6px; padding: 6px 9px; border-radius: var(--r-sm);
  background: var(--amber-soft); color: var(--amber);
  font-size: 11.5px; font-weight: 600; line-height: 1.45;
}

/* Bandeau de trois indicateurs au-dessus d'une liste (historique des ventes) :
   plus compact que la grille du tableau de bord, pour laisser la place aux
   lignes elles-mêmes. */
.kpi-grid.is-trio {
  grid-template-columns: repeat(3, 1fr);
  gap: 8px; margin-bottom: 14px;
}
.kpi-grid.is-trio .kpi { padding: 12px 11px; }
.kpi-grid.is-trio .kpi-label { font-size: 9.5px; letter-spacing: .8px; }
.kpi-grid.is-trio .kpi-value { font-size: 19px; margin-top: 5px; }
@media (min-width: 640px) {
  .kpi-grid.is-trio .kpi-value { font-size: 24px; }
}

/* Commentaire interne d'une vente, sur sa propre ligne sous la rangée.
   Fond distinct : c'est du texte écrit à la main par un vendeur, pas une
   donnée du système — la confusion coûterait cher sur un écran de caisse.
   Retrait à gauche : la note s'aligne sous le texte, pas sous la vignette. */
.row.has-note { flex-wrap: wrap; }
/* `flex-shrink: 1` est indispensable ici : une base de 100 % PLUS le retrait
   de 58 px dépasse la ligne de 58 px, et la fin du texte se faisait couper.
   En autorisant le rétrécissement, la marge est déduite de la base. */
.row-note {
  flex: 1 1 100%; min-width: 0;
  display: flex; align-items: flex-start; gap: 6px;
  margin: 8px 0 0 58px; padding: 5px 8px;
  background: var(--amber-soft); color: var(--amber);
  border-radius: var(--r-sm);
  font-size: 11.5px; line-height: 1.45;
}
@media (max-width: 360px) {
  .row-note { margin-left: 50px; }
}
.row-note .ico { flex: none; margin-top: 1px; }
.row-note span {
  min-width: 0; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Ligne de vente ou d'audit : le sous-titre (heure, vendeur, articles ;
   produit, emplacements, auteur) peut occuper deux lignes — le rogner ferait
   disparaître le nom du vendeur ou le trajet du mouvement, qui sont justement
   ce que l'on cherche dans un historique de caisse ou un journal de stock. */
.row.is-log .row-sub {
  white-space: normal;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── Rangée de vente ───────────────────────────────────────────────────────
 *
 * Deux étages plutôt qu'une rangée générique : le NOM du client est ce qu'on
 * vient lire ici, il doit disposer de toute la largeur moins le montant. Dans
 * la disposition commune (vignette 46 px + colonne de droite + chevron) il n'en
 * restait que 143 sur 345 — tronqué dès dix-huit caractères.
 */
.vente {
  display: flex; flex-direction: column; gap: 3px;
  width: 100%; padding: 12px 14px;
  background: none; border: 0; border-bottom: 1px solid var(--border);
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.card > .vente:last-child { border-bottom: 0; }
.vente:active { background: var(--ink-soft); }

.vente-haut { display: flex; align-items: baseline; gap: 8px; }
.vente-origine { flex: none; width: 15px; height: 15px; color: var(--text-dim);
                 align-self: center; }
.vente-qui {
  flex: 1; min-width: 0;
  font-size: 14.5px; font-weight: 600; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vente-montant { flex: none; font-size: 14.5px; font-weight: 700; white-space: nowrap; }

.vente-bas { display: flex; align-items: center; gap: 8px; padding-left: 23px; }
.vente-refs {
  flex: 1; min-width: 0; font-size: 12.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vente-paiement { flex: none; font-size: 11.5px; color: var(--text-dim); }
.vente-bas .badge, .vente-bas .pill { flex: none; }

.vente-detail {
  padding-left: 23px; font-size: 12px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vente > .row-note { margin-left: 23px; margin-top: 6px; }

/* ═══ VISIONNEUSE D'IMAGES ══════════════════════════════════════════════════
 *
 * Volontairement hors du système de feuilles : une photo se regarde sur toute
 * la surface, pas dans un panneau qui en occupe 88 %. z-index au-dessus des
 * feuilles (60) — on agrandit souvent une image depuis une modale ouverte.
 * ═══════════════════════════════════════════════════════════════════════════ */
.viewer {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column;
  /* Opaque : on regarde une photo, pas une photo par-dessus une page. */
  background: #0C0907;
  padding-top: var(--safe-t); padding-bottom: var(--safe-b);
  animation: fade .16s ease-out;
}
.viewer-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 8px 10px 16px; flex: none; color: #fff;
}
.viewer-legend {
  /* Une seule ligne : la barre doit rester une barre. Le nom complet est de
     toute façon lisible sur la fiche, juste derrière. */
  flex: 1; min-width: 0; font-size: 13px; line-height: 40px;
  color: rgba(255, 255, 255, .82);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.viewer-count {
  font-size: 12px; font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, .6); flex: none;
}
.viewer-close {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 999px; cursor: pointer;
  background: rgba(255, 255, 255, .12); color: #fff;
}
.viewer-close .ico { width: 22px; height: 22px; }

.viewer-stage {
  flex: 1; min-height: 0; position: relative;
  display: grid; place-items: center; padding: 0 8px 12px;
}
.viewer-img {
  max-width: 100%; max-height: 100%;
  object-fit: contain; border-radius: var(--r-sm);
  /* Pas de zoom ici non plus. La photo s'affiche déjà à la taille maximale que
     permet l'écran ; le balayage d'une image à l'autre reste géré par le code
     (écouteurs tactiles), que `touch-action` ne bride pas. */
  touch-action: none;
}
.viewer-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 0; border-radius: 999px; cursor: pointer;
  background: rgba(255, 255, 255, .14); color: #fff;
}
.viewer-nav.is-prev { left: 6px; }
.viewer-nav.is-next { right: 6px; }
.viewer-nav .ico { width: 24px; height: 24px; }

/* ── Vignette agrandissable ────────────────────────────────────────────────
 *
 * L'enveloppe prend la place de la vignette dans la mise en page — elle hérite
 * donc de ses dimensions, y compris celles qui sont surchargées ailleurs
 * (78 px sur la fiche produit, 40 px sur petit écran).
 *
 * Le pictogramme n'est pas décoratif : `cursor: zoom-in` n'existe pas au doigt,
 * et rien ne distinguait une vignette qu'on peut ouvrir d'une vignette inerte
 * dans une rangée déjà entièrement cliquable.
 */
.thumb-zoom {
  position: relative; flex: none;
  display: block; line-height: 0;
}
.thumb-zoom .thumb { display: block; }
.thumb-zoom-ico {
  position: absolute; right: -2px; bottom: -2px;
  width: 17px; height: 17px; padding: 2px;
  box-sizing: content-box;
  border-radius: 999px;
  background: var(--ink); color: var(--text-inv);
  box-shadow: 0 0 0 2px var(--surface);
  pointer-events: none;   /* le clic appartient à l'image, pas au pictogramme */
}
.product-hero .thumb-zoom-ico { width: 20px; height: 20px; right: 0; bottom: 0; }

.thumb.is-zoomable { cursor: zoom-in; }

/* ═══ BLOCS REPLIABLES DE FORMULAIRE ════════════════════════════════════════
 *
 * `<details>` natif : l'ouverture, le clavier et l'annonce au lecteur d'écran
 * sont gérés par le navigateur, pas par nous. Sert à sortir du chemin principal
 * ce qui n'est pas nécessaire pour aboutir — voir repli() dans app.js.
 * ═══════════════════════════════════════════════════════════════════════════ */
.repli {
  margin-top: 12px; border-top: 1px solid var(--border);
}
.repli-tete {
  display: flex; align-items: center; gap: 8px;
  min-height: var(--tap); padding: 10px 0;
  font-size: 11px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--text-dim);
  cursor: pointer; list-style: none;
}
/* Safari dessine son propre triangle : on le retire pour poser le nôtre. */
.repli-tete::-webkit-details-marker { display: none; }
.repli-tete > span { flex: 1; min-width: 0; }
.repli-fleche { flex: none; transition: transform .18s ease; }
.repli[open] .repli-fleche { transform: rotate(90deg); }
.repli-corps { padding-bottom: 12px; }
/* Le dernier interrupteur d'un bloc n'a pas besoin de sa ligne de séparation :
   le bord du bloc la fournit déjà. */
.repli-corps .switch:last-child { border-bottom: 0; }

/* Marque des champs obligatoires. */
.field-req { color: var(--red); margin-left: 3px; font-weight: 700; }

/* ═══ BANDE « CE QUI ATTEND » (tableau de bord) ═════════════════════════════
 *
 * Défilante horizontalement : le nombre d'entrées dépend du rôle (de deux pour
 * un caissier à six pour un administrateur) et il ne doit jamais y avoir de
 * retour à la ligne — la bande resterait alors haute et repousserait les
 * chiffres, ce qu'on cherche justement à éviter.
 * ═══════════════════════════════════════════════════════════════════════════ */
.taches {
  display: flex; gap: 8px; overflow-x: auto;
  margin: 0 -14px 14px; padding: 2px 14px 6px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.taches::-webkit-scrollbar { display: none; }

.tache {
  flex: none; min-width: 92px;
  display: flex; flex-direction: column; gap: 6px;
  padding: 11px 13px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--sh-sm);
  text-decoration: none; color: inherit;
}
.tache:active { transform: scale(.98); }
.tache-haut { display: flex; align-items: center; gap: 7px; }
.tache-haut .ico { flex: none; }
.tache-n {
  font-family: var(--font-serif); font-size: 22px; line-height: 1;
  font-weight: 500; font-variant-numeric: tabular-nums;
}
.tache-label {
  font-size: 11px; font-weight: 600; letter-spacing: .3px;
  color: var(--text-muted); white-space: nowrap;
}

/* Le ton vient du serveur : « alerte » pour ce qui immobilise ou bloque,
   « action » pour ce qui attend un geste. */
.tache.ton-alerte { border-color: rgba(192, 57, 43, .28); background: var(--red-soft); }
.tache.ton-alerte .ico, .tache.ton-alerte .tache-n { color: var(--red); }
.tache.ton-action .ico { color: var(--ink); }

/* À zéro, la case reste — savoir où regarder vaut mieux qu'une case qui
   disparaît — mais elle s'efface pour laisser l'œil aux nombres qui comptent. */
.tache.is-vide { background: var(--surface); border-color: var(--border); box-shadow: none; }
.tache.is-vide .ico, .tache.is-vide .tache-n { color: var(--text-dim); }
.tache.is-vide .tache-label { color: var(--text-dim); }

/* En-tête de section sans marge haute : sert à coiffer le sélecteur de période. */
.section-head.is-tight { margin-top: 4px; margin-bottom: 8px; }
