/* components.css — composants réutilisables (BEM). Valeurs fidèles aux maquettes design/*.jsx.
   Couleurs via tokens uniquement. */

/* ============ PILL ============ */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    font-size: 12px;
    font-weight: 500;
    border-radius: var(--r-pill);
    letter-spacing: 0.01em;
    white-space: nowrap;
    border: none;
}
.pill--sm { padding: 3px 8px; font-size: 11px; }
.pill--neutral  { background: #EEE9DF; color: #3A372F; }
.pill--veg      { background: #E5EFE0; color: #2C5B2C; }
.pill--nonveg   { background: #F4E2D8; color: #7A3A20; }
.pill--warn     { background: #F8E4C8; color: #7A4E15; }
.pill--navy     { background: var(--navy); color: #fff; }
.pill--red      { background: var(--red); color: #fff; }
/* Teinte + encre : l'encre est --*-ink (dark-aware), JAMAIS --navy/--red qui sont des
   APLATS. Mesuré avant correctif : .pill--navySoft tombait à 2,76:1 en sombre. */
.pill--redSoft  { background: var(--red-soft); color: var(--red-ink); }
.pill--navySoft { background: var(--navy-soft); color: var(--navy-ink); }
.pill--ghost    { background: transparent; color: var(--ink-70); border: 1px solid var(--line); }
.pill--locked   { background: rgba(15, 19, 32, 0.06); color: var(--ink-60); }
.theme-dark .pill--locked { background: rgba(242, 240, 235, 0.08); }

/* ============ BTN ============ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 18px;
    border-radius: var(--r-pill);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: -0.01em;
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--line);
    cursor: pointer;
}
.btn--primary  { background: var(--navy); color: #fff; border: none; }
.btn--danger   { background: var(--red); color: #fff; border: none; }
.btn--ghost    { background: transparent; color: var(--ink); border: 1px solid var(--line); }
.btn--full     { width: 100%; }
.btn--sm       { padding: 10px 14px; font-size: 13px; }

/* ---- Export tableur ----------------------------------------------------------------------
   Les 15 boutons d'export du produit portaient 8 habillages différents (btn--ghost, btn--primary,
   adm-chipbtn, arep__btn, btn nu…), trois d'entre eux une flèche d'UPLOAD sur un téléchargement,
   et un une icône inexistante donc invisible. Une seule variante, ici, dans components.css —
   PAS dans une feuille d'écran : head.php ne charge que le CSS de l'écran courant, une classe
   définie ailleurs ne stylerait rien sur les 14 autres pages.
   --excel est un token FIXE (jamais par client) et s'inverse en mode sombre : cf. theme.css. */
.btn--export {
    background: var(--excel);
    color: var(--excel-ink);
    border: none;
    font-weight: 600;
}
.btn--export:hover,
.btn--export:focus-visible { background: var(--excel-hover); color: var(--excel-ink); }
/* L'icône reprend l'encre du bouton et ne doit jamais être « décorée » d'une autre couleur :
   c'est ce qui la garde lisible quand la plaque s'inverse en sombre. */
.btn--export .ico { color: inherit; opacity: 0.95; }
/* Un export est une action secondaire : il ne doit pas peser plus lourd qu'un bouton primaire
   dans la hiérarchie de la page, d'où la taille réduite par défaut. */
.btn--export { padding: 10px 14px; font-size: 13px; }
/* État INDISPONIBLE (pos-history : export non gréé). Remplace un `style="opacity:.4"` inline.
   `filter: grayscale` en plus de l'opacité : sur la plaque sombre du mode nuit, l'opacité seule
   laissait un vert encore franc, qui ne se lisait pas comme « désactivé ». */
.btn--export-off {
    opacity: 0.45;
    filter: grayscale(0.6);
    cursor: default;
    pointer-events: none;
}
.btn:disabled,
.btn--disabled {
    background: rgba(15, 19, 32, 0.06);
    color: var(--ink-40);
    border: none;
    cursor: not-allowed;
}
.theme-dark .btn:disabled,
.theme-dark .btn--disabled { background: rgba(242, 240, 235, 0.08); }

/* Boutons posés sur une carte primaire (fond navy).
   ⚠️ EXCEPTION DOCUMENTÉE à la doctrine des encres : le fond est un #fff FIGÉ, identique
   dans les deux modes. L'encre doit donc rester SOMBRE dans les deux modes → c'est
   --navy (l'aplat, 5,29:1 sur blanc en sombre), surtout PAS --navy-ink qui est calibrée
   pour les surfaces sombres et donnerait 2,06:1 ici. Le token suit le fond, pas l'habitude. */
.btn--on-navy { background: #fff; color: var(--navy); border: none; }
.btn--ghost-on-navy { background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.3); }

/* ============ PLATE (SVG) ============ */
.plate { position: relative; flex-shrink: 0; }
.plate svg { display: block; }
.plate--locked { filter: grayscale(0.7) brightness(0.96); opacity: 0.55; }

/* Photo réelle du plat (cf. partials/components.php::c_dish_image) : même empreinte visuelle que
   l'assiette SVG qu'elle remplace — disque parfait, liseré discret et ombre portée basse, pour
   que photos et assiettes générées cohabitent sans rupture dans une même grille. */
.plate--photo { border-radius: 50%; overflow: hidden; }
.plate--photo img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 50%;
    background: var(--plate-inner);
    box-shadow: 0 6px 14px -8px rgba(0, 0, 0, 0.35);
    border: 1px solid var(--line);
}

/* ============ ICON ============ */
.ico { display: inline-flex; vertical-align: middle; }

/* ============ TOP NAV (desktop) ============ */
.topnav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 36px;
    border-bottom: 1px solid var(--line);
    background: var(--bg);
}
.topnav__left { display: flex; align-items: center; gap: 40px; }
.nav { display: flex; gap: 4px; }
.nav__link {
    padding: 8px 14px;
    border-radius: var(--r-pill);
    font-size: 14px;
    font-weight: 500;
    color: var(--ink-70);
    letter-spacing: -0.01em;
    cursor: pointer;
}
.nav__link--active { background: var(--navy); color: #fff; }

/* Authentification dans la nav (partials/topnav.php) — rendues DANS #primaryNav, donc
   automatiquement présentes dans le hamburger sur mobile et en ligne sur desktop.
   Teinte douce (--navy-soft / --navy) plutôt qu'un aplat --navy + `color:#fff` : en
   theme-dark, --navy est ÉCLAIRCI (theme.css:56 color-mix 72% white) et le blanc en dur
   dessus donne un contraste faible — dette déjà tracée sur .nav__link--active et les cards
   primaires. Le couple soft/plein est, lui, valide dans les DEUX modes (même patron que
   .avatar et .wallet-pill). Aucun hex figé : 100% tokens de marque. */
.nav__link--cta {
    background: var(--navy-soft);
    color: var(--navy-ink);
    font-weight: 600;
}
/* Le bouton de déconnexion est un <button> dans un <form> POST (api/logout.php exige
   POST + CSRF) : on neutralise le rendu natif du bouton pour qu'il se pose exactement
   comme un .nav__link à côté des liens. */
.nav__logout { margin: 0; display: contents; }
.nav__link--logout {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: none;
    border: 0;
    font: inherit;
    font-size: 14px;
    color: var(--ink-70);
    text-align: left;
}
.nav__link--logout .ico { color: inherit; }

.topnav__actions { display: -webkit-inline-box; align-items: center; gap: 14px; }

.search-fake {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    font-size: 13px;
    color: var(--ink-60);
    background: var(--surface);
}
.icon-btn {
    width: 38px;
    height: 38px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line);
    display: grid;
    place-items: center;
    cursor: pointer;
    color: var(--ink);
    position: relative;
}
.icon-btn__dot {
    position: absolute;
    top: 6px;
    right: 8px;
    width: 8px;
    height: 8px;
    background: var(--red);
    border-radius: 50%;
}
.btn-cart {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-radius: var(--r-pill);
    background: var(--navy);
    color: #fff;
    border: none;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}
.btn-cart__badge {
    background: var(--red);
    color: #fff;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--r-pill);
    font-size: 11px;
    font-weight: 600;
    display: grid;
    place-items: center;
}
.avatar {
    width: 38px;
    height: 38px;
    border-radius: var(--r-pill);
    background: var(--navy-soft);
    color: var(--navy-ink);
    display: grid;
    place-items: center;
    font-weight: 600;
    font-size: 13px;
    font-family: var(--display);
}

/* Pastille solde (topnav, #4) — solde wallet du parent connecté. Toujours discrète
   (soft tint navy) ; teinte accent quand le solde est bas (seuil serveur). */
.wallet-pill {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 7px 14px;
    border-radius: var(--r-pill);
    background: var(--navy-soft);
    color: var(--navy-ink);
    text-decoration: none; /* rendu <a href="wallet"> (cliquable) : pas de soulignement de lien */
    font-family: var(--display);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: -0.01em;
    white-space: nowrap;
}
.wallet-pill .ico { color: inherit; }
.wallet-pill--low {
    background: var(--red-soft);
    color: var(--red-ink);
}

/* --- V23 (arborescence de nav) : la pastille devient l'accès PRIMAIRE UNIQUE au portefeuille,
   desktop ET mobile (l'onglet « Portefeuille » quitte la bottom-nav au profit des allergies).
   Deux états s'ajoutent, tous deux en tokens dérivés — aucun hex figé. --- */

/* Solde indisponible (BDD injoignable) : la pastille reste rendue pour ne pas priver le parent
   de son seul lien vers le portefeuille, mais elle n'annonce PAS un montant qu'on n'a pas — elle
   porte le libellé `nav.balance`. Traitement volontairement en retrait (le poids typographique du
   montant n'a plus de raison d'être) et neutre : ce n'est ni une alerte ni un solde bas. */
.wallet-pill--unknown {
    background: color-mix(in oklch, var(--ink) 6%, var(--surface));
    color: var(--muted);
    font-weight: 500;
}

/* Écran courant dans le territoire « portefeuille » (wallet / history / wallet-refund) : la
   pastille joue le rôle de repère de nav que ces trois écrans n'avaient pas — même fonction que
   `.nav__link--active` pour les liens de la topnav, et que le pont `$bn_active` de la bottom-nav.
   Marqueur en bordure interne (box-shadow) plutôt qu'un `border`, pour ne pas décaler la pastille
   de 2px selon la page — un repère de nav ne doit jamais faire bouger la barre. */
.wallet-pill--active {
    box-shadow: inset 0 0 0 1.5px currentColor;
}

/* Panier — bouton flottant (FAB, #5). Accès principal au panier (desktop + mobile PWA).
   Position par défaut (desktop) ; public/css/mobile.css ajuste ≤640px pour ne pas
   recouvrir la bottom-nav. */
.cart-fab {
    position: fixed;
    right: 28px;
    bottom: 28px;
    z-index: 55;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--navy);
    color: #fff;
    border: none;
    display: grid;
    place-items: center;
    cursor: pointer;
    box-shadow: 0 16px 32px -16px rgba(15, 19, 32, 0.45);
}
.cart-fab .ico { color: inherit; }
.cart-fab__badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: var(--red);
    color: #fff;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    font-size: 11px;
    font-weight: 600;
    display: grid;
    place-items: center;
    border: 2px solid var(--bg);
}
/* Le badge est toujours dans le DOM (cf. partials/topnav.php) et masqué à 0 par l'attribut
   `hidden`. `display: grid` ci-dessus l'emporterait sur le `display: none` par défaut de
   `hidden` — d'où cette règle explicite, sans quoi un panier vide afficherait « 0 ». */
.cart-fab__badge[hidden] { display: none; }

/* Retour visuel à l'ajout au panier (public/js/screens/menu.js pose `.is-bumped` après la
   réponse SERVEUR — jamais au clic : l'animation ne doit se jouer que si le panier a
   réellement bougé). Le FAB étant `position: fixed`, on n'anime que `transform` : aucun
   recalcul de mise en page, donc pas de saccade sur mobile. */
@keyframes cart-fab-shake {
    0%   { transform: translateX(0) scale(1); }
    15%  { transform: translateX(-4px) scale(1.06); }
    30%  { transform: translateX(4px) scale(1.06); }
    45%  { transform: translateX(-3px) scale(1.04); }
    60%  { transform: translateX(3px) scale(1.04); }
    75%  { transform: translateX(-2px) scale(1.02); }
    100% { transform: translateX(0) scale(1); }
}
@keyframes cart-fab-badge-pop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.35); }
    100% { transform: scale(1); }
}
.cart-fab.is-bumped { animation: cart-fab-shake 520ms cubic-bezier(0.36, 0.07, 0.19, 0.97); }
.cart-fab.is-bumped .cart-fab__badge { animation: cart-fab-badge-pop 520ms ease-out; }

