/* ============================================================
   exemple-mobile-apps-app.jsx
   10 écrans navigables : Onboarding, Accueil, Chat (liste+détail),
   Calendrier, Maps, Recherche, Scanner QR, Caméra, Fichiers, Paramètres.
   Avatar+menu présents sur tous les écrans en haut à droite.
   ============================================================ */

const { useState: uX, useEffect: eX } = React;

/* ---------- DONNÉES MOCK ---------- */
const CHATS = [
  { id: 1, name: 'Marc Dubois',  avatar: 'var(--accent)', last: 'Tu peux jeter un œil au scan ?', time: '14:02', unread: 2 },
  { id: 2, name: 'Équipe Ops',   avatar: 'var(--blue)',   last: 'Léa : déploiement OK 🚀',         time: '13:14', unread: 0 },
  { id: 3, name: 'Sophie',       avatar: 'var(--purple)', last: 'On se voit ce soir ?',             time: '11:30', unread: 1 },
  { id: 4, name: 'Antoine',      avatar: 'var(--ok)',     last: 'Merci pour le tuyau !',            time: 'hier',   unread: 0 },
  { id: 5, name: 'Notifs syst.', avatar: 'var(--warn)',   last: 'Backup horaire OK · 812 Mo',      time: 'hier',   unread: 0 },
];

const CHAT_MESSAGES = [
  { id: 1, me: false, text: 'Salut ! Tu as eu le temps de regarder le scan d\'hier soir ?', time: '13:58' },
  { id: 2, me: true,  text: 'Oui, je viens de finir. Le node-03 a un soucis de latence', time: '13:59', status: 'read' },
  { id: 3, me: false, text: 'Aïe. Tu peux me partager les détails ?', time: '14:00' },
  { id: 4, me: true,  text: 'Je t\'envoie le rapport dans 5 min', time: '14:01', status: 'read' },
  { id: 5, me: false, text: 'Top, merci 🙏', time: '14:02' },
];

const FILES = [
  { name: 'Documents',     type: 'folder', date: '21 mai' },
  { name: 'Photos 2026',   type: 'folder', date: '12 mai' },
  { name: 'rapport-scan.pdf', type: 'pdf',  date: '21 mai 14:02', size: 384000 },
  { name: 'capture.png',   type: 'image', date: '21 mai 13:58', size: 1280000 },
  { name: 'meeting.mp4',   type: 'video', date: '20 mai',      size: 124000000 },
  { name: 'notes.md',      type: 'code',  date: '20 mai',      size: 4800 },
  { name: 'audio-memo.m4a',type: 'audio', date: '19 mai',      size: 820000 },
  { name: 'backup.zip',    type: 'archive', date: '19 mai 03:00', size: 850000000 },
];

const ONBOARDING_SLIDES = [
  { icon: 'server',  color: 'var(--accent)', title: 'Bienvenue 👋', desc: 'Cette app te permet de superviser tes serveurs et capteurs depuis ton téléphone.' },
  { icon: 'bell',    color: 'var(--blue)',   title: 'Tu es alerté en temps réel', desc: 'Reçois une notification dès qu\'un service tombe ou qu\'une métrique sort des seuils.' },
  { icon: 'network', color: 'var(--purple)', title: 'Maîtrise ton réseau', desc: 'Lance un scan, repère les nouveaux hôtes, ouvre une session SSH depuis le mobile.' },
];

const NOW = new Date(2026, 4, 21); // 21 mai 2026
const EVENTS = new Set([3, 7, 12, 14, 21, 24, 28]);

/* ============================================================
   HeaderAvatar — header partagé (titre + avatar rond)
   Le bouton Avatar ouvre AvatarMenu (popup).
   ============================================================ */
function HeaderAvatar({ title, large, subtitle, onBack, onAvatar, avatarColor = 'var(--accent)' }) {
  return (
    <NavBar
      title={title} large={large} subtitle={subtitle} onBack={onBack}
      right={<Avatar name="Marc" color={avatarColor} size={36} onClick={onAvatar} />}
    />
  );
}

/* ============================================================
   ÉCRANS
   ============================================================ */

/* --- Accueil : grille d'apps --- */
function ScreenHome({ goto, onAvatar }) {
  const apps = [
    { id: 'chat',     icon: 'bell',     color: 'var(--accent)', title: 'Messages',   subtitle: '3 non lus',     badge: 3 },
    { id: 'calendar', icon: 'clock',    color: 'var(--blue)',   title: 'Calendrier', subtitle: 'mai 2026' },
    { id: 'maps',     icon: 'network',  color: 'var(--ok)',     title: 'Carte',      subtitle: '8 pins' },
    { id: 'search',   icon: 'search',   color: 'var(--info)',   title: 'Recherche',  subtitle: 'global' },
    { id: 'scanner',  icon: 'grid',     color: 'var(--purple)', title: 'Scanner QR', subtitle: 'caméra' },
    { id: 'camera',   icon: 'memory',   color: 'var(--warn)',   title: 'Appareil photo', subtitle: 'caméra' },
    { id: 'files',    icon: 'folder',   color: 'var(--info)',   title: 'Fichiers',   subtitle: '124 \u00e9l\u00e9ments' },
    { id: 'settings', icon: 'cog',      color: 'var(--ink-3)',  title: 'Paramètres', subtitle: 'préférences' },
    { id: 'onboard',  icon: 'play',     color: 'var(--accent-soft)', title: 'Onboarding', subtitle: 'revoir l\'intro' },
  ];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <HeaderAvatar large title="Hello Marc" subtitle="prêt à explorer l'app ?" onAvatar={onAvatar} />
      <div style={{ flex: 1, overflowY: 'auto', padding: 14 }}>
        <SearchBar value="" onChange={() => {}} placeholder="Rechercher dans tout…" />
        <div style={{ marginTop: 14, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
          {apps.map((a) => (
            <ActionCard key={a.id} {...a} onClick={() => goto(a.id)} />
          ))}
        </div>
      </div>
    </div>
  );
}

/* --- Chat list --- */
function ScreenChatList({ onAvatar, open }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <HeaderAvatar large title="Messages" subtitle={`${CHATS.reduce((a, c) => a + c.unread, 0)} non lus`} onAvatar={onAvatar} />
      <div style={{ padding: '4px 14px 8px' }}>
        <SearchBar value="" onChange={() => {}} placeholder="Rechercher une conversation" />
      </div>
      <div style={{ flex: 1, overflowY: 'auto' }}>
        {CHATS.map((c) => (
          <SwipeableRow key={c.id}
            onTap={() => open(c)}
            rightActions={[
              { label: 'Lu', icon: 'play', color: 'var(--info)' },
              { label: 'Épingl.', icon: 'bell', color: 'var(--accent)' },
            ]}
            leftActions={[
              { label: 'Archiv.', icon: 'folder', color: 'var(--ok)' },
              { label: 'Suppr.',  icon: 'close',  color: 'var(--err)' },
            ]}>
            <div style={{
              padding: '12px 14px', display: 'flex', gap: 12, alignItems: 'center',
              borderBottom: '1px solid var(--border-1)',
              background: 'var(--bg-3)',
            }}>
              <Avatar name={c.name} color={c.avatar} size={48} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                  <span style={{ fontSize: 15, fontWeight: c.unread ? 700 : 500 }}>{c.name}</span>
                  <span style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)' }}>{c.time}</span>
                </div>
                <div style={{ fontSize: 13, color: 'var(--ink-3)', marginTop: 2, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{c.last}</div>
              </div>
              {c.unread > 0 && (
                <span style={{
                  background: 'var(--accent)', color: 'var(--bg-1)',
                  fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 700,
                  padding: '2px 7px', borderRadius: 999,
                }}>{c.unread}</span>
              )}
            </div>
          </SwipeableRow>
        ))}
      </div>
    </div>
  );
}

/* --- Chat detail --- */
function ScreenChatDetail({ chat, onBack, onAvatar }) {
  const [msgs, setMsgs] = uX(CHAT_MESSAGES);
  const send = (text) => setMsgs([...msgs, { id: Date.now(), me: true, text, time: 'maint.', status: 'sent' }]);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <div style={{
        padding: '8px 12px',
        display: 'flex', alignItems: 'center', gap: 10,
        borderBottom: '1px solid var(--border-2)',
        background: 'var(--surf-glass-strong)',
        backdropFilter: 'blur(14px)',
      }}>
        <button onClick={onBack} className="touch-press" style={{
          width: 36, height: 36, borderRadius: 8, background: 'transparent',
          border: 'none', color: 'var(--accent)', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          WebkitTapHighlightColor: 'transparent',
        }}><Icon name="chevL" size={20} /></button>
        <Avatar name={chat.name} color={chat.avatar} size={32} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 14, fontWeight: 700 }}>{chat.name}</div>
          <div style={{ fontSize: 11, color: 'var(--ok)', fontFamily: 'var(--font-mono)' }}>● en ligne</div>
        </div>
        <Avatar name="M" color="var(--accent)" size={34} onClick={onAvatar} />
      </div>
      <div style={{ flex: 1, overflowY: 'auto', padding: '12px 0', background: 'var(--bg-1)' }}>
        {msgs.map((m) => <ChatBubble key={m.id} {...m} />)}
      </div>
      <ChatComposer onSend={send} />
    </div>
  );
}

