/* ==========================================================================
   NP7 - Unified Stylesheet

   Architecture
   ------------
   1. Fondations       : polices, tokens, thèmes, reset, typographie
   2. Mise en page     : conteneurs, grilles, navigation
   3. Composants       : cartes, boutons, formulaires, tableaux, dialogues
   4. Parcours métier  : transactions, caisse, QR code, statistiques
   5. Utilitaires      : helpers courts et sans contexte métier
   6. Composants riches: éditeur d'avatar

   Ce fichier est volontairement l'unique feuille globale livrée au navigateur.
   Les interfaces autonomes (landing, studio et rendu de carte) conservent leur
   propre feuille afin de ne pas polluer ce socle.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fondations — Polices auto-hébergées
   Chakra Petch, SIL OFL 1.1 (licence dans /assets/fonts/OFL.txt).
   Sous-ensemble latin uniquement :
   couvre le français (accents, €, œ, tirets, guillemets). Ajouter le
   sous-ensemble latin-ext si un caractère rare manque un jour.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-300.woff2') format('woff2');
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-400-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Chakra Petch';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/chakra-petch-700.woff2') format('woff2');
}

/* --------------------------------------------------------------------------
   Fondations — Tokens du design system
   -------------------------------------------------------------------------- */
/* Thème : le sombre est le défaut (bloc :root ci-dessous, inconditionnel).
   Le thème clair est activé par [data-theme="light"] sur <html>, posé par le
   script anti-flash de fragments/entete.php d'après localStorage ('np7-theme').
   En mode Auto, ce script résout la préférence système avant le rendu et suit
   ensuite les changements de prefers-color-scheme. Le sélecteur vit dans
   /mon_compte.php.
   Tout le reste de la feuille ne référence que des tokens — jamais un littéral —
   pour que les deux thèmes se limitent aux deux blocs de :root. */
:root {
  color-scheme: dark;

  /* ---- Surfaces & texte ---- */
  --bg: #0f172a;
  --bg2: #111827;
  --bg-deep: #0b1220;                 /* fond hors-flux (overscroll, base body) */
  --bg-gradient: radial-gradient(1200px 800px at 10% 10%, #0b1220 0%, #0f172a 40%, #0a0f1c 100%);
  --card: #0b1220ee;
  --card-inner: rgba(255, 255, 255, 0.04);
  --border: #1f2937;
  --border-soft: rgba(255, 255, 255, 0.12);
  --input-bg: #0b1426;
  --text: #e5e7eb;
  --muted: #9ca3af;
  --surface-sunken: rgba(11, 20, 38, 0.5);   /* .tx-detail */
  --row-hover: rgba(255, 255, 255, 0.03);
  --scrim: rgba(11, 18, 32, 0.35);            /* .nav::before */
  --scrim-solid: rgba(15, 23, 42, 0.35);      /* .nav::before sur body.bg-solid */
  --shadow-card: 0 0 calc(var(--space) * .5) rgba(0, 0, 0, 0.35);
  --shadow-avatar: 0 6px 16px rgba(0, 0, 0, 0.3);
  --shadow-footer: 0 -6px 14px rgba(0, 0, 0, 0.30);

  /* Palette de marque NP7 (charte graphique) — INVARIANTE entre les thèmes :
       or    #DDB967 — CTA de la landing, puis éléments de valeur (carte NP7,
                       soutien au club, scannette)
       bleu  #33658A — interface, technologie, informations, confiance
       vert  #317B22 — terrain, événements, communauté, actions de soutien
       rouge #B80C09 — danger, erreur, action destructive
     --brand-* = teintes brutes (fonds, bordures, pastilles).
     --accent/--accent-2/--danger = variantes de TEXTE, ajustées par thème
     (sur sombre : éclaircies ; sur clair : teintes brutes ou plus foncées).
     L'or ne passe jamais en texte sur clair (1.67:1) → --gold-ink. */
  --brand-gold: #DDB967;
  --brand-blue: #33658A;
  --brand-green: #317B22;
  --brand-red: #B80C09;
  --accent: #3b9b27;
  --accent-2: #468bbe;
  --danger: #fa2d29;
  --gold-ink: var(--brand-gold);     /* « or » en texte — sur sombre, l'or direct */
  --link: #93c5fd;                    /* a.inline */

  /* Alias sémantiques consommés par des styles inline dans les templates
     (var(--x, #fallback)) — définis ici pour qu'ils suivent le thème. */
  --success: var(--accent);
  --ok: var(--accent);
  --warn: var(--tint-warn-fg);

  /* ---- Boutons ---- */
  --btn-neutral-bg: #0a0a0c;
  --btn-neutral-fg: #fff;
  --btn-border: rgba(255, 255, 255, 0.14);
  --btn-gold-bg: var(--brand-gold);
  --btn-gold-fg: #1a1408;
  --btn-blue-bg: var(--brand-blue);
  --btn-green-bg: var(--brand-green);
  --btn-red-bg: var(--brand-red);

  /* ---- Flash ---- */
  --flash-ok-bg: rgba(49, 123, 34, 0.14);
  --flash-ok-border: rgba(49, 123, 34, 0.4);
  --flash-err-bg: rgba(184, 12, 9, 0.14);
  --flash-err-border: rgba(184, 12, 9, 0.4);

  /* ---- Cartes teintées ---- */
  --tint-warn-bg: rgba(246, 173, 85, 0.05);
  --tint-warn-border: rgba(246, 173, 85, 0.25);
  --tint-warn-fg: #f6ad55;
  --tint-succ-bg: rgba(104, 211, 145, 0.03);
  --tint-succ-border: rgba(104, 211, 145, 0.2);

  /* ---- Badges (fond / texte) ---- */
  --bdg-live-bg: var(--accent);   --bdg-live-fg: #000;
  --bdg-open-bg: #2d6a4f;         --bdg-open-fg: #d8f3dc;
  --bdg-closed-bg: #4a4a6a;       --bdg-closed-fg: #c8c8e8;
  --bdg-admin-bg: #1e2a3a;        --bdg-admin-fg: #93c5fd;
  --bdg-super-bg: #2a1e3a;        --bdg-super-fg: #c4b5fd;
  --bdg-orga-bg: #1a2e1a;         --bdg-orga-fg: #86efac;
  --bdg-enabled-bg: #0f2e1c;      --bdg-enabled-fg: #4ade80;
  --bdg-danger-bg: #3a1c1c;       --bdg-danger-fg: #f87171;
  --bdg-gold-bg: #332a14;         --bdg-gold-fg: var(--brand-gold);
  /* Variante translucide (pilule pleine mais discrète, ex: statut carte NFC) — pas
     de contrepartie thème clair distincte, comportement inchangé par rapport aux
     styles inline qu'elles remplacent. */
  --success-soft-bg: rgba(72, 187, 120, 0.15);
  --danger-soft-bg: rgba(248, 113, 113, 0.15);

  --avatar-fallback: #4a5568;

  /* Échelle d'espacement commune (multiples de 4px). */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space: var(--space-5);
  --card-padding: 20px;
  --action-grid-gap: 15px;
  /* Largeur minimale commune d'un bouton dans un groupe responsive avant que
     le groupe ne change de ligne. Les boutons compacts et carrés l'ignorent. */
  --button-wrap-min: 120px;
  --footer-control-h: 44px;
  --footer-h: calc(var(--footer-control-h) + (var(--space) * 2) + 1px);
  --nav-top: calc(var(--space) / 2);
  --nav-visual-offset: 0px;
  --nav-blur-bottom-offset: calc(var(--space) / 2);
  --nav-h: 44px;

  /* ---- Géométrie des composants ----
     Les rayons expriment un rôle, pas une taille arbitraire. Une surface
     imbriquée reste volontairement à 12px : son rayon n'est pas calculé à
     partir du padding de la carte, car les deux contours ne sont pas assez
     proches pour devoir être concentriques. */
  --radius-card: 18px;
  --radius-dialog: 18px;
  --radius-inner: 12px;
  --radius-action: 12px;
  --radius-field: 10px;
  --radius-feedback: 8px;
  --radius-detail: 6px;

  /* Toujours > la moitié de la hauteur de n'importe quel élément du site :
     le navigateur plafonne l'arrondi tout seul à la moitié exacte de la
     hauteur, donnant des bouts en demi-cercle parfait quelle que soit la
     largeur — contrairement à 50%, qui suit la forme de la boîte et
     ovalise dès qu'elle n'est plus carrée. */
  --radius-full: 1000px;
}

/* --------------------------------------------------------------------------
   Fondations — Thème clair via [data-theme="light"] sur <html>
   Ne redéfinit QUE des couleurs ; aucune autre propriété. Fond « papier
   chaud » cohérent avec les supports de marque (cartes, planches carrousel).
   Ratios de contraste sur blanc : texte 15:1 · muted 5.0:1 · vert 5.3:1 ·
   bleu 6.3:1 · rouge 6.8:1 · gold-ink 5.3:1 (l'or brut : 1.67:1, jamais texte).
   -------------------------------------------------------------------------- */
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #f5f2ea;
  --bg2: #efeadc;
  --bg-deep: #efeadc;
  --bg-gradient: radial-gradient(1200px 800px at 10% 10%, #fcfbf6 0%, #f5f2ea 45%, #efeadc 100%);
  --card: #ffffff;
  --card-inner: #faf6ec;
  --border: #e4dccb;
  --border-soft: #ece5d6;
  --input-bg: #ffffff;
  --text: #1e2530;
  --muted: #5c6470;
  --surface-sunken: #f3eee2;
  --row-hover: rgba(31, 37, 48, 0.035);
  --scrim: rgba(245, 242, 234, 0.72);
  --scrim-solid: rgba(245, 242, 234, 0.72);
  --shadow-card: 0 0 2px rgba(31, 37, 48, 0.06), 0 0 calc(var(--space) * .5) rgba(31, 37, 48, 0.10);
  --shadow-avatar: 0 4px 12px rgba(31, 37, 48, 0.12);
  --shadow-footer: 0 -4px 14px rgba(31, 37, 48, 0.10);

  --accent: #2e7320;
  --accent-2: #33658a;
  --danger: #b80c09;
  --gold-ink: #8a6d2e;
  --link: #2c5677;

  --btn-neutral-bg: #1e2530;
  --btn-neutral-fg: #f5f2ea;
  --btn-border: rgba(31, 37, 48, 0.12);
  --btn-gold-bg: var(--brand-gold);
  --btn-blue-bg: var(--brand-blue);
  --btn-green-bg: var(--brand-green);
  --btn-red-bg: var(--brand-red);

  --flash-ok-bg: #e7f1e2;
  --flash-ok-border: #b9d8ad;
  --flash-err-bg: #fbe8e7;
  --flash-err-border: #efc2c0;

  --tint-warn-bg: #fbf0e0;
  --tint-warn-border: #ead3ae;
  --tint-warn-fg: #b26a15;
  --tint-succ-bg: #eaf3e6;
  --tint-succ-border: #c3deb5;

  --bdg-live-bg: #2e7320;      --bdg-live-fg: #ffffff;
  --bdg-open-bg: #e2f0e5;      --bdg-open-fg: #1e5138;
  --bdg-closed-bg: #e7e7f0;    --bdg-closed-fg: #3c3c5a;
  --bdg-admin-bg: #e3ecf5;     --bdg-admin-fg: #2c5677;
  --bdg-super-bg: #ece7f7;     --bdg-super-fg: #5b3fa6;
  --bdg-orga-bg: #e4f1e0;      --bdg-orga-fg: #245018;
  --bdg-enabled-bg: #e2f0e5;   --bdg-enabled-fg: #1e5138;
  --bdg-danger-bg: #fbe3e2;    --bdg-danger-fg: #9a1b18;
  --bdg-gold-bg: #f3ead3;      --bdg-gold-fg: #7a5c1f;

  --avatar-fallback: #c9bfa8;
}

/* QR : le canvas se dessine en blanc → cerné d'un filet sur carte blanche. */
:root[data-theme="light"] .qrbox canvas {
  box-shadow: 0 0 0 1px var(--border);
  border-radius: var(--radius-detail);
}

/* --------------------------------------------------------------------------
   Fondations — Reset et base
   -------------------------------------------------------------------------- */
* {
  box-sizing: border-box;
}

/* Correctif général : tout élément qui fixe sa propre valeur `display` (.btn, .avatar-dropzone,
   etc.) a une spécificité égale à l'attribut [hidden] natif du navigateur — sans !important,
   c'est l'ordre du fichier qui départage, pas l'intention. Plutôt que rajouter une règle ciblée
   à chaque nouvel endroit où un script cache un élément (element.hidden = true), ce correctif
   unique force [hidden] à toujours gagner, site entier. Les règles ciblées existantes
   (.btn[hidden], .push-settings [hidden], .avatar-editor__actions [hidden], etc.) deviennent
   redondantes avec celle-ci mais restent sans effet de bord à conserver. */
[hidden] {
  display: none !important;
}

html, body {
  height: 100%;
  overflow-x: hidden;
}

/* Barre de scroll masquée uniquement en webapp installée (standalone) : en
   navigateur classique, on la laisse visible pour ne pas casser l'affordance
   de scroll attendue sur desktop.
   Sur iOS, l'indicateur de défilement de la page (le root scroller, `html`)
   est un élément natif que ::-webkit-scrollbar ne peut PAS masquer — ça ne
   marche que sur un conteneur d'overflow imbriqué. On fait donc défiler
   `body` au lieu de `html` (html devient overflow:hidden, body porte le
   scroll) pour que WebKit le traite comme un conteneur imbriqué stylable. */
@media (display-mode: standalone) {
  html {
    height: 100%;
    overflow: hidden;
  }

  body {
    height: 100%;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  body::-webkit-scrollbar {
    display: none;
  }
}

html {
  background: var(--bg-deep);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  padding-top: calc(var(--nav-top) + var(--nav-h) + var(--space));
  font-family: 'Chakra Petch', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  color: var(--text);
  background-color: var(--bg-deep);
  background: var(--bg-gradient);
}

/* Simple solid background */
body.bg-solid {
  background: var(--bg);
}

/* Footer spacing */
body.has-footer {
  padding-bottom: 0;
}

/* --------------------------------------------------------------------------
   Fondations — Typographie
   -------------------------------------------------------------------------- */
.title {
  font-size: clamp(22px, 2.4vw, 28px);
  margin: 0 0 10px;
}

h1 {
  font-size: clamp(22px, 2.4vw, 28px);
  margin: 0 0 6px;
}

h2 {
  font-size: 18px;
  margin: 0 0 12px;
}

.muted {
  color: var(--muted);
}

.section-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  margin: 0 0 8px;
}

/* Sous-titre ouvrant un nouveau groupe à l'intérieur d'une carte. */
.sr-only,
.avatar-file-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   Mise en page — Conteneurs et grilles
   -------------------------------------------------------------------------- */
.wrap {
  max-width: 860px;
  margin: 0 auto 6rem;
  padding: 0 var(--space);
}

.wrap.wide {
  max-width: 980px;
}

.wrap.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space);
}