/* Accessibilité : une secousse est exactement le type de mouvement qui déclenche gêne ou
   nausée chez les personnes sensibles au mouvement. On ne supprime pas le retour visuel — il
   porte une information (« c'est bien parti au panier ») — on le remplace par un flash de
   surbrillance, qui informe sans déplacer quoi que ce soit. */
@media (prefers-reduced-motion: reduce) {
    .cart-fab.is-bumped,
    .cart-fab.is-bumped .cart-fab__badge {
        animation: cart-fab-flash 320ms ease-out;
    }
    @keyframes cart-fab-flash {
        0%, 100% { box-shadow: 0 16px 32px -16px rgba(15, 19, 32, 0.45); }
        50%      { box-shadow: 0 0 0 4px color-mix(in oklch, var(--brand-accent) 45%, transparent); }
    }
}

/* Tickets snack — FAB flottant SECONDAIRE (partials/topnav.php), Phase 4 (.claude/specs/
   snack-tickets.spec.md §2.3). Empilé AU-DESSUS de `.cart-fab` (jamais de recouvrement) : même
   `right`, `bottom` = bottom du panier (28px) + sa hauteur (56px) + 14px de respiration.
   Volontairement plus discret que le panier (48px, pas 56px) : action de transparence secondaire,
   pas le CTA n°1 de la nav. Palette inversée par rapport au panier (corps accent / badge primary)
   pour rester distinguable au premier coup d'œil, tokens uniquement. Position par défaut
   (desktop) ; public/css/mobile.css ajuste ≤640px pour rester empilé au-dessus du panier ET de
   la bottom-nav. */