/* --- Calendar --- */
function ScreenCalendar({ onAvatar, showToast }) {
  const [sel, setSel] = uX(NOW);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <HeaderAvatar large title="Calendrier" subtitle="mai 2026" onAvatar={onAvatar} />
      <div style={{ flex: 1, overflowY: 'auto', paddingBottom: 80 }}>
        <CalendarMonth
          year={2026} month={4}
          selected={sel} onSelect={(d) => { setSel(d); showToast(`${d.getDate()} mai sélectionné`); }}
          events={EVENTS} />
        <ListSection title={`Évènements · ${sel.getDate()} mai`}>
          <ListRow icon="clock" iconColor="var(--accent)" label="Standup équipe" value="09:00" onClick={() => {}} />
          <ListRow icon="user" iconColor="var(--blue)" label="1:1 avec Sophie" value="14:30" onClick={() => {}} />
          <ListRow icon="bell" iconColor="var(--warn)" label="Rappel : sauvegarde" value="18:00" onClick={() => {}} />
        </ListSection>
      </div>
      <FAB icon="plus" label="Nouvel évènement" onClick={() => showToast('Nouvel évènement')} />
    </div>
  );
}

/* --- Maps --- */
function ScreenMaps({ onAvatar }) {
  const [active, setActive] = uX(0);
  const pins = [
    { x: 30, y: 35, color: 'var(--accent)', icon: 'server',  label: 'Bureau' },
    { x: 60, y: 50, color: 'var(--ok)',     icon: 'grid',    label: 'Maison' },
    { x: 75, y: 25, color: 'var(--blue)',   icon: 'bell',    label: 'Café'   },
    { x: 22, y: 70, color: 'var(--err)',    icon: 'power',   label: 'Atelier' },
    { x: 50, y: 80, color: 'var(--warn)',   icon: 'clock' },
    { x: 80, y: 65, color: 'var(--purple)', icon: 'memory' },
  ];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', position: 'relative' }}>
      <StatusBar />
      <NavBar title="Carte" right={<Avatar name="M" size={36} onClick={onAvatar} />} />
      <div style={{ position: 'absolute', top: 88, left: 14, right: 14, zIndex: 10 }}>
        <SearchBar value="" onChange={() => {}} placeholder="Adresse, lieu…" />
      </div>
      <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
        <MapView pins={pins} />
        {/* contrôles flottants */}
        <div style={{
          position: 'absolute', top: 70, right: 12,
          display: 'flex', flexDirection: 'column', gap: 8,
        }}>
          <IconButton icon="plus" label="Zoom +" size={40} />
          <IconButton icon="close" label="Zoom −" size={40} />
          <IconButton icon="network" label="Ma position" size={40} primary />
        </div>
        {/* bottom info */}
        <div className="glass-strong" style={{
          position: 'absolute', bottom: 16, left: 14, right: 14,
          borderRadius: 14, padding: 14,
          display: 'flex', gap: 12, alignItems: 'center',
        }}>
          <div style={{
            width: 40, height: 40, borderRadius: 10,
            background: pins[active].color,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--bg-1)',
          }}><Icon name={pins[active].icon} size={18} /></div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 700 }}>{pins[active].label || 'Lieu sans nom'}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)' }}>48.8566° N · 2.3522° E</div>
          </div>
          <IconButton icon="chevR" label="Itinéraire" primary />
        </div>
      </div>
    </div>
  );
}

