/* LeMultiservice Developers — kit UI repris du design Claude Design (dev-ui.jsx), commun à la console
   partenaire et à la console interne. Écarts avec le design : Modal en position fixe (pleine page),
   Sel accepte des paires [valeur, libellé], Drop rend le File, Tbl accepte `chargement`. */
const MobCtx = React.createContext(false);
const useMob = () => React.useContext(MobCtx);
const V = {bg:'var(--bg)', card:'var(--card)', sub:'var(--sub)', line:'var(--line)', ink:'var(--ink)', ink2:'var(--ink2)', ink3:'var(--ink3)', deep:'var(--deep)', btn:'var(--btn)', acc:'var(--acc)', accD:'var(--accD)', mint:'var(--mint)',
  warn:'var(--warn)', warnBg:'var(--warnBg)', bad:'var(--bad)', badBg:'var(--badBg)', info:'var(--info)', infoBg:'var(--infoBg)', disp:"'Bricolage Grotesque',system-ui,sans-serif", body:"'Plus Jakarta Sans',system-ui,sans-serif", mono:"'JetBrains Mono',ui-monospace,monospace"};
const TN = {ok:[V.mint,V.accD], warn:[V.warnBg,V.warn], bad:[V.badBg,V.bad], info:[V.infoBg,V.info], mute:[V.sub,V.ink2], deep:[V.deep,'#fff']};
const SVCS = {
  assurance:{n:'Assurance automobile', s:'Assurance', i:'car-profile', c:'#2F8F6E', bg:'rgba(47,143,110,.12)', on:true},
  voyage:{n:'Assurance voyage', s:'Voyage', i:'suitcase-rolling', c:'#0A6A83', bg:'rgba(10,106,131,.12)'},
  billet:{n:'Billetterie aérienne', s:'Billetterie', i:'airplane-tilt', c:'#1C5FAF', bg:'rgba(28,95,175,.12)'},
  esim:{n:'eSIM', s:'eSIM', i:'sim-card', c:'#6D3FD0', bg:'rgba(109,63,208,.12)'},
  sms:{n:'SMS', s:'SMS', i:'chat-centered-text', c:'#A8620A', bg:'rgba(168,98,10,.12)'},
};
const BAL_SVCS = ['assurance','billet','esim','sms'];
const fmt = n => Math.round(Math.abs(n)).toString().replace(/\B(?=(\d{3})+(?!\d))/g,' ');