.wrap.footer-clearance {
  margin-bottom: calc(var(--footer-h) + var(--space));
}

/* Two-column grid for index */
.grid {
  display: grid;
  gap: var(--space);
  grid-template-columns: 1fr;
}

.row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.row > * {
  flex: 1;
  min-width: var(--button-wrap-min);
}

/* --------------------------------------------------------------------------
   Composant — Cartes
   -------------------------------------------------------------------------- */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--card-padding);
  box-shadow: var(--shadow-card);
  isolation: isolate;
  max-width: 100%;
  position: relative;
}

/* Encart sponsor de l'espace actu (accueil/_bloc_actu.php) : toute la carte est cliquable
   (vers le site du sponsor confirmé, ou vers devenir_sponsor.php en absence de sponsor). */
.actu-sponsor-card {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  color: inherit;
  transition: filter .15s ease, transform .15s ease;
}

.actu-sponsor-card:hover { filter: brightness(1.08); }
.actu-sponsor-card:active { transform: translateY(1px); }

.actu-sponsor-card__logo {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  object-fit: contain;
  background: #fff;
  padding: 6px;
  flex: none;
}

.actu-sponsor-card__text {
  min-width: 0;
}

.actu-sponsor-card--cta .actu-sponsor-card__text {
  color: var(--muted);
}

.card-badge {
  position: absolute;
  top: var(--card-padding);
  right: var(--card-padding);
}

/* Spacing between cards:
   - use `gap` for .wrap.stack and .grid
   - keep margin spacing for classic flow layouts */
.wrap:not(.stack) > .card + .card {
  margin-top: var(--space);
}
.wrap:not(.stack) > .card + .grid {
  margin-top: var(--space);
}

.wrap.stack .card + .card,
.grid .card + .card {
  margin-top: 0;
}

/* Variantes de carte — gardées avec le composant de base pour que son contrat
   complet reste lisible au même endroit. */
.card-inner {
  background: var(--card-inner);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-inner);
  padding: 16px 20px;
  margin-bottom: 0.8rem;
  box-shadow: none;
}

.card-inner:last-child {
  margin-bottom: 0;
}

.card.compact {
  padding: 14px 18px;
}

.card.card-group-title {
  padding: 12px var(--card-padding);
}

.card.card-group-title .section-title {
  font-size: 13px;
  margin: 0;
}

.card.warning-tint {
  border-color: var(--tint-warn-border);
  background: var(--tint-warn-bg);
}

.card.warning-tint .section-title {
  color: var(--tint-warn-fg);
}

.card.success-tint {
  border-color: var(--tint-succ-border);
  background: var(--tint-succ-bg);
}

/* Espaces de l'accueil : hiérarchie h2/h3 et actions régulières.
   Le padding est porté par l'en-tête et le corps pour rester aligné. */
.space-card {
  padding: 0;
  overflow: clip;
}