/* --- Search avec filtres --- */
function ScreenSearch({ onBack, onAvatar }) {
  const [q, setQ] = uX('');
  const [filters, setFilters] = uX(['all']);
  const allResults = [
    { id: 1, type: 'people',    title: 'Sophie Martin',         meta: 'sophie@exemple.com' },
    { id: 2, type: 'message',   title: 'Tu peux jeter un œil…', meta: 'Marc · hier 14:02' },
    { id: 3, type: 'file',      title: 'rapport-scan.pdf',      meta: '21 mai · 384 Ko' },
    { id: 4, type: 'event',     title: 'Standup équipe',        meta: 'mardi 09:00' },
    { id: 5, type: 'place',     title: 'Bureau Paris 11e',      meta: '48.8566° N · 2.3522° E' },
    { id: 6, type: 'file',      title: 'capture.png',           meta: '21 mai · 1.2 Mo' },
  ];
  const results = allResults.filter((r) => {
    if (q && !r.title.toLowerCase().includes(q.toLowerCase())) return false;
    if (filters.includes('all')) return true;
    return filters.includes(r.type);
  });
  const iconFor = (t) => ({ people: 'user', message: 'bell', file: 'folder', event: 'clock', place: 'network' })[t];
  const colorFor = (t) => ({ people: 'var(--accent)', message: 'var(--blue)', file: 'var(--info)', event: 'var(--purple)', place: 'var(--ok)' })[t];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <NavBar title="Recherche" onBack={onBack} right={<Avatar name="M" size={36} onClick={onAvatar} />} />
      <div style={{ padding: '4px 14px' }}>
        <SearchBar value={q} onChange={setQ} placeholder="Personnes, messages, fichiers…" />
        <div style={{ marginTop: 10 }}>
          <FilterChips value={filters} onChange={(v) => setFilters(v.length ? v : ['all'])}
            options={[
              { value: 'all',     label: 'Tout',      icon: 'grid' },
              { value: 'people',  label: 'Personnes', icon: 'user' },
              { value: 'message', label: 'Messages',  icon: 'bell' },
              { value: 'file',    label: 'Fichiers',  icon: 'folder' },
              { value: 'event',   label: 'Agenda',    icon: 'clock' },
              { value: 'place',   label: 'Lieux',     icon: 'network' },
            ]} />
        </div>
      </div>
      <div style={{ flex: 1, overflowY: 'auto', padding: '14px 14px 80px' }}>
        <div className="label" style={{ marginBottom: 8 }}>{results.length} résultat{results.length > 1 ? 's' : ''}</div>
        <div style={{
          background: 'var(--bg-3)', border: '1px solid var(--border-2)',
          borderRadius: 12, overflow: 'hidden',
        }}>
          {results.map((r) => (
            <ListRow key={r.id} icon={iconFor(r.type)} iconColor={colorFor(r.type)}
              label={r.title} value={r.meta} onClick={() => {}} />
          ))}
          {results.length === 0 && (
            <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>
              Aucun résultat pour "<b>{q}</b>"
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* --- QR Scanner --- */
function ScreenScanner({ onBack, showToast }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <div style={{
        position: 'absolute', top: 44, left: 0, right: 0, zIndex: 10,
        padding: '8px 12px',
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        <button onClick={onBack} className="touch-press" style={{
          width: 36, height: 36, borderRadius: 18,
          background: 'rgba(0,0,0,0.55)', border: 'none', color: '#fff',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
        }}><Icon name="chevL" size={18} /></button>
        <div style={{ flex: 1 }}/>
        <div style={{
          padding: '4px 12px', borderRadius: 999,
          background: 'rgba(0,0,0,0.55)',
          color: '#fff', fontFamily: 'var(--font-mono)', fontSize: 11,
        }}>Scanner QR / code-barres</div>
      </div>
      <div style={{ flex: 1, position: 'relative' }}>
        <QrScannerView onCapture={() => showToast('Code détecté : https://exemple.com')} />
      </div>
    </div>
  );
}

/* --- Caméra --- */
function ScreenCamera({ onBack, showToast }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <div style={{
        position: 'absolute', top: 44, left: 0, right: 0, zIndex: 10,
        padding: '8px 12px',
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        <button onClick={onBack} className="touch-press" style={{
          width: 36, height: 36, borderRadius: 18,
          background: 'rgba(0,0,0,0.55)', border: 'none', color: '#fff',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
        }}><Icon name="chevL" size={18} /></button>
      </div>
      <div style={{ flex: 1, position: 'relative' }}>
        <CameraView onShoot={() => showToast('Photo prise')} />
      </div>
    </div>
  );
}

/* --- Fichiers --- */
function ScreenFiles({ onBack, onAvatar, showToast }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <NavBar title="Fichiers" subtitle="Espace personnel" onBack={onBack}
        right={<Avatar name="M" size={36} onClick={onAvatar} />} />
      <div style={{ padding: '6px 14px' }}>
        <SearchBar value="" onChange={() => {}} placeholder="Rechercher un fichier" />
      </div>
      <div style={{
        padding: '8px 14px',
        fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--ink-3)',
        display: 'flex', alignItems: 'center', gap: 6,
      }}>
        <Icon name="folder" size={12} style={{ color: 'var(--accent)' }} />
        Mes fichiers / Espace personnel
      </div>
      <div style={{ flex: 1, overflowY: 'auto', paddingBottom: 80 }}>
        <FileExplorer items={FILES} onOpen={(it) => showToast(`Ouvrir : ${it.name}`)}
          onAction={(act, it) => showToast(`${act} · ${it.name}`)} />
      </div>
      <FAB icon="plus" label="Importer" onClick={() => showToast('Importer un fichier')} />
    </div>
  );
}

/* --- Settings --- */
function ScreenSettings({ onBack, onAvatar, showToast, openSheet }) {
  const [notif, setNotif] = uX(true);
  const [bio, setBio] = uX(false);
  const [sync, setSync] = uX(true);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <StatusBar />
      <HeaderAvatar large title="Paramètres" onBack={onBack} onAvatar={onAvatar} />
      <div style={{ flex: 1, overflowY: 'auto', paddingBottom: 80 }}>
        <ListSection title="Compte">
          <ListRow icon="user" iconColor="var(--blue)" label="Mon profil"
            value="Marc Dupont" onClick={() => showToast('Profil')} />
          <ListRow icon="bell" iconColor="var(--accent)" label="Email"
            value="marc@exemple.com" onClick={() => {}} />
        </ListSection>
        <ListSection title="Préférences">
          <ListRow icon="moon" iconColor="var(--info)"
            label="Thème" value="Sombre" onClick={() => openSheet('theme')} />
          <ListRow icon="cog" iconColor="var(--ink-3)"
            label="Langue" value="Français" onClick={() => openSheet('lang')} />
          <ListRow icon="bell" iconColor="var(--accent)" label="Notifications"
            right={<Toggle on={notif} onChange={setNotif} />} />
          <ListRow icon="refresh" iconColor="var(--ok)" label="Sync auto"
            right={<Toggle on={sync} onChange={setSync} />} />
          <ListRow icon="power" iconColor="var(--purple)" label="Face ID au démarrage"
            right={<Toggle on={bio} onChange={setBio} />} />
        </ListSection>
        <ListSection title="Confidentialité">
          <ListRow icon="folder" iconColor="var(--blue)" label="Données stockées" value="248 Mo" onClick={() => {}} />
          <ListRow icon="download" iconColor="var(--ok)" label="Exporter mes données" onClick={() => {}} />
          <ListRow icon="close" iconColor="var(--err)" label="Effacer le cache" onClick={() => showToast('Cache effacé')} />
        </ListSection>
        <ListSection title="À propos" hint="Application v1.0.6 · build 2026.05.21">
          <ListRow icon="list" iconColor="var(--ink-3)" label="Conditions générales" onClick={() => {}} />
          <ListRow icon="list" iconColor="var(--ink-3)" label="Politique de confidentialité" onClick={() => {}} />
          <ListRow icon="bell" iconColor="var(--ink-3)" label="Centre d'aide" onClick={() => {}} />
        </ListSection>
        <ListSection>
          <ListRow icon="power" iconColor="var(--err)" label="Se déconnecter"
            danger onClick={() => showToast('Déconnexion')} />
        </ListSection>
      </div>
    </div>
  );
}

Object.assign(window, {
  ScreenHome, ScreenChatList, ScreenChatDetail,
  ScreenCalendar, ScreenMaps, ScreenSearch,
  ScreenScanner, ScreenCamera, ScreenFiles, ScreenSettings,
  ONBOARDING_SLIDES, CHATS, CHAT_MESSAGES, FILES, EVENTS,
});


/* ============================================================
   exemple-mobile-apps-doc.jsx
   Doc panneau droite (catalogue commenté avec visuels) + ROOT.
   ============================================================ */

const { useState: uAD, useEffect: eAD } = React;

/* ============================================================
   ScreenVisual — mini SVG de chaque écran (pour la doc)
   ============================================================ */
function ScreenVisual({ type }) {
  const frame = (inner, bg) => (
    <svg viewBox="0 0 100 180" width="80" height="144" style={{ display: 'block' }}>
      <rect x="3" y="2" width="94" height="176" rx="14" fill={bg || 'var(--bg-3)'} stroke="var(--border-3)" strokeWidth="1.5"/>
      <rect x="38" y="6" width="24" height="3" rx="1.5" fill="var(--ink-4)"/>
      {inner}
    </svg>
  );
  if (type === 'onboarding') return frame(
    <g>
      <rect x="34" y="48" width="32" height="32" rx="8" fill="var(--accent)"/>
      <rect x="20" y="92" width="60" height="4" rx="2" fill="var(--ink-1)"/>
      <rect x="26" y="102" width="48" height="2.5" rx="1" fill="var(--ink-3)"/>
      <rect x="30" y="108" width="40" height="2.5" rx="1" fill="var(--ink-3)"/>
      {/* dots */}
      <circle cx="42" cy="138" r="1.5" fill="var(--ink-4)"/>
      <rect x="46" y="136.5" width="8" height="3" rx="1.5" fill="var(--accent)"/>
      <circle cx="58" cy="138" r="1.5" fill="var(--ink-4)"/>
      {/* button */}
      <rect x="20" y="150" width="60" height="12" rx="5" fill="var(--accent)"/>
    </g>
  );
  if (type === 'home') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="10" y="18" width="40" height="4" rx="2" fill="var(--ink-1)"/>
      <rect x="10" y="25" width="28" height="2.5" rx="1" fill="var(--ink-3)"/>
      <circle cx="88" cy="24" r="6" fill="var(--accent)"/>
      <rect x="10" y="38" width="80" height="9" rx="4" fill="var(--bg-2)" stroke="var(--border-2)" strokeWidth="0.5"/>
      {/* 9 grid */}
      {[0,1,2,0,1,2,0,1,2].map((c, i) => {
        const r = Math.floor(i / 3);
        return <rect key={i} x={10 + c * 27} y={52 + r * 32} width="24" height="28" rx="4"
                     fill="var(--bg-2)" stroke="var(--border-2)" strokeWidth="0.5"/>;
      })}
    </g>
  );
  if (type === 'chat-list') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="10" y="20" width="40" height="4" rx="2" fill="var(--ink-1)"/>
      <circle cx="88" cy="24" r="6" fill="var(--accent)"/>
      {[0,1,2,3].map((i) => (
        <g key={i} transform={`translate(0, ${42 + i * 30})`}>
          <circle cx="14" cy="14" r="8" fill={['var(--accent)', 'var(--blue)', 'var(--purple)', 'var(--ok)'][i]}/>
          <rect x="26" y="8" width="40" height="3" rx="1.5" fill="var(--ink-1)"/>
          <rect x="26" y="14" width="55" height="2" rx="1" fill="var(--ink-3)"/>
          <rect x="72" y="6" width="14" height="2" rx="1" fill="var(--ink-4)"/>
          {i === 0 && <circle cx="88" cy="18" r="4" fill="var(--accent)"/>}
        </g>
      ))}
    </g>
  );
  if (type === 'chat-detail') return frame(
    <g>
      <rect x="3" y="14" width="94" height="16" fill="var(--bg-2)"/>
      <path d="M 10 22 l 4 -4 m -4 4 l 4 4" stroke="var(--accent)" strokeWidth="1.5" fill="none"/>
      <circle cx="22" cy="22" r="5" fill="var(--purple)"/>
      <rect x="32" y="18" width="28" height="3" rx="1.5" fill="var(--ink-1)"/>
      <rect x="32" y="23" width="14" height="2" rx="1" fill="var(--ok)"/>
      {/* messages */}
      <rect x="10" y="40" width="50" height="14" rx="6" fill="var(--bg-2)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="40" y="60" width="50" height="14" rx="6" fill="var(--accent)"/>
      <rect x="10" y="80" width="60" height="18" rx="6" fill="var(--bg-2)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="46" y="104" width="44" height="14" rx="6" fill="var(--accent)"/>
      {/* composer */}
      <rect x="3" y="156" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="20" y="162" width="55" height="9" rx="4" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <circle cx="86" cy="167" r="5" fill="var(--accent)"/>
    </g>
  );
  if (type === 'calendar') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="10" y="20" width="40" height="4" rx="2" fill="var(--ink-1)"/>
      <circle cx="88" cy="24" r="6" fill="var(--accent)"/>
      {/* day labels */}
      {[0,1,2,3,4,5,6].map((d) => (
        <text key={d} x={11 + d * 12} y="48" fontSize="3.5" fontFamily="JetBrains Mono" fill="var(--ink-3)">{['L','M','M','J','V','S','D'][d]}</text>
      ))}
      {/* days */}
      {Array.from({length:28}).map((_, i) => {
        const x = 8 + (i % 7) * 12;
        const y = 52 + Math.floor(i / 7) * 13;
        const sel = i === 20;
        const today = i === 7;
        return (
          <g key={i}>
            <rect x={x} y={y} width="11" height="11" rx="2" fill={sel ? 'var(--accent)' : today ? 'var(--accent-tint)' : 'transparent'}/>
            <text x={x + 5.5} y={y + 8} textAnchor="middle" fontSize="4" fontFamily="JetBrains Mono"
                  fill={sel ? 'var(--bg-1)' : 'var(--ink-1)'}>{i + 1}</text>
            {[2,6,11,13,20].includes(i) && <circle cx={x + 5.5} cy={y + 11} r="1" fill={sel ? 'var(--bg-1)' : 'var(--accent)'}/>}
          </g>
        );
      })}
      <circle cx="84" cy="160" r="9" fill="var(--accent)"/>
      <path d="M 81 160 L 87 160 M 84 157 L 84 163" stroke="var(--bg-1)" strokeWidth="1.5"/>
    </g>
  );
  if (type === 'maps') return frame(
    <g>
      <rect x="3" y="14" width="94" height="16" fill="var(--bg-2)"/>
      <rect x="10" y="18" width="80" height="9" rx="4" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="3" y="30" width="94" height="118" fill="var(--bg-2)"/>
      {/* routes */}
      <path d="M 3 60 Q 50 50 97 90" stroke="var(--ink-4)" strokeWidth="2" fill="none" opacity="0.3"/>
      <path d="M 30 30 Q 50 80 70 148" stroke="var(--ink-4)" strokeWidth="2" fill="none" opacity="0.3"/>
      {/* pins */}
      {[[30,55,'var(--accent)'],[60,80,'var(--ok)'],[75,45,'var(--blue)'],[22,110,'var(--err)']].map(([x,y,c], i) => (
        <g key={i}>
          <path d={`M ${x} ${y - 4} A 4 4 0 1 1 ${x - 0.01} ${y - 4} L ${x} ${y + 2} Z`} fill={c} stroke="var(--bg-1)" strokeWidth="0.5"/>
        </g>
      ))}
      {/* bottom card */}
      <rect x="8" y="152" width="84" height="22" rx="5" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="14" y="158" width="8" height="8" rx="2" fill="var(--accent)"/>
      <rect x="26" y="159" width="30" height="3" rx="1" fill="var(--ink-1)"/>
      <rect x="26" y="164" width="36" height="2" rx="1" fill="var(--ink-3)"/>
    </g>
  );
  if (type === 'search') return frame(
    <g>
      <rect x="3" y="14" width="94" height="16" fill="var(--bg-2)"/>
      <rect x="10" y="34" width="80" height="9" rx="4" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      {/* chips */}
      <rect x="10" y="48" width="18" height="6" rx="3" fill="var(--accent)"/>
      <rect x="31" y="48" width="18" height="6" rx="3" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="52" y="48" width="18" height="6" rx="3" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      <rect x="73" y="48" width="18" height="6" rx="3" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.5"/>
      {/* results */}
      {[0,1,2,3,4].map((i) => (
        <g key={i} transform={`translate(0, ${62 + i * 22})`}>
          <rect x="10" y="0" width="80" height="20" fill="var(--bg-3)" stroke="var(--border-2)" strokeWidth="0.4"/>
          <circle cx="20" cy="10" r="5" fill={['var(--accent)','var(--blue)','var(--info)','var(--purple)','var(--ok)'][i]}/>
          <rect x="30" y="6" width="35" height="3" rx="1" fill="var(--ink-1)"/>
          <rect x="30" y="12" width="48" height="2" rx="1" fill="var(--ink-3)"/>
        </g>
      ))}
    </g>
  );
  if (type === 'scanner') return frame(
    <g>
      <rect x="3" y="14" width="94" height="164" fill="#15110c"/>
      {/* coins */}
      <path d="M 30 60 L 30 50 L 40 50" stroke="var(--accent)" strokeWidth="2.5" fill="none"/>
      <path d="M 70 50 L 80 50 L 80 60" stroke="var(--accent)" strokeWidth="2.5" fill="none"/>
      <path d="M 80 110 L 80 120 L 70 120" stroke="var(--accent)" strokeWidth="2.5" fill="none"/>
      <path d="M 40 120 L 30 120 L 30 110" stroke="var(--accent)" strokeWidth="2.5" fill="none"/>
      {/* scan line */}
      <line x1="32" y1="80" x2="78" y2="80" stroke="var(--accent)" strokeWidth="1"
        style={{filter:'drop-shadow(0 0 2px var(--accent))'}}/>
      {/* shutter */}
      <circle cx="50" cy="155" r="9" fill="var(--accent)" stroke="var(--bg-1)" strokeWidth="2"/>
    </g>,
    '#000'
  );
  if (type === 'camera') return frame(
    <g>
      <rect x="3" y="14" width="94" height="164" fill="#15110c"/>
      <rect x="3" y="14" width="94" height="120" fill="url(#cam-bg)"/>
      <defs>
        <linearGradient id="cam-bg" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor="#6b4423"/>
          <stop offset="1" stopColor="#2a1f15"/>
        </linearGradient>
      </defs>
      {/* règle des tiers */}
      <line x1="34" y1="14" x2="34" y2="134" stroke="#fff" strokeWidth="0.2" opacity="0.3"/>
      <line x1="66" y1="14" x2="66" y2="134" stroke="#fff" strokeWidth="0.2" opacity="0.3"/>
      <line x1="3" y1="54" x2="97" y2="54" stroke="#fff" strokeWidth="0.2" opacity="0.3"/>
      <line x1="3" y1="94" x2="97" y2="94" stroke="#fff" strokeWidth="0.2" opacity="0.3"/>
      {/* shutter */}
      <circle cx="50" cy="158" r="11" fill="#fff" stroke="rgba(255,255,255,0.4)" strokeWidth="2"/>
      <rect x="18" y="152" width="10" height="10" rx="2" fill="var(--accent-soft)"/>
      <circle cx="78" cy="158" r="6" fill="none" stroke="#fff" strokeWidth="1"/>
    </g>,
    '#000'
  );
  if (type === 'files') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="10" y="20" width="40" height="4" rx="2" fill="var(--ink-1)"/>
      {[
        ['folder','var(--accent)','Documents'],
        ['folder','var(--accent)','Photos'],
        ['list','var(--err)','rapport.pdf'],
        ['grid','var(--blue)','capture.png'],
        ['play','var(--purple)','meeting.mp4'],
        ['terminal','var(--ok)','memo.m4a'],
      ].map((it, i) => (
        <g key={i} transform={`translate(0, ${42 + i * 22})`}>
          <rect x="3" y="0" width="94" height="20" fill="var(--bg-3)"/>
          <rect x="10" y="4" width="12" height="12" rx="2" fill="var(--bg-1)" stroke={it[1]} strokeWidth="0.6"/>
          <rect x="26" y="6" width="50" height="3" rx="1" fill="var(--ink-1)"/>
          <rect x="26" y="12" width="35" height="2" rx="1" fill="var(--ink-3)"/>
        </g>
      ))}
      <circle cx="84" cy="160" r="9" fill="var(--accent)"/>
      <path d="M 81 160 L 87 160 M 84 157 L 84 163" stroke="var(--bg-1)" strokeWidth="1.5"/>
    </g>
  );
  if (type === 'settings') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <rect x="10" y="20" width="40" height="4" rx="2" fill="var(--ink-1)"/>
      <circle cx="88" cy="24" r="6" fill="var(--accent)"/>
      {[
        ['user','var(--blue)','Compte'],
        ['cog','var(--ink-3)','Préférences'],
        ['bell','var(--accent)','Notifications', true],
        ['refresh','var(--ok)','Sync', true],
        ['folder','var(--purple)','Données'],
        ['power','var(--err)','Se déconnecter', null, true],
      ].map((it, i) => (
        <g key={i} transform={`translate(0, ${42 + i * 20})`}>
          <rect x="3" y="0" width="94" height="18" fill="var(--bg-3)"/>
          <rect x="10" y="3" width="11" height="11" rx="2" fill={it[1]} opacity="0.85"/>
          <rect x="25" y="6" width="40" height="3" rx="1" fill={it[4] ? 'var(--err)' : 'var(--ink-1)'}/>
          {it[3] && <rect x="76" y="5" width="14" height="7" rx="3.5" fill="var(--accent)"/>}
        </g>
      ))}
    </g>
  );
  if (type === 'avatar-menu') return frame(
    <g>
      <rect x="3" y="14" width="94" height="20" fill="var(--bg-2)"/>
      <circle cx="86" cy="24" r="7" fill="var(--accent)"/>
      <text x="86" y="27" textAnchor="middle" fontSize="6" fontFamily="Inter" fontWeight="700" fill="var(--bg-1)">M</text>
      {/* écran assombri derrière */}
      <rect x="3" y="34" width="94" height="144" fill="#000" opacity="0.4"/>
      {/* popup menu */}
      <rect x="50" y="38" width="45" height="80" rx="6" fill="var(--bg-3)" stroke="var(--border-2)"/>
      <rect x="50" y="38" width="45" height="14" rx="6" fill="var(--bg-2)"/>
      <circle cx="58" cy="45" r="3.5" fill="var(--accent)"/>
      <rect x="64" y="42" width="20" height="2.5" rx="1" fill="var(--ink-1)"/>
      <rect x="64" y="47" width="14" height="2" rx="1" fill="var(--ink-3)"/>
      {/* items */}
      {[0,1,2,3].map((i) => (
        <g key={i} transform={`translate(0, ${56 + i * 14})`}>
          <line x1="50" y1="0" x2="95" y2="0" stroke="var(--border-1)" strokeWidth="0.4"/>
          <circle cx="55" cy="7" r="2" fill="var(--accent)"/>
          <rect x="60" y="6" width="25" height="2" rx="1" fill={i === 3 ? 'var(--err)' : 'var(--ink-1)'}/>
        </g>
      ))}
    </g>
  );
  return frame(null);
}