function I({n, fill, s=18, c, style, label}){ return <i aria-hidden={label?undefined:'true'} aria-label={label} className={`ph${fill?'-fill':''} ph-${n}`} style={{fontSize:s, color:c, lineHeight:1, flex:'none', ...style}}></i>; }
function Btn({kind='primary', icon, iconR, children, onClick, full, sm, disabled, style, type, label}){
  const k = {primary:{background:V.btn, color:'#fff', border:'1px solid transparent'}, secondary:{background:V.card, color:V.ink, border:`1px solid ${V.line}`}, accent:{background:V.acc, color:'#fff', border:'1px solid transparent'},
    ghost:{background:'transparent', color:V.ink2, border:'1px solid transparent'}, danger:{background:V.badBg, color:V.bad, border:'1px solid transparent'}, soft:{background:V.mint, color:V.accD, border:'1px solid transparent'}}[kind];
  return <button type={type||'button'} aria-label={label} className="lb" disabled={disabled} onClick={onClick} style={{height:sm?36:44, padding:children?(sm?'0 13px':'0 18px'):0, width:children?(full?'100%':undefined):(sm?36:44), borderRadius:16, display:'inline-flex', alignItems:'center', justifyContent:'center', gap:8,
    fontFamily:V.body, fontWeight:700, fontSize:sm?13:14, whiteSpace:'nowrap', cursor:disabled?'not-allowed':'pointer', opacity:disabled?.45:1, flex:'none', ...k, ...style}}>
    {icon && <I n={icon} s={sm?16:18}/>}{children}{iconR && <I n={iconR} s={sm?15:17}/>}</button>;
}
function Card({children, pad=20, hover, onClick, style}){
  return <div onClick={onClick} className={hover||onClick?'lc':undefined} style={{background:V.card, border:`1px solid ${V.line}`, borderRadius:24, padding:pad, cursor:onClick?'pointer':undefined, minWidth:0, ...style}}>{children}</div>;
}
const Lbl = ({children, style}) => <span style={{display:'block', fontWeight:700, fontSize:11, letterSpacing:'0.1em', textTransform:'uppercase', color:V.ink3, ...style}}>{children}</span>;
const Muted = ({children, style}) => <p style={{margin:0, fontWeight:500, fontSize:13.5, lineHeight:1.5, color:V.ink3, ...style}}>{children}</p>;
const H = ({children, s=22, style}) => <h2 style={{margin:0, fontFamily:V.disp, fontWeight:800, fontSize:s, letterSpacing:'-0.025em', lineHeight:1.15, color:V.ink, ...style}}>{children}</h2>;
function PageHead({title, sub, actions, back, onBack}){
  const m = useMob();
  return <div style={{display:'flex', flexDirection:'column', gap:10}}>
    {back && <button className="lb ln" onClick={onBack} aria-label={`Retour à ${back}`} style={{alignSelf:'flex-start', display:'inline-flex', alignItems:'center', gap:8, height:36, padding:'0 12px 0 8px', marginLeft:-8, border:0, borderRadius:12, background:'none', color:V.ink2, fontFamily:V.body, fontWeight:700, fontSize:13.5, cursor:'pointer'}}><I n="arrow-left" s={17}/>{back}</button>}
    <div style={{display:'flex', alignItems:m?'flex-start':'flex-end', gap:14, flexWrap:'wrap', flexDirection:m?'column':'row'}}>
      <div style={{flex:1, minWidth:0, width:m?'100%':undefined}}><H s={m?21:24}>{title}</H>{sub && <Muted style={{marginTop:5}}>{sub}</Muted>}</div>
      {actions && <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>{actions}</div>}
    </div>
  </div>;
}
function Grid({cols='1fr 1fr', gap=16, children, style, mcols='1fr'}){ const m = useMob(); return <div style={{display:'grid', gridTemplateColumns:m?mcols:cols, gap:m?12:gap, alignItems:'start', ...style}}>{children}</div>; }
function Page({children, max=1160}){ const m = useMob();
  return <div className="lpage" style={{flex:1, minHeight:0, overflowY:'auto', background:V.bg}}><div style={{maxWidth:max, margin:'0 auto', padding:m?'18px 16px 96px':'28px 32px 48px', display:'flex', flexDirection:'column', gap:m?16:22}}>{children}</div></div>; }

function Badge({tone='mute', icon, children, dot}){ const t = TN[tone];
  return <span style={{display:'inline-flex', alignItems:'center', gap:5, height:24, padding:'0 10px', borderRadius:999, background:t[0], color:t[1], fontWeight:700, fontSize:12, whiteSpace:'nowrap', flex:'none'}}>
    {dot && <span style={{width:6, height:6, borderRadius:99, background:'currentColor'}}></span>}{icon && <I n={icon} s={13} fill/>}{children}</span>; }
const ST = {
  en_cours:['En cours','info','hourglass'], actif:['Actif','ok','check-circle'], a_verifier:['À vérifier','warn','warning'], echec:['Échec','bad','x-circle'], annule:['Annulé','mute','prohibit'], resilie:['Résilié','mute','file-x'], expire:['Expiré','mute','clock-counter-clockwise'],
  attente:['En attente','warn','hourglass'], valide:['Validé','ok','check-circle'], rejete:['Rejeté','bad','x-circle'],
  a_fournir:['À fournir','mute','circle-dashed'], depose:['Déposé','info','upload-simple'], a_corriger:['À corriger','bad','pencil-simple'],
  livre:['Livré','ok','check-circle'], echoue:['Échoué','bad','x-circle'], active:['Active','ok','check-circle'], revoquee:['Révoquée','mute','prohibit'], expire24:['Expire dans 24 h','warn','clock'],
};
const StatusB = ({st}) => { const s = ST[st]; return <Badge tone={s[1]} icon={s[2]}>{s[0]}</Badge>; };
function Http({code}){ const t = code<300?'ok':code<500?'warn':'bad'; return <span style={{fontFamily:V.mono, fontWeight:700, fontSize:12, padding:'3px 8px', borderRadius:8, background:TN[t][0], color:TN[t][1]}}>{code}</span>; }
function Method({m}){ const c = {GET:V.info, POST:V.accD, DELETE:V.bad, PATCH:V.warn}[m]; return <span style={{fontFamily:V.mono, fontWeight:700, fontSize:11.5, color:c, minWidth:40, display:'inline-block'}}>{m}</span>; }
function Money({n, s=15, sign, muted}){
  const neg = n<0; return <span style={{display:'inline-flex', alignItems:'baseline', gap:5, whiteSpace:'nowrap', fontFamily:V.disp, fontWeight:800, fontSize:s, letterSpacing:'-0.02em', color:muted?V.ink2:sign?(neg?V.bad:V.accD):V.ink}}>
    {sign?(neg?'− ':'+ '):''}{fmt(n)}<span style={{fontFamily:V.body, fontWeight:600, fontSize:Math.max(11, s*.55), letterSpacing:0, color:V.ink3}}>F CFA</span></span>; }
