/* ============================================================================
   CléPass v3 — popup « Modifier » (portage de CléPass Éditeur.dc.html).
   Composant importé par l'app principale (dc-import → <CPEditeur …/>).

   Elle édite la FICHE RÉELLEMENT SÉLECTIONNÉE : les blocs et les champs sont
   remplis depuis `item` (la fiche déchiffrée), et « Enregistrer » renvoie la
   saisie à l'application via onSave() — qui la range dans le coffre. Les
   valeurs de démonstration d'origine (« Gd7$k2!pQ9xL4m », « DCAF-5G »,
   « MAIRIE JULLIÉ »…) ont disparu : elles masquaient le contenu du coffre et
   pouvaient être enregistrées par erreur.

   Props :
     item      fiche à éditer : { name, sub, urls, blocks, user, password, … }
               — même structure que l'éditeur intégré (app.jsx / mkBlock)
     onSave    (données) → true si l'enregistrement a été accepté
     onClose   fermeture sans enregistrer
     customCats types personnalisés proposés dans « Ajouter un élément »
   ============================================================================ */
(function () {
const { sx, Hv, DCLogic } = window.CP;

class CPEditeur extends DCLogic {
  state = {
    order: null,
    data: null,
    open: {},
    reveal: {},          // mots de passe affichés en clair, par bloc
    seq: 1,
    addOpen: false,
    dragId: null,
    overId: null,
    info: null,
    errNom: false,
  };
  /* Tic-tac TOTP : re-rend la popup chaque seconde tant qu'un bloc 2FA a une
     clé valide, pour faire tourner le code (fenêtre de 30 s). */
  componentDidMount() {
    this._otpTick = setInterval(() => {
      const d = this.state.data || {};
      for (const k in d) if (d[k].type === 'otp' && window.CP.totp(d[k].secret || '')) { this.setState({ otpNow: Date.now() }); return; }
    }, 1000);
  }
  componentWillUnmount() { if (this._otpTick) clearInterval(this._otpTick); }
  /* Types de blocs : ceux que le coffre sait réellement stocker (mêmes clés
     que app.jsx / mkBlock). L'ancien type « QR code » a été retiré : il n'avait
     aucun équivalent dans la fiche, sa saisie n'aurait donc pas pu être
     enregistrée. Le QR du Wi-Fi, lui, est bien généré depuis le SSID et la clé
     (voir la fiche → section Wi-Fi). */
  meta() {
    return {
      pw: { bg: '#fdeede', fg: '#b87e2e', label: 'Mot de passe simple', desc: 'Identifiant + mot de passe' },
      otp: { bg: '#def3ec', fg: '#2e8f72', label: 'Code 2FA', desc: 'Authenticator (TOTP)' },
      wifi: { bg: '#dfeafc', fg: '#3a6fb8', label: 'Réseau Wi-Fi', desc: 'SSID + clé' },
      card: { bg: '#fbe2ea', fg: '#bb4f78', label: 'Carte bancaire', desc: 'Numéro + expiration' },
      notes: { bg: '#efe7fc', fg: '#6b4fb8', label: 'Note sécurisée', desc: 'Texte libre chiffré' },
    };
  }
  blankFields(type) {
    if (type === 'pw') return { user: '', password: '' };
    if (type === 'otp') return { issuer: '', secret: '' };
    if (type === 'wifi') return { ssid: '', wifiPass: '', security: 'WPA2' };
    if (type === 'card') return { number: '', exp: '', cvv: '' };
    if (type === 'notes') return { text: '' };
    return {};
  }
  /* Champs d'un bloc de la fiche → champs de la popup. */
  champsDepuisBloc(b) {
    if (b.type === 'pw' || b.type === 'o365') return { user: b.user || '', password: b.password || '' };
    if (b.type === 'otp') return { issuer: b.issuer || '', secret: b.secret || '' };
    if (b.type === 'wifi') return { ssid: b.ssid || '', wifiPass: b.wifiPass || '', security: b.security || 'WPA2' };
    if (b.type === 'card') return { number: b.number || '', exp: b.exp || '', cvv: b.cvv || '' };
    if (b.type === 'notes') return { text: b.text || '' };
    return {};
  }
  genPassword() {
    const cs = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789!@#$%&*?';
    let s = '';
    for (let i = 0; i < 16; i++) s += cs[Math.floor(Math.random() * cs.length)];
    return s;
  }
  /* Jauge de force du mot de passe saisi (l'ancienne barre était figée sur
     « FORT » quel que soit le contenu). */
  forceMdp(mdp) {
    const p = String(mdp || '');
    if (!p) return { pct: 0, label: '', color: '#e2d8e6' };
    let n = 0;
    if (p.length >= 8) n++;
    if (p.length >= 12) n++;
    if (p.length >= 16) n++;
    if (/[a-z]/.test(p) && /[A-Z]/.test(p)) n++;
    if (/\d/.test(p)) n++;
    if (/[^A-Za-z0-9]/.test(p)) n++;
    if (n <= 2) return { pct: 30, label: 'FAIBLE', color: '#c0392b' };
    if (n <= 4) return { pct: 65, label: 'MOYEN', color: '#d98a2b' };
    return { pct: 100, label: 'FORT', color: '#2e8f72' };
  }
  iconEl(type) {
    const svg = (kids) => React.createElement('svg', { viewBox: '0 0 24 24', width: 16, height: 16, fill: 'none', stroke: 'currentColor', strokeWidth: 1.9 }, kids);
    if (type === 'otp') return svg([React.createElement('circle', { key: 1, cx: 12, cy: 12, r: 8 }), React.createElement('path', { key: 2, d: 'M12 8v4l3 2' })]);
    if (type === 'wifi') return svg([React.createElement('path', { key: 1, d: 'M4.5 11a11 11 0 0 1 15 0M7.5 14a7 7 0 0 1 9 0' }), React.createElement('circle', { key: 2, cx: 12, cy: 17.5, r: 1, fill: 'currentColor', stroke: 'none' })]);
    if (type === 'notes') return svg([React.createElement('path', { key: 1, d: 'M7 3h7l4 4v12a1.5 1.5 0 0 1-1.5 1.5h-9.5A1.5 1.5 0 0 1 5.5 19V4.5A1.5 1.5 0 0 1 7 3z' }), React.createElement('path', { key: 2, d: 'M14 3v4h4' })]);
    if (type === 'card') return svg([React.createElement('rect', { key: 1, x: 3, y: 6, width: 18, height: 12, rx: 2 }), React.createElement('path', { key: 2, d: 'M3 10h18' })]);
    return svg([React.createElement('circle', { key: 1, cx: 7, cy: 16, r: 3 }), React.createElement('path', { key: 2, d: 'M9.3 13.7l8-8M15 5l3 3' })]);
  }
  reorder(from, to) {
    if (!from || from === to) return;
    this.setState((s) => {
      const fromIdx = s.order.indexOf(from);
      const toIdx = s.order.indexOf(to);
      if (fromIdx < 0 || toIdx < 0) return {};
      const order = s.order.slice();
      order.splice(fromIdx, 1);
      const nt = order.indexOf(to);
      const at = fromIdx < toIdx ? nt + 1 : nt;
      order.splice(at, 0, from);
      return { order };
    });
  }
  addType(type) {
    this.setState((s) => {
      const id = 'e' + s.seq;
      const m = this.meta()[type];
      return {
        order: s.order.concat(id),
        data: Object.assign({}, s.data, { [id]: Object.assign({ type, name: m.label }, this.blankFields(type)) }),
        open: Object.assign({}, s.open, { [id]: true }),
        seq: s.seq + 1,
        addOpen: false,
      };
    });
  }
  /* Type personnalisé (Office 365…) : bloc identifiants portant le nom et
     les couleurs du type. */
  addCustomType(c) {
    this.setState((s) => {
      const id = 'e' + s.seq;
      return {
        order: s.order.concat(id),
        data: Object.assign({}, s.data, { [id]: Object.assign({ type: 'pw', name: c.label, bg: c.bg, fg: c.fg }, this.blankFields('pw')) }),
        open: Object.assign({}, s.open, { [id]: true }),
        seq: s.seq + 1,
        addOpen: false,
      };
    });
  }
  /* Blocs de la fiche réelle. Si elle n'a pas encore de blocs enregistrés
     (fiche jamais éditée), ils sont déduits de ses drapeaux de type — même
     règle que l'éditeur intégré, pour que les deux voies montrent la même
     chose. Les blocs de type personnalisé (cat:…) sont ignorés ici : la popup
     ne sait pas les éditer, l'application les conserve à l'enregistrement. */
  blocsDeLaFiche() {
    const it = this.props.item || {};
    if (it.blocks && it.blocks.length) return it.blocks.filter((b) => String(b.type).indexOf('cat:') !== 0);
    const t = [];
    if (it.o365) t.push('o365'); else if (it.pw) t.push('pw');
    if (it.twofa) t.push('otp');
    if (it.wifi) t.push('wifi');
    if (it.card) t.push('card');
    if (it.notes) t.push('notes');
    if (!t.length) t.push('pw');
    return t.map((type) => {
      if (type === 'pw' || type === 'o365') return { type: type, user: (it.user && it.user !== '—') ? it.user : '', password: it.password || '' };
      if (type === 'otp') return { type: 'otp', issuer: it.name || '', secret: it.secret || '' };
      if (type === 'wifi') return { type: 'wifi', ssid: it.ssid || '', wifiPass: it.wifiPass || '', security: it.security || 'WPA2' };
      if (type === 'card') return { type: 'card', number: it.cardNumber || '', exp: it.cardExp || '', cvv: it.cardCvv || '' };
      return { type: 'notes', text: it.noteText || '' };
    });
  }
  buildFromItem() {
    const M = this.meta();
    const it = this.props.item || {};
    const order = []; const data = {}; const open = {};
    this.blocsDeLaFiche().forEach((b, i) => {
      const id = 'p' + i;
      /* Un bloc « o365 » est un bloc identifiants portant le nom du type. */
      const tt = b.type === 'o365' ? 'pw' : (M[b.type] ? b.type : 'pw');
      const nom = b.type === 'o365' ? 'Office 365' : M[tt].label;
      data[id] = Object.assign({ type: tt, name: nom, bg: '', fg: '' }, this.champsDepuisBloc(b));
      order.push(id); open[id] = (i === 0);
    });
    if (!order.length) { order.push('p0'); data.p0 = Object.assign({ type: 'pw', name: M.pw.label }, this.blankFields('pw')); open.p0 = true; }
    const sub = (it.sub && it.sub !== '—' && it.sub !== 'Aucun domaine') ? it.sub : '';
    return {
      order, data, open, seq: order.length + 1,
      info: { name: it.name || '', sub: sub },
      urls: (it.urls || []).slice(),
    };
  }
  /* « Enregistrer » : renvoie la saisie à l'application. Tant que le nom est
     vide, la popup reste ouverte avec le champ en erreur — enregistrer une
     fiche sans nom la rendrait introuvable dans la liste. */
  enregistrer() {
    const st = this.state;
    const info = st.info || {};
    if (!(info.name || '').trim()) { this.setState({ errNom: true }); return; }
    const elements = st.order.map((id) => Object.assign({}, st.data[id]));
    const ok = this.props.onSave
      && this.props.onSave({ info: { name: info.name, sub: info.sub || '' }, urls: (st.urls || []).slice(), elements: elements });
    if (ok === false) { this.setState({ errNom: true }); return; }   // refus côté application
    if (this.props.onClose) this.props.onClose();
  }
  setField(id, key, val) { this.setState((s) => ({ data: Object.assign({}, s.data, { [id]: Object.assign({}, s.data[id], { [key]: val }) }) })); }
  setInfo(key, val) { this.setState((s) => ({ info: Object.assign({}, s.info, { [key]: val }) })); }
  renderVals() {
    const st = this.state;
    if (!this._init) {
      this._init = true;
      const b = this.buildFromItem();
      st.order = b.order; st.data = b.data; st.open = b.open; st.seq = b.seq;
      st.info = b.info; st.urls = b.urls; st.urlDraft = '';
    }
    const info = st.info || { name: '', sub: '' };
    const M = this.meta();
    const elements = st.order.map((id) => {
      const e = st.data[id] || { type: 'pw', name: '' };
      const open = !!st.open[id];
      const c = M[e.type] || M.pw;
      const tbg = e.bg || c.bg; const tfg = e.fg || c.fg;
      const dragging = st.dragId === id;
      const anyDrag = !!st.dragId;
      const bodyOpen = open && !anyDrag;
      const myIdx = st.order.indexOf(id);
      const dIdx = st.dragId ? st.order.indexOf(st.dragId) : -1;
      const over = (!!st.dragId && st.dragId !== id && st.overId === id);
      /* Code 2FA « tournant » (TOTP 30 s) calculé depuis la clé du bloc. */
      const lt = e.type === 'otp' ? window.CP.totp(e.secret || '') : null;
      const ltPct = lt ? Math.round((lt.remaining / lt.period) * 1000) / 10 : 0;
      return {
        id, name: e.name, open, bodyOpen, drag: true, showBefore: (over && dIdx > myIdx), showAfter: (over && dIdx < myIdx),
        isPw: e.type === 'pw', isOtp: e.type === 'otp', isWifi: e.type === 'wifi', isCard: e.type === 'card', isNotes: e.type === 'notes',
        user: e.user || '', password: e.password || '', showPw: !!st.reveal[id],
        pwType: st.reveal[id] ? 'text' : 'password',
        pwEyeStyle: 'width:42px;border-radius:11px;background:#f7f3fb;border:1.5px solid #ece5f0;display:grid;place-items:center;flex:none;cursor:pointer;color:' + (st.reveal[id] ? '#6b4fb8' : '#8a7f90'),
        force: this.forceMdp(e.password || ''),
        issuer: e.issuer || '', secret: e.secret || '', text: e.text || '',
        otpLiveCode: lt ? (lt.code.length === 6 ? lt.code.slice(0, 3) + ' ' + lt.code.slice(3) : lt.code) : '— — —',
        otpRemaining: lt ? String(lt.remaining) : '',
        otpRingStyle: `width:34px;height:34px;border-radius:50%;background:conic-gradient(#6b4fb8 0 ${ltPct}%,#e8e0ee ${ltPct}% 100%);display:grid;place-items:center`,
        otpHoleStyle: `width:26px;height:26px;border-radius:50%;background:#fff;display:grid;place-items:center;font:600 10px 'IBM Plex Mono';color:#6b4fb8`,
        ssid: e.ssid || '', wifiPass: e.wifiPass || '', security: e.security || '',
        cardNumber: e.number || '', cardExp: e.exp || '', cardCvv: e.cvv || '',
        onUser: (ev) => this.setField(id, 'user', ev.target.value),
        onPassword: (ev) => this.setField(id, 'password', ev.target.value),
        onGen: (ev) => { if (ev && ev.stopPropagation) ev.stopPropagation(); this.setField(id, 'password', this.genPassword()); },
        onReveal: (ev) => { if (ev && ev.stopPropagation) ev.stopPropagation(); this.setState((s) => ({ reveal: Object.assign({}, s.reveal, { [id]: !s.reveal[id] }) })); },
        onIssuer: (ev) => this.setField(id, 'issuer', ev.target.value),
        onSecret: (ev) => this.setField(id, 'secret', ev.target.value),
        onText: (ev) => this.setField(id, 'text', ev.target.value),
        onSsid: (ev) => this.setField(id, 'ssid', ev.target.value),
        onWifiPass: (ev) => this.setField(id, 'wifiPass', ev.target.value),
        onSecurity: (ev) => this.setField(id, 'security', ev.target.value),
        onCardNumber: (ev) => this.setField(id, 'number', ev.target.value),
        onCardExp: (ev) => this.setField(id, 'exp', ev.target.value),
        onCardCvv: (ev) => this.setField(id, 'cvv', ev.target.value),
        cardStyle: `background:#faf7fd;border:1px solid #f0eaf2;border-radius:15px;padding:14px;display:flex;flex-direction:column;gap:11px;transition:opacity .15s,box-shadow .15s;${dragging ? 'opacity:.5;box-shadow:0 12px 30px -12px rgba(80,50,110,.4);' : ''}`,
        tileStyle: `width:30px;height:30px;border-radius:8px;background:${tbg};color:${tfg};display:grid;place-items:center;flex:none`,
        chevStyle: `display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:7px;transition:transform .2s;transform:rotate(${bodyOpen ? 0 : -90}deg);color:#a98bde;flex:none`,
        onToggle: () => this.setState((s) => ({ open: Object.assign({}, s.open, { [id]: !s.open[id] }) })),
        onRemove: (e2) => { if (e2 && e2.stopPropagation) e2.stopPropagation(); this.setState((s) => { const order = s.order.filter((x) => x !== id); const data = Object.assign({}, s.data); delete data[id]; return { order, data }; }); },
        onDragStart: (e2) => { if (e2 && e2.dataTransfer) { e2.dataTransfer.effectAllowed = 'move'; try { e2.dataTransfer.setData('text/plain', id); } catch (x) {} } setTimeout(() => this.setState({ dragId: id }), 0); },
        onDragEnd: () => this.setState({ dragId: null, overId: null }),
        onDragOver: (e2) => { if (e2 && e2.preventDefault) e2.preventDefault(); if (this.state.dragId && this.state.dragId !== id && this.state.overId !== id) this.setState({ overId: id }); },
        onDrop: (e2) => { if (e2 && e2.preventDefault) e2.preventDefault(); this.reorder(this.state.dragId, id); this.setState({ dragId: null, overId: null }); },
      };
    });
    const n = st.order.length;
    const addOptions = ['pw', 'otp', 'wifi', 'card', 'notes'].map((t) => {
      const m = M[t];
      return { label: m.label, desc: m.desc, icon: this.iconEl(t), tileStyle: `width:32px;height:32px;border-radius:9px;background:${m.bg};color:${m.fg};display:grid;place-items:center;flex:none`, onClick: () => this.addType(t) };
    });
    /* Types personnalisés fournis par l'app (customCats ← st.catList). */
    const addCustomOptions = (this.props.customCats || []).map((c) => ({
      label: c.label, desc: 'Fiche · type personnalisé', icon: c.icon || this.iconEl('pw'),
      tileStyle: `width:32px;height:32px;border-radius:9px;background:${c.bg};color:${c.fg};display:grid;place-items:center;font-size:15px;flex:none`,
      onClick: () => this.addCustomType(c),
    }));
    const urlChips = (st.urls || []).map((u, i) => ({
      label: u,
      style: 'display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;padding:4px 9px;border-radius:999px;background:#dfeafc;color:#3a6fb8;cursor:grab;' + ((st.urlOver === i && st.urlDrag !== null && st.urlDrag !== undefined && st.urlDrag !== i) ? 'box-shadow:inset 0 0 0 2px #7c5cff;' : '') + (st.urlDrag === i ? 'opacity:.45;' : ''),
      onDragStart: (ev) => { if (ev && ev.dataTransfer) { ev.dataTransfer.effectAllowed = 'move'; } this.setState({ urlDrag: i }); },
      onDragEnd: () => this.setState({ urlDrag: null, urlOver: null }),
      onOver: (ev) => { if (ev && ev.preventDefault) ev.preventDefault(); if (this.state.urlDrag !== null && this.state.urlDrag !== undefined && this.state.urlDrag !== i && this.state.urlOver !== i) this.setState({ urlOver: i }); },
      onDrop: (ev) => { if (ev && ev.preventDefault) ev.preventDefault(); const from = this.state.urlDrag; if (from !== null && from !== undefined && from !== i) this.setState((s) => { const a = (s.urls || []).slice(); const m = a.splice(from, 1)[0]; a.splice(i, 0, m); return { urls: a, urlDrag: null, urlOver: null }; }); else this.setState({ urlDrag: null, urlOver: null }); },
      onRemove: (ev) => { if (ev && ev.stopPropagation) ev.stopPropagation(); this.setState((s) => ({ urls: (s.urls || []).filter((x, j) => j !== i) })); },
    }));
    return {
      elements,
      countLabel: n + (n > 1 ? ' éléments' : ' élément') + ' · glissez l’en-tête pour réordonner',
      addMenuOpen: st.addOpen,
      addCustomOptions,
      addBtnStyle: `display:flex;align-items:center;justify-content:center;gap:9px;border:1.5px dashed ${st.addOpen ? '#a98bde' : '#d9cde6'};border-radius:13px;padding:13px;color:#6b4fb8;font-size:13px;font-weight:600;cursor:pointer;background:${st.addOpen ? '#f7f3fb' : 'transparent'}`,
      addOptions,
      onAddToggle: () => this.setState((s) => ({ addOpen: !s.addOpen })),
      onAddClose: () => this.setState({ addOpen: false }),
      onEditorClose: () => { if (this.props.onClose) this.props.onClose(); },
      onSave: () => this.enregistrer(),
      infoName: info.name, infoSub: info.sub || '',
      /* Avatar de la fiche réelle (initiale + couleur), à la place du « g »
         lavande figé du design d'origine. */
      avLettre: ((this.props.item || {}).ini || info.name || '?').charAt(0).toUpperCase() || '?',
      avStyle: "width:68px;height:68px;border-radius:18px;background:" + (((this.props.item || {}).avBg) || '#a98bde') + ";display:grid;place-items:center;font:800 28px 'Sora';color:" + (((this.props.item || {}).avFg) || '#ffffff'),
      nameStyle: 'width:100%;box-sizing:border-box;background:#fff;border:1.5px solid ' + (st.errNom ? '#c0392b' : '#6b4fb8') + ';border-radius:10px;padding:9px 11px;font-size:12.5px;color:#3a3340;font-family:inherit;outline:none',
      errNom: !!st.errNom,
      urlChips, urlDraft: st.urlDraft || '',
      onUrlDraft: (ev) => this.setState({ urlDraft: ev.target.value }),
      onUrlKey: (ev) => { if (ev.key === 'Enter') { if (ev.preventDefault) ev.preventDefault(); const v = (this.state.urlDraft || '').trim(); if (v) this.setState((s) => ({ urls: (s.urls || []).concat(v), urlDraft: '' })); } },
      onInfoName: (ev) => { this.setInfo('name', ev.target.value); if (st.errNom) this.setState({ errNom: false }); },
      onInfoSub: (ev) => this.setInfo('sub', ev.target.value),
      onMenuStop: (e2) => { if (e2 && e2.stopPropagation) e2.stopPropagation(); },
      showEnd: (!!st.dragId && st.overId === '__end__'),
      onEndOver: (e2) => { if (e2 && e2.preventDefault) e2.preventDefault(); if (this.state.dragId && this.state.overId !== '__end__') this.setState({ overId: '__end__' }); },
      onEndDrop: (e2) => { if (e2 && e2.preventDefault) e2.preventDefault(); const from = this.state.dragId; if (from) this.setState((s) => { const order = s.order.filter((x) => x !== from); order.push(from); return { order, dragId: null, overId: null }; }); else this.setState({ dragId: null, overId: null }); },
    };
  }

  render() {
    const v = this.renderVals();
    const F = React.Fragment;
    return (
<div onClick={v.onEditorClose} style={sx("position:fixed;inset:0;background:rgba(40,30,55,.42);backdrop-filter:blur(4px);padding:36px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;font-family:'Sora',system-ui,sans-serif;z-index:100")}>
  <div onClick={v.onMenuStop} style={sx('background:#fff;border-radius:22px;position:relative;border:1px solid #e4dce8;box-shadow:0 30px 70px -26px rgba(40,30,55,.45);overflow:hidden;display:flex;width:900px;max-width:100%;max-height:640px')}>

    <div style={sx('width:216px;flex:none;background:#f5f1fb;padding:22px 16px;display:flex;flex-direction:column;gap:13px;border-right:1px solid #ece5f0')}>
      <div style={sx("font:700 10px 'Sora';letter-spacing:.09em;color:#a99fb0")}>INFOS</div>
      <div style={sx('display:flex;flex-direction:column;align-items:center;gap:11px')}>
        <div style={sx(v.avStyle)}>{v.avLettre}</div>
      </div>
      <div style={sx('display:flex;flex-direction:column;gap:8px')}>
        <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>NOM</div><input value={v.infoName} onChange={v.onInfoName} placeholder="Nom de l'entrée" style={sx(v.nameStyle)} />
          {v.errNom && <div style={sx("font:600 10.5px 'Sora';color:#c0392b;margin:5px 2px 0")}>Le nom est obligatoire.</div>}</div>
        <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>SOUS-TITRE</div><Hv tag="input" value={v.infoSub} onChange={v.onInfoSub} placeholder="Sous-titre" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:10px;padding:9px 11px;font-size:12.5px;color:#3a3340;font-family:inherit;outline:none" focus="border-color:#6b4fb8" /></div>
        <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>SITES WEB</div>
          <div style={sx('display:flex;flex-wrap:wrap;gap:6px;align-items:center')}>
            {v.urlChips.map((u, i) => (<span key={i} draggable={true} onDragStart={u.onDragStart} onDragEnd={u.onDragEnd} onDragOver={u.onOver} onDrop={u.onDrop} title="Glisser pour réordonner" style={sx(u.style)}><svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.9"><circle cx="12" cy="12" r="9"></circle><path d="M3 12h18"></path><path d="M12 3c2.4 2.8 2.4 15.2 0 18M12 3c-2.4 2.8-2.4 15.2 0 18"></path></svg>{u.label}<span onClick={u.onRemove} style={sx('cursor:pointer;font-size:11px;opacity:.6')}>✕</span></span>))}
            <Hv tag="input" value={v.urlDraft} onChange={v.onUrlDraft} onKeyDown={v.onUrlKey} placeholder="Ajouter une URL + Entrée" style="flex:1;min-width:110px;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:10px;padding:8px 11px;font-size:12px;color:#3a3340;font-family:inherit;outline:none" focus="border-color:#6b4fb8" />
          </div>
        </div>
      </div>
    </div>

    <div style={sx('flex:1;min-width:0;display:flex;flex-direction:column')}>
      <div style={sx('padding:20px 24px 12px;display:flex;align-items:flex-start;justify-content:space-between')}>
        <div><div style={sx("font:800 20px 'Sora';color:#2a2431")}>Compte complet</div><div style={sx('font-size:12px;color:#a99fb0;margin-top:3px')}>{v.countLabel}</div></div>
        <div onClick={v.onEditorClose} style={sx('width:32px;height:32px;border-radius:9px;background:#f3ecf3;display:grid;place-items:center;color:#8a7f90;cursor:pointer')}>✕</div>
      </div>

      <div style={sx('padding:6px 24px 16px;flex:1;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:12px')}>
        {v.elements.map((el) => (
          <div key={el.id} style={sx('position:relative')}>
            {el.showBefore && <div style={sx('position:absolute;left:0;right:0;top:-8px;height:3px;background:#7c5cff;border-radius:999px;box-shadow:0 0 0 3px rgba(124,92,255,.18);z-index:5')}></div>}
            <div style={sx(el.cardStyle)} onDragOver={el.onDragOver} onDrop={el.onDrop}>
            <div draggable={el.drag} onDragStart={el.onDragStart} onDragEnd={el.onDragEnd} onClick={el.onToggle} style={sx('display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none')}>
              <span title="Glisser pour réordonner" style={sx('display:inline-grid;grid-template-columns:2.5px 2.5px;gap:3px;flex:none;cursor:grab')}><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span><span style={sx('width:2.5px;height:2.5px;border-radius:50%;background:#c9bcd6')}></span></span>
              <span style={sx(el.tileStyle)}>
                {el.isPw && <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9"><circle cx="7" cy="16" r="3"></circle><path d="M9.3 13.7l8-8M15 5l3 3"></path></svg>}
                {el.isOtp && <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9"><circle cx="12" cy="12" r="8"></circle><path d="M12 8v4l3 2"></path></svg>}
                {el.isWifi && <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9"><path d="M4.5 11a11 11 0 0 1 15 0M7.5 14a7 7 0 0 1 9 0"></path><circle cx="12" cy="17.5" r="1" fill="currentColor" stroke="none"></circle></svg>}
                {el.isNotes && <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9"><path d="M7 3h7l4 4v12a1.5 1.5 0 0 1-1.5 1.5h-9.5A1.5 1.5 0 0 1 5.5 19V4.5A1.5 1.5 0 0 1 7 3z"></path><path d="M14 3v4h4"></path></svg>}
                {el.isCard && <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.9"><rect x="3" y="6" width="18" height="12" rx="2"></rect><path d="M3 10h18"></path></svg>}
              </span>
              <span style={sx("font:700 12px 'Sora';letter-spacing:.03em;color:#3a3340;flex:1;text-transform:uppercase")}>{el.name}</span>
              <span onClick={el.onRemove} title="Retirer" style={sx('width:24px;height:24px;border-radius:7px;display:grid;place-items:center;color:#c0899f;font-size:12px;cursor:pointer')}>✕</span>
              <span title="Replier / déplier" style={sx(el.chevStyle)}><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2.2"><path d="M6 9l6 6 6-6"></path></svg></span>
            </div>

            {el.bodyOpen && <div style={sx('display:flex;flex-direction:column;gap:11px')}>
              {el.isPw && <F>
                <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>IDENTIFIANT</div><input value={el.user} onChange={el.onUser} placeholder="identifiant@exemple.fr" style={sx('width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #6b4fb8;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:inherit;outline:none')} /></div>
                <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>MOT DE PASSE</div>
                  <div style={sx('display:flex;gap:8px')}><Hv tag="input" type={el.pwType} value={el.password} onChange={el.onPassword} placeholder="••••••••" style="flex:1;min-width:0;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:'IBM Plex Mono',monospace;outline:none" focus="border-color:#6b4fb8" /><div onClick={el.onReveal} title="Afficher / masquer" style={sx(el.pwEyeStyle)}><svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="1.7"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"></path><circle cx="12" cy="12" r="2.6"></circle></svg></div><div onClick={el.onGen} title="Générer un mot de passe" style={sx('width:42px;border-radius:11px;background:#efe7fc;border:1.5px solid #e2d4f5;display:grid;place-items:center;color:#6b4fb8;flex:none;cursor:pointer')}><svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M20 11a8 8 0 0 0-14-4.5L4 8M4 4v4h4"></path></svg></div></div>
                  <div style={sx('display:flex;align-items:center;gap:10px;margin-top:9px')}><div style={sx('flex:1;height:6px;background:#f0eaf2;border-radius:999px;overflow:hidden')}><div style={sx('height:100%;width:' + el.force.pct + '%;background:' + el.force.color + ';border-radius:999px;transition:width .25s')}></div></div><span style={sx("font:700 10px 'Sora';color:" + el.force.color)}>{el.force.label}</span></div>
                </div>
              </F>}
              {el.isOtp && <F>
                <div style={sx('display:flex;gap:10px')}><div style={sx('flex:1')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>ÉMETTEUR</div><Hv tag="input" value={el.issuer} onChange={el.onIssuer} placeholder="GitHub, Google…" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:inherit;outline:none" focus="border-color:#6b4fb8" /></div><div style={sx('flex:1')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>CLÉ / OTPAUTH</div><Hv tag="input" value={el.secret} onChange={el.onSecret} placeholder="JBSW Y3DP…" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:'IBM Plex Mono',monospace;outline:none" focus="border-color:#6b4fb8" /></div></div>
                <div style={sx('display:flex;align-items:center;gap:12px;background:#fff;border:1px solid #f0eaf2;border-radius:12px;padding:11px 13px')}><div style={sx(el.otpRingStyle)}><div style={sx(el.otpHoleStyle)}>{el.otpRemaining}</div></div><span key={el.otpLiveCode} style={sx("font-family:'IBM Plex Mono',monospace;font-size:19px;font-weight:600;color:#2a2431;letter-spacing:3px;animation:scFade .3s ease")}>{el.otpLiveCode}</span></div>
              </F>}
              {el.isWifi && <F>
                <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>SSID</div><input value={el.ssid} onChange={el.onSsid} placeholder="Nom du réseau" style={sx('width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #6b4fb8;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:inherit;outline:none')} /></div>
                <div style={sx('display:flex;gap:10px')}><div style={sx('flex:1')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>MOT DE PASSE</div><Hv tag="input" value={el.wifiPass} onChange={el.onWifiPass} placeholder="••••••••" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:'IBM Plex Mono',monospace;outline:none" focus="border-color:#6b4fb8" /></div><div style={sx('width:120px;flex:none')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>SÉCURITÉ</div><Hv tag="input" value={el.security} onChange={el.onSecurity} placeholder="WPA2" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:inherit;outline:none" focus="border-color:#6b4fb8" /></div></div>
              </F>}
              {el.isNotes && <F>
                <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>NOTE</div><Hv tag="textarea" rows={5} value={el.text} onChange={el.onText} placeholder="Texte libre, chiffré comme le reste de la fiche" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13px;line-height:1.5;color:#3a3340;font-family:inherit;outline:none;resize:vertical" focus="border-color:#6b4fb8" /></div>
              </F>}
              {el.isCard && <F>
                <div><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>NUMÉRO DE CARTE</div><Hv tag="input" value={el.cardNumber} onChange={el.onCardNumber} placeholder="0000 0000 0000 0000" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:'IBM Plex Mono',monospace;outline:none" focus="border-color:#6b4fb8" /></div>
                <div style={sx('display:flex;gap:10px')}><div style={sx('flex:1')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>EXPIRATION</div><Hv tag="input" value={el.cardExp} onChange={el.onCardExp} placeholder="MM/AA" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:inherit;outline:none" focus="border-color:#6b4fb8" /></div><div style={sx('flex:1')}><div style={sx("font:700 9.5px 'Sora';letter-spacing:.05em;color:#a99fb0;margin:0 2px 5px")}>CVV</div><Hv tag="input" value={el.cardCvv} onChange={el.onCardCvv} placeholder="•••" style="width:100%;box-sizing:border-box;background:#fff;border:1.5px solid #ece5f0;border-radius:11px;padding:11px 13px;font-size:13.5px;color:#3a3340;font-family:'IBM Plex Mono',monospace;outline:none" focus="border-color:#6b4fb8" /></div></div>
              </F>}
            </div>}
            </div>
            {el.showAfter && <div style={sx('position:absolute;left:0;right:0;bottom:-8px;height:3px;background:#7c5cff;border-radius:999px;box-shadow:0 0 0 3px rgba(124,92,255,.18);z-index:5')}></div>}
          </div>
        ))}

        <div style={sx('position:relative')} onDragOver={v.onEndOver} onDrop={v.onEndDrop}>
          {v.showEnd && <div style={sx('position:absolute;left:0;right:0;top:-8px;height:3px;background:#7c5cff;border-radius:999px;box-shadow:0 0 0 3px rgba(124,92,255,.18);z-index:5')}></div>}
          <div onClick={v.onAddToggle} style={sx(v.addBtnStyle)}>＋ Ajouter un élément</div>
        </div>
      </div>

      <div style={sx('padding:14px 24px 18px;display:flex;justify-content:flex-end;gap:10px;border-top:1px solid #f2edf5')}>
        <div onClick={v.onEditorClose} style={sx('padding:10px 18px;border:1.5px solid #e4dce8;border-radius:11px;font-size:13px;font-weight:600;color:#6a6070;cursor:pointer')}>Annuler</div>
        <div onClick={v.onSave} style={sx('padding:10px 24px;border-radius:11px;font-size:13px;font-weight:700;color:#fff;background:linear-gradient(120deg,#7c5cff,#b06ad6);cursor:pointer')}>Enregistrer</div>
      </div>
    </div>

    {v.addMenuOpen && <div onClick={v.onAddClose} style={sx('position:absolute;inset:0;background:rgba(40,30,55,.3);backdrop-filter:blur(6px);z-index:40;display:flex;align-items:center;justify-content:center;padding:24px')}>
      <div onClick={v.onMenuStop} style={sx('width:340px;max-width:100%;background:#fff;border-radius:16px;border:1px solid #e4dce8;box-shadow:0 24px 54px -16px rgba(40,30,55,.5);padding:10px')}>
        <div style={sx("font:700 9.5px 'Sora';letter-spacing:.06em;color:#b3a8b9;padding:8px 10px 9px")}>CHOISIR UN TYPE</div>
        {v.addOptions.map((opt, i) => (
          <Hv key={i} onClick={opt.onClick} style="display:flex;align-items:center;gap:11px;padding:10px;border-radius:10px;cursor:pointer" hover="background:#f7f3fb">
            <span style={sx(opt.tileStyle)}>{opt.icon}</span>
            <div style={sx('min-width:0')}><div style={sx('font-weight:600;font-size:13px;color:#3a3340')}>{opt.label}</div><div style={sx('font-size:10.5px;color:#b3a8b9')}>{opt.desc}</div></div>
          </Hv>
        ))}
        {v.addCustomOptions.length > 0 && <F>
          <div style={sx('height:1px;background:#f0eaf2;margin:6px 8px')}></div>
          <div style={sx("font:700 9.5px 'Sora';letter-spacing:.06em;color:#b3a8b9;padding:8px 10px 9px")}>CHOISIR UN TYPE PERSONNALISÉ</div>
          {v.addCustomOptions.map((opt, i) => (
            <Hv key={'c' + i} onClick={opt.onClick} style="display:flex;align-items:center;gap:11px;padding:10px;border-radius:10px;cursor:pointer" hover="background:#f7f3fb">
              <span style={sx(opt.tileStyle)}>{opt.icon}</span>
              <div style={sx('min-width:0')}><div style={sx('font-weight:600;font-size:13px;color:#3a3340')}>{opt.label}</div><div style={sx('font-size:10.5px;color:#b3a8b9')}>{opt.desc}</div></div>
            </Hv>
          ))}
        </F>}
      </div>
    </div>}

  </div>
</div>
    );
  }
}

window.CPEditeur = CPEditeur;
})();