/* ============================================================
   ScreenCard — carte de présentation d'un écran
   ============================================================ */
function ScreenCard({ type, name, when, why, components, gestures }) {
  return (
    <div className="card">
      <div style={{ display: 'grid', gridTemplateColumns: '90px 1fr', gap: 14, alignItems: 'flex-start' }}>
        <ScreenVisual type={type} />
        <div style={{ minWidth: 0 }}>
          <span className="pill-name" style={{ marginBottom: 10, display: 'inline-flex' }}>Écran {name}</span>
          <div className="row-use"><span className="k">Quand</span><span className="v">{when}</span></div>
          <div className="row-use"><span className="k">Pourquoi</span><span className="v">{why}</span></div>
          {components && <div className="row-use"><span className="k">Composants</span><span className="v" style={{color:'var(--ink-3)', fontFamily:'var(--font-mono)', fontSize: 11.5}}>{components}</span></div>}
          {gestures && <div className="row-use"><span className="k">Gestes</span><span className="v">{gestures}</span></div>}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   NamedItem (avec preview live)
   ============================================================ */
function NamedItem({ name, desc, location, preview }) {
  return (
    <div className="card">
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8, flexWrap: 'wrap' }}>
        <span className="pill-name">&lt;{name}/&gt;</span>
        {location && <span className="legend">📍 {location}</span>}
      </div>
      <div style={{ fontSize: 13.5, color: 'var(--ink-2)', lineHeight: 1.5 }}>{desc}</div>
      {preview && (
        <div style={{
          marginTop: 12, padding: 12,
          background: 'var(--bg-1)',
          border: '1px dashed var(--border-2)',
          borderRadius: 8,
        }}>{preview}</div>
      )}
    </div>
  );
}