const Mono = ({children, s=12.5, c}) => <span style={{fontFamily:V.mono, fontSize:s, color:c||V.ink2, fontWeight:500}}>{children}</span>;

const inpCss = {width:'100%', height:46, padding:'0 14px', borderRadius:16, border:`1px solid ${V.line}`, background:V.card, color:V.ink, fontFamily:V.body, fontWeight:600, fontSize:14, outline:'none'};
function Field({label, hint, err, children, opt}){
  return <label style={{display:'flex', flexDirection:'column', gap:7, minWidth:0}}>
    {label && <span style={{display:'flex', gap:6, fontWeight:700, fontSize:12.5, color:V.ink2}}>{label}{opt && <span style={{fontWeight:500, color:V.ink3}}>(facultatif)</span>}</span>}
    {children}
    {err ? <span style={{display:'flex', alignItems:'center', gap:5, fontWeight:600, fontSize:12, color:V.bad}}><I n="warning-circle" s={14}/>{err}</span> : hint && <span style={{fontWeight:500, fontSize:12, color:V.ink3, lineHeight:1.4}}>{hint}</span>}
  </label>;
}
function Inp({lead, trail, mono, err, style, ...p}){
  return <div style={{position:'relative', display:'flex', alignItems:'center'}}>
    {lead && <span style={{position:'absolute', left:14, display:'flex', color:V.ink3}}>{lead}</span>}
    <input className="li" {...p} style={{...inpCss, paddingLeft:lead?42:14, paddingRight:trail?44:14, fontFamily:mono?V.mono:V.body, fontSize:mono?13:14, borderColor:err?V.bad:undefined, ...style}}/>
    {trail && <span style={{position:'absolute', right:14, display:'flex', color:V.ink3, fontWeight:700, fontSize:13}}>{trail}</span>}
  </div>;
}
function Sel({opts, value, onChange, style, label}){
  return <div style={{position:'relative'}}><select className="li" aria-label={label} value={value} onChange={e=>onChange&&onChange(e.target.value)} style={{...inpCss, appearance:'none', paddingRight:38, cursor:'pointer', ...style}}>{opts.map(o=>Array.isArray(o) ? <option key={o[0]} value={o[0]}>{o[1]}</option> : <option key={o}>{o}</option>)}</select>
    <I n="caret-down" s={15} c={V.ink3} style={{position:'absolute', right:14, top:'50%', transform:'translateY(-50%)', pointerEvents:'none'}}/></div>;
}
function Check({on, set, children}){
  return <label className="lk" style={{display:'flex', alignItems:'flex-start', gap:10, cursor:'pointer', fontWeight:600, fontSize:13.5, color:V.ink2, lineHeight:1.45}}>
    <input type="checkbox" checked={on} onChange={e=>set(e.target.checked)} style={{position:'absolute', opacity:0, width:1, height:1}}/>
    <span style={{width:20, height:20, borderRadius:7, flex:'none', marginTop:1, display:'grid', placeItems:'center', border:`1.5px solid ${on?V.acc:V.line}`, background:on?V.acc:V.card, transition:'all .2s'}}>{on && <I n="check" s={13} c="#fff" style={{fontWeight:700}}/>}</span>{children}</label>;
}
function Toggle({on, set, label}){
  return <button role="switch" aria-checked={on} aria-label={label} className="lb" onClick={()=>set(!on)} style={{width:44, height:26, borderRadius:99, border:0, padding:3, cursor:'pointer', background:on?V.acc:'var(--tog)', transition:'background .2s', flex:'none'}}>
    <span style={{display:'block', width:20, height:20, borderRadius:99, background:'#fff', transform:`translateX(${on?18:0}px)`, transition:'transform .2s'}}></span></button>;
}
function Seg({opts, i, set, style}){
  return <div role="tablist" style={{display:'inline-flex', gap:3, padding:3, borderRadius:14, background:V.sub, border:`1px solid ${V.line}`, ...style}}>
    {opts.map((o,k)=><button key={k} role="tab" aria-selected={i===k} className="lb" onClick={()=>set(k)} style={{height:32, padding:'0 13px', borderRadius:11, border:0, cursor:'pointer', display:'flex', alignItems:'center', gap:6, whiteSpace:'nowrap',
      background:i===k?V.card:'transparent', color:i===k?V.ink:V.ink3, fontFamily:V.body, fontWeight:700, fontSize:13, boxShadow:i===k?'0 1px 2px rgba(15,26,22,.08)':'none'}}>{o}</button>)}</div>;
}
function Tabs({opts, i, set}){
  return <div role="tablist" style={{display:'flex', gap:4, borderBottom:`1px solid ${V.line}`, overflowX:'auto'}}>
    {opts.map((o,k)=>{ const [l, badge, dis] = Array.isArray(o)?o:[o];
      return <button key={k} role="tab" aria-selected={i===k} disabled={dis} className="lb" onClick={()=>set(k)} style={{height:42, padding:'0 12px', border:0, background:'none', cursor:dis?'default':'pointer', display:'flex', alignItems:'center', gap:7, whiteSpace:'nowrap',
        borderBottom:`2px solid ${i===k?V.acc:'transparent'}`, marginBottom:-1, color:i===k?V.ink:dis?V.ink3:V.ink2, opacity:dis?.6:1, fontFamily:V.body, fontWeight:700, fontSize:13.5}}>{l}{badge && <span style={{fontSize:11, fontWeight:700, padding:'2px 7px', borderRadius:99, background:V.sub, color:V.ink3}}>{badge}</span>}</button>; })}</div>;
}
function Tile({icon, c=V.accD, bg=V.mint, s=40, fill}){ return <span style={{display:'grid', placeItems:'center', width:s, height:s, borderRadius:s*.32, background:bg, flex:'none'}}><I n={icon} s={s*.5} c={c} fill={fill}/></span>; }
function Notice({tone='info', icon, title, children, action}){ const t = TN[tone];
  return <div role={tone==='bad'?'alert':undefined} style={{display:'flex', alignItems:'flex-start', gap:12, padding:'14px 16px', borderRadius:18, background:t[0]}}>
    <I n={icon||{info:'info', warn:'warning', bad:'warning-octagon', ok:'check-circle'}[tone]} s={20} c={t[1]} fill/>
    <div style={{flex:1, minWidth:0}}>{title && <span style={{display:'block', fontWeight:800, fontSize:13.5, color:t[1]}}>{title}</span>}{children && <span style={{display:'block', marginTop:title?3:0, fontWeight:500, fontSize:13, lineHeight:1.5, color:V.ink2}}>{children}</span>}</div>
    {action}</div>;
}
function KV({rows}){ return <div>{rows.map((r,i)=><div key={i} style={{display:'flex', gap:12, padding:'9px 0', borderTop:i?`1px solid ${V.line}`:'none'}}>
  <span style={{flex:1, fontWeight:500, fontSize:13, color:V.ink3}}>{r[0]}</span><span style={{fontWeight:700, fontSize:13, color:r[2]||V.ink, textAlign:'right', fontFamily:r[3]?V.mono:undefined}}>{r[1]}</span></div>)}</div>; }