.space-card__header {
  min-height: 52px;
  padding: var(--card-padding) var(--card-padding) var(--space-2);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.space-card--with-toolbar .space-card__header {
  padding-right: calc(var(--card-padding) + 140px);
}

.space-card__title {
  margin: 0;
  color: var(--text);
  font-size: 17px;
  line-height: 1.25;
}

.space-card__body {
  padding: 0 var(--card-padding) var(--card-padding);
}

.space-card--without-title .space-card__body {
  padding-top: var(--card-padding);
}

.space-card__toolbar {
  position: absolute;
  top: var(--card-padding);
  right: var(--card-padding);
  z-index: 1;
  display: flex;
  justify-content: flex-end;
  max-width: 132px;
}

.space-card__toolbar .btn {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.home-page.pull-refresh-enabled {
  /* "none" et pas "contain" : on tue tout rebond natif (haut ET bas) pour que
     le geste de tirer soit exclusivement piloté par ce script, sans lutter
     contre le rubber-band du navigateur. */
  overscroll-behavior-y: none;
}

.home-page.pull-refresh-enabled > .wrap {
  position: relative;
  z-index: 2;
  transform: translateY(var(--pull-offset, 0));
  /* Durée allongée + courbe plus douce (ease-out) : le tirer peut désormais aller assez loin
     (voir pull_to_refresh.js) pour que l'ancienne transition courte et « en ressort » fasse un
     retour au repos trop brusque au relâchement. */
  transition: transform .4s cubic-bezier(.16, 1, .3, 1);
  will-change: transform;
}

.home-page.is-pulling > .wrap {
  transition: none;
}

/* Pull-to-refresh de l'accueil installé sur l'écran d'accueil.
   Le haut du symbole reste le vrai logo de la nav (.nav-logo) tant qu'on
   tire : cette moitié du bas suit le doigt à la même vitesse que le contenu
   (--pull-offset, voir pull_to_refresh.js) et se bloque dès qu'elle a
   rejoint le vrai logo, même si on continue de tirer plus loin.

   Géométrie (mêmes proportions que le calage "carré exact" validé sur la
   marque carrée) : logo-np7-*-transparent-rectangle-ajuste.svg (le fichier
   utilisé par .nav-logo) est recadré à zéro marge sur son propre contenu
   (viewBox 1800x800) — son centre géométrique EST son centre de contenu, ce
   qui permet de faire pivoter une copie 180° exactement à sa place. La
   moitié du bas vient se placer nav-h * 1.25 sous le haut (1.0 = bord à
   bord, + 0,25 = l'écart proportionnel validé). --pull-mark-deficit
   (poussé par le JS, en px) est la distance qu'il reste à parcourir avant
   ce point : plein au repos (bas caché, superposé au haut), 0 une fois le
   carré formé.

   Au relâchement, on ne fait JAMAIS tourner .nav-logo lui-même (élément
   partagé par tout le site) : il s'efface au profit d'une copie identique
   du haut à l'intérieur de ce bloc, pour que les deux moitiés tournent
   ensemble autour de LEUR centre commun — à nav-h * 1.125 sous le centre du
   vrai logo (mi-hauteur de l'ensemble haut + écart + bas). */
.pull-refresh-indicator {
  position: fixed;
  top: calc(var(--nav-top) + var(--nav-visual-offset) + var(--nav-h) * 1.125);
  left: 50%;
  /* Sous la nav (.nav, z-index:100) pendant le tirer : la pastille est cachée par le bandeau
     flouté et ne devient visible qu'une fois sortie de dessous, comme un tiroir. Repasse
     au-dessus dès qu'elle est bloquée (.pull-refresh-ready, cf. plus bas) et pendant le
     chargement (.is-refreshing) : sinon, une fois immobile, son ombre portée (drop-shadow)
     reste visible à travers le bandeau flouté plutôt que d'être masquée avec elle. */
  z-index: 99;
  height: calc(var(--nav-h) * 2.25);
  width: calc(var(--nav-h) * 2.25);
  pointer-events: none;
  transform: translate(-50%, -50%);
}

.pull-refresh-indicator__top,
.pull-refresh-indicator__bottom {
  position: absolute;
  left: 0;
  width: 100%;
  height: calc(100% / 2.25);
}

.pull-refresh-indicator__layer {
  display: block;
  width: 100%;
  height: 100%;
}

.pull-refresh-mark-light {
  display: none;
}

:root[data-theme="light"] .pull-refresh-mark-dark {
  display: none;
}

:root[data-theme="light"] .pull-refresh-mark-light {
  display: block;
}

/* Copie du haut : invisible tant qu'on tire (le vrai .nav-logo suffit),
   ne devient visible que pour remplacer .nav-logo pendant la rotation. */
.pull-refresh-indicator__top {
  top: 0;
  opacity: 0;
  transition: opacity .15s ease;
}

.pull-refresh-indicator.is-refreshing .pull-refresh-indicator__top {
  opacity: 1;
}

.pull-refresh-indicator__bottom {
  top: calc(var(--nav-h) * 1.25);
  opacity: 0;
  transition: opacity .15s ease;
  transform: translateY(calc(-1 * var(--pull-mark-deficit, calc(var(--nav-h) * 1.25)))) rotate(180deg);
}

.pull-refresh-indicator.is-visible .pull-refresh-indicator__bottom {
  opacity: 1;
}

/* Les deux moitiés s'allument ensemble dès que le seuil de relâchement est
   atteint (même avant de relâcher), et restent allumées pendant le
   chargement. Halo neutre inversé au thème (pas --accent, vert) : blanc en
   thème sombre, noir en thème clair. */
body.pull-refresh-ready .nav-logo,
body.pull-refresh-ready .pull-refresh-indicator {
  filter: drop-shadow(0 0 5px #fff);
}

/* Repasse au-dessus de la nav dès le blocage (pas seulement pendant .is-refreshing plus bas) :
   sinon l'ombre portée ci-dessus reste visible à travers le bandeau flouté. */
body.pull-refresh-ready .pull-refresh-indicator {
  z-index: 101;
}

:root[data-theme="light"] body.pull-refresh-ready .nav-logo,
:root[data-theme="light"] body.pull-refresh-ready .pull-refresh-indicator {
  filter: drop-shadow(0 0 5px #000);
}

/* Le vrai logo de la nav s'efface le temps que la copie ci-dessus prenne le
   relais pour tourner (voir .pull-refresh-indicator__top plus haut). */
body.pull-refresh-refreshing .nav-logo {
  transition: opacity .15s ease;
  opacity: 0;
}

.pull-refresh-indicator.is-refreshing {
  /* Au-dessus de la nav pendant le chargement (contrairement au repos, voir plus haut) : la
     pastille qui tourne doit rester bien visible, pas cachée derrière le bandeau flouté. */
  z-index: 101;
  /* Demi-tour unique (pas de rotation continue), puis reste statique — plus de sortie animée —
     jusqu'au rechargement de la page. */
  animation: pull-refresh-flip .5s cubic-bezier(.4, 0, .2, 1) forwards;
}

@keyframes pull-refresh-flip {
  to { transform: translate(-50%, -50%) rotate(180deg); }
}

.space-card__section {
  margin-top: var(--space-4);
}

.space-card__body > .space-card__section:first-of-type {
  margin-top: 0;
}

.space-card__section-title {
  margin: 0 0 var(--space-2);
  color: var(--muted);
  font-size: 11px;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.6px;
}

.space-card__actions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--button-wrap-min), 100%), 1fr));
  gap: var(--action-grid-gap);
}

.space-card__actions .btn {
  width: 100%;
  height: 50px;
  padding: 10px 15px;
  justify-content: center;
  font-size: clamp(13px, 3.8vw, 16px);
  line-height: 1.2;
  text-align: center;
  white-space: normal;
}

.space-card__actions .space-card__action--double {
  grid-column: span 2;
}

/* Cards d'opérations repliables : même chevron › que les lignes de tableaux,
   avec une transition fiable ici car il vit hors du layout d'une table. */
.collapsible-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 32px;
}

.collapsible-card.is-collapsed,
.collapsible-card:not(.is-collapsed) .collapsible-card__header {
  cursor: pointer;
}

.collapsible-card__header .section-title {
  margin: 0;
}

.card-collapse-toggle {
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  margin: -8px -8px -8px 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  /* Le chevron garde une couleur fixe : les états hover/touch ne le font plus alterner entre
     gris et blanc, particulièrement visible en thème sombre. */
  color: var(--text);
  cursor: pointer;
  touch-action: manipulation;
}

.card-collapse-toggle span {
  display: inline-block;
  font-size: 19px;
  font-weight: 900;
  line-height: 1;
  transform: rotate(90deg);
  transition: transform .2s ease;
}

.collapsible-card.is-collapsed .card-collapse-toggle span {
  transform: rotate(0deg);
}

.collapsible-card__content {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows .22s ease, opacity .16s ease, margin-top .22s ease;
  opacity: 1;
  margin-top: var(--space-2);
}

.collapsible-card__content-inner {
  min-height: 0;
  overflow: hidden;
}

.collapsible-card.is-collapsed .collapsible-card__content {
  grid-template-rows: 0fr;
  opacity: 0;
  margin-top: 0;
}

/* Intertitres de Mon compte : séparent les réglages sans ajouter une nouvelle
   carte ni concurrencer les titres des panneaux repliables. */
.account-settings-group {
  margin: var(--space-2) 0 calc(var(--space-2) * -1);
  padding-left: 2px;
  color: var(--accent);
}

/* Les liens depuis la photo de l'accueil et le sélecteur d'espace ciblent ce
   panneau. Garder son en-tête et ses boutons sous la navigation fixe. */
#photo-settings {
  scroll-margin-top: calc(var(--nav-top) + var(--nav-visual-offset) + var(--nav-h) + var(--space) + var(--space));
}

/* Ex-.account-choice-grid/.account-theme-grid (grille à colonnes fixes, incohérente avec le
   nombre réel de boutons — voir historique). Le thème et les actions de notifications sont, comme
   le sélecteur d'espace de la photo de profil (.avatar-space-picker), des groupes de boutons pairs
   qui doivent grandir pour remplir la ligne puis passer naturellement à la ligne suivante plutôt
   que forcer un nombre de colonnes fixe — un seul pattern responsive pour tous ces groupes. */

/* Dernier élément de sa carte (Thème) : la marge basse de .avatar-space-picker s'ajoute au
   padding déjà présent en bas de la carte, laissant un vide disproportionné sous les boutons. */
#theme-picker { margin-bottom: 0; }