/* ============================================================
   DOC PANEL
   ============================================================ */
function Doc() {
  return (
    <div className="doc">
      <section>
        <h2><Icon name="grid" size={22} style={{ color: 'var(--accent)' }} /> Patterns d'app courants</h2>
        <p className="desc">
          Ensemble de patterns que toute app mobile moderne utilise : <strong>onboarding,
          chat, calendrier, maps, recherche, scanner, caméra, fichiers, paramètres</strong>,
          plus le <strong>bouton avatar en haut à droite</strong> qui ouvre un menu utilisateur.
          Chaque écran est navigable dans le smartphone à gauche.
        </p>
      </section>

      <section id="screens">
        <h2><Icon name="list" size={22} style={{ color: 'var(--accent)' }} /> 10 écrans modèles</h2>
        <p className="desc">Bascule entre eux depuis l'Accueil du smartphone (grille d'icônes). L'écran <strong>Onboarding</strong> se lance automatiquement au premier passage.</p>

        <ScreenCard type="onboarding" name="Onboarding"
          when="Premier lancement de l'app, ou pour ré-expliquer une fonctionnalité importante."
          why="Présente l'app en 2-4 slides illustrées avec pagination claire."
          components="OnboardingSlider · AvatarLogo"
          gestures="Tap sur les dots pour aller à une slide · bouton Suivant / Passer" />

        <ScreenCard type="home" name="Accueil"
          when="Page d'entrée après login. Donne accès à tous les autres écrans."
          why="Grille d'ActionCards 3 colonnes : visuel immédiat, hit targets larges."
          components="SearchBar · ActionCard · Avatar"
          gestures="Tap sur une carte ouvre l'écran correspondant" />

        <ScreenCard type="chat-list" name="Liste conversations"
          when="L'utilisateur veut voir ses conversations récentes et en ouvrir une."
          why="Liste swipeable avec avatar + nom + dernier message + heure + badge non lus."
          components="Avatar · SwipeableRow · SearchBar"
          gestures="SwipeLeft : archive/supprime · SwipeRight : marque lu/épingle · Tap : ouvre" />

        <ScreenCard type="chat-detail" name="Conversation"
          when="Échange de messages avec un contact."
          why="Bulles différenciées (envoyé orange à droite, reçu sombre à gauche), composer en bas avec joindre/audio/envoyer."
          components="ChatBubble · ChatComposer · Avatar"
          gestures="Tap retour · Tap envoyer · Tap micro pour audio" />

        <ScreenCard type="calendar" name="Calendrier"
          when="Voir / créer des évènements datés."
          why="Vue mois compacte avec points sous les jours marqués. Sélection met à jour la liste d'évènements du jour."
          components="CalendarMonth · ListSection · FAB"
          gestures="Tap sur une cellule · FAB pour créer · swipe (hors démo) pour changer de mois" />

        <ScreenCard type="maps" name="Carte / Maps"
          when="Voir des points d'intérêt géolocalisés ou se diriger."
          why="Carte plein écran avec pins colorés + carte d'info en bas + boutons zoom flottants."
          components="MapView · SearchBar · IconButton · glass-strong"
          gestures="Pan pour déplacer · Pinch pour zoomer · Tap pin pour voir détails" />

        <ScreenCard type="search" name="Recherche avancée"
          when="L'utilisateur cherche dans tout le contenu de l'app."
          why="SearchBar en haut + FilterChips horizontaux + liste de résultats unifiée toutes catégories."
          components="SearchBar · FilterChips · ListRow"
          gestures="Tap sur un chip filtre · scroll horizontal des chips" />

        <ScreenCard type="scanner" name="Scanner QR / code-barres"
          when="Lecture rapide d'un QR code ou code-barres pour récupérer une info."
          why="Plein écran caméra avec viseur central animé (ligne scan) et boutons galerie/shutter/flash."
          components="QrScannerView · IconButton"
          gestures="Tap shutter pour capturer · auto-détection en vrai usage" />

        <ScreenCard type="camera" name="Appareil photo"
          when="Prendre une photo dans l'app."
          why="Plein écran avec règle des tiers, sélecteur de mode (vidéo/photo/portrait), shutter rond classique."
          components="CameraView · IconButton"
          gestures="Tap shutter · swipe les modes en bas (hors démo) · Tap aperçu pour revoir" />

        <ScreenCard type="files" name="Gestion de fichiers"
          when="Parcourir, ouvrir, partager des fichiers."
          why="Liste avec icône colorée par type + nom + taille + date. Actions cachées révélées au swipe."
          components="FileExplorer (SwipeableRow + ListRow) · SearchBar · FAB"
          gestures="SwipeLeft : supprimer · SwipeRight : renommer/partager · Tap : ouvrir" />

        <ScreenCard type="settings" name="Paramètres"
          when="Toujours accessible. Configuration globale + déconnexion."
          why="Sections groupées (Compte, Préférences, Confidentialité, À propos) avec ListRow et toggles inline."
          components="ListSection · ListRow · Toggle · BottomSheet"
          gestures="Tap ligne · Toggle inline · Tap se déconnecter en bas (rouge)" />
      </section>

      <section id="avatar">
        <h2><Icon name="user" size={22} style={{ color: 'var(--accent)' }} /> Bouton Avatar + menu utilisateur</h2>
        <p className="desc">
          Pattern récurrent : un <strong>bouton rond avec initiales</strong> en haut à droite de chaque écran.
          Au tap, il ouvre un <strong>menu déroulant</strong> avec accès rapide à Profil, Paramètres, Aide et Déconnexion.
          Pour le tester : tape sur le rond orange en haut à droite de n'importe quel écran (sauf Onboarding/Scanner/Caméra).
        </p>
        <ScreenCard type="avatar-menu" name="Avatar Menu"
          when="L'utilisateur veut un accès rapide à son compte sans passer par un onglet dédié."
          why="Toujours visible, identité claire (initiales + couleur), menu compact. Standard sur la plupart des apps web/mobiles."
          components="Avatar · AvatarMenu" />

        <NamedItem name="Avatar" location="En haut à droite de chaque écran"
          desc="Bouton rond utilisateur. Affiche les initiales du nom avec un dégradé coloré. Accepte name, color, size, onClick, active (ring orange si actif). Pas d'image bitmap nécessaire — l'initial sur fond coloré reste lisible et identifiable."
          preview={<div style={{display:'flex', gap: 12, justifyContent:'center'}}>
            <Avatar name="Marc Dubois" color="var(--accent)" />
            <Avatar name="Sophie M." color="var(--blue)" />
            <Avatar name="Antoine" color="var(--ok)" />
            <Avatar name="Léa" color="var(--purple)" active />
          </div>} />

        <NamedItem name="AvatarMenu" location="Popup descendant depuis l'Avatar"
          desc="Menu déroulant affiché en haut à droite, avec header (avatar + nom + email) et liste d'actions. Anime à l'ouverture (drop-in + fade backdrop). Item avec danger:true s'affiche en rouge. Items typiques : Profil, Paramètres, Notifications, Aide, Se déconnecter."
          preview={
            <div style={{
              width: '100%', maxWidth: 260,
              background: 'var(--bg-3)', border: '1px solid var(--border-2)',
              borderRadius: 12, overflow: 'hidden', margin: '0 auto',
            }}>
              <div style={{ padding: 12, display: 'flex', gap: 10, alignItems: 'center', borderBottom: '1px solid var(--border-1)', background: 'var(--bg-2)' }}>
                <Avatar name="Marc" size={32} />
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700 }}>Marc Dupont</div>
                  <div style={{ fontSize: 10, color: 'var(--ink-3)', fontFamily: 'var(--font-mono)' }}>marc@exemple.com</div>
                </div>
              </div>
              {[
                { icon: 'user', label: 'Mon profil' },
                { icon: 'cog', label: 'Paramètres' },
                { icon: 'bell', label: 'Notifications' },
                { icon: 'power', label: 'Se déconnecter', danger: true },
              ].map((it, i) => (
                <div key={i} style={{
                  padding: '8px 12px', display: 'flex', alignItems: 'center', gap: 8,
                  borderTop: i > 0 ? '1px solid var(--border-1)' : 'none',
                  color: it.danger ? 'var(--err)' : 'var(--ink-1)', fontSize: 13,
                }}>
                  <Icon name={it.icon} size={13} style={{ color: it.danger ? 'var(--err)' : 'var(--accent)' }}/>
                  <span style={{ flex: 1 }}>{it.label}</span>
                </div>
              ))}
            </div>
          } />
      </section>

      <section id="components">
        <h2><Icon name="cog" size={22} style={{ color: 'var(--accent)' }} /> Nouveaux composants</h2>
        <p className="desc">Tous accessibles globalement après chargement de <code className="mono" style={{color:'var(--accent)'}}>mobile-apps.jsx</code>.</p>

        <NamedItem name="OnboardingSlider" location="Premier lancement"
          desc="Slider plein-écran avec pagination (dots), bouton 'Passer' en haut, bouton 'Suivant' (puis 'Commencer') en bas. Prop slides = [{icon, color, title, desc}]."
          preview={<div style={{display:'flex', justifyContent:'center', gap: 4}}>
            {[0,1,2].map((i) => (
              <span key={i} style={{ width: i === 0 ? 24 : 8, height: 8, borderRadius: 4, background: i === 0 ? 'var(--accent)' : 'var(--border-3)' }}/>
            ))}
          </div>} />

        <NamedItem name="ChatBubble" location="Conversation"
          desc="Bulle de message. me:true => orange à droite avec coin bas-droit aplati. me:false => sombre à gauche avec coin bas-gauche aplati. Affiche heure + statut (✓ sent, ✓✓ read)."
          preview={
            <div>
              <ChatBubble me={false} text="Salut, ça va ?" time="14:00" />
              <ChatBubble me={true}  text="Oui et toi !" time="14:01" status="read" />
            </div>
          } />

        <NamedItem name="ChatComposer" location="Bas conversation"
          desc="Barre d'envoi : bouton + (joindre) · input texte arrondi · bouton micro (vide) ou envoi (avec texte). Auto-bascule selon le contenu."
          preview={<ChatComposer onSend={() => {}} />} />

        <NamedItem name="CalendarMonth" location="Calendrier"
          desc="Vue mois 7 colonnes. Aujourd'hui = fond accent-tint. Sélectionné = fond accent plein. Jours avec évènement = point sous le chiffre. Props : year, month (0-11), selected, onSelect, events (Set de numéros)."
          preview={<div style={{maxWidth: 280, margin: '0 auto'}}>
            <CalendarMonth year={2026} month={4} selected={new Date(2026, 4, 21)} onSelect={() => {}} events={new Set([3, 7, 14, 21])} />
          </div>} />

        <NamedItem name="MapView" location="Carte"
          desc="Placeholder visuel de carte avec routes/zones stylisées + pins colorés. Props pins = [{x, y, color, icon, label}] (x/y en % du viewport)."
          preview={<div style={{height: 140, width: '100%', border: '1px solid var(--border-2)', borderRadius: 8, overflow: 'hidden'}}>
            <MapView pins={[
              { x: 30, y: 35, color: 'var(--accent)', icon: 'server', label: 'Ici' },
              { x: 70, y: 60, color: 'var(--ok)', icon: 'grid' },
            ]} />
          </div>} />

        <NamedItem name="FilterChips" location="Recherche"
          desc="Liste horizontale de chips multi-sélectionnables. Scrollable. Options = liste de strings ou {value, label, icon}."
          preview={<FilterChips value={['a']} onChange={() => {}} options={[
            { value: 'a', label: 'Tout', icon: 'grid' },
            { value: 'b', label: 'Personnes', icon: 'user' },
            { value: 'c', label: 'Fichiers', icon: 'folder' },
            { value: 'd', label: 'Messages', icon: 'bell' },
          ]} />} />

        <NamedItem name="QrScannerView" location="Scanner"
          desc="Plein écran caméra noir avec viseur carré central (4 coins angle accent) + ligne scan animée verticalement. Overlay assombri hors viseur. Boutons galerie / shutter / flash en bas."
          preview={<div style={{height: 160, width: '100%', border: '1px solid var(--border-2)', borderRadius: 8, overflow: 'hidden'}}>
            <QrScannerView onCapture={() => {}} />
          </div>} />

        <NamedItem name="CameraView" location="Appareil photo"
          desc="Plein écran caméra avec règle des tiers, top bar (flash/timer/grille), sélecteur de mode (vidéo/photo/portrait), shutter rond blanc bas-centre."
          preview={<div style={{height: 160, width: '100%', border: '1px solid var(--border-2)', borderRadius: 8, overflow: 'hidden'}}>
            <CameraView onShoot={() => {}} />
          </div>} />

        <NamedItem name="FileExplorer" location="Fichiers"
          desc="Liste de fichiers/dossiers. Chaque item a {name, type, date, size}. Type définit icône+couleur : folder/image/video/audio/pdf/code/archive. Wrappe chaque ligne dans SwipeableRow (renommer, partager, supprimer)."
          preview={
            <div style={{maxWidth: 320, margin: '0 auto', border: '1px solid var(--border-2)', borderRadius: 8, overflow: 'hidden'}}>
              <FileExplorer items={[
                { name: 'Documents', type: 'folder', date: '21 mai' },
                { name: 'rapport.pdf', type: 'pdf', date: '20 mai', size: 384000 },
                { name: 'capture.png', type: 'image', date: '20 mai', size: 1280000 },
              ]} onOpen={() => {}} />
            </div>
          } />
      </section>

      <section id="checklist">
        <h2><Icon name="download" size={22} style={{ color: 'var(--accent)' }} /> Antisèche — pattern par cas</h2>
        <div className="card">
          <div className="row-use"><span className="k">Premier lancement</span><span className="v">OnboardingSlider en plein écran, store flag "vu" en localStorage</span></div>
          <div className="row-use"><span className="k">Liste à actions</span><span className="v">SwipeableRow avec leftActions (destructives) + rightActions (utiles)</span></div>
          <div className="row-use"><span className="k">Identité utilisateur</span><span className="v">Avatar en haut à droite + AvatarMenu au tap → profil/réglages/déconnexion</span></div>
          <div className="row-use"><span className="k">Recherche multi-types</span><span className="v">SearchBar + FilterChips + ListRow (un seul container pour tous types)</span></div>
          <div className="row-use"><span className="k">Caméra/Scanner</span><span className="v">Plein écran sombre, header floating semi-transparent, contrôles flottants en bas</span></div>
          <div className="row-use"><span className="k">Carte</span><span className="v">MapView + boutons flottants (zoom, position) + BottomSheet/glass-strong pour les détails du pin</span></div>
          <div className="row-use"><span className="k">Réglages</span><span className="v">Empilement de ListSection avec en bas un ListRow danger "Se déconnecter"</span></div>
        </div>
      </section>
    </div>
  );
}