.snack-fab {
    position: fixed;
    right: 28px;
    bottom: calc(28px + 56px + 14px);
    z-index: 54;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--red);
    color: #fff;
    border: none;
    display: grid;
    place-items: center;
    cursor: pointer;
    box-shadow: 0 16px 32px -16px rgba(15, 19, 32, 0.45);
}
.snack-fab .ico { color: inherit; }
.snack-fab__badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: var(--navy);
    color: #fff;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    font-size: 11px;
    font-weight: 600;
    display: grid;
    place-items: center;
    border: 2px solid var(--bg);
}
/* Solde NUL (décision propriétaire 2026-08-14 : le FAB reste visible même à 0). Le badge informe
   sans réclamer l'attention d'un solde plein : teinte dérivée du token (color-mix, jamais un gris
   figé — donc correct chez tous les clients white-label et dans les deux modes), et encre --ink
   plutôt que blanc, seule combinaison lisible sur un fond aussi clair. */
.snack-fab__badge--zero {
    background: color-mix(in oklch, var(--navy) 14%, var(--surface));
    color: var(--ink);
    font-weight: 500;
}

/* Sélecteur d'école (multi-tenant location) + langue */
.school-switch {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    font-size: 13px;
    color: var(--ink);
    cursor: pointer;
}
.school-switch select,
.lang-switch select {
    border: none;
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    color: inherit;
    cursor: pointer;
    outline: none;
}
.lang-switch {
    display: flex;
    align-items: center;
    padding: 6px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 12px;
}