/* 3 options fixes (Auto/Clair/Sombre) : quand elles ne tiennent pas sur une ligne, "Auto" doit se
   retrouver seul sur la ligne du haut et Clair+Sombre ensemble sur celle du dessous — l'inverse de
   l'empilement naturel de flex-wrap (les 2 premiers, Auto+Clair, resteraient ensemble et Sombre
   seul en dessous). flex-basis:100% force Auto à occuper toute la ligne, donc les 2 suivants
   passent ensemble à la ligne suivante. Seuil dérivé de la mise en page réelle (3 × 120px
   --button-wrap-min + 2 × 8px gap = 376px de contenu nécessaire ; carte de mon_compte.php dans
   .wrap (max 860px, padding 20px) + .card (padding 20px) → le contenu atteint 376px à partir
   d'un viewport de 456px) : à revérifier si --button-wrap-min, --space-2, --space ou
   --card-padding changent. */
@media (max-width: 455px) {
  #theme-picker [data-theme-choice="auto"] {
    flex-basis: 100%;
  }
}

/* Catalogue : le formulaire conserve deux moitiés régulières et les actions
   changent de disposition dès que leur largeur intrinsèque ne tient plus.
   catalog_price.js mesure les vrais libellés avec la police et le padding actifs. */
.catalog-item-layout {
  width: 100%;
}

.catalog-editor-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: end;
  gap: var(--action-grid-gap);
  margin-top: 12px;
}

.catalog-editor-price,
.catalog-editor-actions {
  min-width: 0;
}

.catalog-price-field {
  position: relative;
}

.catalog-price-field input {
  width: 100%;
  box-sizing: border-box;
  padding-right: 34px;
}

.catalog-price-suffix {
  position: absolute;
  top: 50%;
  right: 13px;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
}

.catalog-price-suffix[hidden] {
  display: none;
}

.catalog-editor-actions {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--action-grid-gap);
}

.catalog-editor-actions .btn {
  width: 100%;
  justify-content: center;
  white-space: nowrap;
}

.catalog-editor-cancel {
  grid-column: 1 / -1;
  width: 100%;
  justify-content: center;
}

.catalog-item-controls {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: stretch;
  gap: 8px;
  width: 100%;
}

.tx-detail.catalog-item-detail > td {
  padding-left: 0;
  padding-right: 0;
}

.catalog-order-controls {
  display: grid;
  grid-auto-flow: column;
  gap: 8px;
  grid-column: 1;
}

.catalog-order-controls form,
.catalog-item-actions form {
  margin: 0;
}

.catalog-order-button {
  width: 44px;
  height: 44px;
  min-height: 44px;
  padding: 0;
  justify-content: center;
}

.catalog-item-actions {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  grid-column: 2;
  min-width: 0;
}