function Tbl({cols, rows, onRow, empty, foot, head, chargement}){
  const m = useMob();
  if(chargement) return <Card pad={0} style={{overflow:'hidden'}}>{head}<SkelRows n={4}/></Card>;
  rows = rows || [];
  if(m) return <Card pad={0} style={{overflow:'hidden'}}>{head}{rows.length ? rows.map((r,i)=><div key={i} onClick={onRow?()=>onRow(i):undefined} className={onRow?'lr':undefined} style={{padding:'13px 16px', borderTop:i||head?`1px solid ${V.line}`:'none', display:'flex', flexDirection:'column', gap:6, cursor:onRow?'pointer':undefined}}>
      <div style={{display:'flex', justifyContent:'space-between', gap:10, alignItems:'center'}}><span style={{minWidth:0}}>{r[cols[0].k]}</span><span>{r[cols[cols.length-1].k]}</span></div>
      <div style={{display:'flex', flexWrap:'wrap', gap:'4px 12px', fontSize:12.5, color:V.ink3}}>{cols.slice(1,-1).filter(c=>!c.hideM).map(c=><span key={c.k}>{r[c.k]}</span>)}</div></div>) : empty}{foot}</Card>;
  return <Card pad={0} style={{overflow:'hidden'}}>{head}
    <div role="table">
      <div role="row" style={{display:'grid', gridTemplateColumns:cols.map(c=>c.w||'1fr').join(' '), gap:14, padding:'11px 20px', borderTop:head?`1px solid ${V.line}`:'none', background:V.sub}}>
        {cols.map(c=><span role="columnheader" key={c.k} style={{fontWeight:700, fontSize:11, letterSpacing:'0.08em', textTransform:'uppercase', color:V.ink3, textAlign:c.r?'right':'left', whiteSpace:'nowrap'}}>{c.l}</span>)}</div>
      {rows.length ? rows.map((r,i)=><div role="row" key={i} tabIndex={onRow?0:undefined} onClick={onRow?()=>onRow(i):undefined} className={onRow?'lr':undefined} style={{display:'grid', gridTemplateColumns:cols.map(c=>c.w||'1fr').join(' '), gap:14, padding:'13px 20px', alignItems:'center', borderTop:`1px solid ${V.line}`, cursor:onRow?'pointer':undefined, fontSize:13.5, fontWeight:600, color:V.ink}}>
        {cols.map(c=><span role="cell" key={c.k} style={{minWidth:0, textAlign:c.r?'right':'left', display:'flex', justifyContent:c.r?'flex-end':'flex-start', alignItems:'center', overflow:'hidden'}}>{r[c.k]}</span>)}</div>) : empty}
    </div>{foot}</Card>;
}
function Pager({n, per=25, page=1, set}){ const a = n ? (page-1)*per+1 : 0, b = Math.min(page*per, n);
  return <div style={{display:'flex', alignItems:'center', gap:8, padding:'12px 20px', borderTop:`1px solid ${V.line}`}}><span style={{flex:1, fontWeight:500, fontSize:12.5, color:V.ink3}}>{a}–{b} sur {fmt(n)}</span><Btn kind="secondary" sm icon="caret-left" label="Page précédente" disabled={page<=1} onClick={()=>set&&set(page-1)}/><Btn kind="secondary" sm icon="caret-right" label="Page suivante" disabled={b>=n} onClick={()=>set&&set(page+1)}/></div>; }