/* ============ LOGO ============ */
.logo { display: flex; align-items: center; gap: 10px; }
.logo__tile {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: var(--navy);
    display: grid;
    place-items: center;
    color: #fff;
    font-family: var(--display);
    font-weight: 600;
    font-size: 16px;
    letter-spacing: -0.02em;
    position: relative;
    overflow: hidden;
}
.logo__tile span { position: relative; z-index: 1; }
.logo__corner {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 60%;
    height: 40%;
    background: var(--red);
    border-top-left-radius: 60%;
}
.logo__word {
    font-family: var(--display);
    font-size: 21px;
    font-weight: 500;
    letter-spacing: -0.03em;
    color: var(--ink);
}
.logo__word b { color: var(--red-ink); font-weight: 500; }
.logo__word em { color: var(--ink-60); font-weight: 400; font-style: normal; }

/* ============ UTILITAIRES A11Y ============ */
/* Visuellement masqué mais toujours annoncé par un lecteur d'écran (jamais display:none /
   visibility:hidden pour du contenu qui doit rester accessible — ex. thead replié ≤480px). */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ============ BTN — état de chargement (spec A-logo-upload §11) ============ */
/* c_btn($html, $variant, ['loadable' => true]) enveloppe le libellé dans <span class="btn__label">
   (opt-in, ne casse pas l'espacement inline-flex/gap:8px des boutons icône+texte existants). */
