// FOOTPASS v2 · AUTORISATIONS · le système de Autorisations, aligné sur la spec produit
// Modèle : Émetteur → Bénéficiaire · accord BILATÉRAL (pending tant que les 2 n'ont pas dit oui)
// 5 réglages : Niveau (Information / Négociation / Signature) · Territoire (Monde → Club)
//              Durée · Révocation (unilatérale / bilatérale) · Visibilité (publique / privée)
//              + Délégation autorisée (décidée à la création, JAMAIS modifiable)
// Badge = indicateur de confiance, pas un verrou. Révocation en cascade sur les sous-autorisations.
// Coffre-fort jamais délégable. Journal append-only (SHA-256, Ed25519).
const { useState: useStateMd } = React;

// ── Icônes locales ──
const MdEye = ({ s = 13 }) => (
  <svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.3">
    <path d="M1.2 7S3.4 3.2 7 3.2 12.8 7 12.8 7 10.6 10.8 7 10.8 1.2 7 1.2 7z" strokeLinejoin="round"></path>
    <circle cx="7" cy="7" r="1.8"></circle>
  </svg>
);
const MdEyeOff = ({ s = 13 }) => (
  <svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round">
    <path d="M5.6 3.5A5.7 5.7 0 0 1 7 3.3c3.5 0 5.6 3.7 5.6 3.7a10.4 10.4 0 0 1-1.9 2.3M3.4 4.5A10.3 10.3 0 0 0 1.4 7s2.1 3.7 5.6 3.7c.6 0 1.1-.1 1.6-.3"></path>
    <path d="M1.8 1.8l10.4 10.4"></path>
  </svg>
);
const MdSign = ({ s = 13 }) => (
  <svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round">
    <path d="M2 11.4l.6-2.5 6.2-6.2a1.2 1.2 0 011.7 0l.2.2a1.2 1.2 0 010 1.7L4.5 10.8 2 11.4z"></path>
    <path d="M2.2 13h9.6"></path>
  </svg>
);
const MdBlock = ({ s = 13 }) => (
  <svg width={s} height={s} viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.3">
    <circle cx="7" cy="7" r="5.4"></circle>
    <path d="M3.2 3.2l7.6 7.6"></path>
  </svg>
);
const MdArrow = ({ s = 11 }) => (
  <svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
    <path d="M2 6h8M6.5 2.5L10 6l-3.5 3.5"></path>
  </svg>
);
// Visibilité d'une autorisation : publique (visible par les parties) vs privée (cachée) · lisible d'un coup d'œil
const MdVisib = ({ v }) => (
  <span className={`md-visib ${v === 'priv' ? 'priv' : 'pub'}`}>
    {v === 'priv' ? <MdEyeOff s={11} /> : <MdEye s={11} />}
    {TX(v === 'priv' ? 'Privée' : 'Publique')}
  </span>
);
const MdCheck = ({ s = 11 }) => (
  <svg width={s} height={s} viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
    <path d="M2 6.4L4.8 9 10 3.4"></path>
  </svg>
);
const MdSend = ({ s = 15 }) => (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M16 2L8.4 9.6M16 2l-5 14-2.6-6.4L2.6 7 16 2z"></path></svg>);
const MdShare = ({ s = 15 }) => (<svg width={s} height={s} viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="13.5" cy="4" r="2.2"></circle><circle cx="4.5" cy="9" r="2.2"></circle><circle cx="13.5" cy="14" r="2.2"></circle><path d="M6.4 8l5.2-3M6.4 10l5.2 3"></path></svg>);

const mdMe = () => window.fpUpperLast(FP_ME.name);

// Identité présentée comme partout dans l'app (Messages, Agenda, Appels) : Prénom NOM · rôle teinté famille · structure
const MD_ROLE_FAM = { 'Joueur': 'joueur', 'Agent': 'agent', 'Directeur sportif': 'staff', 'Recruteur': 'staff', 'Entraîneur': 'staff', 'Club': 'staff', 'CEO': 'staff', 'Président': 'staff', 'Juriste': 'reseau', 'Médecin': 'reseau', 'Avocat': 'reseau' };
const mdIdLine = (px) => {
  if (!px || !px.role) return null;
  const c = window.m4RoleColor ? window.m4RoleColor(px.fam || MD_ROLE_FAM[px.role] || 'staff') : 'inherit';
  return (
    <span className="md-party-id">
      <span className="role" style={{ color: c, fontStyle: 'normal' }}>{TX(px.role)}</span>
      {px.org ? <span className="sep"> · </span> : null}
      {px.org ? <span className="org">{px.org}</span> : null}
    </span>
  );
};

// Avatar club / structure · monogramme coloré (carré arrondi = organisation, vs rond = personne).
// Pas de logo de marque codé en dur (droits) : couleur dérivée du nom, stable d'une fois à l'autre.
const mdHashHue = (s) => { s = String(s || ''); let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return h % 360; };
// Blasons déposés par l'utilisateur (il a les droits) · persistés par clé de structure.
// AUCUN logo de marque codé en dur : le repli est toujours le monogramme couleur.
if (!window.FP_CLUB_LOGOS) { try { window.FP_CLUB_LOGOS = JSON.parse(localStorage.getItem('fp_club_logos') || '{}'); } catch (e) { window.FP_CLUB_LOGOS = {}; } }
const mdOrgKey = (name) => String(name || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\b(fc|cf|as|sc|ac|cd|rc|ogc|ol|om)\b/g, '').replace(/[^a-z0-9]+/g, '');
// Blasons fournis par l'utilisateur (déposés dans le projet, il en a les droits) · mappés par clé de structure.
const MD_ORG_LOGOS = {
  [mdOrgKey('Real Madrid CF')]: 'real-madrid',
  [mdOrgKey('FC Nantes')]: 'fc-nantes',
};
const mdLogoData = (k) => (k && window.FP_LOGO_DATA ? window.FP_LOGO_DATA[k] : null) || null;
const mdGetLogo = (name) => { const k = mdOrgKey(name); return window.FP_CLUB_LOGOS[k] || mdLogoData(MD_ORG_LOGOS[k]) || null; };
const mdSetLogo = (name, url) => { window.FP_CLUB_LOGOS[mdOrgKey(name)] = url; try { localStorage.setItem('fp_club_logos', JSON.stringify(window.FP_CLUB_LOGOS)); } catch (e) {} };
const MdOrgAvatar = ({ name, size = 34, editable, onChange }) => {
  const [logoState, setLogoState] = useStateMd(() => mdGetLogo(name));
  const ref = React.useRef(null);
  const logo = editable ? logoState : mdGetLogo(name);
  const hue = mdHashHue(name);
  const words = String(name || '').replace(/\b(FC|CF|AS|SC|AC|CD|RC|OGC|OL|OM)\b/gi, ' ').replace(/\s+/g, ' ').trim().split(' ').filter(Boolean);
  const initials = (words.length >= 2 ? words[0][0] + words[1][0] : (words[0] || '?').slice(0, 2)).toUpperCase();
  const pick = (e) => { const f = e.target.files && e.target.files[0]; if (!f) return; const r = new FileReader(); r.onload = () => { mdSetLogo(name, r.result); setLogoState(r.result); onChange && onChange(r.result); }; r.readAsDataURL(f); };
  const body = logo
    ? <span className="md-orgav has-img" style={{ width: size, height: size }}><img src={logo} alt="" /></span>
    : <span className="md-orgav" style={{ width: size, height: size, fontSize: Math.round(size * 0.37), background: `oklch(0.62 0.12 ${hue} / 0.16)`, color: `oklch(0.58 0.14 ${hue})` }}>{initials}</span>;
  if (!editable) return body;
  return (
    <span className="md-orgav-edit">
      <button type="button" className="md-orgav-btn" onClick={() => ref.current && ref.current.click()} aria-label={TX('Ajouter ou changer le logo')}>
        {body}
        <i className="md-orgav-cam"><svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M2.5 5.5h2l1-1.5h5l1 1.5h2v7h-11z"></path><circle cx="8" cy="9" r="2.2"></circle></svg></i>
      </button>
      <input ref={ref} type="file" accept="image/*" onChange={pick} hidden />
    </span>
  );
};

// ── Store (module, mutable · survit aux démontages) ──
// create : 'GIFT' (don, l'émetteur propose) | 'REQUEST' (demande, le bénéficiaire initie) | 'SUB'
// level  : Information | Négociation | Signature · terr : cascade Monde → Club
// rev    : 'uni' | 'bi' · visib : 'pub' | 'priv' · deleg : re-délégation (immuable)
// acc    : { i, b } · accord bilatéral émetteur / bénéficiaire
if (!window.FP_AUTH) {
  window.FP_AUTH = {
    requests: [
      { id: 'q1', org: 'Real Madrid CF', create: 'REQUEST', type: 'Demande d’autorisation', for: 'Autorisation de contact · Lucas Ferreira', level: 'Information', terr: 'Europe', until: 'Répondre sous 6 jours', rev: 'uni', visib: 'priv', deleg: false, acc: { i: false, b: true }, status: 'pending' },
    ],
    // À traiter · historique des demandes reçues (approuvées / refusées) · le journal des décisions d’autorisation
    processed: [
      { id: 'p1', org: 'FC Nantes', create: 'REQUEST', type: 'Demande d’autorisation', for: 'Autorisation de contact · Mathis Renard', level: 'Information', terr: 'France', until: '2 juil. 2026', status: 'approved' },
      { id: 'p2', org: 'Scout externe · Vertex', create: 'REQUEST', type: 'Demande d’autorisation', for: 'Premier contact · joueur U19', level: 'Information', terr: 'Europe', until: '28 juin 2026', status: 'refused' },
    ],
    mandates: [
      { id: 'M1', pid: 'lucas', dirIn: true, create: 'GIFT', type: 'Autorisation de représentation', for: 'Agir en son nom', level: 'Négociation', terr: 'Monde', from: '1 juil. 2025', until: '30 juin 2027', rev: 'uni', visib: 'pub', deleg: true, acc: { i: true, b: true }, status: 'active', num: 'FP-A-2026-0114' },
      { id: 'M2', pid: 'mathis', dirIn: true, create: 'GIFT', type: 'Autorisation de représentation · jeune joueur', for: 'Premier contrat pro · en son nom', level: 'Négociation', terr: 'France · Ligue 1', from: '1 juil. 2025', until: '30 juin 2028', rev: 'uni', visib: 'pub', deleg: false, acc: { i: true, b: true }, status: 'active', num: 'FP-A-2026-0089', note: 'Co-signée par les représentants légaux' },
    ],
    received: [
      { id: 'r1', pid: 'lucas', dirIn: true, create: 'GIFT', type: 'Autorisation limitée', for: 'Droit à l\u2019image · négociation équipementier', level: 'Négociation', terr: 'Europe', until: '31 déc. 2026', rev: 'bi', visib: 'priv', deleg: false, acc: { i: true, b: true }, status: 'active' },
      { id: 'r2', pid: 'mathis', dirIn: true, create: 'GIFT', type: 'Autorisation parentale', for: 'Contacts clubs · premier contrat pro', level: 'Information', terr: 'France', until: '12 sept. 2026', rev: 'uni', visib: 'pub', deleg: false, acc: { i: true, b: true }, status: 'soon', note: 'Signée par les représentants légaux' },
    ],
    given: [
      { id: 'g1', pid: 'karim', dirIn: false, create: 'REQUEST', type: 'Autorisation limitée', for: 'Agenda partagé · visite médicale Mathis R.', level: 'Information', terr: 'FC Nantes', until: '30 juin 2026', rev: 'uni', visib: 'priv', deleg: false, acc: { i: true, b: true }, status: 'soon' },
    ],
    delegations: [
      { id: 'd1', pid: 'sira', parent: 'M1', dirIn: false, create: 'SUB', chain: ['Lucas Ferreira', 'MOI', 'Sira Keïta'], type: 'Sous-autorisation', for: 'Relecture contractuelle · lecture seule', level: 'Information', terr: 'Monde', until: '30 juin 2027', rev: 'uni', visib: 'priv', deleg: false, acc: { i: true, b: true }, status: 'active' },
    ],
    expired: [
      { id: 'x1', kind: 'Représentation', title: 'Lucas Ferreira', type: 'Représentation 2023\u20132025 · renouvelée depuis', until: 'Expirée le 30 juin 2025', status: 'expired' },
      { id: 'x2', kind: 'Sous-autorisation', title: 'Scout externe', type: 'Premier contact uniquement', until: 'Expirée le 23 oct. 2025 · cascade', status: 'expired' },
    ],
    access: [
      { pid: 'sira', note: 'Juriste · lecture-écriture', rows: [['Contrat pro v2.3', 'rw'], ['Avenant droit à l\u2019image', 'rw'], ['Note juridique', 'r'], ['Dossier médical', 'no']] },
      { pid: 'lucas', note: 'Joueur · lecture + signature', rows: [['Contrat pro v2.3', 'rs'], ['Autorisation · Représentation 2026', 'rs'], ['Dossier médical', 'r'], ['Notes internes', 'no']] },
      { pid: 'karim', note: 'Club · accès ponctuel', rows: [['Convocation · visite médicale', 'r'], ['Dossier médical Mathis R.', 'r'], ['Contrats', 'no']] },
      { pid: 'mufc', note: 'Club · dossier en cours', rows: [['Dossier · Lucas Ferreira 2026', 'r'], ['Lettre d\u2019intention', 'rs'], ['Autres documents', 'no']] },
    ],
  };
}

// Statut → pill
const mdPill = (s) => s === 'active' ? <span className="pg-pill ok">{TX('Active')}</span>
  : s === 'soon' ? <span className="pg-pill warn">{TX('Expire bientôt')}</span>
  : s === 'pending' ? <span className="pg-pill warn">{TX('En attente')}</span>
  : s === 'revpending' ? <span className="pg-pill warn">{TX('Révocation demandée')}</span>
  : s === 'approved' ? <span className="pg-pill ok">{TX('Approuvée')}</span>
  : s === 'refused' ? <span className="pg-pill off">{TX('Refusée')}</span>
  : s === 'revoked' ? <span className="pg-pill off">{TX('Révoquée')}</span>
  : <span className="pg-pill off">{TX('Expirée')}</span>;

const mdLevelIc = (level) => level === 'Négociation' ? <HMI.Pen s={12} /> : <MdEye />;

// Niveau d'action → jauge 2 segments (Information · Négociation) · la négociation est le maximum ;
// dans le foot on ne signe jamais au nom du joueur, la signature reste son acte.
const MdMeter = ({ level, big }) => {
  const n = level === 'Négociation' ? 2 : 1;
  return <span className={`md-meter ${big ? 'big' : ''}`} aria-label={level}>{[1, 2].map((i) => <i key={i} className={i <= n ? 'on' : ''}></i>)}</span>;
};

// Sync avec FP_MANDATES (Accueil « Mes autorisations », À traiter)
const mdSync = (pid, status) => {
  const map = { lucas: 'm1', mathis: 'm2', sira: 'm3', monaco: 'm4' };
  const id = map[pid];
  if (!id || !window.FP_MANDATES) return;
  window.FP_MANDATES = window.FP_MANDATES.map((m) => (m.id === id ? { ...m, status } : m));
};

// ═══════ PAGE ═══════
function MandatesPage({ onBack, showToast, startCreate }) {
  const [, bump] = useStateMd(0);
  const rerender = () => bump((n) => n + 1);
  const A = window.FP_AUTH;
  const [tab, setTab] = useStateMd('tous');
  const [fam, setFam] = useStateMd('all'); // filtre par famille d'interlocuteur (Joueurs / Clubs / Agents)
  const [dir, setDir] = useStateMd('recues');
  const [sel, setSel] = useStateMd(null);       // { kind, item }
  const [arm, setArm] = useStateMd(false);      // confirmation révocation
  const [creating, setCreating] = useStateMd(!!startCreate);
  const [q, setQ] = useStateMd('');          // recherche instantanée · toutes autorisations
  const [showSearch, setShowSearch] = useStateMd(false); // repliée par défaut · même pattern que Messages et Agenda
  const mdSearchRef = React.useRef(null);
  React.useEffect(() => { if (showSearch && mdSearchRef.current) mdSearchRef.current.focus(); }, [showSearch]);
  const toggleSearch = () => setShowSearch((s) => { if (s) setQ(''); return !s; });
  // Résumé (3 compteurs) masquable · choix persistant par personne
  const [showStats, setShowStats] = useStateMd(() => { try { return localStorage.getItem('fp_autorisations_stats') !== '0'; } catch (e) { return true; } });
  const toggleStats = () => setShowStats((s) => { try { localStorage.setItem('fp_autorisations_stats', s ? '0' : '1'); } catch (e) {} return !s; });
  const [soon, setSoonView] = useStateMd(false); // vue « expirent bientôt » (chip actionnable)
  const tabsRef = React.useRef(null);
  const mdScrollTo = (el) => {
    if (!el) return;
    const sc = el.closest('.pg-scroll');
    if (sc) sc.scrollTo({ top: Math.max(0, el.getBoundingClientRect().top - sc.getBoundingClientRect().top + sc.scrollTop - 10), behavior: 'smooth' });
  };
  // Onglet actif toujours visible dans la rangée défilante
  React.useEffect(() => {
    const row = tabsRef.current;
    if (!row) return;
    const pill = row.querySelector('.md-tab.on');
    if (!pill) return;
    const pl = pill.offsetLeft, pr = pl + pill.offsetWidth;
    if (pr > row.scrollLeft + row.clientWidth - 44) row.scrollTo({ left: pr - row.clientWidth + 52, behavior: 'smooth' });
    else if (pl < row.scrollLeft + 18) row.scrollTo({ left: Math.max(0, pl - 18), behavior: 'smooth' });
  }, [tab, q, soon]);
  const open = (kind, item) => { setSel({ kind, item }); setArm(false); };

  // ── Actions ──
  // Approuver / refuser une demande reçue → elle bascule dans « Traitées » (historique des décisions)
  const approve = (q) => {
    A.requests = A.requests.filter((x) => x.id !== q.id);
    A.processed = [{ ...q, id: 'p' + Date.now(), status: 'approved', until: 'Aujourd\u2019hui' }, ...(A.processed || [])];
    setSel(null); rerender();
    showToast(TX('Demande approuvée · ') + (q.org || ''));
  };
  const refuse = (q) => {
    A.requests = A.requests.filter((x) => x.id !== q.id);
    A.processed = [{ ...q, id: 'p' + Date.now(), status: 'refused', until: 'Aujourd\u2019hui' }, ...(A.processed || [])];
    setSel(null); rerender();
    showToast(TX('Demande refusée'));
  };
  // Révocation · cascade automatique sur les sous-autorisations dépendantes
  const revoke = (list, item) => {
    const toExpired = (x, why) => ({ id: 'x' + Date.now() + x.id, kind: x.create === 'SUB' ? 'Sous-autorisation' : 'Autorisation', title: x.pid ? window.fpUpperLast(FP_PEERS[x.pid].name) : x.org, type: (x.type || '') + ' · ' + (x.for || ''), until: why, status: 'revoked' });
    if (item.rev === 'bi' && item.status !== 'revpending') {
      item.status = 'revpending'; setSel(null); rerender();
      showToast(TX('Révocation demandée · l\u2019autre partie doit confirmer'));
      return;
    }
    A[list] = A[list].filter((x) => x.id !== item.id);
    let cascade = 0;
    if (list === 'mandates') {
      const subs = A.delegations.filter((d) => d.parent === item.id);
      cascade = subs.length;
      subs.forEach((d) => { A.expired = [toExpired(d, 'Invalidée en cascade · 18 juin 2026'), ...A.expired]; if (d.pid === 'sira') mdSync('sira', 'revoked'); });
      A.delegations = A.delegations.filter((d) => d.parent !== item.id);
      mdSync(item.pid, 'revoked');
    }
    if (item.pid === 'sira') mdSync('sira', 'revoked');
    A.expired = [toExpired(item, 'Révoquée le 18 juin 2026'), ...A.expired];
    setSel(null); rerender();
    showToast(TX('Révoquée · effet immédiat') + (cascade ? ' · ' + cascade + ' ' + TX('sous-autorisation invalidée en cascade') : ''));
  };
  const createAuth = (item, sens) => {
    if (sens === 'don') A.given = [item, ...A.given]; else A.received = [item, ...A.received];
    setCreating(false); setTab('autorisations'); setDir(sens === 'don' ? 'donnees' : 'recues'); rerender();
    showToast(TX('Envoyée pour accord · en attente de ') + window.fpUpperLast(FP_PEERS[item.pid].name));
  };

  // ── Cartes · une phrase, un niveau, une échéance · le détail vit dans la feuille ──
  const card = (kind, item) => {
    const p = item.pid ? FP_PEERS[item.pid] : null;
    const who = p ? window.fpUpperLast(p.name) : (item.org || TX(item.title || ''));
    const head = kind === 'expired' ? who
      : kind === 'request' ? who + ' · ' + TX('demande d’autorisation')
      : kind === 'deleg' ? TX('Vous déléguez à') + ' ' + who
      : item.dirIn === false ? TX('Vous autorisez') + ' ' + who
      : who + ' ' + TX('vous autorise');
    const quiet = item.status === 'active' || item.status === 'soon';
    return (
      <button key={item.id} className="md-card" onClick={() => open(kind, item)}>
        <span className="md-card-top">
          {p ? (
            <span className="md-avwrap">
              <FPAvatar size={34} initials={p.initials} src={p.photo} />
              {quiet ? <i className={`md-dot ${item.status === 'soon' ? 'warn' : ''}`}></i> : null}
            </span>
          ) : <MdOrgAvatar name={item.org || TX(item.title || '')} />}
          <span className="md-card-who">
            <b>{head}</b>
            <em>{TX(item.for || item.type)}</em>
          </span>
          {quiet ? <HMI.Chevron c="#A1A1AA" /> : mdPill(item.status)}
        </span>
        {item.chain ? (
          <span className="md-chain">
            {item.chain.map((n, i) => (
              <React.Fragment key={n}>
                {i ? <i className="md-chain-arr"><MdArrow /></i> : null}
                <span className={`md-chain-n ${n === 'MOI' ? 'me' : ''}`}>{n === 'MOI' ? mdMe() : n}</span>
              </React.Fragment>
            ))}
          </span>
        ) : null}
        <span className="md-card-meta">
          {item.level ? (
            <span className="md-permline"><MdMeter level={item.level} /><b>{TX(item.level)}</b><MdTerr t={item.terr} /></span>
          ) : null}
          <span className="md-card-until">{item.visib ? <MdVisib v={item.visib} /> : null}{item.visib ? <span className="md-card-sep">·</span> : null}{TX(item.until)}</span>
        </span>
      </button>
    );
  };

  // ── Vue « Tous » : une carte PAR PERSONNE ──
  // On agrège les autorisations d'une même personne et on retire les doublons : une portée large
  // (Monde) couvre déjà les plus étroites (Europe, pays…). L'autorisation « gouvernante » = niveau le
  // plus élevé, puis territoire le plus large ; le reste est replié en « Couvre aussi ».
  const LVL_RANK = { 'Négociation': 2, 'Information': 1 };
  const terrRank = (t) => { t = String(t || ''); if (/monde/i.test(t)) return 4; if (/europe|uefa|afrique|asie|am[ée]rique/i.test(t)) return 3; if (/ligue|serie|liga|premier|club|\bfc\b|real|nantes|monaco/i.test(t)) return 1; return 2; };
  const govSort = (a, b) => ((LVL_RANK[b[1].level] || 0) - (LVL_RANK[a[1].level] || 0)) || (terrRank(b[1].terr) - terrRank(a[1].terr));
  const personCard = (pid, pairs) => {
    const p = FP_PEERS[pid];
    const sorted = [...pairs].sort(govSort);
    const [gk, gov] = sorted[0];
    const alsoObjs = sorted.slice(1).map(([, x]) => TX(x.for || x.type)).filter(Boolean);
    const dirIn = gov.dirIn !== false;
    const head = dirIn ? window.fpUpperLast(p.name) + ' ' + TX('vous autorise') : TX('Vous autorisez') + ' ' + window.fpUpperLast(p.name);
    const soon = pairs.some(([, x]) => x.status === 'soon');
    const quiet = pairs.some(([, x]) => x.status === 'active' || x.status === 'soon');
    return (
      <button key={pid} className="md-card" onClick={() => open(gk, gov)}>
        <span className="md-card-top">
          <span className="md-avwrap">
            <FPAvatar size={34} initials={p.initials} src={p.photo} />
            {quiet ? <i className={`md-dot ${soon ? 'warn' : ''}`}></i> : null}
          </span>
          <span className="md-card-who">
            <b>{head}</b>
            <em>{TX(gov.for || gov.type)}</em>
          </span>
          <HMI.Chevron c="#A1A1AA" />
        </span>
        <span className="md-card-meta">
          <span className="md-permline"><MdMeter level={gov.level} /><b>{TX(gov.level)}</b><MdTerr t={gov.terr} /></span>
          <span className="md-card-until">{gov.visib ? <MdVisib v={gov.visib} /> : null}{gov.visib ? <span className="md-card-sep">·</span> : null}{TX(gov.until)}</span>
        </span>
      </button>
    );
  };

  // ── Accès par personne (coffre-fort : JAMAIS délégable, accès strictement personnel) ──
  const accessRow = (a) => {
    const p = FP_PEERS[a.pid];
    const glyphs = (lvl) => lvl === 'no' ? <i className="md-lvl no"><MdBlock /></i>
      : <React.Fragment>
          <i className="md-lvl"><MdEye /></i>
          {lvl.includes('w') ? <i className="md-lvl"><HMI.Pen s={12} /></i> : null}
          {lvl.includes('s') ? <i className="md-lvl"><MdSign /></i> : null}
        </React.Fragment>;
    return (
      <button key={a.pid} className="md-card" onClick={() => open('access', a)}>
        <span className="md-card-top">
          <FPAvatar size={34} initials={p.initials} src={p.photo} />
          <span className="md-card-who"><b>{window.fpUpperLast(p.name)}</b><em>{TX(a.note)}</em></span>
          <HMI.Chevron c="#A1A1AA" />
        </span>
        <span className="md-acclist">
          {a.rows.slice(0, 3).map(([doc, lvl]) => (
            <span key={doc} className="md-accrow"><em>{TX(doc)}</em><span className="md-lvls">{glyphs(lvl)}</span></span>
          ))}
          {a.rows.length > 3 ? <span className="md-accmore">+{a.rows.length - 3} {TX('documents')}</span> : null}
        </span>
      </button>
    );
  };

  const auths = dir === 'recues' ? A.received : A.given;

  // Famille d'une autorisation (l'interlocuteur) · même logique de puces que le reste de l'app
  const itemFam = (x) => {
    if (x && x.pid && FP_PEERS[x.pid]) {
      const r = FP_PEERS[x.pid].role || '';
      if (/joueur/i.test(r)) return 'joueur';
      if (/agent/i.test(r)) return 'agent';
      return 'club';
    }
    return 'club';
  };
  const famFilter = (arr) => (fam === 'all' ? (arr || []) : (arr || []).filter((x) => itemFam(x) === fam));
  const FP_FAMS = [['all', 'Tous', null], ['joueur', 'Joueurs', '#00C49A'], ['club', 'Clubs', '#E0A93E'], ['agent', 'Agents', '#A07CEA']];
  const famsPresent = new Set([...A.mandates, ...A.received, ...A.given, ...A.expired, ...A.requests, ...(A.processed || [])].map(itemFam));

  // ── Recherche instantanée : nom, club, championnat, niveau, objet · toutes listes confondues ──
  const norm = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  const all = [
    ...A.requests.map((x) => ['request', x]),
    ...A.mandates.map((x) => ['mandate', x]),
    ...A.received.map((x) => ['auth', x]),
    ...A.given.map((x) => ['auth', x]),
    ...A.expired.map((x) => ['expired', x]),
    ...(A.processed || []).map((x) => ['request', x]),
  ];
  const results = q.trim() ? all.filter(([, x]) => {
    const p = x.pid && FP_PEERS[x.pid];
    return norm([p && p.name, p && p.org, p && p.role, x.org, x.title, TX(x.type || ''), TX(x.for || ''), TX(x.level || ''), TX(x.terr || ''), TX(x.until || '')].join(' ')).includes(norm(q));
  }) : [];
  const soonItems = all.filter(([k, x]) => k !== 'expired' && x.status === 'soon');
  const shown = q.trim() ? results : soonItems;

  const body = tab === 'mandats' ? famFilter(A.mandates).map((m) => card('mandate', m))
    : tab === 'autorisations' ? famFilter(auths).map((a) => card('auth', a))
    : tab === 'delegations' ? A.delegations.map((d) => card('deleg', d))
    : tab === 'acces' ? A.access.map(accessRow)
    : famFilter(A.expired).map((x) => card('expired', x));

  // Vue « Tous » regroupée par personne (une carte / personne · doublons repliés)
  const tousGroups = [];
  const tousIdx = {};
  [
    ...famFilter(A.mandates).map((x) => ['mandate', x]),
    ...famFilter(A.received).map((x) => ['auth', x]),
    ...famFilter(A.given).map((x) => ['auth', x]),
  ].forEach((pair) => {
    const pid = pair[1].pid;
    if (!pid) { tousGroups.push([null, [pair]]); return; }
    if (tousIdx[pid] == null) { tousIdx[pid] = tousGroups.length; tousGroups.push([pid, []]); }
    tousGroups[tousIdx[pid]][1].push(pair);
  });

  const nSoon = [...A.mandates, ...A.received, ...A.given].filter((x) => x.status === 'soon').length;
  const nPend = A.requests.length + [...A.received, ...A.given].filter((x) => x.status === 'pending').length;
  const nActive = A.mandates.length + A.received.concat(A.given).filter((x) => x.status !== 'pending').length;

  return (
    <PgPage title="Autorisations" onBack={onBack} tabbar
      action={
        <div className="pg-headacts2">
          <button className={`hm-iconbtn ${showStats ? 'on' : ''}`} onClick={toggleStats} aria-label={TX('Afficher ou masquer le résumé')} aria-pressed={showStats}>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M3 10.5v2M8 6v6.5M13 3v9.5"></path></svg>
          </button>
          <button className={`hm-iconbtn ${showSearch ? 'on' : ''}`} onClick={toggleSearch} aria-label="Rechercher"><HMI.Search s={17} /></button>
          <button className="hm-iconbtn" onClick={() => setCreating(true)} aria-label="Nouvelle autorisation"><HMI.Pen s={16} /></button>
        </div>
      }>
      {/* L'état en un coup d'œil · 3 compteurs actionnables · masquable */}
      {showStats ? (
      <div className="md-stats">
        <button className={`md-stat ok ${nActive ? '' : 'zero'}`} onClick={() => { setSoonView(false); setQ(''); }}>
          <b>{nActive}</b><span>{TX('Autorisations actives')}</span>
        </button>
        <button className={`md-stat warn ${nPend ? '' : 'zero'}`} onClick={() => { setSoonView(false); setQ(''); setTab('atraiter'); mdScrollTo(tabsRef.current); }}>
          <b>{nPend}</b><span>{TX('À traiter')}</span>
        </button>
        <button className={`md-stat warn ${nSoon ? '' : 'zero'} ${soon ? 'on' : ''}`} aria-pressed={soon} onClick={() => setSoonView(!soon)}>
          <b>{nSoon}</b><span>{TX(nSoon > 1 ? 'Expirent bientôt' : 'Expire bientôt')}</span>
        </button>
      </div>
      ) : null}

      {/* Recherche · même composant et même déclencheur que Messages et Agenda */}
      {showSearch ? (
      <div className="hm-search">
        <HMI.Search />
        <input ref={mdSearchRef} value={q} onChange={(e) => setQ(e.target.value)} placeholder={TX('Joueur, club, championnat, niveau…')} aria-label="Rechercher" />
        {q ? (
          <button className="hm-search-clear" onClick={() => setQ('')} aria-label="Effacer">
            <svg width="13" height="13" viewBox="0 0 22 22" fill="none"><path d="M16 6L6 16M6 6l10 10" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"></path></svg>
          </button>
        ) : null}
      </div>
      ) : null}

      {q.trim() || soon ? (
        <React.Fragment>
          <div className="hm-sechead"><span>{q.trim() ? TX('Résultats') : TX('Expirent bientôt')}<i className="ac-count">{shown.length}</i></span></div>
          <div className="md-list" key={'q' + q + (soon ? 's' : '')}>
            {shown.length ? shown.map(([k, x]) => card(k, x)) : <div className="hm-empty">{q.trim() ? TX('Aucun résultat pour') + ' « ' + q + ' »' : TX('Rien ici pour le moment.')}</div>}
          </div>
        </React.Fragment>
      ) : (
      <React.Fragment>
      <div className="md-tabs" ref={tabsRef}>
        {[['tous', 'Tous', 0], ['atraiter', 'À traiter', nPend], ['mandats', 'Représentation', A.mandates.length], ['autorisations', 'Limitées', A.received.length + A.given.length], ['expires', 'Expirées', A.expired.length]].map(([id, lb, n]) => (
          <button key={id} className={`md-tab ${tab === id ? 'on' : ''}`} onClick={() => { setTab(id); if (id === 'tous') setFam('all'); }}>{TX(lb)}{n ? <i>{n}</i> : null}</button>
        ))}
      </div>
      {/* Puces de famille · toggle (re-clic = tout) · même logique que les thèmes de l'Agenda ; pas de « Tous » ici, il vit dans les onglets */}
      {famsPresent.size > 1 ? (
        <div className="hm-chips md-fams">
          {FP_FAMS.filter(([k]) => k !== 'all' && famsPresent.has(k)).map(([k, lb, c]) => (
            <button key={k} className={`hm-chip ${fam === k ? 'on' : ''}`} onClick={() => setFam(fam === k ? 'all' : k)}>
              <span className="hm-chip-dot" style={{ background: c }}></span>
              <span>{TX(lb)}</span>
            </button>
          ))}
        </div>
      ) : null}
      {(tab === 'atraiter' || tab === 'tous') ? (
        <div className="md-list" key={tab + fam}>
          {famFilter(A.requests).map((q) => (
            <div key={q.id} className="md-req">
              <button className="hm-row" onClick={() => open('request', q)}>
                <MdOrgAvatar name={q.org} size={32} />
                <span className="hm-row-txt"><b>{q.org}</b><em>{TX(q.for)}</em></span>
                <HMI.Chevron c="#A1A1AA" />
              </button>
              <div className="md-req-acts">
                <button className="a3-act pri" onClick={() => approve(q)}>{TX('Approuver')}</button>
                <button className="a3-act" onClick={() => refuse(q)}>{TX('Refuser')}</button>
              </div>
            </div>
          ))}
          {tab === 'atraiter' && famFilter(A.processed || []).length ? (
            <React.Fragment>
              <div className="hm-sechead" style={{ marginTop: famFilter(A.requests).length ? 4 : 0 }}><span>{TX('Traitées')}<i className="ac-count">{famFilter(A.processed || []).length}</i></span></div>
              {famFilter(A.processed || []).map((x) => card('request', x))}
            </React.Fragment>
          ) : null}
          {tab === 'tous' ? (
            <React.Fragment>
              {tousGroups.map(([pid, pairs]) => pid ? personCard(pid, pairs) : card(pairs[0][0], pairs[0][1]))}
              {famFilter(A.expired).map((x) => card('expired', x))}
            </React.Fragment>
          ) : null}
          {(() => {
            const nA = famFilter(A.requests).length + (tab === 'atraiter' ? famFilter(A.processed || []).length : 0);
            const nRest = tab === 'tous' ? (famFilter(A.mandates).length + famFilter([...A.received, ...A.given]).length + famFilter(A.expired).length) : 0;
            return (nA + nRest) ? null : <div className="hm-empty">{TX(tab === 'tous' ? 'Aucune autorisation pour le moment.' : 'Rien à traiter pour le moment.')}</div>;
          })()}
        </div>
      ) : null}
      {tab === 'autorisations' ? (
        <div className="md-subrow">
          <div className="pg-seg">
            <button className={dir === 'recues' ? 'on' : ''} onClick={() => setDir('recues')}>{TX('Reçues')}</button>
            <button className={dir === 'donnees' ? 'on' : ''} onClick={() => setDir('donnees')}>{TX('Données')}</button>
          </div>
          <span className="md-subhint">{TX(dir === 'recues' ? 'On vous autorise · vous agissez en leur nom' : 'Vous autorisez · ils agissent en votre nom')}</span>
        </div>
      ) : null}
      {tab === 'delegations' ? <div className="md-subrow"><span className="md-subhint">{TX('Jamais plus que le parent · jamais re-délégable · révoquée en cascade avec lui')}</span></div> : null}
      {tab === 'acces' ? <div className="md-legend"><span><i className="md-lvl"><MdEye /></i> {TX('Lire')}</span><span><i className="md-lvl"><HMI.Pen s={12} /></i> {TX('Modifier')}</span><span><i className="md-lvl"><MdSign /></i> {TX('Signer')}</span><span><i className="md-lvl no"><MdBlock /></i> {TX('Aucun accès')}</span></div> : null}

      {(tab !== 'atraiter' && tab !== 'tous') ? <div className="md-list" key={tab + dir + fam}>{body.length ? body : <div className="hm-empty">{TX('Rien ici pour le moment.')}</div>}</div> : null}
      {tab === 'acces' ? <div className="hm-listfoot" style={{ marginTop: -8 }}><FPIcon.Shield s={10} /> {TX('Le coffre-fort n\u2019est jamais délégable · l\u2019accès aux documents reste strictement personnel')}</div> : null}

      <div className="hm-listfoot"><FPIcon.Shield s={10} /> {TX('Journal infalsifiable · SHA-256 · horodatage eIDAS · conforme FIFA')}</div>
      </React.Fragment>
      )}

      {sel ? <MdSheet sel={sel} onClose={() => setSel(null)} arm={arm} setArm={setArm}
        approve={approve} refuse={refuse} revoke={revoke} showToast={showToast} /> : null}
      {creating ? <MdNewAuth onClose={() => setCreating(false)} onCreate={createAuth} /> : null}
    </PgPage>
  );
}

// ═══════ FEUILLE DÉTAIL ═══════
function MdSheet({ sel, onClose, arm, setArm, approve, refuse, revoke, showToast }) {
  const { kind, item } = sel;
  const [more, setMore] = useStateMd(false); // « Détails & garanties » repliés par défaut
  const [panel, setPanel] = useStateMd('main'); // main | broadcast | modify | revoke
  const [bSent, setBSent] = useStateMd(false);
  const [bTo, setBTo] = useStateMd(null); // null = tous les contacts clubs cochés
  const [bProof, setBProof] = useStateMd(true);
  const [bQ, setBQ] = useStateMd('');
  const [mLevel, setMLevel] = useStateMd(item.level || 'Négociation');
  const [mUntil, setMUntil] = useStateMd(item.until || '');
  const [mMsg, setMMsg] = useStateMd('');
  const [bMsg, setBMsg] = useStateMd(() => {
    const p2 = item.pid ? FP_PEERS[item.pid] : null;
    const iss = item.dirIn === false ? mdMe() : (p2 ? window.fpUpperLast(p2.name) : (item.org || ''));
    return 'Bonjour, je représente ' + iss + '. Je reste à votre disposition pour échanger sur les prochaines étapes. · ' + mdMe();
  });
  const p = item.pid ? FP_PEERS[item.pid] : null;
  const title = kind === 'mandate' ? 'Autorisation de représentation'
    : kind === 'request' ? 'Demande d’autorisation'
    : kind === 'access' ? 'Accès de ' + (p ? window.fpUpperLast(p.name) : '')
    : kind === 'deleg' ? 'Sous-autorisation'
    : 'Détails de l\u2019autorisation';
  const row = (k, v, cls) => (v ? <div className="pg-inforow"><span>{TX(k)}</span><b className={cls || ''} style={{ maxWidth: 190, textAlign: 'right' }}>{TX(String(v))}</b></div> : null);
  const other = p ? window.fpUpperLast(p.name) : (item.org || '');
  // Sens : dirIn = l'autre est l'émetteur, moi bénéficiaire
  const issuer = item.dirIn === false ? mdMe() : other;
  const benef = item.dirIn === false ? other : mdMe();
  // Nom toujours sur UNE ligne : la taille s'ajuste à la longueur
  const mdName = (n) => <b style={{ fontSize: String(n).length > 20 ? 10.5 : String(n).length > 15 ? 11.5 : 13, whiteSpace: 'nowrap' }}>{n}</b>;
  const list = kind === 'deleg' ? 'delegations' : kind === 'mandate' ? 'mandates' : (window.FP_AUTH.given.some((x) => x.id === item.id) ? 'given' : 'received');
  const isLive = (kind === 'mandate' || kind === 'auth' || kind === 'deleg') && (item.status === 'active' || item.status === 'soon');
  const clearTerr = (t) => !t ? '' : /monde/i.test(t) ? 'partout dans le monde' : /europe/i.test(t) ? 'en Europe' : /ligue 1/i.test(t) ? 'en Ligue 1' : /ligue 2/i.test(t) ? 'en Ligue 2' : 'sur ' + t;
  const reachOf = (t) => {
    const meta = (window.MD_TERR_META || {})[t] || {};
    const n = (String(meta.sub || '').match(/(\d+)\s*club/i) || [])[1];
    if (/monde/i.test(t)) return { label: 'vos contacts clubs', count: null };
    if (/europe/i.test(t)) return { label: 'clubs européens', count: null };
    if (n) return { label: 'clubs de ' + t.replace(/^.*·\s*/, ''), count: +n };
    return { label: t || 'clubs concernés', count: null };
  };
  const reach = reachOf(item.terr);
  const reachLabel = (reach.count ? reach.count + ' ' : '') + reach.label + ' · message signé de votre part';
  const sendLabel = 'Envoyer à ' + (reach.count ? reach.count + ' ' : '') + reach.label;
  const clr = clearTerr(item.terr);
  // ── Diffusion : destinataires côté clubs, cochables (même patron que l'invitation agenda) ──
  const MD_CLUB_ROLES = ['Recruteur', 'Directeur sportif', 'Entraîneur', 'CEO', 'Président', 'Club'];
  const bAll = Object.values(FP_PEERS).filter((x) => MD_CLUB_ROLES.includes(x.role));
  const toIds = bTo || bAll.map((x) => x.id);
  const bToggle = (id) => setBTo(toIds.includes(id) ? toIds.filter((i) => i !== id) : [...toIds, id]);
  const bDeacc = (s) => String(s || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
  const bList = bAll.filter((x) => { const q = bDeacc(bQ.trim()); return !q || bDeacc(x.name + ' ' + TX(x.role) + ' ' + (x.org || '')).includes(q); });
  const summarySentence = (item.dirIn === false ? mdMe() + ' autorise ' + other : other + ' vous autorise') + " à " + String(item.for || '').toLowerCase() + (clr ? ', ' + clr : '') + (item.until && item.from ? ", jusqu'au " + item.until : '') + '.';

  return (
    <PgSheet onClose={onClose} title={title} centerTitle={kind !== 'access'}>
      {kind === 'access' ? (
        <React.Fragment>
          <div className="pg-info">
            {item.rows.map(([doc, lvl]) => (
              <div key={doc} className="pg-inforow"><span style={{ maxWidth: 170 }}>{TX(doc)}</span>
                <b className="md-lvls">{lvl === 'no' ? <i className="md-lvl no"><MdBlock /></i> : (
                  <React.Fragment>
                    <i className="md-lvl"><MdEye /></i>
                    {lvl.includes('w') ? <i className="md-lvl"><HMI.Pen s={12} /></i> : null}
                    {lvl.includes('s') ? <i className="md-lvl"><MdSign /></i> : null}
                  </React.Fragment>
                )}</b>
              </div>
            ))}
            <div className="pg-inforow"><span>{TX('Coffre-fort')}</span><b>{TX('Jamais délégable · accès personnel')}</b></div>
          </div>
          <div className="pg-sheet-actions">
            <button className="pg-btn primary" onClick={() => { showToast(TX('Permissions mises à jour · effet immédiat')); onClose(); }}>{TX('Modifier les permissions')}</button>
            <button className="pg-btn" onClick={() => { showToast(TX('Historique des accès · 12 événements')); }}>{TX('Historique')}</button>
          </div>
        </React.Fragment>
      ) : (
        <React.Fragment>
          {p ? (
            <div className="md-parties">
              <span className="md-party"><FPAvatar size={40} initials={item.dirIn === false ? (FP_ME.initials || 'AD') : p.initials} src={item.dirIn === false ? FP_ME.photo : p.photo} />{mdName(issuer)}{mdIdLine(item.dirIn === false ? FP_ME : p)}<em>{TX('Émetteur')}</em></span>
              <i className="md-chain-arr big"><MdArrow s={14} /></i>
              <span className="md-party"><FPAvatar size={40} initials={item.dirIn === false ? p.initials : (FP_ME.initials || 'AD')} src={item.dirIn === false ? p.photo : FP_ME.photo} />{mdName(benef)}{mdIdLine(item.dirIn === false ? p : FP_ME)}<em>{TX('Bénéficiaire')}</em></span>
            </div>
          ) : null}
          {!p && item.org ? (
            <div className="md-orghead">
              <MdOrgAvatar name={item.org} size={56} editable onChange={() => showToast(TX('Logo enregistré · ') + item.org)} />
              <b>{item.org}</b>
            </div>
          ) : null}
          <div className="md-herostat">
            {mdPill(item.status)}
          </div>

          {/* Ce que ça permet · l'essentiel, lisible en 2 secondes */}
          {item.pendingChange ? (<div className="md-banner"><span className="md-banner-ic"><HMI.Pen s={12} /></span><span><b>{TX('Modification proposée')}</b> · {TX('en attente de la validation de')} {other}</span></div>) : null}

          {isLive && panel === 'broadcast' ? (
            <div className="md-panel">
              {bSent ? (
                <div className="md-sent">
                  <span className="md-sent-ic"><MdCheck s={30} /></span>
                  <b>{TX('Message envoyé')}</b>
                  <em>{toIds.length} {TX(toIds.length > 1 ? 'destinataires' : 'destinataire')}{bProof ? ' · ' + TX('preuve d’autorisation jointe') : ''}</em>
                  <button className="pg-btn primary" onClick={() => { setBSent(false); setPanel('main'); }}>{TX('Terminé')}</button>
                </div>
              ) : (
                <React.Fragment>
                  <button className="md-panel-back" onClick={() => setPanel('main')}><MdArrow s={12} /> {TX('Retour')}</button>
                  <div className="md-panel-h"><b>{TX('Contacter les clubs concernés')}</b><em>{TX('choisissez qui reçoit le message · envoyé dans Messages, signé de votre part')}</em></div>
                  <div className="md-blbl-row"><span>{TX('Destinataires')} · {toIds.length}/{bAll.length}</span><button className="md-selall" onClick={() => setBTo(toIds.length === bAll.length ? [] : bAll.map((x) => x.id))}>{TX(toIds.length === bAll.length ? 'Aucun' : 'Tous')}</button></div>
                  <div className="ag-contacts">
                    <div className="ag-contact-search">
                      <input value={bQ} onChange={(e) => setBQ(e.target.value)} placeholder={TX('Rechercher un contact…')} />
                      {bQ ? <button className="ag-contact-clear" onClick={() => setBQ('')} aria-label="Effacer">×</button> : null}
                    </div>
                    {bList.length ? bList.map((r) => {
                      const on = toIds.includes(r.id);
                      return (
                        <button key={r.id} className={`ag-contact ${on ? 'on' : ''}`} onClick={() => bToggle(r.id)}>
                          <FPAvatar size={26} initials={r.initials} src={r.photo} />
                          <span className="ag-contact-txt">
                            <b>{window.fpUpperLast(r.name)}</b>
                            <em>{TX(r.role)}{r.org ? ' · ' + r.org : ''}</em>
                          </span>
                          <span className="ag-check">{on ? '✓' : '+'}</span>
                        </button>
                      );
                    }) : <div className="ag-pick-empty">{TX('Aucun contact trouvé')}</div>}
                  </div>
                  <div className="md-blbl-row"><span>{TX('Message')}</span></div>
                  <textarea className="md-msg" value={bMsg} onChange={(e) => setBMsg(e.target.value)} rows={4}></textarea>
                  <label className={`md-attach ${bProof ? '' : 'off'}`} onClick={() => setBProof(!bProof)}><span className="md-attach-box">{bProof ? <MdCheck s={10} /> : null}</span> {TX('Joindre la preuve d’autorisation vérifiable')}</label>
                  <div className="md-blbl-row"><span>{TX('Aperçu')} · {TX('ce que chaque destinataire recevra')}</span></div>
                  <div className="md-pv">
                    <div className="md-pv-from"><FPAvatar size={18} initials={FP_ME.initials || 'AD'} src={FP_ME.photo} /> {mdMe()} · {TX(FP_ME.role || 'Agent')}</div>
                    <div className="md-pv-bubble">{bMsg.trim() || TX('Votre message…')}</div>
                    {bProof ? (
                      <div className="md-pv-proof">
                        <i><FPIcon.Shield s={16} c="currentColor" /></i>
                        <span>
                          <b>{TX('Preuve d’autorisation')} · {issuer} → {benef}</b>
                          <em>{TX(item.level)} · {TX(item.terr)}{item.until ? ' · ' + TX("jusqu'au") + ' ' + TX(item.until) : ''} · {TX('lien vérifiable')}</em>
                        </span>
                      </div>
                    ) : null}
                  </div>
                  <button className="pg-btn primary" disabled={!toIds.length || !bMsg.trim()} onClick={() => { setBSent(true); showToast(TX('Message envoyé') + ' · ' + toIds.length + ' ' + TX(toIds.length > 1 ? 'destinataires' : 'destinataire')); }}>{TX('Envoyer à')} {toIds.length} {TX(toIds.length > 1 ? 'destinataires' : 'destinataire')}</button>
                </React.Fragment>
              )}
            </div>
          ) : isLive && panel === 'modify' ? (
            <div className="md-panel">
              <button className="md-panel-back" onClick={() => setPanel('main')}><MdArrow s={12} /> {TX('Retour')}</button>
              <div className="md-panel-h"><b>{TX('Proposer une modification')}</b><em>{TX('Validée par les deux parties avant de prendre effet')}</em></div>
              <div className="md-field"><label>{TX("Niveau d'action")}</label><div className="md-seg">{['Information', 'Négociation'].map((l) => <button key={l} className={mLevel === l ? 'on' : ''} onClick={() => setMLevel(l)}>{TX(l)}</button>)}</div></div>
              <div className="md-field"><label>{TX('Échéance')}</label><input className="md-input" value={mUntil} onChange={(e) => setMUntil(e.target.value)} /></div>
              <div className="md-field"><label>{TX('Message pour')} {other} <span className="md-opt">{TX('(optionnel)')}</span></label><textarea className="md-msg" value={mMsg} onChange={(e) => setMMsg(e.target.value)} rows={3}></textarea></div>
              <p className="md-note">{TX('Rien ne change tant que')} {other} {TX("ne l'a pas validé · vous serez notifié dès sa réponse.")}</p>
              <button className="pg-btn primary" onClick={() => { item.pendingChange = { level: mLevel, until: mUntil, msg: mMsg }; showToast(TX('Proposition envoyée · en attente de ') + other); setPanel('main'); }}>{TX('Envoyer la proposition')}</button>
            </div>
          ) : isLive ? (
            <React.Fragment>
              <div className="md-scopelbl">{TX('Périmètre')}</div>
              <div className="md-chiprow center">
                <span className="md-sumchip"><MdMeter level={item.level} /> {TX(item.level)}</span>
                <span className="md-sumchip"><MdTerr t={item.terr} /></span>
                {item.until ? <span className="md-sumchip mut">{TX("jusqu'au")} {TX(item.until)}</span> : null}
              </div>
              <div className="md-actzone">
                <button className="md-act primary" onClick={() => { setBSent(false); setPanel('broadcast'); }}>
                  <span className="md-act-ic"><MdSend /></span>
                  <span className="md-act-txt"><b>{TX('Contacter les clubs concernés')}</b><em>{(reach.count ? reach.count + ' ' : '') + reach.label}</em></span>
                  <span className="md-act-go"><MdArrow s={13} /></span>
                </button>
                <button className="md-act" onClick={() => showToast(TX('Preuve d’autorisation partagée · lien vérifiable copié'))}>
                  <span className="md-act-ic soft"><MdShare /></span>
                  <span className="md-act-txt"><b>{TX('Partager la preuve')}</b><em>{TX(item.visib === 'priv' ? 'autorisation privée · partage restreint' : 'aux personnes concernées · lien vérifiable')}</em></span>
                  <span className="md-act-go"><MdArrow s={13} /></span>
                </button>
                <button className="md-act" onClick={() => setPanel('modify')}>
                  <span className="md-act-ic soft"><HMI.Pen s={15} /></span>
                  <span className="md-act-txt"><b>{TX('Proposer une modification')}</b><em>{TX('accord des deux parties')}</em></span>
                  <span className="md-act-go"><MdArrow s={13} /></span>
                </button>
              </div>
              <button className={`md-fold ${more ? 'open' : ''}`} onClick={() => setMore(!more)} aria-expanded={more}><span>{TX('Voir tous les détails')}</span><HMI.Chevron c="currentColor" /></button>
              {more ? (
                <div className="pg-info md-foldbody">
                  {row('Objet', item.for)}
                  {item.from ? row('Début', item.from) : null}
                  {row('Fin', item.until)}
                  {row('Type', item.type)}
                  {item.create ? row('Origine', item.create === 'GIFT' ? "Proposée par l'émetteur" : item.create === 'REQUEST' ? 'Demandée par le bénéficiaire' : 'Déléguée depuis une autorisation mère') : null}
                  {item.rev ? row('Retrait', item.rev === 'uni' ? 'À tout moment · prend effet aussitôt' : "D'un commun accord · les deux parties confirment") : null}
                  {item.visib ? row('Visibilité', item.visib === 'pub' ? 'Publique' : 'Privée') : null}
                  {item.acc ? (<div className="pg-inforow"><span>{TX('Accord bilatéral')}</span><b className={item.acc.i && item.acc.b ? 'ok' : ''}>{item.acc.i ? <MdCheck /> : '·'} {TX('Émetteur')} &nbsp; {item.acc.b ? <MdCheck /> : '·'} {TX('Bénéficiaire')}</b></div>) : null}
                  {item.note ? row('Note', item.note) : null}
                  {item.num ? <div className="pg-inforow"><span>{TX('Référence')}</span><b className="pg-mono">{item.num}</b></div> : null}
                  <div className="pg-inforow"><span>{TX('Journal')}</span><b>{TX('Infalsifiable · chaque action horodatée et signée')}</b></div>
                </div>
              ) : null}
              <div className="md-livefoot">
                {panel === 'revoke' ? (
                  <div className="md-revoke-confirm">
                    <p>{item.rev === 'bi' ? TX("La révocation devra être confirmée par l'autre partie.") : TX('Effet immédiat · toutes les parties sont notifiées.')}</p>
                    <div className="md-revoke-acts">
                      <button className="pg-btn" onClick={() => setPanel('main')}>{TX('Annuler')}</button>
                      <button className="pg-btn danger" onClick={() => revoke(list, item)}>{item.rev === 'bi' ? TX('Demander la révocation') : TX('Confirmer')}</button>
                    </div>
                  </div>
                ) : (
                  <button className="md-revoke-link" onClick={() => setPanel('revoke')}>{TX('Révoquer cette autorisation')}</button>
                )}
              </div>
            </React.Fragment>
          ) : (
          <React.Fragment>
          <div className="pg-info">
            {item.level ? (
              <div className="pg-inforow"><span>{TX('Niveau d\u2019action')}</span><b className="md-lvls"><MdMeter level={item.level} big /> {TX(item.level)}</b></div>
            ) : null}
            {item.terr ? <div className="pg-inforow"><span>{TX('Territoire')}</span><b className="md-lvls"><MdTerr t={item.terr} big /></b></div> : null}
            {item.from ? row('Début', item.from) : null}
            {row(item.from ? 'Fin' : 'Validité', item.until)}
            {row('Objet', item.for)}
            {!item.level ? row('Type', item.type) : null}
            {item.chain ? <div className="pg-inforow"><span>{TX('Chaîne')}</span><b style={{ maxWidth: 190, textAlign: 'right' }}>{item.chain.map((n) => (n === 'MOI' ? mdMe() : n)).join(' → ')}</b></div> : null}
          </div>

          {/* Légende · lire la fiche en un coup d'œil */}
          {item.level ? (
            <div className="md-legend">
              <div className="md-legend-title"><FPIcon.Shield s={11} c="currentColor" /> {TX('Comment lire cette autorisation')}</div>
              <div className="md-legend-row"><i className="md-lg-seg one"></i><b>{TX('Information')}</b><span>{TX('consulter, être tenu informé')}</span></div>
              <div className="md-legend-row"><i className="md-lg-seg two"></i><b>{TX('Négociation')}</b><span>{TX('discuter, engager et mener les échanges')}</span></div>
              <p className="md-legend-note">{TX('La négociation inclut l’information. Le badge atteste de l’autorisation · il ne bloque rien : il prouve qui agit, pour qui, et jusqu’où. La signature reste toujours l’acte du joueur.')}</p>
            </div>
          ) : null}

          {/* Détails & garanties · la profondeur à la demande */}
          {(item.create || item.acc || item.rev || item.num) ? (
            <button className={`md-fold ${more ? 'open' : ''}`} onClick={() => setMore(!more)} aria-expanded={more}>
              <span>{TX('Détails & garanties')}</span>
              <HMI.Chevron c="currentColor" />
            </button>
          ) : null}
          {more ? (
          <div className="pg-info md-foldbody">
            {item.level ? row('Type', item.type) : null}
            {item.create ? row('Origine', item.create === 'GIFT' ? 'Proposée par l\u2019émetteur' : item.create === 'REQUEST' ? 'Demandée par le bénéficiaire' : 'Déléguée depuis une autorisation mère') : null}
            {item.rev ? row('Retrait', item.rev === 'uni' ? 'À tout moment · prend effet aussitôt' : 'D\u2019un commun accord · les deux parties doivent confirmer') : null}
            {item.visib ? row('Visibilité', item.visib === 'pub' ? 'Publique · affiche « Autorisé par ' + issuer + ' »' : 'Privée · affiche « Représentant autorisé »') : null}
            {item.create === 'SUB' ? row('Re-délégation', 'Impossible · une sous-autorisation ne se redélègue pas')
              : item.deleg !== undefined ? row('Délégation', item.deleg ? 'Autorisée · fixée à la création' : 'Impossible · fixée à la création, définitive') : null}
            {item.acc ? (
              <div className="pg-inforow"><span>{TX('Accord bilatéral')}</span>
                <b className={item.acc.i && item.acc.b ? 'ok' : ''}>
                  {item.acc.i ? <MdCheck /> : '·'} {TX('Émetteur')} &nbsp; {item.acc.b ? <MdCheck /> : '·'} {TX('Bénéficiaire')}
                </b>
              </div>
            ) : null}
            {item.note ? row('Note', item.note) : null}
            {item.num ? <div className="pg-inforow"><span>{TX('Référence')}</span><b className="pg-mono">{item.num}</b></div> : null}
            <div className="pg-inforow"><span>{TX('Journal')}</span><b>{TX('Infalsifiable · chaque action horodatée et signée')}</b></div>
          </div>
          ) : null}
          <div className="pg-sheet-actions">
            {kind === 'request' ? (
              (item.status === 'approved' || item.status === 'refused') ? (
                <div className="hm-empty" style={{ paddingTop: 4 }}>{TX(item.status === 'approved' ? 'Demande approuvée' : 'Demande refusée')}{item.until ? ' · ' + TX(item.until) : ''}</div>
              ) : (
                <React.Fragment>
                  <button className="pg-btn primary" onClick={() => approve(item)}>{TX('Approuver la demande')}</button>
                  <button className="pg-btn danger" onClick={() => refuse(item)}>{TX('Refuser')}</button>
                </React.Fragment>
              )
            ) : kind === 'expired' ? (
              <React.Fragment>
                <button className="pg-btn primary" onClick={() => { showToast(TX('Demande de renouvellement envoyée à ') + (item.title || '')); onClose(); }}>{TX('Renouveler')}</button>
                <button className="pg-btn" onClick={() => showToast(TX('Historique des accès · 12 événements'))}>{TX('Historique')}</button>
              </React.Fragment>
            ) : item.status === 'pending' ? (
              <button className="pg-btn" onClick={() => { showToast(TX('Rappel envoyé · en attente de l\u2019accord de ') + other); onClose(); }}>{TX('Relancer pour accord')}</button>
            ) : item.status === 'revpending' ? (
              <button className="pg-btn" onClick={() => { showToast(TX('Révocation demandée · l\u2019autre partie doit confirmer')); onClose(); }}>{TX('En attente de confirmation')}</button>
            ) : (
              <React.Fragment>
                {kind === 'mandate' ? <button className="pg-btn primary" onClick={() => { showToast(TX('PDF signé exporté · ') + item.num); onClose(); }}>{TX('Télécharger le PDF')}</button> : null}
                {item.rev === 'bi' ? (
                  <button className="pg-btn danger" onClick={() => revoke(list, item)}>{TX('Demander la révocation')}</button>
                ) : (
                  <button className="pg-btn danger" onClick={() => (arm ? revoke(list, item) : setArm(true))}>{arm ? TX('Confirmer la révocation') : TX('Révoquer · effet immédiat')}</button>
                )}
              </React.Fragment>
            )}
          </div>
          {arm ? <div className="hm-empty" style={{ paddingTop: 4 }}>{TX(kind === 'mandate' && item.deleg ? 'Effet immédiat · les sous-autorisations dépendantes seront invalidées en cascade.' : 'Effet immédiat · toutes les parties sont notifiées, l\u2019autorisation est archivée.')}</div> : null}
          </React.Fragment>
          )}
        </React.Fragment>
      )}
    </PgSheet>
  );
}

Object.assign(window, { MandatesPage, mdPill, mdLevelIc, MdMeter, MdArrow, MdEye, MdSign, MdCheck });