function EmptyS({icon, title, sub, action, tone}){ return <div style={{padding:'44px 20px', display:'flex', flexDirection:'column', alignItems:'center', textAlign:'center', gap:10}}>
  <Tile icon={icon} s={56} c={tone==='bad'?V.bad:V.accD} bg={tone==='bad'?V.badBg:V.mint}/><span style={{fontFamily:V.disp, fontWeight:800, fontSize:17, color:V.ink, marginTop:4}}>{title}</span>{sub && <Muted style={{maxWidth:360}}>{sub}</Muted>}{action && <div style={{marginTop:6}}>{action}</div>}</div>; }
const Skel = ({w='100%', h=14, r=8, style}) => <span className="lsk" style={{display:'block', width:w, height:h, borderRadius:r, ...style}}></span>;
function SkelRows({n=4}){ return <div style={{display:'flex', flexDirection:'column'}}>{Array.from({length:n}).map((_,i)=><div key={i} style={{display:'flex', gap:16, alignItems:'center', padding:'15px 20px', borderTop:`1px solid ${V.line}`}}><Skel w={36} h={36} r={12}/><div style={{flex:1, display:'flex', flexDirection:'column', gap:7}}><Skel w="40%"/><Skel w="24%" h={11}/></div><Skel w={90}/></div>)}</div>; }