.catalog-item-actions .btn {
  width: 100%;
  height: 100%;
  justify-content: center;
  white-space: nowrap;
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-item-controls {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  grid-template-rows: repeat(2, minmax(44px, auto));
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-order-controls {
  grid-column: 1;
  grid-row: 1 / 3;
  grid-auto-flow: row;
  grid-template-rows: repeat(2, 44px);
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-item-actions {
  grid-column: 2;
  grid-row: 1 / 3;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(44px, auto));
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-action-edit {
  grid-column: 1;
  grid-row: 1;
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-action-toggle {
  grid-column: 2;
  grid-row: 1;
}

.catalog-item-layout[data-catalog-layout="2"] .catalog-action-delete {
  grid-column: 1 / -1;
  grid-row: 2;
}

.catalog-editor-container[data-catalog-editor-layout="stacked"] .catalog-editor-row {
  grid-template-columns: minmax(0, 1fr);
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-item-controls {
  grid-template-columns: 44px minmax(0, 1fr);
  grid-template-rows: repeat(3, minmax(44px, auto));
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-order-controls {
  grid-column: 1;
  grid-row: 1 / 3;
  grid-auto-flow: row;
  grid-template-rows: repeat(2, 44px);
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-item-actions {
  display: contents;
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-action-edit {
  grid-column: 2;
  grid-row: 1;
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-action-toggle {
  grid-column: 2;
  grid-row: 2;
}

.catalog-item-layout[data-catalog-layout="3"] .catalog-action-delete {
  grid-column: 1 / -1;
  grid-row: 3;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-item-controls {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: 44px repeat(3, minmax(44px, auto));
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-order-controls {
  grid-column: 1 / -1;
  grid-row: 1;
  grid-auto-flow: column;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: 44px;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-order-controls form {
  display: flex;
  justify-content: center;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-item-actions {
  display: contents;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-action-edit {
  grid-column: 1 / -1;
  grid-row: 2;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-action-toggle {
  grid-column: 1 / -1;
  grid-row: 3;
}

.catalog-item-layout[data-catalog-layout="4"] .catalog-action-delete {
  grid-column: 1 / -1;
  grid-row: 4;
}

.push-settings__status {
  margin: 0;
}

/* .actions définit un display explicite : il doit laisser hidden masquer les
   contrôles tant que l'app n'est pas installée ou que les notifications sont
   indisponibles. */
.push-settings [hidden] {
  display: none !important;
}

.push-settings__status--ok {
  color: var(--success, #4ade80);
}

.push-settings__status--error {
  color: var(--danger, #f87171);
}

.push-template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
  gap: var(--space);
}

.push-template {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.push-template__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.push-template__header h2 {
  margin: 0;
  font-size: 18px;
}

.push-preview {
  padding: 12px 14px;
  border: 1px solid color-mix(in srgb, var(--accent-2) 40%, var(--border));
  border-radius: var(--radius-field);
  background: color-mix(in srgb, var(--surface-sunken) 88%, #000);
  box-shadow: inset 0 1px rgba(255,255,255,.04);
  font-size: 14px;
  line-height: 1.35;
}

.push-preview__app {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 8px;
  color: var(--muted);
  font-size: 11px;
}

.push-preview__icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--text);
  color: var(--surface);
  font-size: 8px;
  font-weight: 800;
}

.push-preview strong,
.push-preview [data-preview-body] { display: block; }
.push-preview [data-preview-body] { margin-top: 3px; color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .card-collapse-toggle span,
  .collapsible-card__content {
    transition: none;
  }
}

/* Accueil : les espaces sont accessibles au swipe. Le snap maintient chaque
   panneau en place après le geste, comme une page native. */
.home-space-swipe {
  display: flow-root;
  min-width: 0;
  margin-right: calc(var(--space) * -1);
  margin-left: calc(var(--space) * -1);
  overflow: visible;
}

.home-space-swipe__track {
  display: flex;
  align-items: flex-start;
  gap: 0;
  width: 100%;
  /* Réserve la portée de l'ombre sans déplacer les cartes. */
  margin-top: calc(var(--space) * -.5);
  padding-top: calc(var(--space) * .5);
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  scroll-behavior: auto;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  touch-action: pan-x pan-y;
  -webkit-overflow-scrolling: touch;
}

.home-space-swipe.is-swipe-disabled .home-space-swipe__track {
  overflow-x: hidden;
  touch-action: pan-y;
}

.home-space-swipe__track::-webkit-scrollbar {
  display: none;
}

.home-space-swipe__panel {
  /* Les deux paddings conservent la carte active alignée à --space du bord et
     créent exactement 2 × --space entre deux cartes successives. */
  flex: 0 0 100%;
  min-width: 0;
  padding-right: var(--space);
  padding-left: var(--space);
  display: flex;
  flex-direction: column;
  gap: var(--space);
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

@media (prefers-reduced-motion: reduce) {
  .home-space-swipe__track {
    scroll-behavior: auto;
    transition: none;
  }
}

/* Même grille et mêmes dimensions que les quatre actions Participant. Quand
   elle est en 2 × 2, on ne change que leur position visuelle afin que l'ordre
   sur une ligne reste Cash, CB, NFC, Remboursement. */
@media (min-width: 375px) and (max-width: 684px) {
  .space-card__actions--caisse > :nth-child(1) { grid-column: 1; grid-row: 1; }
  .space-card__actions--caisse > :nth-child(2) { grid-column: 1; grid-row: 2; }
  .space-card__actions--caisse > :nth-child(3) { grid-column: 2; grid-row: 1; }
  .space-card__actions--caisse > :nth-child(4) { grid-column: 2; grid-row: 2; }
}

/* --------------------------------------------------------------------------
   Composant — Navigation
   -------------------------------------------------------------------------- */
.nav {
  position: fixed;
  top: calc(var(--nav-top) + var(--nav-visual-offset));
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: var(--nav-h);
  padding: 0;
  padding-left: var(--space);
  padding-right: var(--space);
  z-index: 100;
  isolation: isolate;
  background: transparent;
  border: none;
  border-radius: 0;
}

.nav::before {
  content: "";
  position: absolute;
  top: calc(-1 * (var(--nav-top) + var(--nav-visual-offset)));
  right: 0;
  bottom: calc(-1 * var(--nav-blur-bottom-offset));
  left: 0;
  background: var(--scrim);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 0;
}

/* Sur un fond uni, le verre reprend exactement sa teinte de base. Le scrim
   historique fondé sur --bg-deep reste réservé aux pages en dégradé. */
body.bg-solid .nav::before {
  background: var(--scrim-solid);
}

.nav-left,
.nav-right {
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
  z-index: 1;
}

/* La hauteur réelle des actions définit toute la géométrie verticale :
   première carte = --nav-top + bouton + --space. */
.nav-left .btn,
.nav-right .btn {
  min-height: var(--nav-h);
}

/* Le lien porte lui-même le positionnement fixed (ex-porté par .nav-logo) : les deux
   <img> ne font plus que s'empiler dedans (grid-area commun), pour garder une vraie
   boîte cliquable/focusable — display:contents donnait un rect vide, invisible aux
   lecteurs d'écran et à toute automatisation ciblant l'élément par son rect. */
.nav-logo-link {
  display: grid;
  position: fixed;
  top: calc(var(--nav-top) + var(--nav-visual-offset) + var(--nav-h) / 2);
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 101;
  cursor: pointer;
}

.nav-logo {
  height: var(--nav-h);
  width: auto;
  display: block;
  grid-area: 1 / 1;
}

.nav-logo-link:focus-visible {
  outline: 2px solid var(--accent, #2563eb);
  outline-offset: 4px;
  border-radius: 4px;
}

.nav-logo-light {
  display: none;
}

:root[data-theme="light"] .nav-logo-dark {
  display: none;
}

:root[data-theme="light"] .nav-logo-light {
  display: block;
}

.nav > * {
  position: relative;
  z-index: 1;
}

/* --------------------------------------------------------------------------
   Composant — Boutons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  font-weight: 700;
  border-radius: var(--radius-action);
  padding: 0.8rem 1rem;
  border: 1px solid var(--btn-border);
  cursor: pointer;
  touch-action: manipulation;
  font-size: 14px;
  transition: transform 0.16s ease, filter 0.16s ease, box-shadow 0.16s ease;
  background: var(--btn-neutral-bg);
  color: var(--btn-neutral-fg);
  min-width: 0;
}

/* display:inline-flex ci-dessus a la même spécificité que le [hidden] natif du navigateur et
   vient après lui en ordre de cascade : sans cette règle, un .btn masqué par script
   (element.hidden = true) reste visible. Générale une bonne fois pour toutes plutôt qu'un
   correctif au cas par cas à chaque nouvel endroit qui cache un bouton (voir aussi les règles
   plus ciblées .push-settings/.avatar-editor__actions/[data-dirty-save-btn], laissées telles
   quelles : redondantes avec celle-ci mais sans effet de bord à les garder). */
.btn[hidden] {
  display: none !important;
}

.btn:not(:disabled):not([disabled]):hover {
  transform: translateY(-1px);
  filter: brightness(1.04);
}

.btn:focus-visible,
.card-collapse-toggle:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 5px var(--brand-blue);
}

.btn:not(:disabled):not([disabled]):active {
  transform: translateY(1px);
  filter: brightness(0.96);
}

.card-collapse-toggle:hover,
.card-collapse-toggle:active {
  background: transparent;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .btn,
  .card-collapse-toggle {
    transition: none;
  }

  .btn:not(:disabled):not([disabled]):hover,
  .btn:not(:disabled):not([disabled]):active,
  .card-collapse-toggle:active {
    transform: none;
  }
}

.btn.ghost {
  background: transparent;
  border-color: var(--border);
  color: var(--text);
  font-weight: 600;
}

.btn.gold {
  background: var(--btn-gold-bg);
  color: var(--btn-gold-fg);
}

.btn.blue {
  background: var(--btn-blue-bg);
  color: #fff;
}

.btn.green {
  background: var(--btn-green-bg);
  color: #fff;
}

.btn.red {
  background: var(--btn-red-bg);
  color: #fff;
}

/* Une couleur combinée à .ghost conserve un fond transparent : la couleur
   exprime le sens de l'action sans lui donner le poids d'un bouton plein. */
.btn.ghost.red {
  background: transparent;
  border-color: var(--brand-red);
  color: var(--danger);
}

.btn.ghost.green {
  background: transparent;
  border-color: var(--brand-green);
  color: var(--success);
}

.btn.small {
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-field);
  font-weight: 600;
}

/* Sur écran tactile, les actions secondaires restent aussi faciles à viser que
   les gros boutons métier. La règle ne grossit pas l'interface à la souris. */
@media (pointer: coarse), (max-width: 684px) {
  .btn,
  .btn.small,
  .card-collapse-toggle {
    min-height: 44px;
  }
  .card-collapse-toggle {
    width: 44px;
    height: 44px;
  }
}

.btn:disabled,
.btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   Composant — Pilules
   -------------------------------------------------------------------------- */
.pill {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
  font-size: 12px;
  padding: 0.35rem 0.6rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--input-bg);
}

.pill.small {
  font-size: 10px;
  vertical-align: middle;
}

.pill.ok  { background: var(--success-soft-bg); color: var(--success); }
.pill.err { background: var(--danger-soft-bg);  color: var(--bdg-danger-fg); }

/* --------------------------------------------------------------------------
   Composant — Messages flash
   -------------------------------------------------------------------------- */
.flash {
  padding: 12px;
  border-radius: var(--radius-feedback);
  margin: 0;
}


.flash.ok {
  background: var(--flash-ok-bg);
  border: 1px solid var(--flash-ok-border);
  cursor: pointer;
}

.flash.err {
  background: var(--flash-err-bg);
  border: 1px solid var(--flash-err-border);
}

/* --------------------------------------------------------------------------
   Composant — Formulaires
   -------------------------------------------------------------------------- */
label {
  display: block;
  font-weight: 600;
  margin: 0.5rem 0 0.25rem;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
}

button,
input,
select,
textarea {
  font: inherit;
}

input,
select,
textarea {
  width: 100%;
  padding: 0.7rem 0.8rem;
  border-radius: var(--radius-field);
  border: 1px solid var(--border);
  background: var(--input-bg);
  color: var(--text);
  background-clip: padding-box;
  -webkit-appearance: none;
  appearance: none;
  outline: none;
  font-size: 16px; /* iOS: prevents auto-zoom on focus when < 16px */
  transform: translateZ(0);
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--accent-2);
}

/* Les contrôles de choix ne doivent pas hériter du champ texte pleine largeur.
   Sans cette remise à zéro, appearance:none masquait entièrement leur état coché. */
input[type="checkbox"],
input[type="radio"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  accent-color: var(--accent-2);
  -webkit-appearance: auto;
  appearance: auto;
  transform: none;
}

/* Remove number input spinners */
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* --------------------------------------------------------------------------
   Composant — Tableaux
   -------------------------------------------------------------------------- */
.table-wrap {
  overflow-x: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

th,
td {
  text-align: left;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border);
}

th {
  font-size: 12px;
  text-transform: uppercase;
  color: var(--muted);
}

tbody tr:last-child td {
  border-bottom: none;
}

.amount-pos {
  color: var(--accent);
  font-weight: 700;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.amount-neg {
  color: var(--danger);
  font-weight: 700;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.price {
  font-weight: 700;
}

/* --------------------------------------------------------------------------
   Composant — Carte d'en-tête
   -------------------------------------------------------------------------- */
.header-card {
  /* Doit toujours être ≥ la hauteur réelle de .header-card__identity (nom + login + asso,
     voir leurs line-height fixes plus bas) — sinon la grille agrandit la ligne au-delà des
     ronds pour loger le texte, qui se retrouve alors avec plus de padding que --card-padding
     au-dessus/en-dessous. Plancher relevé de 64px à 90px (mesuré : 3 lignes ≈ 87px sur mobile,
     marge de ~3px) pour couvrir ce cas, y compris sur petit écran où 18vw descendait sous 64px. */
  --header-orb-size: clamp(90px, 18vw, 112px);
  display: grid;
  grid-template-columns: var(--header-orb-size) minmax(0, 1fr) var(--header-orb-size);
  gap: clamp(8px, 3vw, 16px);
  align-items: center;
  padding-right: var(--space);
  padding-left: var(--space);
}

.header-card .avatar {
  width: var(--header-orb-size);
  height: var(--header-orb-size);
  border-radius: 50%;
  object-fit: cover;
}

.profile-coin {
  display: block;
  justify-self: start;
  width: var(--header-orb-size);
  height: var(--header-orb-size);
  border-radius: 50%;
  perspective: 700px;
  text-decoration: none;
  transition: transform .16s ease, filter .16s ease, box-shadow .16s ease;
}

.profile-coin:hover {
  filter: brightness(.97);
  transform: translateY(-1px);
}

.profile-coin:active {
  transform: translateY(1px);
}

.profile-coin:focus-visible {
  outline: 0;
  border-radius: 50%;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 5px var(--brand-blue);
}

.profile-coin__inner {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  transform: rotateY(var(--profile-coin-rotation, 0deg));
  transform-style: preserve-3d;
  -webkit-transform-style: preserve-3d;
  will-change: transform;
}

.profile-coin__face {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 50%;
  box-shadow: var(--shadow-avatar);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.profile-coin__front {
  transform: rotateY(0deg) translateZ(1px);
}

.header-card .profile-coin .avatar {
  width: 100%;
  height: 100%;
  border: 0;
  box-shadow: none;
}

/* Le logo sponsor (rectangulaire, souvent sur fond transparent) ne doit pas être recadré en
   cercle plein cadre comme une photo de visage : on l'inscrit en entier dans le médaillon. */
.header-card .profile-coin .avatar[data-profile-coin-photo="sponsor"] {
  object-fit: contain;
  background: #fff;
  padding: 8px;
  box-sizing: border-box;
}

.profile-coin__photo {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
}

.profile-coin__photo.is-active {
  opacity: 1;
  visibility: visible;
}

/* Repli espace actu sans sponsor : le SVG rond porte déjà son propre fond (inverse du thème),
   on l'inscrit en entier plutôt que de le recadrer façon photo de visage. */
.profile-coin__photo--np7-dark,
.profile-coin__photo--np7-light {
  object-fit: contain;
}

.profile-coin__photo--np7-light { display: none; }
:root[data-theme="light"] .profile-coin__photo--np7-dark { display: none; }
:root[data-theme="light"] .profile-coin__photo--np7-light { display: block; }

.profile-coin__back {
  background: #0b1220;
  transform: rotateY(180deg) translateZ(1px);
}

.profile-coin__back img {
  width: 86%;
  height: 86%;
  object-fit: contain;
}

/* Plein cadre comme les images du côté avant (.header-card .profile-coin .avatar), pas
   l'inset à 86% du logo NP7 d'origine : sinon un anneau de fond visible cerne le badge. */
.profile-coin__back img.profile-coin__back-img--dark,
.profile-coin__back img.profile-coin__back-img--light {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.profile-coin__back-img--light { display: none; }
:root[data-theme="light"] .profile-coin__back-img--dark { display: none; }
:root[data-theme="light"] .profile-coin__back-img--light { display: block; }

.header-card__identity {
  min-width: 0;
  text-align: center;
}

.header-card__display-name,
.header-card__login,
.header-card__association-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* line-height fixe (indépendante de la vw pour login/asso, dont le font-size n'est lui-même
   pas responsive) plutôt que "normal" : ces 3 lignes portent data-fit-text (fit-text réduit
   leur font-size sur un texte long, jamais leur line-height) — sans ce verrou, un nom plus
   long/court change la hauteur de .header-card__identity donc de toute la card selon le
   panneau actif (participant/sponsor/actu), ce que --header-orb-size (fixé plus haut sur
   cette même mesure) ne peut plus compenser dynamiquement. */
.header-card__display-name {
  margin: 0;
  line-height: clamp(29px, 3.17vw, 37px);
}

.header-card__login,
.header-card__association-name {
  line-height: 21px;
}

.header-card__login {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin-top: var(--space-2);
}

.header-card__login > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.header-card__email-warning {
  flex: 0 0 auto;
  color: var(--danger);
  text-decoration: none;
}

.header-card__association-name {
  margin-top: var(--space-2);
  font-weight: 600;
}

.association-mark {
  display: grid;
  place-items: center;
  justify-self: end;
  width: var(--header-orb-size);
  height: var(--header-orb-size);
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  box-shadow: var(--shadow-avatar);
  color: var(--text);
  font-size: 34px;
  font-weight: 700;
  text-decoration: none;
  transition: transform .16s ease, filter .16s ease, box-shadow .16s ease;
}

.association-mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.association-mark:hover {
  filter: brightness(.97);
  transform: translateY(-1px);
}

.association-mark:active {
  transform: translateY(1px);
}

.association-mark:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 5px var(--brand-blue);
}

.header-card .balance {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.balance-amount {
  font-size: 28px;
  font-weight: 700;
  margin: 4px 0 8px;
}

@media (max-width: 500px) {
  .association-mark {
    font-size: 26px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .profile-coin__inner {
    transform: none;
    will-change: auto;
  }
  .profile-coin,
  .association-mark {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   Composant — Barre d'action fixe
   -------------------------------------------------------------------------- */
#screen-denoms {
  display: flex;
  flex-direction: column;
  gap: var(--space);
}

.footer-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
  background: var(--card);
  border-top: 1px solid var(--border);
  padding: var(--space);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: var(--shadow-footer);
}

/* En PWA paysage, garde le contenu et les contrôles hors de la Dynamic Island
   ou de l'encoche. Les deux écrans QR gèrent leur rotation et leurs safe areas
   séparément dans qr_orientation.css : ils sont volontairement exclus ici. */
@media (display-mode: standalone) and (orientation: landscape) {
  body:not(.qr-orientation-page) > .wrap {
    max-width: calc(860px - var(--space) - var(--space) + max(var(--space), env(safe-area-inset-left, 0px)) + max(var(--space), env(safe-area-inset-right, 0px)));
    padding-left: max(var(--space), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space), env(safe-area-inset-right, 0px));
  }

  body:not(.qr-orientation-page) > .wrap.wide {
    max-width: calc(980px - var(--space) - var(--space) + max(var(--space), env(safe-area-inset-left, 0px)) + max(var(--space), env(safe-area-inset-right, 0px)));
  }

  body:not(.qr-orientation-page) .nav {
    padding-left: max(var(--space), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space), env(safe-area-inset-right, 0px));
  }

  body:not(.qr-orientation-page) .footer-bar {
    padding-right: max(var(--space), env(safe-area-inset-right, 0px));
    padding-bottom: max(var(--space), env(safe-area-inset-bottom, 0px));
    padding-left: max(var(--space), env(safe-area-inset-left, 0px));
  }
}

.footer-inner {
  max-width: 860px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0;
}

.footer-third {
  flex: 1 1 33%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.footer-third > .btn {
  width: 100%;
  height: var(--footer-control-h);
  min-width: max-content;
  justify-content: center;
  white-space: nowrap;
}

#btn_submit {
  flex: 0 0 auto;
  width: auto;
  justify-content: center;
}

#items_list {
  width: 100%;
}

#total_view {
  flex: 1 1 50%;
  min-width: 0;
  font-size: 20px;
  font-weight: 700;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Composant — Barre de recherche
   -------------------------------------------------------------------------- */
.search-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
}

/* --------------------------------------------------------------------------
   Composant — Listes
   -------------------------------------------------------------------------- */
.list {
  margin: 0.2rem 0 0;
  padding-left: 1.1rem;
}

.list li {
  margin: 0.25rem 0;
}

/* --------------------------------------------------------------------------
   Composant — Liens inline
   -------------------------------------------------------------------------- */
a.inline {
  color: var(--link);
  text-decoration: none;
}

/* --------------------------------------------------------------------------
   Composant — Badges de rôle et de statut
   -------------------------------------------------------------------------- */
.badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  border-radius: var(--radius-detail);
  padding: 2px 7px;
  white-space: nowrap;
  vertical-align: middle;
}
.badge.live          { background: var(--bdg-live-bg);    color: var(--bdg-live-fg); }
.badge.event-open    { background: var(--bdg-open-bg);    color: var(--bdg-open-fg); font-size: 11px; }
.badge.event-closed  { background: var(--bdg-closed-bg);  color: var(--bdg-closed-fg); font-size: 11px; }
.badge.role-admin    { background: var(--bdg-admin-bg);   color: var(--bdg-admin-fg); }
.badge.role-super-orga { background: var(--bdg-super-bg); color: var(--bdg-super-fg); }
.badge.role-orga     { background: var(--bdg-orga-bg);    color: var(--bdg-orga-fg); }
.badge.role-participant { background: var(--bdg-closed-bg); color: var(--bdg-closed-fg); }
.badge.enabled       { background: var(--bdg-enabled-bg); color: var(--bdg-enabled-fg); }
.badge.danger        { background: var(--bdg-danger-bg);  color: var(--bdg-danger-fg); }
.badge.gold          { background: var(--bdg-gold-bg);    color: var(--bdg-gold-fg); }

/* --------------------------------------------------------------------------
   Composant — En-tête de section
   -------------------------------------------------------------------------- */
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.section-header .section-title {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   Composant — Barre de progression
   -------------------------------------------------------------------------- */
.progress-bar-wrap {
  margin-top: 18px;
}
.progress-bar-labels {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 4px;
}
.progress-bar {
  background: var(--border);
  border-radius: var(--radius-detail);
  height: 8px;
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  border-radius: inherit;
  transition: width .4s;
}

/* --------------------------------------------------------------------------
   Parcours métier — Actions et confirmation de caisse
   -------------------------------------------------------------------------- */
.actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

/* Les groupes d'actions ordinaires partagent le même plancher de largeur.
   La navigation et les commandes carrées ne sont pas dans ces groupes. */
.actions > .btn,
.actions > form {
  min-width: min(100%, var(--button-wrap-min));
}

.actions > .btn {
  justify-content: center;
}

.actions > form > .btn {
  width: 100%;
  justify-content: center;
}

/* Confirmation des opérations de caisse — pensée mobile first. */
.cash-confirm-dialog {
  width: calc(100% - 32px);
  max-width: 460px;
  max-height: calc(100dvh - 32px);
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-dialog);
  background: var(--card);
  color: var(--text);
  box-shadow: var(--shadow-card);
}
.cash-confirm-dialog::backdrop {
  background: rgba(3, 8, 18, .72);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.cash-confirm-dialog__body {
  padding: 20px;
}
.cash-confirm-recap {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 7px;
  margin-bottom: 16px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-field);
  background: var(--surface2, var(--surface));
  font-size: 14px;
}
.cash-confirm-recap__row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}
.cash-confirm-recap__row--total {
  grid-column: 1 / -1;
  padding-top: 7px;
  border-top: 1px solid var(--border);
}
.cash-confirm-recap[hidden] { display: none; }
.cash-confirm-note summary {
  display: inline-block;
  list-style: none;
  outline: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
}
.cash-confirm-note summary::-webkit-details-marker { display: none; }
.cash-confirm-note summary:focus-visible { color: var(--text); text-decoration: underline; }
.cash-confirm-note[open] summary { margin-bottom: 8px; }
@media (max-width: 640px) {
  .cash-confirm-dialog { width: calc(100% - 20px); max-height: calc(100dvh - 20px); }
  .cash-confirm-dialog__body { padding: 18px 16px; }
  .cash-confirm-dialog .actions .btn { flex: 1; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Parcours métier — Historique des transactions
   -------------------------------------------------------------------------- */
.tx-row,
.tx-row td {
  cursor: pointer;
  touch-action: manipulation;
  transition: background 0.15s ease;
}

/* Liste des opérations wallet (fragment _tableau_transactions.php).
   table-layout reste "auto" : chaque colonne (dont la date) peut se replier
   librement selon la place. Le JS de _accordeon.php met ensuite toutes les
   lignes du tableau à la hauteur de la plus haute → contenu centré. */
.tx-wallet tr.tx-row > td {
  vertical-align: middle;
}

/* Colonne montant alignée à droite → les « € » se suivent verticalement */
.tx-table th.th-amount,
.tx-table tr.tx-row > td.amount-pos,
.tx-table tr.tx-row > td.amount-neg {
  text-align: right;
}

/* Date secondaire : atténuée. Sur écran large, une seule ligne (« 29/08 20:44 »).
   Sous 460px, jour et heure passent sur deux lignes pour TOUTES les lignes de
   TOUS les tableaux wallet — décision pilotée par la largeur, pas par le
   contenu : l'aperçu « Dernières opérations » et l'historique complet se
   replient donc de façon identique à largeur égale. Le JS de _accordeon.php se
   contente ensuite d'aligner toutes les lignes sur la plus haute. */
.tx-table tr.tx-row > td.tx-date {
  color: var(--muted);
  white-space: nowrap;
}
@media (max-width: 460px) {
  .tx-wallet tr.tx-row > td.tx-date {
    white-space: normal;
  }
  .tx-wallet tr.tx-row > td.tx-date .tx-t {
    display: block;
  }
}

.tx-row:hover {
  background: var(--row-hover);
}

/* Indicateur d'expansion : chevron « › » (span injecté par _accordeon.php à la
   place du « ▾ » historique). Il bascule à 90° quand la ligne est ouverte —
   sans transition : dans une cellule de tableau, le relayout déclenché par
   l'ouverture du détail interrompt l'animation de façon non fiable. */
.tx-toggle {
  text-align: right;
  color: var(--muted);
  font-size: 13px;
  user-select: none;
  width: 26px;
}

.tx-toggle .tx-chev {
  display: inline-block;
  font-size: 17px;
  line-height: 1;
}

.tx-row.open .tx-toggle .tx-chev {
  transform: rotate(90deg);
}

.tx-detail {
  display: none;
}

.tx-detail td {
  padding: 8px 8px 12px;
  font-size: 13px;
  color: var(--muted);
  background: var(--surface-sunken);
}

/* Une ligne ouverte et son détail forment un seul bloc : le trait intermédiaire
   disparaît et la séparation est reportée après le contenu et ses actions. */
.tx-table tbody tr.tx-row.open > td {
  border-bottom: 0;
}

/* La règle générique des tableaux retire la bordure de leur dernière ligne.
   Une dernière ligne de détail ouverte doit au contraire fermer le bloc. */
.tx-table tbody tr.tx-detail:last-child > td {
  border-bottom: 1px solid var(--border);
}

.tx-detail ul {
  margin: 0;
  padding-left: 18px;
}

.tx-detail li {
  margin-bottom: 2px;
}

/* Historiques opérationnels : trois niveaux visuels — jour, séquence de
   scannette, puis opération. */
.history-table .history-day-heading > td {
  border-top: 1px solid var(--border-soft);
}

.history-table .history-day-heading button,
.history-table .history-device-heading button {
  min-height: 44px;
}

.history-table .history-day-chevron,
.history-table .history-device-chevron {
  display: inline-block;
  width: 18px;
  font-size: 19px;
  font-weight: 900;
  line-height: 1;
  text-align: center;
}

.history-table [data-history-day-toggle][aria-expanded="true"] .history-day-chevron,
.history-table [data-history-device-toggle][aria-expanded="true"] .history-device-chevron {
  transform: rotate(90deg);
}

.history-table .history-device-heading > td {
  background: var(--surface-sunken) !important;
}

.history-table .history-device-heading button {
  border-left: 3px solid var(--accent-2) !important;
}

.history-table tr.tx-row > td {
  height: 44px;
}

.history-table tr.history-nested-row > td:first-child {
  padding-left: 28px;
}

.history-correction-action {
  display: flex;
  justify-content: flex-end;
  margin-top: 12px;
}

.history-correction-dialog {
  width: min(460px, calc(100% - 24px));
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-dialog);
  padding: 20px;
  box-shadow: var(--shadow-card);
}

.history-correction-dialog::backdrop {
  background: rgba(0, 0, 0, 0.65);
}

.history-correction-title {
  margin: 0 0 14px;
  font-size: 20px;
}

.history-correction-options {
  display: grid;
  gap: 8px;
}

.history-correction-option {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-field);
  background: var(--card-inner);
}

/* --------------------------------------------------------------------------
   Parcours métier — Contrôles de quantité
   -------------------------------------------------------------------------- */
.item-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  width: 100%;
}

.item-row .label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}

.item-row .item-label-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.item-row .item-label-value {
  flex: none;
  margin-left: auto;
  white-space: nowrap;
  color: var(--muted);
  font-size: 14px;
  font-weight: 600;
}

/* Mode encaissement : le prix ouvre la ligne et le nom reste aligné à droite.
   Les autres écrans qui réutilisent les contrôles de quantité sont inchangés. */
.service-bar-page .item-row .item-label-value {
  order: -1;
  margin-right: auto;
  margin-left: 0;
}

.service-bar-page .item-row .item-label-name {
  text-align: right;
}

.item-row .controls {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
  align-items: stretch;
  justify-items: stretch;
  width: 100%;
}

.item-row .controls button {
  width: 100%;
  min-width: 0;
  height: 44px;
  border-radius: var(--radius-action);
  font-size: 22px;
  line-height: 1;
  padding: 0 0.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

input.qty {
  width: 100%;
  height: 44px;
  text-align: center;
  font-weight: 700;
  font-size: 20px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-action);
  background: transparent;
  color: var(--text);
  -moz-appearance: textfield;
  appearance: textfield;
}

@media (max-width: 520px) {
  .item-row .controls button {
    font-size: 20px;
  }
}

@media (max-width: 480px) {
  input.qty {
    font-size: 18px;
  }
  .item-row .label {
    font-size: 12px;
  }
}

/* --------------------------------------------------------------------------
   Parcours métier — Catalogue
   -------------------------------------------------------------------------- */
.disabled {
  opacity: 0.5;
}

/* --------------------------------------------------------------------------
   Parcours métier — Cadre QR code
   -------------------------------------------------------------------------- */
.qrbox {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  min-height: 320px;
}

/* --------------------------------------------------------------------------
   Parcours métier — Canvas QR
   -------------------------------------------------------------------------- */
#qrCanvas,
#qr {
  width: 320px;
  height: 320px;
  image-rendering: pixelated;
}

/* --------------------------------------------------------------------------
   Dashboard — stat grid
   -------------------------------------------------------------------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: 14px;
}

.stat-block {
  background: var(--card-inner);
  border: 1px solid var(--border);
  border-radius: var(--radius-inner);
  padding: 14px 16px;
}

.history-stat-fee .stat-value {
  color: var(--danger);
}

.history-stat-saving .stat-value {
  color: var(--accent);
}

.history-stat-wide {
  grid-column: 1 / -1;
}

.history-stat-wide .stat-value {
  white-space: nowrap;
}

@media (max-width: 700px) {
  .history-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .history-stat-grid .stat-block {
    min-width: 0;
    padding: 12px;
  }

  .history-stat-grid .stat-value {
    font-size: 20px;
  }
}

@media (max-width: 330px) {
  .history-stat-grid {
    grid-template-columns: 1fr;
  }
}

.stat-value {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
  margin-bottom: 4px;
}

.stat-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
}

.stat-sub {
  font-size: 12px;
  color: var(--muted);
  margin-top: 4px;
}

/* --------------------------------------------------------------------------
   Utilitaires — Espacement et alignement
   -------------------------------------------------------------------------- */
.m-0  { margin: 0; }
.mt-6 { margin-top: 6px; }
.mt-8 { margin-top: 8px; }
.mt-10 { margin-top: 10px; }
.mt-12 { margin-top: 12px; }
.mt-14 { margin-top: 14px; }
.mt-16 { margin-top: 16px; }
.mb-0  { margin-bottom: 0; }
.mb-4  { margin-bottom: 4px; }
.mb-6  { margin-bottom: 6px; }
.mb-8  { margin-bottom: 8px; }
.mb-10 { margin-bottom: 10px; }
.mb-12 { margin-bottom: 12px; }

.pad-28 { padding: 28px; }

/* --------------------------------------------------------------------------
   Utilitaires — Centrage
   -------------------------------------------------------------------------- */
.center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.actions.center {
  text-align: center;
}

/* --------------------------------------------------------------------------
   Utilitaires — Contexte de page
   -------------------------------------------------------------------------- */
.card-top-nav {
  margin: 0;
}

.muted.subtitle {
  margin-bottom: .6rem;
}

/* --------------------------------------------------------------------------
   Utilitaires — Flex, texte et états
   -------------------------------------------------------------------------- */
.muted-lead {
  margin: 0.4rem 0 1.2rem;
}

.muted-section {
  margin: 0.2rem 0 1rem;
}

.muted-note {
  margin-top: 0.6rem;
}

.flex-1 {
  flex: 1;
}

.flex-1-min0 {
  flex: 1;
  min-width: 0;
}

.flex-auto-nowrap {
  flex: 0 0 auto;
  white-space: nowrap;
}

.row.nowrap {
  flex-wrap: nowrap;
}

.row.gap-10 {
  gap: 10px;
}

.letterspace-1 {
  letter-spacing: 1px;
}

.avatar-fallback {
  background: var(--avatar-fallback);
  border-radius: 50%;
}

.qr-expiry {
  margin-top: 8px;
  text-align: center;
}

/* --------------------------------------------------------------------------
   Utilitaires — Mise en page
   -------------------------------------------------------------------------- */

/* Flex row with space-between (card action rows) */
.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   Utilitaires — Typographie
   -------------------------------------------------------------------------- */

.fs-12 { font-size: 12px; }
.fs-13 { font-size: 13px; }

/* Bold label inside a card item */
.item-title {
  font-weight: 600;
  margin-bottom: 4px;
}

/* Uppercase accent label (e.g. don, transfer amounts) */
.label-accent {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.3rem;
}

/* --------------------------------------------------------------------------
   Utilitaires — Boutons
   -------------------------------------------------------------------------- */

/* Full-width centered button */
.btn.full {
  width: 100%;
  justify-content: center;
  padding: 14px;
  font-size: 15px;
}

.btn.block {
  width: 100%;
  justify-content: center;
}

.btn.limit-width {
  width: min(100%, 280px);
  justify-content: center;
}

.btn.no-shrink {
  flex-shrink: 0;
}

/* Visibilité pilotée par JavaScript. Le script ajoute ou retire cette classe
   au lieu d'injecter display:none dans chaque page. */
.btn.is-hidden {
  display: none;
}

/* Form wrapper transparent to flex/grid layout */
.form-contents {
  display: contents;
}

/* --------------------------------------------------------------------------
   Utilitaires — Affichage et couleurs
   -------------------------------------------------------------------------- */

/* Text */
.text-right  { text-align: right; }
.text-center { text-align: center; }
.nowrap      { white-space: nowrap; }
.inline      { display: inline; }
.hidden      { display: none; }
.fw-600      { font-weight: 600; }

/* Semantic colors */
.text-accent   { color: var(--accent); }
.text-accent-2 { color: var(--accent-2); }

/* Margin-left */
.ml-1 { margin-left: 4px; }
.ml-2 { margin-left: 6px; }
.ml-3 { margin-left: 8px; }

/* h1/title margin inside header cards */
.card-top-nav h1,
.card-top-nav .title {
  margin: 0;
}

/* --------------------------------------------------------------------------
   Composant riche — Éditeur d'avatar
   -------------------------------------------------------------------------- */
.avatar-editor {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: start;
  margin-top: 16px;
}

.avatar-space-picker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space);
}

/* .mb-0 seul (spécificité 0,1,0) ne suffit pas à battre la règle ci-dessus : les deux sont à
   une seule classe, donc c'est l'ordre dans la feuille de style qui tranche, pas l'ordre dans
   l'attribut class="" du HTML — .avatar-space-picker étant déclarée après .mb-0 dans ce fichier,
   elle l'emportait silencieusement partout où les deux étaient combinées (Identité, Login,
   Email, Mot de passe, Masquer sponsor, Suppression, Préférences, Déconnexion…), d'où l'espace
   en trop sous le dernier bouton de chaque carte malgré la classe posée. Sélecteur composé
   (0,2,0) pour trancher dans le bon sens sans dépendre de l'ordre des règles. */
.avatar-space-picker.mb-0 {
  margin-bottom: 0;
}

.avatar-space-picker .btn {
  flex: 1 1 var(--button-wrap-min);
  justify-content: center;
  text-align: center;
}

.avatar-editor__visual {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}

.avatar-editor__preview {
  width: 84px;
  height: 84px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 50%;
  border: 1px solid var(--border-soft);
  background: linear-gradient(145deg, var(--card-inner), var(--input-bg));
  box-shadow: var(--shadow-avatar);
}

.avatar-editor__preview img,
.avatar-editor__preview svg {
  width: 100%;
  height: 100%;
}

.avatar-editor__preview img { object-fit: cover; }
.avatar-editor__preview svg { padding: 22px; }
.avatar-editor__preview [hidden] { display: none; }

.avatar-editor__status {
  display: inline-block;
  margin-top: 0;
  color: var(--muted);
  font-size: 11px;
  line-height: 1.35;
}

.avatar-editor__body { min-width: 0; }

.avatar-editor__heading {
  margin: 0 0 4px;
  font-size: 17px;
  line-height: 1.25;
}

.avatar-editor__intro {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
}

.avatar-editor__privacy {
  margin: -5px 0 14px;
  padding-left: 10px;
  border-left: 2px solid var(--accent-2);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}

.avatar-dropzone {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  gap: 12px;
  align-items: center;
  margin: 0;
  padding: 14px;
  border: 1px dashed var(--accent-2);
  border-radius: 14px;
  background: color-mix(in srgb, var(--accent-2) 7%, transparent);
  color: var(--text);
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
  transition: border-color .2s ease, background .2s ease, transform .2s ease;
}

.avatar-dropzone:hover,
.avatar-dropzone.is-dragging {
  border-color: var(--brand-gold);
  background: color-mix(in srgb, var(--brand-gold) 9%, transparent);
  transform: translateY(-1px);
}

.avatar-dropzone:focus-within {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

.avatar-dropzone__icon {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 11px;
  background: var(--input-bg);
  border: 1px solid var(--border);
  color: var(--accent-2);
}

.avatar-dropzone__icon svg {
  width: 20px;
  height: 20px;
}

.avatar-dropzone__copy strong,
.avatar-dropzone__copy span {
  display: block;
}

.avatar-dropzone__copy strong {
  font-size: 14px;
  color: var(--text);
}

.avatar-dropzone__copy span {
  margin-top: 2px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.35;
}

.avatar-file-feedback {
  min-height: 18px;
  margin: 7px 2px 0;
  color: var(--muted);
  font-size: 12px;
}

.avatar-file-feedback.is-error { color: var(--danger); }

.avatar-cropper {
  margin-top: 12px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--input-bg);
}

.avatar-cropper[hidden] { display: none; }

.avatar-cropper__hint {
  margin: 0 0 9px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

.avatar-cropper__stage {
  position: relative;
  width: min(100%, 320px);
  margin: 0 auto;
  overflow: hidden;
  border: 2px solid var(--brand-gold);
  border-radius: 0;
  background: #09101d;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
  cursor: grab;
  touch-action: none;
  user-select: none;
}

.avatar-cropper__stage::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .2);
}

.avatar-cropper__stage.is-moving { cursor: grabbing; }

.avatar-cropper__stage canvas {
  display: block;
  width: 100%;
  height: auto;
}

.avatar-cropper__controls {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 9px 12px;
  align-items: center;
  margin-top: 12px;
}

.avatar-cropper__controls label {
  margin: 0;
  font-size: 12px;
}

.avatar-cropper__controls input[type="range"] {
  min-width: 0;
  margin: 0;
  accent-color: var(--brand-gold);
}

.avatar-cropper__controls .btn {
  grid-column: 1 / -1;
  justify-content: center;
}

.avatar-quality {
  margin-top: 11px;
  padding: 9px 11px;
  border-radius: var(--radius-field);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
}

.avatar-quality strong,
.avatar-quality span { display: block; }

.avatar-quality span {
  margin-top: 3px;
  color: var(--muted);
  font-weight: 400;
}

.avatar-quality.is-good {
  color: var(--success);
  background: color-mix(in srgb, var(--success) 10%, transparent);
}

.avatar-quality.is-warning {
  color: var(--brand-gold);
  background: color-mix(in srgb, var(--brand-gold) 10%, transparent);
}

.avatar-quality.is-error {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
}

.avatar-editor__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* Même comportement que .avatar-space-picker (sélecteur d'espace de cette même page) : chaque
   bouton grandit pour remplir la ligne à partir d'un plancher commun, au lieu de flex:1 1 auto
   (chacun garde sa largeur de contenu propre — "Cadrer" étroit à côté de "Supprimer" plus large,
   tailles visiblement différentes). */
.avatar-editor__actions .btn {
  flex: 1 1 var(--button-wrap-min);
  justify-content: center;
}

.avatar-editor__actions [hidden] {
  display: none !important;
}

/* Même piège que .push-settings/.avatar-editor__actions ci-dessus : .btn { display: inline-flex }
   prime sur l'attribut [hidden] natif à spécificité égale (assets/js/dirty_form.js). */
[data-dirty-save-btn][hidden] {
  display: none !important;
}

.avatar-editor__alternative {
  margin-top: 14px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}

.avatar-editor__alternative summary {
  width: fit-content;
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
}

.avatar-editor__alternative summary:hover { color: var(--text); }
.avatar-editor__alternative form { margin-top: 12px; }

@media (min-width: 621px) {
  .avatar-editor__preview {
    width: 112px;
    height: 112px;
    border-radius: 50%;
  }
  .avatar-dropzone {
    grid-template-columns: 38px minmax(0, 1fr);
    padding: 16px;
  }
  .avatar-dropzone__icon {
    width: 38px;
    height: 38px;
  }
  .avatar-cropper { padding: 16px; }
  .avatar-cropper__controls {
    grid-template-columns: auto minmax(120px, 1fr) auto;
  }
  .avatar-cropper__controls .btn { grid-column: auto; }
}

/* Liste verticale de cases à cocher (Mon compte → Notifications). */
.mc-checkbox-list { display: flex; flex-direction: column; gap: 6px; }

/* Retour de validation IBAN en direct (Mon compte, remboursement par virement). */
.iban-feedback { font-size: 12px; margin-top: 5px; min-height: 18px; }

/* Respecte le réglage système des personnes sensibles aux animations. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