.btn__label { display: inline-flex; align-items: center; gap: 8px; }

.btn.is-loading { position: relative; pointer-events: none; opacity: 0.8; }
.btn.is-loading .btn__label { visibility: hidden; }
.btn.is-loading::after {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 2px solid color-mix(in oklch, currentColor 25%, transparent);
    border-top-color: currentColor;
    animation: btnspin 0.6s linear infinite;
}
@keyframes btnspin {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    .btn.is-loading::after { animation-duration: 1.6s; }
}

/* ============ PERIOD-FILTER — filtre de période partagé, .claude/specs/FINANCE-ecrans.spec.md §3
   ============ */
/* Chargée INCONDITIONNELLEMENT (partials/head.php) car partagée par plusieurs écrans admin
   (Rapport Financier, Rapport Comptable, et hors ce lot Reversement) — même raison que
   pagination.css (§1bis de STANDARDS-UI.md). Toutes les classes sont préfixées `.period-filter`
   pour ne rien faire déborder sur le reste du produit. */
.period-filter {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: 16px;
    margin-bottom: 18px;
}
.period-filter__legend {
    font: 600 11px/1 var(--ui);
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--ink-60);
    margin-bottom: 10px;
}
.period-filter__pills { display: flex; flex-wrap: wrap; gap: 8px; }
.period-filter__pill {
    display: inline-flex;
    align-items: center;
    height: 34px;
    padding: 0 14px;
    border-radius: 999px;
    font: 600 13px/1 var(--ui);
    background: var(--surface);
    border: 1px solid var(--line);
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
}
.period-filter__pill.is-active {
    background: var(--navy-soft);
    color: var(--navy-ink);
    border-color: transparent;
}
.period-filter__form {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 10px;
    margin-top: 12px;
}
.period-filter__field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font: 600 11px/1 var(--ui);
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-60);
}
.period-filter__field input[type="date"] {
    height: 38px;
    padding: 0 12px;
    border-radius: 10px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
    font: 400 13px/1 var(--ui);
    text-transform: none;
    letter-spacing: normal;
}
.period-filter__error {
    margin: 10px 0 0;
    padding: 8px 12px;
    border-radius: 10px;
    background: var(--red-soft);
    color: var(--red-ink);
    font-size: 13px;
}
@media (max-width: 480px) {
    .period-filter__pill { min-height: 44px; }
    .period-filter__field input[type="date"] { min-height: 44px; }
}