function Modal({title, icon, tone, onClose, children, w=520, foot}){
  const m = useMob();
  React.useEffect(()=>{ const k = e => e.key==='Escape' && onClose && onClose(); addEventListener('keydown', k); return ()=>removeEventListener('keydown', k); },[]);
  return <div onClick={onClose} style={{position:'fixed', inset:0, zIndex:80, background:'rgba(8,20,16,.45)', display:'flex', alignItems:m?'flex-end':'center', justifyContent:'center', animation:'lfade .2s ease'}}>
    <div role="dialog" aria-modal="true" aria-label={title} onClick={e=>e.stopPropagation()} style={{width:m?'100%':w, maxHeight:m?'92%':'88%', overflowY:'auto', background:V.card, borderRadius:m?'28px 28px 0 0':28, animation:m?'lup .25s ease':'lpop .2s ease'}}>
      <div style={{display:'flex', alignItems:'center', gap:12, padding:'20px 22px 0'}}>{icon && <Tile icon={icon} s={40} c={tone==='bad'?V.bad:tone==='warn'?V.warn:V.accD} bg={tone==='bad'?V.badBg:tone==='warn'?V.warnBg:V.mint}/>}
        <span style={{flex:1, fontFamily:V.disp, fontWeight:800, fontSize:19, letterSpacing:'-0.02em', color:V.ink}}>{title}</span>{onClose && <Btn kind="ghost" sm icon="x" label="Fermer" onClick={onClose}/>}</div>
      <div style={{padding:22, display:'flex', flexDirection:'column', gap:16}}>{children}</div>
      {foot && <div style={{display:'flex', gap:8, justifyContent:'flex-end', padding:'14px 22px', borderTop:`1px solid ${V.line}`, flexDirection:m?'column-reverse':'row'}}>{foot}</div>}
    </div></div>;
}
function useCopy(){ const [c, setC] = React.useState(false); return [c, txt=>{ try{ navigator.clipboard.writeText(txt); }catch(e){} setC(true); setTimeout(()=>setC(false),1400); }]; }
function CopyBtn({text, sm=true, dark}){ const [c, copy] = useCopy();
  return <button className="lb" onClick={()=>copy(text)} aria-label="Copier" style={{height:sm?30:40, padding:'0 10px', borderRadius:10, border:0, cursor:'pointer', display:'inline-flex', alignItems:'center', gap:6, background:dark?'rgba(255,255,255,.08)':V.sub, color:c?(dark?'#9FE0C6':V.accD):(dark?'rgba(255,255,255,.7)':V.ink2), fontFamily:V.body, fontWeight:700, fontSize:12, flex:'none'}}>
    <I n={c?'check':'copy'} s={15}/>{c?'Copié':'Copier'}</button>; }