/* ============================================================
   APP ROOT
   ============================================================ */
function PhoneAppApps({ theme }) {
  const [screen, setScreen] = uAD('home');
  const [openedChat, setOpenedChat] = uAD(null);
  const [toast, setToast] = uAD(null);
  const [avatarOpen, setAvatarOpen] = uAD(false);
  const [sheet, setSheet] = uAD(null);

  const showToast = (msg) => setToast(msg);
  const goto = (s) => { setScreen(s); setOpenedChat(null); };
  const onAvatar = () => setAvatarOpen(true);

  return (
    <div data-theme={theme} style={{
      width: '100%', height: '100%',
      display: 'flex', flexDirection: 'column',
      background: 'var(--bg-1)', color: 'var(--ink-1)',
      position: 'relative', overflow: 'hidden',
    }}>
      <div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
        {screen === 'onboard' && (
          <OnboardingSlider slides={ONBOARDING_SLIDES} onFinish={() => goto('home')} />
        )}
        {screen === 'home'     && <ScreenHome     goto={goto} onAvatar={onAvatar} />}
        {screen === 'chat'     && !openedChat && <ScreenChatList onAvatar={onAvatar} open={setOpenedChat} />}
        {screen === 'chat'     && openedChat && <ScreenChatDetail chat={openedChat} onBack={() => setOpenedChat(null)} onAvatar={onAvatar} />}
        {screen === 'calendar' && <ScreenCalendar onAvatar={onAvatar} showToast={showToast} />}
        {screen === 'maps'     && <ScreenMaps onAvatar={onAvatar} />}
        {screen === 'search'   && <ScreenSearch onBack={() => goto('home')} onAvatar={onAvatar} />}
        {screen === 'scanner'  && <ScreenScanner onBack={() => goto('home')} showToast={showToast} />}
        {screen === 'camera'   && <ScreenCamera  onBack={() => goto('home')} showToast={showToast} />}
        {screen === 'files'    && <ScreenFiles   onBack={() => goto('home')} onAvatar={onAvatar} showToast={showToast} />}
        {screen === 'settings' && <ScreenSettings onBack={() => goto('home')} onAvatar={onAvatar} showToast={showToast} openSheet={setSheet} />}
      </div>

      {screen !== 'onboard' && screen !== 'scanner' && screen !== 'camera' && (
        <TabBar
          active={screen === 'chat' ? 'chat' : screen}
          onSelect={(id) => { goto(id); }}
          items={[
            { id: 'home',     icon: 'grid',    label: 'accueil' },
            { id: 'chat',     icon: 'bell',    label: 'chat' },
            { id: 'calendar', icon: 'clock',   label: 'agenda' },
            { id: 'maps',     icon: 'network', label: 'carte' },
          ]}
        />
      )}

      <AvatarMenu
        open={avatarOpen}
        onClose={() => setAvatarOpen(false)}
        name="Marc Dupont"
        email="marc@exemple.com"
        items={[
          { icon: 'user',    label: 'Mon profil',     onClick: () => showToast('Profil') },
          { icon: 'cog',     label: 'Paramètres',     onClick: () => goto('settings') },
          { icon: 'bell',    label: 'Notifications',  onClick: () => showToast('Notifications') },
          { icon: 'folder',  label: 'Mes fichiers',   onClick: () => goto('files') },
          { icon: 'list',    label: "Centre d'aide",   onClick: () => showToast('Aide') },
          { icon: 'power',   label: 'Se déconnecter', danger: true, onClick: () => goto('onboard') },
        ]}
      />

      <BottomSheet open={sheet === 'theme'} onClose={() => setSheet(null)} title="Choisir le thème">
        <SegmentedControl value="dark" onChange={() => {}} options={[
          { value: 'dark',  label: 'Sombre', icon: 'moon' },
          { value: 'light', label: 'Clair',  icon: 'sun' },
        ]} />
      </BottomSheet>
      <BottomSheet open={sheet === 'lang'} onClose={() => setSheet(null)} title="Langue">
        <RadioGroup value="fr" onChange={() => {}} options={[
          { value: 'fr', label: 'Français' },
          { value: 'en', label: 'English' },
          { value: 'de', label: 'Deutsch' },
        ]} />
      </BottomSheet>

      <Toast open={toast !== null} onClose={() => setToast(null)} message={toast} variant="ok" />
    </div>
  );
}