/* ============ PAY-BADGE — pastille ronde d'un moyen de paiement, partials/pay-method-badge.php
   (.claude/specs/FINANCE-ecrans.spec.md §2.5) ============ */
/* `var(--bg-2)` volontairement, PAS un tint de la marque du client : ce cercle accueille un logo
   TIERS (Wave/Orange/MTN…) — le poser sur un tint de marque ferait se battre deux marques dans
   44px et lirait comme une caution de la marque cliente sur le prestataire (cf. spec §2.5). */
.pay-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--bg-2);
    border: 1px solid var(--line);
    color: var(--navy-ink);
    overflow: hidden;
}
.pay-badge img { display: block; object-fit: contain; }
.pay-badge--44 { width: 44px; height: 44px; }
.pay-badge--24 { width: 24px; height: 24px; }
@media (max-width: 480px) {
    .pay-badge--44 { width: 40px; height: 40px; }
}

/* ---- Bannière d'installation PWA (partials/pwa-install.php) ----------------------------------
   Placée dans components.css et non dans une feuille d'écran : elle est rendue par foot.php, donc
   présente sur TOUS les écrans parent, dont certains n'ont pas de feuille dédiée.
   `position: fixed` en bas : elle ne doit pas pousser le contenu ni sauter à la lecture. Le décalage
   de 76px laisse la bottom-nav parent visible — la recouvrir masquerait la navigation principale.
   Aucun hex : tokens uniquement, donc re-teintée par client et correcte en clair comme en sombre. */
.pwa-install {
    position: fixed;
    left: 12px;
    right: 12px;
    bottom: 76px;
    z-index: 60;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    border-radius: 16px;
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: 0 8px 28px rgba(0, 0, 0, .16);
    max-width: 560px;
    margin: 0 auto;
}
.pwa-install[hidden] { display: none; }
.pwa-install__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.pwa-install__title { font-family: var(--display); font-size: 16px; font-weight: 500; color: var(--ink); }
.pwa-install__text { margin: 0; font-size: 13px; color: var(--ink-60); line-height: 1.45; }
.pwa-install__ios { margin: 0; font-size: 13px; color: var(--ink); line-height: 1.45; }
.pwa-install__btn { align-self: flex-start; margin-top: 4px; }
/* Cible tactile de 44px : la croix est le seul moyen de refuser, elle ne doit pas se rater. */
.pwa-install__close {
    flex-shrink: 0;
    width: 44px; height: 44px;
    display: grid; place-items: center;
    border: none; background: none; cursor: pointer;
    font-size: 24px; line-height: 1; color: var(--ink-60);
    border-radius: 12px;
}
.pwa-install__close:hover { background: var(--bg-2); color: var(--ink); }
.pwa-install__close:focus-visible { outline: 2px solid var(--red-ink); outline-offset: 2px; }

/* ---- UTILITAIRES D'ALIGNEMENT ------------------------------------------------------------
   DEPLACES DEPUIS screens/admin-orders.css LE 2026-08-18.
   Ils y etaient les seules definitions du depot, alors que la page admin-orders.php n'est plus
   qu'une redirection 301 : sa feuille n'est chargee par PERSONNE (aucun $screen_css ne la
   reclame). Resultat : `.ta-r` / `.ta-c` etaient ecrits dans 26 fichiers pour ~87 occurrences,
   et n'alignaient RIEN. Defaut invisible a la relecture — la classe existait, la regle existait,
   la feuille n'etait jamais servie (STANDARDS-UI §1bis).
   Leur place est ici : components.css est charge sur toutes les pages (partials/head.php:38),
   ce qui est la seule garantie valable pour un utilitaire transverse. */