const LANGS = ['cURL','JavaScript','PHP','Python','Java'];
const LangCtx = React.createContext([0,()=>{}]);
function hl(src){
  return src.split('\n').map((ln,i)=><div key={i}>{ln.split(/("[^"]*"|'[^']*'|\/\/.*|#.*$|\b\d+\b|\b(?:const|await|new|return|import|from|curl|def|public|class|String|var|if|echo)\b)/g).map((t,k)=>{
    if(!t) return null; let c;
    if(/^["']/.test(t)) c='#9FE0C6'; else if(/^(\/\/|#)/.test(t)) c='#6B8079'; else if(/^\d+$/.test(t)) c='#F2C679'; else if(/^(const|await|new|return|import|from|curl|def|public|class|String|var|if|echo)$/.test(t)) c='#8FB8FF';
    return <span key={k} style={{color:c}}>{t}</span>; })}{ln===''?'\u00a0':null}</div>);
}
function Code({code, title, langs=true, h}){
  const [li, setLi] = React.useContext(LangCtx);
  const src = typeof code==='string' ? code : (code[LANGS[li]] || code[Object.keys(code)[0]]);
  return <div style={{borderRadius:20, background:'#0E1F19', border:'1px solid rgba(255,255,255,.06)', overflow:'hidden', minWidth:0}}>
    <div style={{display:'flex', alignItems:'center', gap:4, padding:'8px 8px 8px 14px', borderBottom:'1px solid rgba(255,255,255,.07)', flexWrap:'wrap'}}>
      {title && <span style={{fontFamily:V.mono, fontSize:12, color:'rgba(255,255,255,.55)', marginRight:8, whiteSpace:'nowrap'}}>{title}</span>}
      {langs && typeof code!=='string' && LANGS.filter(l=>code[l]).map(l=><button key={l} className="lb" onClick={()=>setLi(LANGS.indexOf(l))} style={{height:28, padding:'0 10px', borderRadius:8, border:0, cursor:'pointer', background:LANGS[li]===l?'rgba(159,224,198,.14)':'transparent', color:LANGS[li]===l?'#9FE0C6':'rgba(255,255,255,.55)', fontFamily:V.body, fontWeight:700, fontSize:12, whiteSpace:'nowrap'}}>{l}</button>)}
      <span style={{flex:1}}></span><CopyBtn text={src} dark/></div>
    <pre style={{margin:0, padding:'14px 16px', fontFamily:V.mono, fontSize:12.5, lineHeight:1.65, color:'#E4EEE9', overflowX:'auto', maxHeight:h, whiteSpace:'pre'}}>{hl(src)}</pre>
  </div>;
}
function Drop({onFile, hint='PDF, JPG ou PNG · 10 Mo maximum', compact, accept}){
  const [over, setOver] = React.useState(false);
  return <label className="ld" onDragOver={e=>{e.preventDefault(); setOver(true);}} onDragLeave={()=>setOver(false)} onDrop={e=>{e.preventDefault(); setOver(false); onFile&&e.dataTransfer.files[0]&&onFile(e.dataTransfer.files[0]);}}
    style={{display:'flex', flexDirection:compact?'row':'column', alignItems:'center', justifyContent:'center', gap:compact?10:8, padding:compact?'12px 16px':'26px 16px', borderRadius:18, border:`1.5px dashed ${over?V.acc:V.line}`, background:over?V.mint:V.sub, cursor:'pointer', textAlign:'center', transition:'all .2s'}}>
    <input type="file" accept={accept} style={{display:'none'}} onChange={e=>onFile&&e.target.files[0]&&onFile(e.target.files[0])}/>
    <I n="cloud-arrow-up" s={compact?20:28} c={V.acc}/><span><span style={{display:'block', fontWeight:700, fontSize:13.5, color:V.ink}}>Glissez un fichier ou <span style={{color:V.accD, textDecoration:'underline'}}>parcourez</span></span><span style={{display:'block', marginTop:2, fontWeight:500, fontSize:12, color:V.ink3}}>{hint}</span></span></label>;
}
function Progress({steps, i}){ const m = useMob();
  return <div style={{display:'flex', gap:m?4:8}}>{steps.map((s,k)=><div key={s} style={{flex:1, minWidth:0}}>
    <span style={{display:'block', height:4, borderRadius:99, background:k<=i?V.acc:V.line, transition:'background .3s'}}></span>
    {!m && <span style={{display:'flex', alignItems:'center', gap:6, marginTop:8, fontWeight:700, fontSize:12, color:k===i?V.ink:k<i?V.accD:V.ink3, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>{k<i && <I n="check-circle" s={14} fill/>}{s}</span>}</div>)}</div>; }
function SvcIcon({k, s=40}){ const v = SVCS[k]; return <Tile icon={v.i} s={s} c={v.c} bg={v.bg}/>; }
function Av({n, s=34}){ const ini = n.split(' ').map(w=>w[0]).slice(0,2).join(''); return <span style={{display:'grid', placeItems:'center', width:s, height:s, borderRadius:99, background:V.mint, color:V.accD, fontWeight:800, fontSize:s*.36, flex:'none'}}>{ini}</span>; }

Object.assign(window, {MobCtx, useMob, V, TN, SVCS, BAL_SVCS, fmt, I, Btn, Card, Lbl, Muted, H, PageHead, Grid, Page, Badge, ST, StatusB, Http, Method, Money, Mono, inpCss, Field, Inp, Sel, Check, Toggle, Seg, Tabs, Tile, Notice, KV, Tbl, Pager, EmptyS, Skel, SkelRows, Modal, useCopy, CopyBtn, LANGS, LangCtx, Code, Drop, Progress, SvcIcon, Av});