function App() {
  const [theme, setTheme] = uAD('dark');
  const [device, setDevice] = uAD('ios');
  eAD(() => { document.documentElement.dataset.theme = theme; }, [theme]);

  return (
    <React.Fragment>
      <header className="page-top">
        <div style={{
          width: 32, height: 32, borderRadius: 8,
          background: 'var(--accent)', color: 'var(--bg-1)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.3), 0 2px 6px var(--accent-glow)',
        }}>
          <Icon name="grid" size={16} />
        </div>
        <h1>Exemple mobile · patterns <small>chat · calendrier · maps · scanner · caméra · fichiers · avatar menu</small></h1>
        <span style={{ flex: 1 }}></span>
        <a href="exemple-mobile-saisie.html" style={{
          fontFamily: 'var(--font-mono)', fontSize: 12,
          color: 'var(--ink-3)', textDecoration: 'none',
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}><Icon name="chevL" size={12} /> exemple saisie</a>
      </header>

      <div className="layout">
        <div className="phone-col">
          <div className="phone-controls">
            <div className="seg">
              <button className={device === 'ios' ? 'active' : ''} onClick={() => setDevice('ios')}>iOS</button>
              <button className={device === 'android' ? 'active' : ''} onClick={() => setDevice('android')}>Android</button>
            </div>
            <div className="seg">
              <button className={theme === 'dark' ? 'active' : ''} onClick={() => setTheme('dark')}>Sombre</button>
              <button className={theme === 'light' ? 'active' : ''} onClick={() => setTheme('light')}>Clair</button>
            </div>
          </div>
          <div className="phone" style={device === 'android' ? { borderRadius: 28 } : {}}>
            {device === 'ios' && <div className="phone-notch"></div>}
            <div className="phone-screen" style={device === 'android' ? { borderRadius: 18 } : {}}>
              <PhoneAppApps theme={theme} />
            </div>
          </div>
          <div className="legend">↑ commence par l'accueil, puis explore les 9 autres écrans</div>
        </div>
        <Doc />
      </div>
    </React.Fragment>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