.ta-r { text-align: right; }
.ta-c { text-align: center; }

/* Variante ADMIN du sélecteur de langue ------------------------------------------------------
   `.lang-switch select` porte `color: inherit`, ce qui convient à la barre PARENT (fond clair,
   encre sombre). Les deux chromes admin imposent `color: #fff` — la pastille garderait donc son
   fond `--surface` (clair) avec un texte BLANC : invisible. On force l'encre, sans toucher à la
   règle parente.
   Placé dans components.css (toujours chargée, cf. partials/head.php:38) et non dans une feuille
   d'écran : les deux chromes admin couvrent des dizaines d'écrans, dont chacun ne charge QUE sa
   propre feuille — une règle déposée ailleurs ne s'appliquerait nulle part (STANDARDS-UI §1bis). */
.lang-switch--adm select { color: var(--ink); }
.lang-switch--adm option { color: var(--ink); background: var(--surface); }

/* ---- BASCULE CLAIR / SOMBRE (partials/theme-switch.php, 2026-08-19) -------------------------
   Dans components.css et non dans une feuille d'ecran : le composant est rendu par les TROIS
   chromes (barre parent, shell admin, barre admin heritee), qui couvrent des dizaines de pages
   chargeant chacune sa propre feuille. Une regle deposee ailleurs ne s'appliquerait nulle part
   (STANDARDS-UI §1bis).
   Encre EXPLICITE (--ink) et non `inherit` : les deux chromes admin imposent `color:#fff`, or la
   pastille garde un fond clair — sans cela, des icones blanches sur fond blanc. Meme piege que
   .lang-switch, corrige le meme jour. */
.theme-switch {
    display: inline-flex; align-items: center; gap: 2px;
    padding: 3px; border: 1px solid var(--line); border-radius: var(--r-pill);
    background: var(--surface); color: var(--ink); flex: none;
}
.theme-switch__b {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 26px; border-radius: var(--r-pill);
    color: var(--ink); opacity: .55; text-decoration: none;
}
.theme-switch__b:hover { opacity: 1; }
/* L'etat actif est porte par un FOND, pas seulement par l'opacite : un ecart d'opacite seul ne
   passe pas le contraste minimal et disparait pour un daltonien comme sur un ecran delave. */
.theme-switch__b--on {
    opacity: 1;
    background: color-mix(in oklab, var(--brand-primary) 14%, var(--surface));
    color: var(--navy-ink);
}
.theme-switch__b:focus-visible { outline: 2px solid var(--red-ink); outline-offset: 2px; }
/* .theme-switch__auto RETIRE le 2026-08-19 avec son bouton : une regle sans emetteur est
   du CSS mort, et ce depot en a deja deterre trois gisements le meme jour. */

/* ---- MONTANT EMPILE (c_money_stacked, partials/components.php) -----------------------------
   Le nombre au-dessus, la DEVISE en dessous — la devise venant du reglage par ecole
   (`enterprise_settings.currency_symbol`), jamais d'un « FCFA » fige dans un gabarit.
   Ici et non dans une feuille d'ecran : le helper est appelable depuis n'importe quelle page,
   et une regle deposee ailleurs ne s'appliquerait qu'a un seul ecran (STANDARDS-UI §1bis). */
.c-money { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.05; }
.c-money__n { font-variant-numeric: tabular-nums; white-space: nowrap; }
.c-money__c {
    font: 600 11px/1 var(--ui);
    letter-spacing: .06em;
    text-transform: uppercase;
    opacity: .55;
    margin-top: 5px;
}

/* ---- BANDEAU DE PERIODE (partials/period-banner.php, 2026-08-20) ----------------------------
   Dans components.css : le partial est rendu par plusieurs ecrans financiers, qui chargent chacun
   leur propre feuille. Une regle deposee dans l'une d'elles ne styleraient rien dans les autres
   (STANDARDS-UI §1bis) — piege deja rencontre le meme jour avec .c-money.
   La variante --default se DISTINGUE visuellement : « aucun filtre choisi » est une information
   que l'operateur doit remarquer, pas un etat neutre. Sans cela, il lit des totaux en croyant
   avoir filtre. */
.period-banner {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 12px 14px; margin: 0 0 16px;
    border: 1px solid var(--line); border-radius: 14px;
    background: var(--bg-2); color: var(--ink);
}
.period-banner--default {
    background: color-mix(in oklab, var(--brand-accent) 8%, var(--surface));
    border-color: color-mix(in oklab, var(--brand-accent) 30%, var(--line));
}
.period-banner__ico { display: inline-flex; flex: none; margin-top: 1px; color: var(--ink-60); }
.period-banner--default .period-banner__ico { color: var(--red-ink); }
.period-banner__text { margin: 0; font-size: 14px; line-height: 1.5; }
.period-banner__lead { color: var(--ink-70); }
/* La PLAGE en gras : c'est l'information qu'on vient chercher, elle doit se lire sans lire la
   phrase entiere. */
.period-banner__range { font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ============================================================================================
   AXES DES GRAPHIQUES ADMIN (.adm-chart) — enveloppe produite par partials/admin-charts.php.
   Ici et non dans une feuille d'ecran : deux ecrans s'en servent (admin-governance,
   admin-report-finance) et partials/head.php n'en charge qu'UNE seule (STANDARDS-UI §1bis) —
   posee dans l'une, l'autre afficherait des chiffres non positionnes.

   Les graduations sont du TEXTE HTML, pas du <text> SVG : le viewBox des graphiques est etire
   (preserveAspectRatio="none"), donc tout texte pose dedans grossit d'environ 1,5x en largeur.
   ============================================================================================ */
.adm-chart { margin: 0; }

.adm-chart__plot {
    position: relative;
    /* Gouttiere de gauche : la place reservee aux ordonnees. Le SVG occupe le reste, il n'est
       donc jamais recouvert par un chiffre. */
    padding-left: 46px;
    box-sizing: content-box;
}
.adm-chart__svg { height: 100%; }
.adm-chart__svg > svg { display: block; }

.adm-chart__y { position: absolute; inset: 0 auto 0 0; width: 46px; }
.adm-chart__ytick {
    position: absolute;
    right: 8px;
    /* Centre sur la ligne de grille : `top` porte l'ordonnee EXACTE de la valeur. */
    transform: translateY(-50%);
    font-size: 10.5px;
    line-height: 1;
    color: var(--ink-60);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.adm-chart__x { position: relative; height: 18px; margin-left: 46px; }
.adm-chart__xtick {
    position: absolute;
    top: 5px;
    font-size: 10.5px;
    line-height: 1;
    color: var(--ink-60);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.adm-chart__xtick--mid   { transform: translateX(-50%); }
/* Bornes de la periode : ancrees par leur bord, sinon la moitie du texte sort du cadre. */
.adm-chart__xtick--start { transform: none; }
.adm-chart__xtick--end   { transform: translateX(-100%); }

@media (max-width: 640px) {
    /* Gouttiere reduite et graduations plus petites : sur mobile la carte fait ~330px, 46px de
       marge en mangeraient 14%. Les chiffres restent lisibles, l'aire de trace reste utile. */
    .adm-chart__plot { padding-left: 34px; }
    .adm-chart__y    { width: 34px; }
    .adm-chart__x    { margin-left: 34px; }
    .adm-chart__ytick,
    .adm-chart__xtick { font-size: 9.5px; }
    .adm-chart__ytick { right: 5px; }
}
