// Ludilove — Screens part 2: Ludothèque, Jeu sélectionné, Produit,
// Découvrir, Scan IA, Filtres, Profil, Historique

// ──────────────────────────────────────────────────────────────
// LUDOTHÈQUE (library grid)
// ──────────────────────────────────────────────────────────────
function LibraryScreen({ onNav, onOpenGame, onScan }) {
  const [peers, setPeers] = React.useState(LUDOTHEQUES);
  const [selectedLibs, setSelectedLibs] = React.useState([]);
  const [pickerOpen, setPickerOpen] = React.useState(false);
  const [myGames, setMyGames] = React.useState([]);
  const [confirmDelete, setConfirmDelete] = React.useState(null); // game object pending deletion
  const [editMode, setEditMode] = React.useState(false);
  const [searchOpen, setSearchOpen] = React.useState(false);

  async function reloadLibrary() {
    try {
      const mine = await LLAPI.myLibrary();
      if (mine?.games) setMyGames(mine.games);
    } catch (err) { console.warn('reload lib:', err); }
  }

  async function deleteGame(game) {
    try {
      await LLAPI.removeFromLib(game.id);
      setMyGames(arr => arr.filter(g => g.id !== game.id));
      setConfirmDelete(null);
    } catch (err) {
      alert('Suppression échouée : ' + err.message);
    }
  }

  async function shareLibrary() {
    const text = `Ma ludothèque Ludilove (${myGames.length} jeux) :\n` +
      myGames.slice(0, 12).map(g => '• ' + g.name).join('\n') +
      (myGames.length > 12 ? `\n…et ${myGames.length - 12} autres.` : '') +
      `\n\nDécouvre Ludilove → ${window.location.origin}`;
    const data = { title: 'Ma ludothèque Ludilove', text };
    if (navigator.share) {
      try { await navigator.share(data); return; } catch (_) {}
    }
    try {
      await navigator.clipboard.writeText(text);
      alert('Liste copiée dans le presse-papier !');
    } catch {
      alert(text);
    }
  }

  // Fetch peers + current library
  React.useEffect(() => {
    (async () => {
      try {
        if (!LLAPI.isLoggedIn()) return;
        const [peersResp, mine] = await Promise.all([
          LLAPI.peers().catch(() => null),
          LLAPI.myLibrary().catch(() => null),
        ]);
        if (peersResp?.peers) {
          setPeers(peersResp.peers);
          const me = peersResp.peers.find(p => p.me);
          if (me && selectedLibs.length === 0) setSelectedLibs([me.id]);
        }
        if (mine?.games) setMyGames(mine.games);
      } catch (err) { console.warn('lib:', err); }
    })();
  }, []);

  // Refresh games when selection changes (combine collections)
  React.useEffect(() => {
    if (selectedLibs.length === 0) return;
    (async () => {
      try {
        const { games } = await LLAPI.combinedLib(selectedLibs);
        if (games) setMyGames(games);
      } catch (err) { console.warn('combine:', err); }
    })();
  }, [selectedLibs.join(',')]);

  const libs = peers.filter(l => selectedLibs.includes(l.id));
  const libLabel = libs.length <= 1
    ? (libs[0]?.me ? 'Ma ludothèque' : `ludothèque de ${libs[0]?.name || '…'}`)
    : `${libs.length} ludothèques combinées`;
  const totalGames = myGames.length;

  function toggleLib(id) {
    setSelectedLibs(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]);
  }

  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={4} padBottom={110}>
        <div style={{ padding: '4px 22px 20px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 34,
            color: LL.navyInk, margin: '4px 0 14px',
          }}>
            Ludothèque
          </h1>

          <FoldedCard fold={24} style={{ height: 66, marginBottom: 16 }} onClick={() => setPickerOpen(true)}>
            <div style={{
              height: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '0 14px',
            }}>
              {libs.length <= 1 ? (
                <Avatar size={42} name={libs[0]?.initial || '?'}/>
              ) : (
                <StackedAvatars libs={libs.slice(0,3)} />
              )}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{
                  fontFamily: LL.fontBody, fontWeight: 600, color: LL.navyInk, fontSize: 14,
                  overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                }}>
                  {libLabel}
                </div>
                <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, fontWeight: 500 }}>
                  {totalGames} jeux · appuyer pour changer
                </div>
              </div>
              <svg width="16" height="16" viewBox="0 0 16 16" style={{ flexShrink: 0 }}>
                <path d="M6 3 L 11 8 L 6 13" stroke={LL.navy} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            </div>
          </FoldedCard>

          {/* Edit toggle + Share */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, gap: 8 }}>
            <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, fontWeight: 600 }}>
              {myGames.length} jeu{myGames.length > 1 ? 'x' : ''}
            </div>
            <div style={{ display: 'flex', gap: 6 }}>
              <button onClick={() => shareLibrary()} style={{
                padding: '6px 14px', borderRadius: 999, cursor: 'pointer',
                border: `1.5px solid ${LL.navy}`,
                background: 'transparent', color: LL.navyInk,
                fontFamily: LL.fontBody, fontWeight: 600, fontSize: 12,
                display: 'flex', alignItems: 'center', gap: 5,
              }}>
                <svg width="11" height="11" viewBox="0 0 24 24" fill="none">
                  <path d="M12 3 L 7 8 H 10 V 16 H 14 V 8 H 17 Z" fill={LL.navy}/>
                  <path d="M4 13 V 20 H 20 V 13" stroke={LL.navy} strokeWidth="2" fill="none"/>
                </svg>
                Partager
              </button>
              <button onClick={() => setEditMode(m => !m)} style={{
                padding: '6px 14px', borderRadius: 999, cursor: 'pointer',
                border: `1.5px solid ${LL.navy}`,
                background: editMode ? LL.navy : 'transparent',
                color: editMode ? LL.cream : LL.navyInk,
                fontFamily: LL.fontBody, fontWeight: 600, fontSize: 12,
              }}>
                {editMode ? 'Terminer' : 'Modifier'}
              </button>
            </div>
          </div>

          {myGames.length === 0 ? (
            <div style={{
              textAlign: 'center', padding: '40px 20px',
              border: `2px dashed ${LL.navy}33`, borderRadius: 18,
              background: 'rgba(23,70,141,0.04)',
            }}>
              <div style={{ width: 52, height: 52, margin: '0 auto 8px' }}>
                <svg viewBox="0 0 48 48" width="100%" height="100%" fill="none" stroke={LL.navy} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
                  <rect x="4" y="13" width="40" height="28" rx="4"/>
                  <path d="M16 13 L19 8 H29 L32 13"/>
                  <circle cx="24" cy="27" r="7"/>
                </svg>
              </div>
              <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 20, color: LL.navyInk, marginBottom: 6 }}>
                Ta ludothèque est vide
              </div>
              <div style={{ fontFamily: LL.fontBody, fontSize: 13, color: LL.mutedInk, marginBottom: 20 }}>
                Scanne la boîte d'un jeu ou recherche-le par son nom.
              </div>
              <div style={{ display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
                <button onClick={onScan} style={{
                  padding: '12px 22px', borderRadius: 999,
                  background: LL.navy, color: LL.cream, border: 'none', cursor: 'pointer',
                  fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14,
                }}>
                  Scanner un jeu
                </button>
                <button onClick={() => setSearchOpen(true)} style={{
                  padding: '12px 22px', borderRadius: 999,
                  background: 'transparent', color: LL.navyInk,
                  border: `2px solid ${LL.navy}`, cursor: 'pointer',
                  fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14,
                }}>
                  Rechercher
                </button>
              </div>
            </div>
          ) : (
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14,
          }}>
            {myGames.map(g => (
              <div key={g.id} style={{ position: 'relative' }}>
                <div onClick={() => !editMode && onOpenGame(g)} style={{ cursor: editMode ? 'default' : 'pointer' }}>
                  <GameTile game={g} size="lg" style={{ width: '100%', aspectRatio: '1/1', height: 'auto' }}/>
                </div>
                {editMode && (
                  <button
                    onClick={(e) => { e.stopPropagation(); setConfirmDelete(g); }}
                    aria-label={`Supprimer ${g.name}`}
                    style={{
                      position: 'absolute', top: -8, right: -8,
                      width: 32, height: 32, borderRadius: '50%',
                      background: LL.red, color: LL.cream,
                      border: `2.5px solid ${LL.cream}`, cursor: 'pointer',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      boxShadow: '0 4px 10px rgba(0,0,0,0.25)',
                      padding: 0, zIndex: 5,
                    }}
                  >
                    <svg width="12" height="12" viewBox="0 0 12 12">
                      <path d="M2 2 L10 10 M10 2 L2 10" stroke={LL.cream} strokeWidth="2.5" strokeLinecap="round"/>
                    </svg>
                  </button>
                )}
              </div>
            ))}
          </div>
          )}
        </div>
      </ScreenBody>

      {confirmDelete && (
        <ConfirmDialog
          title="Supprimer ce jeu ?"
          message={`Êtes-vous sûr de vouloir supprimer « ${confirmDelete.name} » de votre ludothèque ?`}
          confirmLabel="Supprimer"
          danger
          onConfirm={() => deleteGame(confirmDelete)}
          onCancel={() => setConfirmDelete(null)}
        />
      )}
      {/* FABs: Search + Scan */}
      <div style={{
        position: 'absolute', left: '50%', bottom: 104,
        transform: 'translateX(-50%)',
        display: 'flex', gap: 14, zIndex: 30,
      }}>
        <button onClick={() => setSearchOpen(true)} aria-label="Rechercher un jeu" style={{
          width: 60, height: 60, borderRadius: '50%',
          background: LL.cream, border: `2.5px solid ${LL.navy}`,
          boxShadow: '0 6px 18px rgba(14,47,99,0.35), 0 2px 6px rgba(0,0,0,0.18)',
          cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none">
            <circle cx="10.5" cy="10.5" r="6.5" stroke={LL.navy} strokeWidth="2.4"/>
            <path d="M15.5 15.5 L 20 20" stroke={LL.navy} strokeWidth="2.4" strokeLinecap="round"/>
          </svg>
        </button>
        <button onClick={onScan} aria-label="Scanner un jeu" style={{
          width: 60, height: 60, borderRadius: '50%',
          background: LL.navy, border: `2.5px solid ${LL.cream}`,
          boxShadow: '0 6px 18px rgba(14,47,99,0.35), 0 2px 6px rgba(0,0,0,0.18)',
          cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <svg width="28" height="26" viewBox="0 0 40 36">
            <path d="M4 10 V 6 A 2 2 0 0 1 6 4 H 12 L 14 1 H 26 L 28 4 H 34 A 2 2 0 0 1 36 6 V 30 A 2 2 0 0 1 34 32 H 6 A 2 2 0 0 1 4 30 Z"
              stroke={LL.cream} strokeWidth="2.4" fill="none"/>
            <circle cx="20" cy="18" r="6" stroke={LL.cream} strokeWidth="2.4" fill="none"/>
            <path d="M18 16 V 20 M 16 18 H 24" stroke={LL.cream} strokeWidth="2.4" strokeLinecap="round"/>
          </svg>
        </button>
      </div>
      <BottomNav current="add" onNav={onNav} />
      {pickerOpen && (
        <LudothequePicker
          peers={peers}
          selected={selectedLibs}
          onToggle={toggleLib}
          onClose={() => setPickerOpen(false)}
        />
      )}
      {searchOpen && (
        <SearchGameModal
          existingIds={myGames.map(g => g.id)}
          onClose={() => setSearchOpen(false)}
          onAdded={(game) => { setMyGames(arr => arr.some(g => g.id === game.id) ? arr : [...arr, game]); }}
        />
      )}
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// RECHERCHE DE JEUX (modal pour ajouter à sa ludothèque)
// ──────────────────────────────────────────────────────────────
function SearchGameModal({ onClose, onAdded, existingIds = [] }) {
  const [q, setQ] = React.useState('');
  const [results, setResults] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [addingId, setAddingId] = React.useState(null);
  const [addedIds, setAddedIds] = React.useState(new Set(existingIds));
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    if (!q.trim()) { setResults([]); setError(null); return; }
    const h = setTimeout(async () => {
      setLoading(true); setError(null);
      try {
        const { games } = await LLAPI.searchGames(q, 12);
        setResults(games || []);
      } catch (err) {
        setError(err.message || 'Erreur de recherche');
        setResults([]);
      } finally { setLoading(false); }
    }, 300);
    return () => clearTimeout(h);
  }, [q]);

  async function addGame(g) {
    setAddingId(g.id);
    try {
      await LLAPI.addToLibrary(g.id);
      setAddedIds(s => new Set([...s, g.id]));
      onAdded && onAdded(g);
    } catch (err) {
      alert('Ajout échoué : ' + err.message);
    } finally { setAddingId(null); }
  }

  return (
    <div style={{
      position: 'fixed', inset: 0, background: 'rgba(14,47,99,0.55)',
      zIndex: 100, display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
    }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{
        background: LL.cream, width: '100%', maxHeight: '88vh',
        borderTopLeftRadius: 24, borderTopRightRadius: 24,
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
      }}>
        <div style={{ padding: '16px 20px 10px', display: 'flex', alignItems: 'center', gap: 10 }}>
          <h2 style={{
            flex: 1, margin: 0, fontFamily: LL.fontDisplay, fontWeight: 800,
            fontSize: 22, color: LL.navyInk,
          }}>Rechercher un jeu</h2>
          <button onClick={onClose} aria-label="Fermer" style={{
            width: 32, height: 32, borderRadius: '50%',
            background: 'transparent', border: `1.5px solid ${LL.navy}`, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <svg width="12" height="12" viewBox="0 0 12 12">
              <path d="M2 2 L10 10 M10 2 L2 10" stroke={LL.navy} strokeWidth="2" strokeLinecap="round"/>
            </svg>
          </button>
        </div>
        <div style={{ padding: '0 20px 12px' }}>
          <input
            type="text"
            value={q}
            onChange={e => setQ(e.target.value)}
            autoFocus
            placeholder="Ex: Catan, Dixit, Wingspan..."
            style={{
              width: '100%', boxSizing: 'border-box',
              padding: '12px 16px', borderRadius: 12,
              border: `2px solid ${LL.navy}33`,
              fontFamily: LL.fontBody, fontSize: 15, color: LL.navyInk,
              background: LL.cream, outline: 'none',
            }}
          />
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '0 20px 24px' }}>
          {loading && (
            <div style={{ textAlign: 'center', padding: 20, color: LL.muted, fontFamily: LL.fontBody, fontSize: 13 }}>
              Recherche…
            </div>
          )}
          {!loading && error && (
            <div style={{ textAlign: 'center', padding: 20, color: LL.red, fontFamily: LL.fontBody, fontSize: 13 }}>
              {error}
            </div>
          )}
          {!loading && !error && q.trim() && results.length === 0 && (
            <div style={{ textAlign: 'center', padding: 20, color: LL.muted, fontFamily: LL.fontBody, fontSize: 13 }}>
              Aucun résultat
            </div>
          )}
          {!loading && !q.trim() && (
            <div style={{ textAlign: 'center', padding: 20, color: LL.muted, fontFamily: LL.fontBody, fontSize: 13 }}>
              Tape le nom d'un jeu pour le trouver.
            </div>
          )}
          {results.map(g => {
            const already = addedIds.has(g.id);
            const busy = addingId === g.id;
            return (
              <div key={g.id} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 0', borderBottom: `1px solid ${LL.navy}15`,
              }}>
                <div style={{ width: 56, height: 56, borderRadius: 10, overflow: 'hidden', flexShrink: 0 }}>
                  <GameTile game={g} size="sm" style={{ width: 56, height: 56 }} />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: LL.fontDisplay, fontWeight: 700, fontSize: 15, color: LL.navyInk,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{g.name}</div>
                  <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted }}>
                    {g.players} · {g.duration}
                  </div>
                </div>
                <button
                  disabled={already || busy}
                  onClick={() => addGame(g)}
                  style={{
                    padding: '8px 14px', borderRadius: 999,
                    border: 'none', cursor: already ? 'default' : 'pointer',
                    background: already ? LL.navy + '22' : LL.navy,
                    color: already ? LL.navy : LL.cream,
                    fontFamily: LL.fontBody, fontWeight: 700, fontSize: 12,
                    opacity: busy ? 0.6 : 1,
                  }}>
                  {already ? '✓ Ajouté' : busy ? '…' : '+ Ajouter'}
                </button>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// JEU SÉLECTIONNÉ (a chosen game after match — simpler view)
// ──────────────────────────────────────────────────────────────
function SelectedGameScreen({ game, onBack, onDetails }) {
  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} />
      <div style={{ padding: '0 22px', textAlign: 'center' }}>
        <h1 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
          color: LL.navyInk, margin: '0 0 18px', textAlign: 'left',
        }}>
          Jeu sélectionné
        </h1>

        <div style={{
          fontFamily: LL.fontDisplay, fontWeight: 900, fontSize: 36,
          color: LL.navyInk, letterSpacing: 1, margin: '8px 0 16px',
        }}>
          {game.name.toUpperCase()} !
        </div>

        <div style={{ display: 'flex', justifyContent: 'center' }}>
          <GameTile game={game} size="xl" />
        </div>

        <div style={{
          marginTop: 30, color: LL.navyInk,
          fontFamily: LL.fontDisplay, fontSize: 18, fontStyle: 'italic',
        }}>
          « Alea jacta est ! »
        </div>

        <div style={{ marginTop: 30 }}>
          <LLButton onClick={onDetails} size="md" style={{ width: '100%' }}>
            Voir la fiche détaillée
          </LLButton>
        </div>
      </div>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// PRODUIT / FICHE DÉTAILLÉE
// ──────────────────────────────────────────────────────────────
function ProductScreen({ game, onBack, onNav, onStartGame, onTutorial }) {
  if (!game) return null;
  const rules = RULES[game.id] || RULES.default;
  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={0} padBottom={200}>
        <BackHeader onBack={onBack} />

        {/* HERO — cover en bandeau avec titre en overlay */}
        <div style={{
          position: 'relative', margin: '0 22px 0',
          borderRadius: 24, overflow: 'hidden',
          background: game.bg || '#E8D5B0',
          aspectRatio: '16 / 10',
          boxShadow: '0 8px 24px rgba(14,47,99,0.18)',
        }}>
          <GameCover game={game} />
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(14,47,99,0) 45%, rgba(14,47,99,0.78) 100%)',
          }}/>
          <div style={{
            position: 'absolute', left: 18, right: 18, bottom: 14,
            color: LL.cream,
          }}>
            <div style={{
              fontFamily: LL.fontBody, fontSize: 10, letterSpacing: 2,
              textTransform: 'uppercase', opacity: 0.8, fontWeight: 600,
              marginBottom: 2,
            }}>
              {rules.tagline}
            </div>
            <div style={{
              fontFamily: LL.fontDisplay, fontSize: 30, fontWeight: 800, lineHeight: 1.05,
            }}>
              {game.name}
            </div>
          </div>
        </div>

        <div style={{ padding: '20px 22px 0' }}>
          {/* Metadata strip */}
          <div style={{
            display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0,
            padding: '14px 4px', marginBottom: 20,
            borderTop: `1px solid rgba(23,70,141,0.18)`,
            borderBottom: `1px solid rgba(23,70,141,0.18)`,
          }}>
            {[
              ['joueurs', game.players],
              ['durée', game.duration],
              ['âge', rules.age],
              ['niveau', `${game.weight}/5`],
            ].map(([l,v], i) => (
              <div key={l} style={{
                textAlign: 'center',
                borderLeft: i > 0 ? `1px solid rgba(23,70,141,0.12)` : 'none',
              }}>
                <div style={{
                  fontFamily: LL.fontDisplay, fontSize: 18, fontWeight: 800,
                  color: LL.navyInk, lineHeight: 1,
                }}>{v}</div>
                <div style={{
                  fontFamily: LL.fontBody, fontSize: 10,
                  color: LL.muted, textTransform: 'uppercase',
                  letterSpacing: 1, marginTop: 3, fontWeight: 600,
                }}>{l}</div>
              </div>
            ))}
          </div>

          {/* Tabs */}
          <div style={{ display: 'flex', gap: 6, marginBottom: 18 }}>
            {['Règles', 'Mise en place', 'Astuces'].map((t, i) => (
              <div key={t} style={{
                padding: '8px 14px', borderRadius: 999,
                background: i === 0 ? LL.navy : 'transparent',
                color: i === 0 ? LL.cream : LL.navyInk,
                border: `1.5px solid ${LL.navy}`,
                fontFamily: LL.fontBody, fontSize: 12, fontWeight: 600,
                cursor: 'pointer',
              }}>{t}</div>
            ))}
          </div>

          {/* BUT DU JEU */}
          <RuleBlock label="But du jeu" roman="I">
            <p style={rulesPStyle}>{rules.goal}</p>
          </RuleBlock>

          {/* MISE EN PLACE */}
          <RuleBlock label="Mise en place" roman="II">
            <ul style={rulesListStyle}>
              {rules.setup.map((s, i) => (
                <li key={i} style={rulesLiStyle}>{s}</li>
              ))}
            </ul>
          </RuleBlock>

          {/* TOUR DE JEU — étapes numérotées */}
          <RuleBlock label="Tour de jeu" roman="III">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              {rules.turn.map((step, i) => (
                <div key={i} style={{ display: 'flex', gap: 14 }}>
                  <div style={{
                    width: 34, height: 34, borderRadius: '50%',
                    background: LL.navy, color: LL.cream,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 16,
                    flexShrink: 0,
                  }}>{i + 1}</div>
                  <div style={{ flex: 1, paddingTop: 3 }}>
                    <div style={{
                      fontFamily: LL.fontBody, fontSize: 14, fontWeight: 700,
                      color: LL.navyInk, marginBottom: 2,
                    }}>{step.t}</div>
                    <div style={{
                      fontFamily: LL.fontBody, fontSize: 13.5,
                      color: LL.mutedInk, lineHeight: 1.55,
                    }}>{step.d}</div>
                  </div>
                </div>
              ))}
            </div>
          </RuleBlock>

          {/* FIN DE PARTIE */}
          <RuleBlock label="Fin de partie" roman="IV">
            <p style={rulesPStyle}>{rules.end}</p>
          </RuleBlock>

          {/* AVIS — communautaire (composant réutilisable) */}
          <RuleBlock label="Avis des joueurs" roman="V">
            <ReviewsBody gameId={game.id} />
          </RuleBlock>

          {/* CTA — tuto vidéo */}
          <div onClick={() => onTutorial && onTutorial(game)} style={{
            marginTop: 8, padding: 16, borderRadius: 18,
            background: LL.navy, color: LL.cream,
            display: 'flex', alignItems: 'center', gap: 14,
            cursor: 'pointer',
          }}>
            <div style={{
              width: 44, height: 44, borderRadius: '50%',
              background: LL.cream,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              flexShrink: 0,
            }}>
              <svg width="14" height="16" viewBox="0 0 14 16">
                <path d="M1 1 L 13 8 L 1 15 Z" fill={LL.navy}/>
              </svg>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14 }}>
                Tutoriel vidéo
              </div>
              <div style={{ fontSize: 12, opacity: 0.75, marginTop: 2 }}>
                3 min · explique les règles en action
              </div>
            </div>
            <svg width="8" height="14" viewBox="0 0 8 14">
              <path d="M1 1 L 7 7 L 1 13" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
            </svg>
          </div>
        </div>
      </ScreenBody>

      {/* Floating CTA — Lancer la partie */}
      {onStartGame && (
        <div style={{
          position: 'absolute', left: 22, right: 22, bottom: 100,
          zIndex: 50,
          pointerEvents: 'auto',
        }}>
          <button
            type="button"
            onClick={(e) => { e.stopPropagation(); onStartGame(); }}
            style={{
              width: '100%', height: 58, borderRadius: 16,
              background: LL.navy, color: LL.cream,
              border: 'none', cursor: 'pointer',
              fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 700,
              letterSpacing: 0.4,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
              boxShadow: '0 8px 22px rgba(14,47,99,0.35)',
              pointerEvents: 'auto',
            }}
          >
            <svg width="18" height="18" viewBox="0 0 18 18">
              <path d="M3 2 L 15 9 L 3 16 Z" fill={LL.cream}/>
            </svg>
            Lancer la partie
          </button>
        </div>
      )}

      <BottomNav current="discover" onNav={onNav} />
    </Screen>
  );
}
// 5-star input — shared by the review form and the end-game rating prompt.
function StarsInput({ value = 0, onChange, size = 28 }) {
  return (
    <div style={{ display: 'flex', gap: 6 }}>
      {[1, 2, 3, 4, 5].map(i => (
        <button key={i} onClick={() => onChange(i)} aria-label={`${i} étoile${i > 1 ? 's' : ''}`} style={{
          background: 'transparent', border: 'none', cursor: 'pointer', padding: 0,
        }}>
          <svg width={size} height={size} viewBox="0 0 24 24"
            fill={i <= value ? LL.gold : 'none'}
            stroke={i <= value ? LL.gold : 'rgba(23,70,141,0.4)'}
            strokeWidth="1.7" strokeLinejoin="round">
            <path d="M12 2 L 14.6 8.8 L 22 9.5 L 16.3 14.2 L 18 21.5 L 12 17.5 L 6 21.5 L 7.7 14.2 L 2 9.5 L 9.4 8.8 Z"/>
          </svg>
        </button>
      ))}
    </div>
  );
}

// Read-only stars display (uses the gold/empty color pair).
function StarsRow({ value = 0, size = 14 }) {
  return (
    <div style={{ display: 'inline-flex', gap: 2 }}>
      {[1, 2, 3, 4, 5].map(i => (
        <svg key={i} width={size} height={size} viewBox="0 0 24 24"
          fill={i <= value ? LL.gold : 'none'}
          stroke={LL.gold} strokeWidth="1.6" strokeLinejoin="round">
          <path d="M12 2 L 14.6 8.8 L 22 9.5 L 16.3 14.2 L 18 21.5 L 12 17.5 L 6 21.5 L 7.7 14.2 L 2 9.5 L 9.4 8.8 Z"/>
        </svg>
      ))}
    </div>
  );
}

// Reusable reviews block — used on both ProductScreen (rules) and ProductShopScreen.
function ReviewsBody({ gameId }) {
  const [reviews, setReviews] = React.useState([]);
  const [summary, setSummary] = React.useState({ count: 0, avg: null });
  const [mine, setMine] = React.useState(null);
  const [open, setOpen] = React.useState(false);
  const [rating, setRating] = React.useState(0);
  const [comment, setComment] = React.useState('');

  async function load() {
    try {
      const r = await LLAPI.reviews(gameId, 5);
      setReviews(r.reviews || []);
      setSummary(r.summary || { count: 0, avg: null });
      if (r.mine) {
        setMine(r.mine);
        setRating(r.mine.rating);
        setComment(r.mine.comment || '');
      } else {
        setMine(null);
      }
    } catch (err) { console.warn('reviews:', err); }
  }
  React.useEffect(() => { if (gameId) load(); }, [gameId]);

  async function submit() {
    if (rating < 1) return;
    try {
      await LLAPI.submitReview(gameId, rating, comment);
      setOpen(false);
      await load();
    } catch (err) { alert('Envoi échoué : ' + err.message); }
  }
  async function toggleLike(reviewId) {
    setReviews(arr => arr.map(r => r.id === reviewId
      ? { ...r, my_like: !r.my_like, likes_count: r.likes_count + (r.my_like ? -1 : 1) } : r));
    try { await LLAPI.likeReview(reviewId); }
    catch {
      setReviews(arr => arr.map(r => r.id === reviewId
        ? { ...r, my_like: !r.my_like, likes_count: r.likes_count + (r.my_like ? -1 : 1) } : r));
    }
  }

  return (
    <>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 14, marginBottom: 14,
        padding: '12px 14px', borderRadius: 14,
        background: 'rgba(23,70,141,0.06)',
      }}>
        <div style={{ flex: 1 }}>
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 26, fontWeight: 800, color: LL.navyInk, lineHeight: 1,
          }}>
            {summary.avg != null ? summary.avg.toFixed(1) : '—'}
            <span style={{ fontSize: 14, color: LL.muted, fontWeight: 600, marginLeft: 4 }}>/5</span>
          </div>
          <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, marginTop: 4 }}>
            {summary.count} avis
          </div>
        </div>
        <button onClick={() => setOpen(o => !o)} style={{
          padding: '10px 14px', borderRadius: 999, cursor: 'pointer',
          background: LL.navy, color: LL.cream, border: 'none',
          fontFamily: LL.fontBody, fontSize: 12, fontWeight: 700,
        }}>
          {mine ? 'Modifier mon avis' : 'Donner un avis'}
        </button>
      </div>

      {open && (
        <div style={{
          padding: '4px 0 4px', marginBottom: 14, background: 'transparent',
        }}>
          <div style={{
            fontFamily: LL.fontBody, fontSize: 12, letterSpacing: 1.4,
            textTransform: 'uppercase', color: LL.muted, fontWeight: 700, marginBottom: 8,
          }}>Ta note</div>
          <StarsInput value={rating} onChange={setRating} />
          <div style={{
            fontFamily: LL.fontBody, fontSize: 12, letterSpacing: 1.4,
            textTransform: 'uppercase', color: LL.muted, fontWeight: 700, margin: '14px 0 6px',
          }}>Ton commentaire</div>
          <textarea
            value={comment}
            onChange={(e) => setComment(e.target.value)}
            placeholder="Partage ton ressenti, l'ambiance, ce que tu as aimé…"
            maxLength={600}
            style={{
              width: '100%', minHeight: 110, padding: 4,
              borderRadius: 0, border: 'none',
              background: 'transparent', color: LL.navyInk, resize: 'vertical',
              fontFamily: LL.fontBody, fontSize: 15, lineHeight: 1.55,
              outline: 'none', boxSizing: 'border-box',
            }}
          />
          <div style={{
            fontFamily: LL.fontBody, fontSize: 10, color: LL.muted,
            textAlign: 'right', marginTop: 4,
          }}>{comment.length}/600</div>
          <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
            <LLButton onClick={() => setOpen(false)} variant="outline" size="md" style={{ flex: 1 }}>
              Annuler
            </LLButton>
            <LLButton onClick={submit} size="md" style={{ flex: 1, opacity: rating < 1 ? 0.4 : 1 }}>
              Publier
            </LLButton>
          </div>
        </div>
      )}

      {reviews.length === 0 ? (
        <div style={{ fontFamily: LL.fontBody, fontSize: 13, color: LL.muted, padding: '4px 0 12px' }}>
          Sois le premier à donner ton avis sur ce jeu.
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {reviews.map(r => (
            <ReviewCard key={r.id} review={r} onLike={() => toggleLike(r.id)} />
          ))}
        </div>
      )}
    </>
  );
}

function ReviewCard({ review, onLike }) {
  const u = review.user || {};
  return (
    <div style={{
      border: `1.5px solid rgba(23,70,141,0.18)`, borderRadius: 14,
      padding: 12, background: LL.cream,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
        <div style={{
          width: 32, height: 32, borderRadius: '50%',
          background: u.avatar_color || LL.navy, color: LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: LL.fontDisplay, fontSize: 13, fontWeight: 800,
          overflow: 'hidden', flexShrink: 0,
        }}>
          {u.avatar_url
            ? <img src={u.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
            : (u.name || '?')[0]}
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: LL.fontBody, fontSize: 13, fontWeight: 700, color: LL.navyInk }}>{u.name || 'Anonyme'}</div>
          <StarsRow value={review.rating} />
        </div>
        <button onClick={onLike} style={{
          display: 'flex', alignItems: 'center', gap: 4, padding: '6px 10px',
          borderRadius: 999, background: review.my_like ? LL.navy : 'transparent',
          border: `1.5px solid ${LL.navy}`, color: review.my_like ? LL.cream : LL.navyInk,
          cursor: 'pointer', fontFamily: LL.fontBody, fontSize: 12, fontWeight: 600,
        }}>
          <svg width="12" height="12" viewBox="0 0 18 16" fill={review.my_like ? LL.cream : 'none'} stroke={review.my_like ? LL.cream : LL.navy} strokeWidth="1.7">
            <path d="M9 15 C -3 8 2 0 9 5 C 16 0 21 8 9 15 Z"/>
          </svg>
          {review.likes_count}
        </button>
      </div>
      {review.comment && (
        <div style={{
          fontFamily: LL.fontBody, fontSize: 13, color: LL.navyInk, lineHeight: 1.5,
        }}>{review.comment}</div>
      )}
    </div>
  );
}

const rulesPStyle = {
  fontFamily: LL.fontBody, fontSize: 14, color: LL.navyInk,
  lineHeight: 1.6, margin: 0,
};
const rulesListStyle = { margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 8 };
const rulesLiStyle = {
  fontFamily: LL.fontBody, fontSize: 13.5, color: LL.navyInk,
  lineHeight: 1.55, paddingLeft: 18, position: 'relative',
};

function RuleBlock({ label, roman, children }) {
  return (
    <div style={{ marginBottom: 26 }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 12 }}>
        <span style={{
          fontFamily: LL.fontDisplay, fontSize: 12, fontWeight: 700,
          color: LL.muted, letterSpacing: 2,
        }}>{roman}</span>
        <h3 style={{
          fontFamily: LL.fontDisplay, fontSize: 22, fontWeight: 800,
          color: LL.navyInk, margin: 0,
        }}>{label}</h3>
        <div style={{ flex: 1, height: 1, background: 'rgba(23,70,141,0.15)' }}/>
      </div>
      <div style={{ paddingLeft: 2 }}>
        <style>{`
          .ll-rules-li::before {
            content: '';
            position: absolute; left: 0; top: 9px;
            width: 7px; height: 7px; border-radius: 50%;
            background: ${LL.navy};
          }
        `}</style>
        {children}
      </div>
    </div>
  );
}

// Règles des jeux — contenu éditorial
const RULES = {
  default: {
    tagline: 'Règles du jeu',
    age: '10+',
    goal: "Marquez plus de points que vos adversaires en utilisant habilement les ressources et les cartes à votre disposition avant la fin de la partie.",
    setup: [
      "Mélangez la pioche et distribuez 5 cartes à chaque joueur.",
      "Placez le plateau au centre de la table, face visible.",
      "Chaque joueur choisit une couleur et prend les 4 pions correspondants.",
      "Le joueur le plus jeune commence.",
    ],
    turn: [
      { t: 'Piocher', d: "Prenez la première carte de la pioche, sans la révéler aux autres joueurs." },
      { t: 'Jouer une action', d: "Vous pouvez placer un pion, défausser une carte, ou activer un pouvoir spécial imprimé sur vos cartes en main." },
      { t: 'Résoudre les effets', d: "Appliquez dans l'ordre les effets déclenchés par votre action, en commençant par vous." },
      { t: 'Passer la main', d: "Passez le tour au joueur situé à votre gauche." },
    ],
    end: "La partie s'arrête dès qu'un joueur atteint 30 points de victoire, ou lorsque la pioche est épuisée. Le joueur ayant le plus de points l'emporte ; en cas d'égalité, celui qui a le plus de ressources gagne.",
  },
  catan: {
    tagline: 'Jeu de stratégie · 1995',
    age: '10+',
    goal: "Soyez le premier joueur à atteindre 10 points de victoire en construisant colonies, villes et routes sur l'île de Catane.",
    setup: [
      "Assemblez les 19 tuiles hexagonales en un plateau aléatoire, entouré des tuiles maritimes.",
      "Placez les jetons numérotés sur chaque hexagone de terre, dans l'ordre alphabétique.",
      "Chaque joueur reçoit 5 routes, 4 colonies et 4 villes de sa couleur.",
      "Placez 2 colonies et 2 routes chacun, en serpent ; la seconde colonie donne les ressources adjacentes.",
    ],
    turn: [
      { t: 'Lancer les dés', d: "Les joueurs dont les colonies bordent un hexagone au chiffre correspondant récoltent les ressources associées." },
      { t: 'Commercer', d: "Échangez des ressources avec les autres joueurs ou la banque (4:1, ou 3:1/2:1 sur un port)." },
      { t: 'Construire', d: "Dépensez vos ressources pour bâtir routes, colonies, villes, ou acheter une carte Développement." },
      { t: 'Passer les dés', d: "Lorsque vous avez terminé toutes vos actions, passez les dés au joueur suivant." },
    ],
    end: "Dès qu'un joueur totalise 10 points de victoire à son tour, la partie s'arrête et il est déclaré vainqueur. Les points proviennent des colonies (1), villes (2), cartes Victoire (1), plus grande armée et plus longue route (2 chacun).",
  },
  dixit: {
    tagline: "Jeu d'ambiance · narration",
    age: '8+',
    goal: "Trouvez l'équilibre : faites deviner votre carte à certains joueurs — mais pas à tous. Atteignez 30 points en premier.",
    setup: [
      "Chaque joueur reçoit 6 cartes illustrées, gardées secrètes.",
      "Posez le plateau de score au centre ; chacun prend un lapin de vote de sa couleur.",
      "Distribuez à chacun les jetons de vote numérotés selon le nombre de joueurs.",
    ],
    turn: [
      { t: 'Le conteur parle', d: "Le joueur actif choisit une carte de sa main et prononce un indice : un mot, une phrase, une mélodie — ni trop précis, ni trop vague." },
      { t: 'Les autres proposent', d: "Chaque joueur choisit secrètement dans sa main la carte qui correspond le mieux à l'indice et la remet au conteur." },
      { t: 'Vote', d: "Les cartes sont mélangées puis révélées. Chacun vote pour celle qu'il pense être celle du conteur." },
      { t: 'Score', d: "Si tous devinent, ou personne, le conteur marque 0 et les autres 2. Sinon, le conteur et ceux qui ont trouvé marquent 3." },
    ],
    end: "La partie se termine quand un joueur atteint 30 points sur la piste — ou, en version courte, à la fin de la pioche. Le joueur en tête l'emporte.",
  },
  scythe: {
    tagline: 'Stratégie · 4X · 1920 alternatif',
    age: '14+',
    goal: "Accumulez richesses, territoires et réputation pour atteindre 6 étoiles, dans une Europe de l'Est uchronique, en 1920.",
    setup: [
      "Chaque joueur reçoit un plateau de faction et un plateau d'amélioration asymétriques.",
      "Placez les méchas et ouvriers de départ sur les positions indiquées par votre plateau.",
      "Prenez 4 cartes Objectif et conservez-en 2, ainsi que votre stock de départ de pièces et ressources.",
    ],
    turn: [
      { t: 'Choisir une section', d: "Sélectionnez une section de votre plateau d'action différente de celle du tour précédent." },
      { t: 'Action du haut', d: "Optionnel : Bouger, Commercer, Produire, ou Renforcer. Chaque action est liée à une ressource." },
      { t: 'Action du bas', d: "Optionnel : Construire, Déployer, Enrôler, ou Améliorer — les actions coûteuses qui développent votre empire." },
      { t: 'Étoiles & fin', d: "Posez une étoile si vous remplissez une condition (6 étoiles = fin)." },
    ],
    end: "Dès qu'un joueur pose sa 6ᵉ étoile, la partie s'arrête. Chacun calcule ses pièces selon ses étoiles, territoires et ressources. Le plus riche l'emporte.",
  },
  trio: {
    tagline: 'Jeu de déduction · cartes',
    age: '6+',
    goal: "Réunissez trois trios de cartes de même valeur en fouillant les mains des adversaires et les cartes face cachée au centre.",
    setup: [
      "Mélangez les 36 cartes (3 exemplaires de chaque valeur de 1 à 12).",
      "Distribuez le même nombre de cartes à chaque joueur, le reste est placé au centre face cachée.",
      "Chaque joueur trie ses cartes par ordre croissant, face cachée devant lui.",
    ],
    turn: [
      { t: 'Demander une carte', d: "Désignez un joueur et demandez la carte la plus haute ou la plus basse de sa main. Il doit la révéler." },
      { t: 'Révéler du centre', d: "Vous pouvez aussi retourner une carte face cachée du centre." },
      { t: 'Former un trio', d: "Si les cartes révélées ce tour forment un trio de même valeur, vous le mettez de côté et rejouez." },
      { t: 'Échec', d: "Sinon, les cartes retournent face cachée à leur place, et c'est au joueur suivant." },
    ],
    end: "Dès qu'un joueur complète 3 trios, il gagne. Variante courte : 2 trios suffisent.",
  },
  'blood-rage': {
    tagline: 'Stratégie épique · Ragnarök',
    age: '14+',
    goal: "Accumulez le plus de gloire avant le Ragnarök, en combattant, en pillant, et en mourant héroïquement.",
    setup: [
      "Assemblez le plateau de Scandinavie. Chaque joueur prend un clan viking et son plateau associé.",
      "Placez le pion de chaque joueur en position 0 sur la piste de gloire.",
      "Préparez les cartes d'âge : 3 âges successifs rythment la partie.",
    ],
    turn: [
      { t: 'Drafting', d: "À chaque âge, draftez 8 cartes qui définissent vos pouvoirs, quêtes et monstres jusqu'à la fin de l'âge." },
      { t: 'Actions', d: "En tour de table : Invasion, Marche, Quête, Pillage, ou Amélioration — dépensez votre rage." },
      { t: 'Pillage & combat', d: "Lors d'un pillage, les joueurs engagés jouent une carte secrète — le plus fort remporte le butin." },
      { t: 'Ragnarök', d: "À la fin de chaque âge, certaines régions sont détruites ; les unités qui s'y trouvent meurent héroïquement (gloire)." },
    ],
    end: "À la fin du 3ᵉ âge, le joueur ayant accumulé le plus de points de gloire triomphe — même s'il ne contrôle plus aucun territoire.",
  },
  darwin: {
    tagline: "Course d'exploration · Beagle",
    age: '12+',
    goal: "Incarnez un scientifique à bord du Beagle, explorez les îles, cataloguez les espèces et marquez plus de points que Darwin lui-même.",
    setup: [
      "Placez le plateau central représentant l'Amérique du Sud et les îles Galápagos.",
      "Chacun prend son plateau scientifique et 3 pions explorateur.",
      "Révélez les premières cartes d'espèces et d'objectifs.",
    ],
    turn: [
      { t: 'Planifier', d: "Choisissez vos actions avec vos cartes Planning — exploration, cueillette, correspondance, envoi de spécimen." },
      { t: 'Explorer les îles', d: "Déplacez un explorateur sur une nouvelle zone ; prenez les espèces associées." },
      { t: 'Cataloguer', d: "Associez une espèce à une théorie sur votre plateau pour marquer des points et débloquer des bonus." },
      { t: 'Correspondance', d: "Envoyez des lettres à la Royal Society pour gagner en influence et obtenir des cartes spéciales." },
    ],
    end: "La partie se termine après un nombre fixe de tours ou quand Darwin rentre à Londres. Les points viennent des espèces cataloguées, des théories publiées et des objectifs remplis.",
  },
  'dice-forge': {
    tagline: 'Deck-building avec dés',
    age: '10+',
    goal: "Forgez les meilleurs dés en remplaçant leurs faces, accomplissez des exploits héroïques, et offrez plus de gloire aux dieux.",
    setup: [
      "Chaque joueur reçoit 2 dés de base et les monte avec les faces de départ.",
      "Placez le plateau central, révélez les cartes Exploit et Prouesse disponibles.",
      "Chacun commence avec quelques pièces d'or et soleils/lunes.",
    ],
    turn: [
      { t: 'Lancer les dés', d: "Tous les joueurs lancent leurs deux dés en même temps — même le joueur non-actif récolte ses ressources." },
      { t: "Achat d'une face", d: "Avec vos soleils, achetez une nouvelle face et remplacez-la sur un de vos dés." },
      { t: 'Exploit', d: "Avec votre or et vos lunes, achetez une carte Exploit : pouvoirs uniques et points de gloire." },
      { t: 'Bonus', d: "Certaines faces donnent accès à des Prouesses à déclencher plus tard dans la manche." },
    ],
    end: "À la fin des 9 ou 10 tours (selon le nombre de joueurs), chacun totalise ses points de gloire — le forgeron le plus accompli l'emporte.",
  },
};

// ──────────────────────────────────────────────────────────────
// DÉCOUVRIR
// ──────────────────────────────────────────────────────────────
function DiscoverScreen({ onNav, onOpenGame }) {
  const [sections, setSections] = React.useState(null);
  const [query, setQuery] = React.useState('');
  const [results, setResults] = React.useState(null);
  const [searchMode, setSearchMode] = React.useState(null);
  const [searching, setSearching] = React.useState(false);

  React.useEffect(() => {
    (async () => {
      try {
        const { sections } = await LLAPI.discover();
        if (sections && sections.length) setSections(sections);
      } catch (err) { console.warn('discover:', err); }
    })();
  }, []);

  // Debounced search
  React.useEffect(() => {
    const q = query.trim();
    if (!q) { setResults(null); setSearchMode(null); return; }
    const t = setTimeout(async () => {
      setSearching(true);
      try {
        const r = await LLAPI.searchGames(q, 8);
        setResults(r.games || []);
        setSearchMode(r.mode);
      } catch (err) { console.warn('search:', err); }
      finally { setSearching(false); }
    }, 350);
    return () => clearTimeout(t);
  }, [query]);

  const fallback = [
    { title: 'Recommandés pour vous', icon: 'heart', games: GAMES },
    { title: 'Populaire en ce moment', games: [GAMES[2], GAMES[1], GAMES[5], GAMES[3]].filter(Boolean) },
    { title: "Jeux d'ambiance", games: [GAMES[2], GAMES[1], GAMES[7], GAMES[6]].filter(Boolean) },
    { title: 'Stratégie profonde', games: [GAMES[0], GAMES[4], GAMES[6], GAMES[3]].filter(Boolean) },
  ];
  const list = sections || fallback;

  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={4} padBottom={110}>
        <div style={{ padding: '4px 22px 16px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 34,
            color: LL.navyInk, margin: '4px 0 12px',
          }}>
            Découvrir
          </h1>

          <div style={{
            display: 'flex', alignItems: 'center', gap: 8,
            border: `2px solid ${LL.navy}`, borderRadius: 999,
            background: LL.cream, padding: '8px 14px', marginBottom: 12,
            boxShadow: '0 2px 6px rgba(23,70,141,0.1)',
          }}>
            <svg width="18" height="18" viewBox="0 0 20 20" fill="none">
              <circle cx="9" cy="9" r="6" stroke={LL.navy} strokeWidth="2"/>
              <path d="M14 14 L18 18" stroke={LL.navy} strokeWidth="2" strokeLinecap="round"/>
            </svg>
            <input
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="Rechercher : « coop 4 joueurs 30 min »…"
              style={{
                flex: 1, border: 'none', outline: 'none', background: 'transparent',
                fontFamily: LL.fontBody, fontSize: 14, color: LL.navyInk,
              }}
            />
            {query && (
              <button onClick={() => setQuery('')} style={{
                border: 'none', background: 'transparent', cursor: 'pointer', color: LL.muted, padding: 0,
              }}>✕</button>
            )}
          </div>

          {query.trim() ? (
            <>
              <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, marginBottom: 8, letterSpacing: 0.5 }}>
                {searching ? 'Recherche…' : results ? `${results.length} résultat(s) · ${searchMode || ''}` : ''}
              </div>
              {results && results.length === 0 && (
                <div style={{ fontFamily: LL.fontBody, fontSize: 13, color: LL.muted, padding: '20px 0' }}>
                  Aucun jeu ne correspond. Essayez une autre formulation.
                </div>
              )}
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 }}>
                {(results || []).map(g => (
                  <div key={g.id} onClick={() => onOpenGame(g)} style={{ cursor: 'pointer' }}>
                    <GameTile game={g} size="lg" style={{ width: '100%', aspectRatio: '1/1', height: 'auto' }}/>
                    {typeof g.similarity === 'number' && (
                      <div style={{ fontFamily: LL.fontMono, fontSize: 10, color: LL.muted, textAlign: 'center', marginTop: 2 }}>
                        {Math.round(g.similarity * 100)}% match
                      </div>
                    )}
                  </div>
                ))}
              </div>
            </>
          ) : list.map((s, i) => (
            <React.Fragment key={i}>
              <SectionHeader heart={s.icon === 'heart' || i === 0}>{s.title}</SectionHeader>
              <HScroller games={s.games || []} onOpenGame={onOpenGame} />
            </React.Fragment>
          ))}
        </div>
      </ScreenBody>
      <BottomNav current="discover" onNav={onNav} />
    </Screen>
  );
}

function SectionHeader({ children, heart }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      fontFamily: LL.fontDisplay, fontSize: 18, fontWeight: 700, color: LL.navyInk,
      margin: '18px 0 10px',
    }}>
      {heart && (
        <svg width="18" height="16" viewBox="0 0 18 16">
          <path d="M9 15 C -3 8 2 0 9 5 C 16 0 21 8 9 15 Z" fill={LL.navy}/>
        </svg>
      )}
      {children}
    </div>
  );
}

function HScroller({ games, onOpenGame }) {
  const scrollRef = React.useRef(null);
  const [canL, setCanL] = React.useState(false);
  const [canR, setCanR] = React.useState(false);

  React.useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const onScroll = () => {
      setCanL(el.scrollLeft > 8);
      setCanR(el.scrollLeft + el.clientWidth + 8 < el.scrollWidth);
    };
    onScroll();
    el.addEventListener('scroll', onScroll, { passive: true });
    return () => el.removeEventListener('scroll', onScroll);
  }, [games?.length]);

  function nudge(dir) {
    const el = scrollRef.current;
    if (el) el.scrollBy({ left: dir * (el.clientWidth * 0.85), behavior: 'smooth' });
  }

  return (
    <div style={{ position: 'relative' }}>
      <div ref={scrollRef} style={{
        display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 6,
        marginRight: -22, paddingRight: 22, scrollSnapType: 'x mandatory',
      }}>
        {games.map((g, i) => (
          <div key={`${g.id}-${i}`} onClick={() => onOpenGame(g)}
            style={{ cursor: 'pointer', flexShrink: 0, scrollSnapAlign: 'start' }}>
            <GameTile game={g} size="md"/>
          </div>
        ))}
      </div>
      {canL && <ScrollArrow dir="left"  onClick={() => nudge(-1)} />}
      {canR && <ScrollArrow dir="right" onClick={() => nudge(+1)} />}
    </div>
  );
}

function ScrollArrow({ dir, onClick }) {
  const isLeft = dir === 'left';
  return (
    <button onClick={onClick} aria-label={isLeft ? 'Précédent' : 'Suivant'} style={{
      position: 'absolute', top: '50%',
      [isLeft ? 'left' : 'right']: 4,
      transform: 'translateY(-50%)',
      width: 32, height: 32, borderRadius: '50%',
      background: LL.cream, border: `1.5px solid ${LL.navy}`, cursor: 'pointer',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      boxShadow: '0 4px 10px rgba(14,47,99,0.25)',
      zIndex: 5, padding: 0,
    }}>
      <svg width="12" height="14" viewBox="0 0 12 14" style={{ transform: isLeft ? 'rotate(180deg)' : 'none' }}>
        <path d="M2 1 L9 7 L2 13" stroke={LL.navy} strokeWidth="2.4" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    </button>
  );
}

// ──────────────────────────────────────────────────────────────
// SCAN IA — Google Cloud Vision
// ──────────────────────────────────────────────────────────────
function ScanScreen({ onBack, onDetected }) {
  // Phases:
  //   idle      → ready, user picks camera or gallery
  //   preview   → image selected, confirm before sending
  //   analyzing → POST /api/scan, Vision is working
  //   detected  → result card (success or "non identifié")
  const [phase, setPhase] = React.useState('idle');
  const [file, setFile] = React.useState(null);
  const [previewUrl, setPreviewUrl] = React.useState(null);
  const [detected, setDetected] = React.useState(null);
  const [confidence, setConfidence] = React.useState(0);
  const [error, setError] = React.useState(null);

  const cameraRef = React.useRef(null);
  const galleryRef = React.useRef(null);

  React.useEffect(() => () => { if (previewUrl) URL.revokeObjectURL(previewUrl); }, [previewUrl]);

  function pickFile(f, autoAnalyze = false) {
    if (!f) return;
    setFile(f);
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(URL.createObjectURL(f));
    setDetected(null);
    setError(null);
    if (autoAnalyze) {
      setPhase('analyzing');
      doAnalyze(f);
    } else {
      setPhase('preview');
    }
  }

  async function doAnalyze(f) {
    try {
      const r = await LLAPI.scan(f);
      if (r.ok && r.detected) {
        setDetected(r.detected);
        setConfidence(r.confidence || 0.8);
      } else {
        setDetected(null);
        setError(r.message || "Jeu non identifié.");
      }
    } catch (err) {
      setError(err.message || 'Erreur réseau');
    } finally {
      setPhase('detected');
    }
  }

  async function analyze() {
    if (!file) return;
    setPhase('analyzing');
    setError(null);
    await doAnalyze(file);
  }

  function reset() {
    setPhase('idle');
    setFile(null);
    if (previewUrl) { URL.revokeObjectURL(previewUrl); setPreviewUrl(null); }
    setDetected(null);
    setError(null);
  }

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} title="Scanner un jeu" />
      <ScreenBody padTop={44} padBottom={20}>
        <div style={{ padding: '0 22px' }}>
          <p style={{
            fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk,
            margin: '0 0 18px', lineHeight: 1.5,
          }}>
            Prenez en photo la boîte de votre jeu, notre IA l'identifie et l'ajoute à votre ludothèque.
          </p>

          {/* Viewport */}
          <FoldedCard fold={30} color={LL.navy} bg={LL.cream} stroke={2} radius={22}
            style={{ height: 380, marginBottom: 18, position: 'relative', overflow: 'hidden' }}>
            <div style={{ position: 'absolute', inset: 0, background: '#0E2F63' }}>

              {/* No image yet */}
              {phase === 'idle' && (
                <div style={{
                  position: 'absolute', inset: 0,
                  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                  gap: 16, color: LL.cream, textAlign: 'center',
                }}>
                  <ScanBoxIcon />
                  <div style={{ fontFamily: LL.fontDisplay, fontSize: 18, fontWeight: 700 }}>
                    Prêt à scanner
                  </div>
                  <div style={{ fontSize: 12, opacity: 0.65, padding: '0 30px' }}>
                    Cadrez bien la couverture, en bonne lumière
                  </div>
                </div>
              )}

              {/* Image preview or analyzing */}
              {(phase === 'preview' || phase === 'analyzing' || phase === 'detected') && previewUrl && (
                <>
                  <img src={previewUrl} alt="" style={{
                    position: 'absolute', inset: 0,
                    width: '100%', height: '100%', objectFit: 'cover',
                  }}/>

                  {/* Dim overlay during analyze */}
                  {phase === 'analyzing' && (
                    <div style={{ position: 'absolute', inset: 0, background: 'rgba(14,47,99,0.55)' }}/>
                  )}

                  {/* Corner brackets */}
                  {[[1,1],[-1,1],[1,-1],[-1,-1]].map(([sx, sy], i) => (
                    <div key={i} style={{
                      position: 'absolute',
                      [sx > 0 ? 'left' : 'right']: 16,
                      [sy > 0 ? 'top' : 'bottom']: 16,
                      width: 28, height: 28,
                      borderLeft:  sx > 0 ? `3px solid ${LL.gold}` : 'none',
                      borderRight: sx < 0 ? `3px solid ${LL.gold}` : 'none',
                      borderTop:   sy > 0 ? `3px solid ${LL.gold}` : 'none',
                      borderBottom:sy < 0 ? `3px solid ${LL.gold}` : 'none',
                    }}/>
                  ))}

                  {/* Scan line */}
                  {phase === 'analyzing' && (
                    <>
                      <div style={{
                        position: 'absolute', left: 16, right: 16, height: 2,
                        background: `linear-gradient(90deg, transparent, ${LL.gold}, transparent)`,
                        boxShadow: `0 0 12px ${LL.gold}`,
                        animation: 'scanLine 1.5s ease-in-out infinite',
                      }}/>
                      <div style={{
                        position: 'absolute', bottom: 20, left: 0, right: 0, textAlign: 'center',
                        color: LL.gold, fontFamily: LL.fontMono, fontSize: 11,
                        letterSpacing: 2, fontWeight: 600,
                      }}>ANALYSE IA EN COURS…</div>
                    </>
                  )}

                  {/* Success outline */}
                  {phase === 'detected' && detected && (
                    <div style={{
                      position: 'absolute', inset: 16,
                      border: `3px solid ${LL.green}`, borderRadius: 10,
                    }}/>
                  )}
                </>
              )}
            </div>
          </FoldedCard>

          {/* Actions */}
          {phase === 'idle' && (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <ScanActionCard
                icon={<CameraIcon/>}
                title="Caméra"
                subtitle="Prendre en photo"
                onClick={() => cameraRef.current && cameraRef.current.click()}
              />
              <ScanActionCard
                icon={<GalleryIcon/>}
                title="Galerie"
                subtitle="Choisir une photo"
                onClick={() => galleryRef.current && galleryRef.current.click()}
              />
            </div>
          )}

          {phase === 'preview' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <LLButton onClick={analyze} size="lg" style={{ width: '100%' }}>
                Identifier avec l'IA
              </LLButton>
              <LLButton onClick={reset} variant="outline" size="md" style={{ width: '100%' }}>
                Reprendre la photo
              </LLButton>
            </div>
          )}

          {phase === 'analyzing' && (
            <div style={{
              textAlign: 'center', fontFamily: LL.fontBody, fontSize: 13,
              color: LL.muted, marginTop: 8,
            }}>
              Comparaison avec la base de jeux…
            </div>
          )}

          {phase === 'detected' && detected && (
            <FoldedCard fold={26} color={LL.green} bg={LL.cream} stroke={2} radius={20}
              style={{ padding: 0, marginBottom: 12 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 14 }}>
                <GameTile game={detected} size="sm" style={{ width: 64, height: 64 }}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.green, fontWeight: 700, letterSpacing: 1 }}>
                    ✓ IDENTIFIÉ — {Math.round(confidence * 100)}%
                  </div>
                  <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, color: LL.navyInk, fontSize: 18, lineHeight: 1.1 }}>
                    {detected.name}
                  </div>
                  <div style={{ fontSize: 11, color: LL.muted, marginTop: 2 }}>
                    {detected.players} · {detected.duration}
                  </div>
                </div>
              </div>
              <div style={{ display: 'flex', gap: 8, padding: '0 14px 14px' }}>
                <LLButton onClick={() => onDetected(detected)} size="md" style={{ flex: 1 }}>
                  Ajouter à ma ludothèque
                </LLButton>
                <LLButton onClick={reset} variant="outline" size="md" style={{ flex: '0 0 auto', padding: '0 14px' }}>
                  Rescanner
                </LLButton>
              </div>
            </FoldedCard>
          )}

          {phase === 'detected' && !detected && (
            <FoldedCard fold={22} color={LL.red} bg={LL.cream} stroke={2} radius={18}
              style={{ padding: 14, marginBottom: 12 }}>
              <div style={{ fontFamily: LL.fontBody, fontSize: 13, color: LL.navyInk, marginBottom: 10 }}>
                {error || "Jeu non identifié. Essayez une photo plus nette de la boîte."}
              </div>
              <LLButton onClick={reset} variant="outline" size="md" style={{ width: '100%' }}>
                Réessayer
              </LLButton>
            </FoldedCard>
          )}

          {/* File inputs — hidden */}
          <input
            ref={cameraRef} type="file" accept="image/*" capture="environment"
            style={{ display: 'none' }}
            onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) pickFile(f, true); e.target.value = ''; }}
          />
          <input
            ref={galleryRef} type="file" accept="image/*"
            style={{ display: 'none' }}
            onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) pickFile(f, true); e.target.value = ''; }}
          />
        </div>
      </ScreenBody>

      <style>{`
        @keyframes scanLine {
          0% { top: 8%; opacity: 0; }
          10% { opacity: 1; }
          90% { opacity: 1; }
          100% { top: 92%; opacity: 0; }
        }
      `}</style>
    </Screen>
  );
}

function ScanActionCard({ icon, title, subtitle, onClick }) {
  return (
    <FoldedCard fold={22} color={LL.navy} bg={LL.cream} stroke={2} radius={18}
      onClick={onClick} style={{ padding: 18, cursor: 'pointer' }}>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
        <div style={{
          width: 46, height: 46, borderRadius: 14,
          background: LL.navy, color: LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          {icon}
        </div>
        <div style={{ fontFamily: LL.fontDisplay, fontSize: 15, fontWeight: 700, color: LL.navyInk }}>
          {title}
        </div>
        <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, textAlign: 'center' }}>
          {subtitle}
        </div>
      </div>
    </FoldedCard>
  );
}

function CameraIcon() {
  return (
    <svg width="24" height="22" viewBox="0 0 26 22" fill="none">
      <path d="M3 6h5l2-3h6l2 3h5a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"
        stroke={LL.cream} strokeWidth="2" fill="none"/>
      <circle cx="13" cy="12" r="4" stroke={LL.cream} strokeWidth="2" fill="none"/>
      <circle cx="20" cy="8.5" r="0.8" fill={LL.cream}/>
    </svg>
  );
}

function GalleryIcon() {
  return (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
      <rect x="3" y="3" width="18" height="18" rx="2.5" stroke={LL.cream} strokeWidth="2" fill="none"/>
      <circle cx="9" cy="9" r="1.8" fill={LL.cream}/>
      <path d="M3 18 L9 12 L13 16 L17 12 L21 16 L21 21 L3 21 Z" fill={LL.cream}/>
    </svg>
  );
}

function ScanBoxIcon() {
  return (
    <svg width="48" height="48" viewBox="0 0 48 48" fill="none">
      {/* Corner brackets */}
      <path d="M4 14 V6 a2 2 0 0 1 2-2 h8" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
      <path d="M34 4 h8 a2 2 0 0 1 2 2 v8" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
      <path d="M44 34 v8 a2 2 0 0 1-2 2 h-8" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
      <path d="M14 44 h-8 a2 2 0 0 1-2-2 v-8" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
      {/* Box inside */}
      <rect x="14" y="14" width="20" height="20" rx="2" stroke={LL.gold} strokeWidth="2" fill="none"/>
      <path d="M18 24 L22 28 L30 20" stroke={LL.gold} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// FILTRES (swipe filters modal)
// ──────────────────────────────────────────────────────────────
function FiltersScreen({ onBack, onApply }) {
  const [duration, setDuration] = React.useState(60);
  const [players, setPlayers] = React.useState(4);
  const [competition, setCompetition] = React.useState(2);
  const [moods, setMoods] = React.useState(['chill', 'party']);
  const moodOpts = [
    { id: 'chill', label: 'Chill' },
    { id: 'party', label: 'Ambiance' },
    { id: 'strat', label: 'Stratégie' },
    { id: 'think', label: 'Réflexion' },
    { id: 'coop', label: 'Coopératif' },
    { id: 'fast', label: 'Rapide' },
  ];
  const toggleMood = m => setMoods(s => s.includes(m) ? s.filter(x => x !== m) : [...s, m]);

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} />
      <div style={{ padding: '0 22px 22px' }}>
        <h1 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
          color: LL.navyInk, margin: '0 0 6px',
        }}>
          Filtres
        </h1>
        <div style={{ color: LL.mutedInk, fontSize: 13, marginBottom: 18 }}>
          Ajustez les envies du groupe
        </div>

        <FilterSection label="Durée maximale">
          <Slider value={duration} onChange={setDuration} min={15} max={180} step={15} suffix=" min"/>
        </FilterSection>

        <FilterSection label="Nombre de joueurs">
          <div style={{ display: 'flex', gap: 8 }}>
            {[2,3,4,5,6,'+7'].map(n => (
              <button key={n} onClick={() => setPlayers(n)} style={{
                flex: 1, height: 44, borderRadius: 12, cursor: 'pointer',
                border: `1.5px solid ${LL.navy}`,
                background: players === n ? LL.navy : LL.cream,
                color: players === n ? LL.cream : LL.navyInk,
                fontFamily: LL.fontBody, fontWeight: 600,
              }}>{n}</button>
            ))}
          </div>
        </FilterSection>

        <FilterSection label="Niveau de compétition">
          <div style={{ display: 'flex', gap: 8 }}>
            {['Coopératif','Neutre','Compétitif'].map((label, i) => (
              <button key={i} onClick={() => setCompetition(i)} style={{
                flex: 1, height: 46, borderRadius: 12, cursor: 'pointer',
                border: `1.5px solid ${LL.navy}`,
                background: competition === i ? LL.navy : LL.cream,
                color: competition === i ? LL.cream : LL.navyInk,
                fontFamily: LL.fontBody, fontWeight: 600, fontSize: 13,
              }}>{label}</button>
            ))}
          </div>
        </FilterSection>

        <FilterSection label="Humeur">
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
            {moodOpts.map(m => {
              const on = moods.includes(m.id);
              return (
                <button key={m.id} onClick={() => toggleMood(m.id)} style={{
                  padding: '10px 16px', borderRadius: 999, cursor: 'pointer',
                  border: `1.5px solid ${LL.navy}`,
                  background: on ? LL.navy : LL.cream,
                  color: on ? LL.cream : LL.navyInk,
                  fontFamily: LL.fontBody, fontWeight: 600, fontSize: 13,
                }}>{m.label}</button>
              );
            })}
          </div>
        </FilterSection>

        <LLButton onClick={onApply} size="lg" style={{ width: '100%', marginTop: 16 }}>
          Appliquer les filtres
        </LLButton>
      </div>
    </Screen>
  );
}

function FilterSection({ label, children }) {
  return (
    <div style={{ marginBottom: 20 }}>
      <div style={{ fontFamily: LL.fontBody, fontSize: 13, fontWeight: 700, color: LL.navyInk, marginBottom: 10, letterSpacing: 0.3 }}>
        {label}
      </div>
      {children}
    </div>
  );
}

function Slider({ value, onChange, min, max, step = 1, suffix = '' }) {
  const pct = ((value - min) / (max - min)) * 100;
  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
        <span style={{ fontSize: 12, color: LL.muted }}>{min}{suffix}</span>
        <span style={{ fontFamily: LL.fontDisplay, fontSize: 18, fontWeight: 700, color: LL.navyInk }}>{value}{suffix}</span>
        <span style={{ fontSize: 12, color: LL.muted }}>{max}{suffix}</span>
      </div>
      <input
        type="range" value={value} min={min} max={max} step={step}
        onChange={e => onChange(+e.target.value)}
        style={{
          width: '100%', WebkitAppearance: 'none', appearance: 'none',
          height: 8, borderRadius: 999,
          background: `linear-gradient(90deg, ${LL.navy} 0 ${pct}%, ${LL.creamDark} ${pct}% 100%)`,
          outline: 'none',
        }}
      />
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// PROFIL
// ──────────────────────────────────────────────────────────────
function ProfileScreen({ onNav, onHistory, onFollowers, onFollowing, user: propUser, onLogout }) {
  const [profile, setProfile] = React.useState(null);
  const [stats, setStats] = React.useState({ games: 0, parties: 0, matches: 0, wins: 0 });
  const [leaderboard, setLeaderboard] = React.useState([]);
  const [editing, setEditing] = React.useState(false);
  const [uploading, setUploading] = React.useState(false);
  const fileRef = React.useRef(null);

  React.useEffect(() => {
    (async () => {
      try {
        const { user, stats } = await LLAPI.profile();
        setProfile(user); setStats(stats);
      } catch (err) { console.warn('profile:', err); }
      try {
        const { leaderboard } = await LLAPI.leaderboard();
        if (leaderboard) setLeaderboard(leaderboard);
      } catch (err) { console.warn('leaderboard:', err); }
    })();
  }, []);

  async function handleAvatar(file) {
    if (!file) return;
    setUploading(true);
    try {
      const r = await LLAPI.uploadAvatar(file);
      setProfile(p => ({ ...p, avatar_url: r.avatar_url }));
    } catch (err) { alert('Upload échoué: ' + err.message); }
    finally { setUploading(false); }
  }

  async function handleSave(patch) {
    try {
      const r = await LLAPI.updateProfile(patch);
      setProfile(r.user);
      setEditing(false);
    } catch (err) { alert('Sauvegarde échouée: ' + err.message); }
  }

  const p = profile || propUser || {};
  const age = p.birthday ? Math.floor((Date.now() - new Date(p.birthday)) / (365.25 * 86400000)) : null;

  if (editing) {
    return <ProfileEditScreen profile={p} onCancel={() => setEditing(false)} onSave={handleSave} />;
  }

  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={4} padBottom={110}>
        <div style={{ padding: '4px 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 34,
            color: LL.navyInk, margin: '4px 0 16px',
          }}>
            Profil
          </h1>

          {/* Hero profil — navy band + avatar + stats tiles */}
          <div style={{
            position: 'relative', marginBottom: 18,
            borderRadius: 24, overflow: 'hidden',
            border: `1.5px solid ${LL.navy}`,
          }}>
            {/* Navy header with decorative pattern */}
            <div style={{
              position: 'relative',
              background: `linear-gradient(135deg, ${LL.navy} 0%, ${LL.navyDeep} 100%)`,
              padding: '22px 22px 56px',
              overflow: 'hidden',
            }}>
              {/* Sparkles / stars decoration */}
              <svg width="100%" height="100%" viewBox="0 0 320 140" preserveAspectRatio="none"
                style={{ position: 'absolute', inset: 0, opacity: 0.18, pointerEvents: 'none' }}>
                {[
                  [30,24,7], [78,84,4], [140,18,5], [210,58,8], [270,30,5],
                  [298,94,6], [178,102,4], [50,110,5], [244,10,4], [112,72,6],
                ].map(([cx,cy,r], i) => (
                  <g key={i} transform={`translate(${cx} ${cy})`}>
                    <path d={`M0 -${r*1.6} L${r*0.4} -${r*0.4} L${r*1.6} 0 L${r*0.4} ${r*0.4} L0 ${r*1.6} L-${r*0.4} ${r*0.4} L-${r*1.6} 0 L-${r*0.4} -${r*0.4} Z`}
                      fill={LL.cream}/>
                  </g>
                ))}
              </svg>
              <div style={{
                fontFamily: LL.fontBody, fontSize: 11, letterSpacing: 1.6,
                color: 'rgba(242,229,202,0.72)', fontWeight: 600, textTransform: 'uppercase',
                marginBottom: 2, position: 'relative',
              }}>
                {p.created_at ? `membre depuis ${new Date(p.created_at).getFullYear()}` : 'membre'}
                {age !== null && ` · ${age} ans`}
                {p.city && ` · ${p.city}`}
              </div>
              <div style={{
                fontFamily: LL.fontDisplay, fontSize: 30, fontWeight: 800,
                color: LL.cream, lineHeight: 1.05, position: 'relative',
              }}>
                {p.name || '—'}
              </div>
              <div style={{
                fontFamily: LL.fontMono, fontSize: 13, color: 'rgba(242,229,202,0.7)',
                marginTop: 4, position: 'relative',
              }}>
                @{p.handle || ''}
              </div>
            </div>

            {/* Avatar — monté plus haut dans la bande navy */}
            <div
              onClick={() => fileRef.current && fileRef.current.click()}
              style={{
                position: 'absolute', top: 20, right: 22,
                width: 88, height: 88, borderRadius: '50%',
                background: LL.cream, padding: 4,
                boxShadow: '0 4px 16px rgba(0,0,0,0.18)',
                cursor: 'pointer', opacity: uploading ? 0.6 : 1,
                overflow: 'hidden',
              }}>
              {p.avatar_url ? (
                <img src={p.avatar_url} alt="" style={{ width: 80, height: 80, borderRadius: '50%', objectFit: 'cover', display: 'block' }}/>
              ) : (
                <Avatar size={80} name={(p.name || '?')[0]} />
              )}
            </div>
            <input ref={fileRef} type="file" accept="image/*" style={{ display: 'none' }}
              onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) handleAvatar(f); }}/>

            {/* Stats band — 2 rows: top (jeux/parties/victoires), bottom (abonnés/abonnements) */}
            <div style={{ background: LL.cream, padding: '16px 0 12px' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', marginBottom: 12 }}>
                {[
                  [String(stats.games ?? 0), 'jeux'],
                  [String(stats.parties ?? 0), 'parties'],
                  [String(stats.wins ?? 0), 'victoires'],
                ].map(([v, l], i) => (
                  <div key={l} style={{
                    textAlign: 'center',
                    borderLeft: i > 0 ? `1px solid rgba(23,70,141,0.18)` : 'none',
                    padding: '2px 4px',
                  }}>
                    <div style={{ fontFamily: LL.fontDisplay, fontSize: 28, fontWeight: 800, color: LL.navyInk, lineHeight: 1 }}>{v}</div>
                    <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, textTransform: 'uppercase', letterSpacing: 1.2, marginTop: 4, fontWeight: 600 }}>{l}</div>
                  </div>
                ))}
              </div>
              <div style={{
                borderTop: `1px solid rgba(23,70,141,0.12)`,
                paddingTop: 10,
                display: 'grid', gridTemplateColumns: '1fr 1fr',
              }}>
                {[
                  [String(stats.followers ?? 0), 'abonnés', onFollowers],
                  [String(stats.following ?? 0), 'abonnements', onFollowing],
                ].map(([v, l, handler], i) => (
                  <button key={l} onClick={handler} style={{
                    textAlign: 'center', background: 'transparent', border: 'none',
                    borderLeft: i > 0 ? `1px solid rgba(23,70,141,0.18)` : 'none',
                    cursor: 'pointer', padding: '4px 6px',
                  }}>
                    <div style={{ fontFamily: LL.fontDisplay, fontSize: 22, fontWeight: 800, color: LL.navyInk, lineHeight: 1 }}>{v}</div>
                    <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, textTransform: 'uppercase', letterSpacing: 1.2, marginTop: 3, fontWeight: 600 }}>{l}</div>
                  </button>
                ))}
              </div>
            </div>
          </div>

          {/* Leaderboard — me + friends ranked by wins */}
          {leaderboard.length > 0 && (
            <>
              <SectionHeader>
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
                    <path d="M7 4 h10 v4 a5 5 0 0 1 -10 0 z" fill={LL.gold}/>
                    <path d="M7 5 Q 3 5 3 8 Q 3 11 7 10" stroke={LL.gold} strokeWidth="1.6" fill="none"/>
                    <path d="M17 5 Q 21 5 21 8 Q 21 11 17 10" stroke={LL.gold} strokeWidth="1.6" fill="none"/>
                    <rect x="10.5" y="12" width="3" height="4" fill={LL.gold}/>
                    <rect x="7" y="16" width="10" height="2.5" rx="1" fill={LL.gold}/>
                    <rect x="5" y="18.5" width="14" height="2" rx="1" fill={LL.gold}/>
                  </svg>
                  Classement entre amis
                </span>
              </SectionHeader>
              <div style={{
                border: `1.5px solid ${LL.navy}`, borderRadius: 16,
                overflow: 'hidden', marginBottom: 18, background: LL.cream,
              }}>
                {leaderboard.map((u, i) => {
                  const isMe = u.me;
                  const isPodium = i < 3;
                  const podiumColor = ['#E2B13A', '#C0C7D0', '#B07A45'][i] || 'transparent';
                  return (
                    <div key={u.id} style={{
                      display: 'flex', alignItems: 'center', gap: 12,
                      padding: '12px 14px',
                      borderTop: i > 0 ? `1px solid rgba(23,70,141,0.12)` : 'none',
                      background: isMe ? 'rgba(23,70,141,0.06)' : 'transparent',
                    }}>
                      <div style={{
                        width: 28, height: 28, borderRadius: '50%',
                        background: isPodium ? podiumColor : 'rgba(23,70,141,0.1)',
                        color: isPodium ? LL.navyDeep : LL.muted,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 13,
                        flexShrink: 0,
                      }}>{i + 1}</div>
                      <div style={{
                        width: 36, height: 36, borderRadius: '50%',
                        background: u.avatar_color || LL.navy, color: LL.cream,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontFamily: LL.fontDisplay, fontSize: 15, fontWeight: 800,
                        overflow: 'hidden', flexShrink: 0,
                      }}>
                        {u.avatar_url
                          ? <img src={u.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                          : (u.name || '?')[0]}
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{
                          fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14,
                          color: LL.navyInk,
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        }}>{u.name}{isMe && ' (toi)'}</div>
                        <div style={{ fontFamily: LL.fontMono, fontSize: 11, color: LL.muted }}>
                          {u.parties} partie{u.parties > 1 ? 's' : ''}
                        </div>
                      </div>
                      <div style={{
                        textAlign: 'right',
                        fontFamily: LL.fontDisplay, fontWeight: 800,
                        color: LL.navyInk, lineHeight: 1.1,
                      }}>
                        <div style={{ fontSize: 22 }}>{u.wins}</div>
                        <div style={{
                          fontFamily: LL.fontBody, fontSize: 9, color: LL.muted,
                          textTransform: 'uppercase', letterSpacing: 1, fontWeight: 600,
                        }}>victoire{u.wins > 1 ? 's' : ''}</div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </>
          )}

          <SectionHeader>Raccourcis</SectionHeader>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <ProfileRow onClick={onHistory} icon="history" label="Historique des parties" count={stats.parties ?? ''} />
            <ProfileRow icon="star" label="Mes favoris" count={stats.favorites ?? ''} />
            <ProfileRow icon="settings" label="Paramètres" onClick={() => setEditing(true)} />
            <ProfileRow icon="policy" label="Politique de confidentialité"
              onClick={() => window.open('/privacy.html', '_blank')} />
            {onLogout && (
              <ProfileRow icon="logout" label="Se déconnecter" onClick={onLogout} />
            )}
          </div>
        </div>
      </ScreenBody>
      <BottomNav current="profile" onNav={onNav} />
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// Bloc "Trouver des joueurs" — recherche + suivi
// ──────────────────────────────────────────────────────────────
function FollowSection({ currentUserId }) {
  const [query, setQuery] = React.useState('');
  const [results, setResults] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [subState, setSubState] = React.useState({}); // userId → bool
  const debounceRef = React.useRef(null);

  React.useEffect(() => {
    clearTimeout(debounceRef.current);
    if (query.trim().length < 2) { setResults(null); return; }
    debounceRef.current = setTimeout(async () => {
      setLoading(true);
      try {
        const { users } = await LLAPI.searchUsers(query.trim());
        setResults(users || []);
        const initial = {};
        (users || []).forEach(u => { initial[u.id] = u.is_subscribed; });
        setSubState(s => ({ ...initial, ...s }));
      } catch (err) { console.warn('searchUsers:', err); setResults([]); }
      finally { setLoading(false); }
    }, 320);
    return () => clearTimeout(debounceRef.current);
  }, [query]);

  async function toggleFollow(userId) {
    setSubState(s => ({ ...s, [userId]: !s[userId] }));
    try { await LLAPI.toggleSubscribe(userId); }
    catch { setSubState(s => ({ ...s, [userId]: !s[userId] })); }
  }

  return (
    <div style={{ marginBottom: 18 }}>
      <SectionHeader>Trouver des joueurs</SectionHeader>
      <div style={{ position: 'relative', marginBottom: 10 }}>
        <input
          value={query}
          onChange={e => setQuery(e.target.value)}
          placeholder="Rechercher par nom ou @pseudo…"
          style={{
            width: '100%', padding: '11px 14px 11px 38px',
            border: `1.5px solid ${LL.navy}`, borderRadius: 12,
            background: LL.cream, fontFamily: LL.fontBody, fontSize: 14,
            color: LL.navyInk, outline: 'none', boxSizing: 'border-box',
          }}
        />
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={LL.muted} strokeWidth="2" strokeLinecap="round"
          style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none' }}>
          <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
        </svg>
      </div>

      {loading && (
        <div style={{ textAlign: 'center', padding: '12px 0', color: LL.muted, fontSize: 13, fontFamily: LL.fontBody }}>Recherche…</div>
      )}

      {!loading && results && results.length === 0 && (
        <div style={{ textAlign: 'center', padding: '10px 0', color: LL.muted, fontSize: 13, fontFamily: LL.fontBody }}>Aucun résultat</div>
      )}

      {!loading && results && results.length > 0 && (
        <div style={{
          border: `1.5px solid ${LL.navy}`, borderRadius: 14,
          overflow: 'hidden', background: LL.cream,
        }}>
          {results.map((u, i) => {
            const followed = subState[u.id] ?? u.is_subscribed;
            return (
              <div key={u.id} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                padding: '11px 14px',
                borderTop: i > 0 ? `1px solid rgba(23,70,141,0.1)` : 'none',
              }}>
                {/* Avatar */}
                <div style={{
                  width: 40, height: 40, borderRadius: '50%', flexShrink: 0,
                  background: u.avatar_color || LL.navy, color: LL.cream,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 16,
                  overflow: 'hidden',
                }}>
                  {u.avatar_url
                    ? <img src={u.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                    : (u.name || '?')[0]}
                </div>
                {/* Info */}
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14, color: LL.navyInk, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {u.name}
                  </div>
                  <div style={{ fontFamily: LL.fontMono, fontSize: 11, color: LL.muted }}>
                    @{u.handle}{u.followers_count > 0 ? ` · ${u.followers_count} abonné${u.followers_count > 1 ? 's' : ''}` : ''}
                  </div>
                </div>
                {/* Follow button */}
                <button
                  onClick={() => toggleFollow(u.id)}
                  style={{
                    padding: '7px 14px', borderRadius: 999, cursor: 'pointer',
                    fontFamily: LL.fontBody, fontSize: 12.5, fontWeight: 700,
                    background: followed ? 'transparent' : LL.navy,
                    color: followed ? LL.navy : LL.cream,
                    border: `1.5px solid ${LL.navy}`,
                    transition: 'all 0.15s',
                    flexShrink: 0,
                  }}
                >
                  {followed ? 'Abonné ✓' : 'Suivre'}
                </button>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function PrefTile({ label, value }) {
  return (
    <div style={{
      border: `1.5px solid ${LL.navy}`, borderRadius: 14, padding: '10px 12px',
      background: LL.cream,
    }}>
      <div style={{
        fontFamily: LL.fontBody, fontSize: 10, color: LL.muted,
        letterSpacing: 1.2, textTransform: 'uppercase', fontWeight: 600, marginBottom: 2,
      }}>{label}</div>
      <div style={{ fontFamily: LL.fontDisplay, fontSize: 16, fontWeight: 700, color: LL.navyInk }}>
        {String(value)}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Profile editor — inline sub-screen
// ──────────────────────────────────────────────────────────────
function ProfileEditScreen({ profile, onCancel, onSave }) {
  const [data, setData] = React.useState(() => ({
    name: profile.name || '',
    bio: profile.bio || '',
    city: profile.city || '',
    country: profile.country || 'FR',
    birthday: profile.birthday || '',
    phone: profile.phone || '',
    preferred_players: profile.preferred_players || 4,
    preferred_duration: profile.preferred_duration || 60,
    privacy_level: profile.privacy_level || 'friends',
    notifications: profile.notifications ?? true,
    favorite_moods: profile.favorite_moods || [],
  }));
  const set = (k) => (v) => setData(d => ({ ...d, [k]: v }));
  const moodOpts = [
    { id: 'chill', label: 'Chill' },
    { id: 'party', label: 'Ambiance' },
    { id: 'strategic', label: 'Stratégie' },
    { id: 'competitive', label: 'Compétitif' },
    { id: 'family', label: 'Famille' },
    { id: 'creative', label: 'Créatif' },
  ];
  const toggleMood = m => set('favorite_moods')(
    data.favorite_moods.includes(m) ? data.favorite_moods.filter(x => x !== m) : [...data.favorite_moods, m]
  );

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onCancel} title="Modifier mon profil" />
      <ScreenBody padTop={0} padBottom={120}>
        <div style={{ padding: '0 22px' }}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 16 }}>
            <OutlineField label="Nom" value={data.name} onChange={e => set('name')(e.target.value)} rounded={18}/>
            <OutlineField label="Ville" value={data.city} onChange={e => set('city')(e.target.value)} rounded={18}/>
            <OutlineField label="Date de naissance (AAAA-MM-JJ)" value={data.birthday} onChange={e => set('birthday')(e.target.value)} rounded={18}/>
            <OutlineField label="Téléphone" value={data.phone} onChange={e => set('phone')(e.target.value)} rounded={18}/>
          </div>

          <div style={{ fontFamily: LL.fontBody, fontSize: 13, fontWeight: 700, color: LL.navyInk, marginBottom: 6 }}>
            Bio
          </div>
          <textarea
            value={data.bio}
            onChange={e => set('bio')(e.target.value)}
            placeholder="Dis-en un peu plus sur tes goûts ludiques…"
            style={{
              width: '100%', minHeight: 80, padding: 12, borderRadius: 14,
              border: `2px solid ${LL.navy}`, background: LL.cream, color: LL.navyInk,
              fontFamily: LL.fontBody, fontSize: 14, resize: 'vertical', marginBottom: 16,
              boxSizing: 'border-box', outline: 'none',
            }}
          />

          <FilterSection label="Nombre de joueurs préféré">
            <Slider value={data.preferred_players} onChange={set('preferred_players')} min={1} max={14}/>
          </FilterSection>

          <FilterSection label="Durée préférée">
            <Slider value={data.preferred_duration} onChange={set('preferred_duration')} min={15} max={180} step={15} suffix=" min"/>
          </FilterSection>

          <FilterSection label="Ambiances favorites">
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {moodOpts.map(m => {
                const on = data.favorite_moods.includes(m.id);
                return (
                  <button key={m.id} onClick={() => toggleMood(m.id)} style={{
                    padding: '8px 14px', borderRadius: 999, cursor: 'pointer',
                    border: `1.5px solid ${LL.navy}`,
                    background: on ? LL.navy : LL.cream,
                    color: on ? LL.cream : LL.navyInk,
                    fontFamily: LL.fontBody, fontWeight: 600, fontSize: 12,
                  }}>{m.label}</button>
                );
              })}
            </div>
          </FilterSection>

          <FilterSection label="Visibilité du profil">
            <div style={{ display: 'flex', gap: 6 }}>
              {[['public','Public'],['friends','Amis'],['private','Privé']].map(([id, label]) => (
                <button key={id} onClick={() => set('privacy_level')(id)} style={{
                  flex: 1, height: 40, borderRadius: 10, cursor: 'pointer',
                  border: `1.5px solid ${LL.navy}`,
                  background: data.privacy_level === id ? LL.navy : LL.cream,
                  color: data.privacy_level === id ? LL.cream : LL.navyInk,
                  fontFamily: LL.fontBody, fontWeight: 600, fontSize: 12,
                }}>{label}</button>
              ))}
            </div>
          </FilterSection>

          <label style={{
            display: 'flex', alignItems: 'center', gap: 10, marginBottom: 20,
            fontFamily: LL.fontBody, fontSize: 14, color: LL.navyInk, cursor: 'pointer',
          }}>
            <input type="checkbox" checked={data.notifications} onChange={e => set('notifications')(e.target.checked)}/>
            Activer les notifications
          </label>

          <LLButton onClick={() => onSave(data)} size="lg" style={{ width: '100%', marginBottom: 8 }}>
            Enregistrer
          </LLButton>
          <LLButton onClick={onCancel} variant="ghost" size="md" style={{ width: '100%' }}>
            Annuler
          </LLButton>
        </div>
      </ScreenBody>
    </Screen>
  );
}

function ProfileStat({ v, l }) {
  return (
    <div>
      <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 18, color: LL.navyInk }}>{v}</div>
      <div style={{ fontSize: 11, color: LL.muted, marginTop: -2 }}>{l}</div>
    </div>
  );
}
function ProfileRow({ icon, label, count, onClick }) {
  return (
    <div onClick={onClick} style={{
      display: 'flex', alignItems: 'center', gap: 14,
      padding: '16px 18px', cursor: 'pointer',
      background: 'transparent', border: `1.5px solid ${LL.navy}`, borderRadius: 18,
      transition: 'background .15s',
    }}
      onMouseEnter={e => e.currentTarget.style.background = 'rgba(23,70,141,0.06)'}
      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
    >
      <ProfileIcon kind={icon} />
      <div style={{ flex: 1, fontWeight: 600, color: LL.navyInk, fontFamily: LL.fontBody, fontSize: 15 }}>{label}</div>
      {count && (
        <div style={{ fontSize: 13, color: LL.muted, fontFamily: LL.fontMono, fontWeight: 600 }}>
          {count}
        </div>
      )}
      <svg width="8" height="14" viewBox="0 0 8 14">
        <path d="M1 1 L 7 7 L 1 13" stroke={LL.navy} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    </div>
  );
}

function ProfileIcon({ kind }) {
  const c = LL.navy;
  switch (kind) {
    case 'policy':
      // shield with check mark
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
          <path d="M12 2 L20 5 V12 C20 17 16 21 12 22 C8 21 4 17 4 12 V5 Z" stroke={c} strokeWidth="1.7" strokeLinejoin="round"/>
          <path d="M8.5 12 L11 14.5 L15.5 9.5" stroke={c} strokeWidth="1.7" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      );
    case 'logout':
      // door with arrow out
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
          <path d="M14 4 H5 a1 1 0 0 0 -1 1 v14 a1 1 0 0 0 1 1 h9" stroke={c} strokeWidth="1.7" fill="none" strokeLinecap="round"/>
          <path d="M11 12 H21 M17 8 L21 12 L17 16" stroke={c} strokeWidth="1.7" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      );
    case 'history':
      // clockwise arrow around clock
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
          <path d="M12 4 A 8 8 0 1 1 4 12" stroke={c} strokeWidth="1.8" strokeLinecap="round"/>
          <path d="M12 4 L 12 1 L 15 4 L 12 7 Z" fill={c}/>
          <circle cx="12" cy="12" r="1.2" fill={c}/>
          <path d="M12 8 V 12 L 15 14" stroke={c} strokeWidth="1.8" strokeLinecap="round" fill="none"/>
        </svg>
      );
    case 'star':
      return (
        <svg width="22" height="22" viewBox="0 0 24 24">
          <path d="M12 2 L 14.6 8.8 L 22 9.5 L 16.3 14.2 L 18 21.5 L 12 17.5 L 6 21.5 L 7.7 14.2 L 2 9.5 L 9.4 8.8 Z"
            fill="none" stroke={c} strokeWidth="1.7" strokeLinejoin="round"/>
        </svg>
      );
    case 'friends':
      return (
        <svg width="24" height="20" viewBox="0 0 26 22" fill="none">
          <circle cx="9" cy="7" r="4" stroke={c} strokeWidth="1.7"/>
          <path d="M2 20 C 3 15 6 13.5 9 13.5 C 12 13.5 15 15 16 20" stroke={c} strokeWidth="1.7" strokeLinecap="round"/>
          <circle cx="18" cy="6" r="3.2" stroke={c} strokeWidth="1.7"/>
          <path d="M14 19 C 15 15.5 17 14.2 19 14.2 C 21 14.2 23 15.2 24 18"
            stroke={c} strokeWidth="1.7" strokeLinecap="round"/>
        </svg>
      );
    case 'settings':
      // Rouage (cogwheel) avec 8 dents
      return (
        <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
          <path d="M12 2.5 L13.2 4.8 L15.7 4.3 L15.7 6.9 L18 7.9 L17 10.1 L19 11.8 L17 13.4 L18 15.6 L15.7 16.5 L15.7 19.1 L13.2 18.6 L12 20.9 L10.8 18.6 L8.3 19.1 L8.3 16.5 L6 15.6 L7 13.4 L5 11.8 L7 10.1 L6 7.9 L8.3 6.9 L8.3 4.3 L10.8 4.8 Z"
            stroke={c} strokeWidth="1.5" strokeLinejoin="round" fill="none"/>
          <circle cx="12" cy="11.8" r="2.8" stroke={c} strokeWidth="1.5" fill="none"/>
        </svg>
      );
    default:
      return null;
  }
}

// ──────────────────────────────────────────────────────────────
// HISTORIQUE
// ──────────────────────────────────────────────────────────────
function HistoryScreen({ onBack, onOpenGame }) {
  const [sessions, setSessions] = React.useState(null);

  React.useEffect(() => {
    (async () => {
      try {
        if (!LLAPI.isLoggedIn()) return;
        const r = await LLAPI.history();
        if (r.sessions) {
          const formatted = r.sessions.map(s => {
            const d = new Date(s.played_at);
            const now = new Date();
            const days = Math.floor((now - d) / (86400000));
            const label = days === 0 ? "Aujourd'hui" : days === 1 ? 'Hier' : d.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
            const full = (window.GAMES || GAMES).find(g => g.id === s.game_id) || s.game;
            return { ...s, date: label, game: full };
          });
          setSessions(formatted);
        }
      } catch (err) { console.warn('history:', err); }
    })();
  }, []);

  const fallback = [
    { date: "Hier", game: GAMES[5], players: 4, winner: 'Marie', duration: '52 min' },
    { date: "Samedi 18 avr.", game: GAMES[0], players: 5, winner: 'Augustin', duration: '2h 10' },
    { date: "Mercredi 15 avr.", game: GAMES[2], players: 6, winner: '—', duration: '18 min' },
    { date: "12 avr.", game: GAMES[3], players: 4, winner: 'Léo', duration: '1h 20' },
    { date: "8 avr.", game: GAMES[1], players: 5, winner: 'Inès', duration: '35 min' },
  ];
  const items = sessions && sessions.length ? sessions : fallback;
  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} />
      <ScreenBody padTop={44} padBottom={30}>
        <div style={{ padding: '0 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
            color: LL.navyInk, margin: '0 0 16px',
          }}>
            Historique
          </h1>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            {items.map((s, i) => (
              <div key={i} onClick={() => onOpenGame(s.game)} style={{
                display: 'flex', gap: 12, alignItems: 'center',
                padding: 12,
                background: LL.white, border: `1.5px solid ${LL.navy}`, borderRadius: 16,
                cursor: 'pointer',
              }}>
                <GameTile game={s.game} size="sm"/>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 11, color: LL.muted, fontWeight: 600 }}>{s.date}</div>
                  <div style={{ fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 700, color: LL.navyInk }}>
                    {s.game.name}
                  </div>
                  <div style={{ fontSize: 11, color: LL.muted, marginTop: 2 }}>
                    {s.players} joueurs · {s.duration} · Gagnant : {s.winner}
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </ScreenBody>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// FICHE PRODUIT — e-commerce view (Découvrir → jeu)
// ──────────────────────────────────────────────────────────────
function ProductShopScreen({ game, onBack, onNav, onOpenGame }) {
  const [retailers, setRetailers] = React.useState(null);
  React.useEffect(() => {
    if (!game?.id) return;
    (async () => {
      try {
        const r = await LLAPI.retailers(game.id);
        setRetailers(r.retailers || null);
      } catch (err) { console.warn('retailers:', err); }
    })();
  }, [game?.id]);
  if (!game) return null;
  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={0} padBottom={120}>
        <BackHeader onBack={onBack} />
        <div style={{ padding: '0 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 34,
            color: LL.navyInk, margin: '0 0 14px',
          }}>
            {game.name}
          </h1>

          <div style={{
            display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18,
          }}>
            <div style={{ fontFamily: LL.fontDisplay, fontSize: 26, fontWeight: 800, color: LL.navyInk }}>
              {game.price.toFixed(2)} €
            </div>
            <div style={{
              padding: '3px 10px', borderRadius: 999,
              background: 'rgba(79,142,90,0.15)',
              color: LL.green, fontSize: 11, fontWeight: 700,
              letterSpacing: 0.5,
            }}>
              EN STOCK
            </div>
          </div>

          <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 22 }}>
            <GameTile game={game} size="xl"/>
          </div>

          {/* thumbnails */}
          <div style={{ display: 'flex', gap: 8, marginBottom: 22, justifyContent: 'center' }}>
            {[0,1,2,3].map(i => (
              <div key={i} style={{
                width: 52, height: 52, borderRadius: 10,
                background: game.bg || '#E8D5B0',
                position: 'relative', overflow: 'hidden',
                border: i === 0 ? `2px solid ${LL.navy}` : `1px solid rgba(23,70,141,0.2)`,
                opacity: i === 0 ? 1 : 0.7,
                cursor: 'pointer',
              }}>
                <GameCover game={game} />
              </div>
            ))}
          </div>

          <div style={{
            display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, marginBottom: 22,
          }}>
            <Stat label="Joueurs" value={game.players} />
            <Stat label="Durée" value={game.duration} />
            <Stat label="Complexité" value={`${game.weight}/5`} />
          </div>

          <div style={{
            fontFamily: LL.fontBody, fontSize: 13.5, color: LL.navyInk, lineHeight: 1.6,
            marginBottom: 22,
          }}>
            {game.desc}
          </div>

          {/* Retailers */}
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 16, fontWeight: 700,
            color: LL.navyInk, margin: '10px 0 10px',
          }}>
            Acheter ce jeu
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 22 }}>
            {(retailers || [
              { logo: 'a', color: '#FF9900', name: 'Amazon',    sub: 'Livraison 24h · Prime éligible', price: game.price,     url: game.amazon_url },
              { logo: 'P', color: '#0654BA', name: 'Philibert', sub: 'Spécialiste jeux · Livraison 48h', price: game.price + 2, url: '#' },
              { logo: 'C', color: '#D43E3E', name: 'Cultura',   sub: 'Retrait magasin gratuit',          price: Math.max(0, game.price - 1), url: '#' },
            ]).map((r, i) => (
              <RetailerRow key={i} logo={r.logo || r.name[0]} color={r.color || '#777'} name={r.name} sub={r.sub} price={r.price} url={r.url}/>
            ))}
          </div>

          {/* Avis des joueurs — même composant que sur la fiche règles */}
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 16, fontWeight: 700,
            color: LL.navyInk, margin: '14px 0 10px',
          }}>
            Avis des joueurs
          </div>
          <ReviewsBody gameId={game.id} />

          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 16, fontWeight: 700,
            color: LL.navyInk, margin: '20px 0 10px',
          }}>
            Vous aimerez aussi
          </div>
          <div style={{ display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 10, marginRight: -22 }}>
            {GAMES.filter(g => g.id !== game.id).slice(0, 5).map(g => (
              <div key={g.id} onClick={() => onOpenGame && onOpenGame(g)} style={{ flexShrink: 0 }}>
                <GameTile game={g} size="md"/>
              </div>
            ))}
          </div>
        </div>
      </ScreenBody>
      <BottomNav current="discover" onNav={onNav} />
    </Screen>
  );
}

function Stat({ label, value }) {
  return (
    <div style={{
      border: `1.5px solid ${LL.navy}`, borderRadius: 12,
      padding: '10px 8px', textAlign: 'center', background: LL.cream,
    }}>
      <div style={{ fontSize: 10, color: LL.muted, fontWeight: 600, letterSpacing: 0.5, textTransform: 'uppercase' }}>{label}</div>
      <div style={{ fontFamily: LL.fontDisplay, fontWeight: 700, color: LL.navyInk, fontSize: 16, marginTop: 2 }}>{value}</div>
    </div>
  );
}

function RetailerRow({ logo, color, name, sub, price, url }) {
  const clickable = url && url !== '#';
  return (
    <a href={clickable ? url : undefined} target="_blank" rel="noopener noreferrer sponsored"
       onClick={clickable ? undefined : (e) => e.preventDefault()}
       style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: 14, borderRadius: 16,
      border: `1.5px solid ${LL.navy}`,
      background: LL.cream,
      textDecoration: 'none',
      cursor: clickable ? 'pointer' : 'default',
    }}>      <div style={{
        width: 42, height: 42, borderRadius: 10,
        background: color, color: '#fff',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: LL.fontDisplay, fontWeight: 900, fontSize: 20,
        flexShrink: 0,
      }}>{logo}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: LL.fontBody, fontSize: 14, fontWeight: 700,
          color: LL.navyInk,
        }}>{name}</div>
        <div style={{ fontSize: 11.5, color: LL.muted, marginTop: 1 }}>{sub}</div>
      </div>
      <div style={{
        display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4,
      }}>
        <div style={{
          fontFamily: LL.fontDisplay, fontSize: 16, fontWeight: 800,
          color: LL.navyInk,
        }}>{price.toFixed(2)} €</div>
        <svg width="14" height="14" viewBox="0 0 14 14">
          <path d="M1 1 L 7 7 L 1 13" stroke={LL.navyInk} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </div>
    </a>
  );
}

// ──────────────────────────────────────────────────────────────
// FIRST PLAYER — tirage au sort animé
// ──────────────────────────────────────────────────────────────
const FP_COLORS = ['#E8A048', '#4F8E5A', '#C94A3A', '#9B7BB8', '#2A7FB8', '#D4862A', '#6B4EA0', '#3A8A7F'];

function FirstPlayerScreen({ game, session, user, onBack }) {
  // Build the list of players from the real session participants. Fallback to
  // the current user alone when there's no session (e.g. launched from the
  // library page for a solo/offline game).
  const players = React.useMemo(() => {
    const src = session?.participants?.length
      ? session.participants.map(p => ({ name: p.name || p.handle || '?' }))
      : (user ? [{ name: user.name || user.handle || 'Toi' }] : []);
    return src.map((p, i) => ({ ...p, color: FP_COLORS[i % FP_COLORS.length] }));
  }, [session?.id, session?.participants, user?.id]);

  const [phase, setPhase] = React.useState('spinning'); // spinning → revealed → scoreboard
  const [highlight, setHighlight] = React.useState(0);
  const [initialCelebration, setInitialCelebration] = React.useState(null);

  // Non-host clients auto-transition to scoreboard when the host enters it,
  // and see the celebration when the host ends the game — even if they're
  // still on the draw animation.
  React.useEffect(() => {
    if (!session?.id) return;
    const unsub = LLAPI.subscribe(session.id, (evt) => {
      if (evt.type === 'score') {
        setPhase(p => p === 'scoreboard' ? p : 'scoreboard');
      } else if (evt.type === 'game-ended') {
        const ranking = evt.ranking || [];
        const winner = evt.winner || ranking[0] || null;
        setInitialCelebration({ winner, ranking });
        setPhase('scoreboard');
      }
    });
    return unsub;
  }, [session?.id]);
  // Prefer the server-picked index (synced across every participant). Fallback
  // to a local random when the screen is used solo (no session).
  const serverIdx = session?.first_player_idx;
  const [winnerLocked, setWinnerLocked] = React.useState(() => {
    if (typeof serverIdx === 'number' && players.length > 0) return serverIdx % players.length;
    return players.length > 0 ? Math.floor(Math.random() * players.length) : 0;
  });
  React.useEffect(() => {
    if (typeof serverIdx === 'number' && players.length > 0 && serverIdx !== winnerLocked) {
      setWinnerLocked(serverIdx % players.length);
    }
  }, [serverIdx, players.length]);
  const winner = players[winnerLocked] || { name: '—', color: LL.navy };

  React.useEffect(() => {
    if (phase !== 'spinning' || players.length === 0) return;
    const schedule = [];
    let t = 0;
    const total = players.length;
    for (let i = 0; i < 22; i++) {
      const progress = i / 21;
      const delay = 80 + Math.pow(progress, 2.2) * 380;
      t += delay;
      schedule.push({ at: t, i: i % total });
    }
    schedule[schedule.length - 1].i = winnerLocked;
    schedule[schedule.length - 2].i = (winnerLocked + total - 1) % total;
    const timeouts = schedule.map(s => setTimeout(() => setHighlight(s.i), s.at));
    const reveal = setTimeout(() => setPhase('revealed'), t + 350);
    return () => { timeouts.forEach(clearTimeout); clearTimeout(reveal); };
  }, [phase, winnerLocked]);

  // 2-step quit confirmation — the game is launched, a tap on back should
  // not accidentally drop the user out mid-game. MUST be declared before any
  // early return to respect React's rules of hooks.
  const [quitStep, setQuitStep] = React.useState(0);

  if (phase === 'scoreboard') {
    return <ScoreScreen game={game} players={players} startPlayerIdx={winnerLocked}
      session={session} user={user} onBack={onBack}
      initialCelebration={initialCelebration}/>;
  }

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={() => setQuitStep(1)} title="" />
      {quitStep === 1 && (
        <ConfirmDialog
          title="Quitter la partie ?"
          message="La partie vient d'être lancée. Tu es sûr ?"
          confirmLabel="Quitter"
          cancelLabel="Rester"
          danger
          onConfirm={() => setQuitStep(2)}
          onCancel={() => setQuitStep(0)}
        />
      )}
      {quitStep === 2 && (
        <ConfirmDialog
          title="Confirmer"
          message="Dernier avertissement : tu vas sortir de la partie en cours."
          confirmLabel="Oui, quitter"
          cancelLabel="Annuler"
          danger
          onConfirm={() => { setQuitStep(0); onBack && onBack(); }}
          onCancel={() => setQuitStep(0)}
        />
      )}
      <style>{`
        @keyframes fp-tick    { 0%,100% { transform: scale(1) } 50% { transform: scale(1.12) } }
        @keyframes fp-pop     { 0% { transform: scale(.6); opacity: 0 } 100% { transform: scale(1); opacity: 1 } }
        @keyframes fp-slide   { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: translateY(0) } }
      `}</style>

      <div style={{ padding: '0 22px', textAlign: 'center' }}>
        <div style={{
          fontFamily: LL.fontBody, fontSize: 11, letterSpacing: 2,
          textTransform: 'uppercase', fontWeight: 600,
          color: LL.muted, marginBottom: 4,
        }}>
          {game?.name || 'Premier joueur'}
        </div>
        <h1 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 30,
          color: LL.navyInk, margin: '0 0 6px',
        }}>
          {phase === 'spinning' ? 'Qui commence ?' : `${winner.name} commence !`}
        </h1>
        <div style={{
          fontFamily: LL.fontBody, fontSize: 13, color: LL.mutedInk, marginBottom: 26,
        }}>
          {phase === 'spinning' ? 'Tirage au sort…' : 'À toi de poser la première carte.'}
        </div>

        {/* Player list */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 30 }}>
          {players.map((p, i) => {
            const isActive = phase === 'spinning' && highlight === i;
            const isWinner = phase === 'revealed' && i === winnerLocked;
            const dim = phase === 'revealed' && !isWinner;
            return (
              <div key={p.name} style={{
                display: 'flex', alignItems: 'center', gap: 14,
                padding: '12px 16px',
                borderRadius: 16,
                background: isWinner ? LL.navy : LL.cream,
                border: `1.5px solid ${isActive || isWinner ? LL.navy : 'rgba(23,70,141,0.2)'}`,
                opacity: dim ? 0.35 : 1,
                transform: isActive ? 'scale(1.02)' : 'scale(1)',
                transition: 'all .18s ease',
                boxShadow: isWinner ? '0 6px 16px rgba(14,47,99,0.25)' : 'none',
              }}>
                <div style={{
                  width: 38, height: 38, borderRadius: '50%',
                  background: p.color, color: LL.cream,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 16,
                  flexShrink: 0,
                  animation: isActive ? 'fp-tick .25s ease' : 'none',
                }}>{p.name[0]}</div>
                <div style={{
                  flex: 1, textAlign: 'left',
                  fontFamily: LL.fontBody, fontWeight: 600, fontSize: 15,
                  color: isWinner ? LL.cream : LL.navyInk,
                }}>{p.name}</div>
                {isWinner && (
                  <div style={{ animation: 'fp-pop .35s cubic-bezier(.2,1.4,.4,1)' }}>
                    <TrophyIcon color={LL.gold} size={20}/>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>

      {/* CTA — bottom safe zone */}
      {phase === 'revealed' && (
        <div style={{
          position: 'absolute', bottom: 24, left: 22, right: 22,
          display: 'flex', flexDirection: 'column', gap: 10,
          animation: 'fp-slide .35s ease .15s both',
        }}>
          <LLButton onClick={() => setPhase('scoreboard')} size="lg" style={{ width: '100%' }}>
            Démarrer la partie
          </LLButton>
        </div>
      )}
    </Screen>
  );
}

// Friendly name for an ambient track based on its file slug.
const TRACK_LABELS = {
  gymnopedie:  'Satie · Gymnopédie n°2',
  nocturne:    'Chopin · Nocturne op. 9 n°2',
  canon:       'Pachelbel · Canon en ré',
  mozart:      'Mozart · Sonate K.330',
  vivaldi:     'Vivaldi · Quatre Saisons (Hiver)',
  tchaikovsky: 'Tchaïkovski · Valse des fleurs',
};
function trackLabelFor(url) {
  if (!url) return null;
  const m = String(url).match(/\/audio\/([^/.?]+)\.mp3/);
  return m ? TRACK_LABELS[m[1]] || 'Musique classique' : 'Musique classique';
}

// ──────────────────────────────────────────────────────────────
// SCORE SCREEN — tableau manches × joueurs, total auto en bas
// ──────────────────────────────────────────────────────────────
function ScoreScreen({ game, players, startPlayerIdx = 0, session, user, onBack, initialCelebration = null }) {
  const isHost = !!(session?.host_id && user?.id && session.host_id === user.id);
  const sessionId = session?.id;

  // rounds[r][p] = score of player p in round r (null if empty)
  const [rounds, setRounds] = React.useState(() => [players.map(() => null)]);
  const [running, setRunning] = React.useState(false);
  const [elapsed, setElapsed] = React.useState(0);
  const [endConfirm, setEndConfirm] = React.useState(false);
  const [endConfirm2, setEndConfirm2] = React.useState(false);
  const [quitStep, setQuitStep] = React.useState(0); // 0 = no modal, 1 = first confirm, 2 = final confirm
  const [celebration, setCelebration] = React.useState(initialCelebration);
  const [saving, setSaving] = React.useState(false);
  const startedAt = React.useRef(null);
  const pushTimerRef = React.useRef(null);
  const savedRef = React.useRef(false);

  // Host: on first mount, push an empty scoreboard so non-hosts stuck on
  // the first-player reveal transition to the scoreboard view too.
  React.useEffect(() => {
    if (!isHost || !sessionId) return;
    LLAPI.updateScore(sessionId, {
      rounds: [players.map(() => null)],
      running: false, elapsed: 0, started_at: null,
      players: players.map(p => p.name),
    }).catch(() => {});
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Host pushes scoreboard to server (debounced) — broadcasts to all clients
  function pushScore(nextRounds, nextRunning = running, nextElapsed = elapsed) {
    if (!isHost || !sessionId) return;
    clearTimeout(pushTimerRef.current);
    pushTimerRef.current = setTimeout(() => {
      LLAPI.updateScore(sessionId, {
        rounds: nextRounds, running: nextRunning, elapsed: nextElapsed,
        started_at: startedAt.current, players: players.map(p => p.name),
      }).catch(() => {});
    }, 250);
  }

  // Non-host clients subscribe to score updates via SSE
  React.useEffect(() => {
    if (isHost || !sessionId) return;
    const unsub = LLAPI.subscribe(sessionId, (evt) => {
      if (evt.type === 'score' && evt.scoreboard) {
        if (Array.isArray(evt.scoreboard.rounds)) setRounds(evt.scoreboard.rounds);
        if (typeof evt.scoreboard.elapsed === 'number') setElapsed(evt.scoreboard.elapsed);
        if (typeof evt.scoreboard.running === 'boolean') setRunning(evt.scoreboard.running);
        if (typeof evt.scoreboard.started_at === 'number') startedAt.current = evt.scoreboard.started_at;
      } else if (evt.type === 'game-ended') {
        // Show celebration on every device
        const ranking = evt.ranking || [];
        const winner = evt.winner || ranking[0] || null;
        setCelebration({ winner, ranking });
      }
    });
    // Polling fallback — pulls scoreboard from DTO if SSE dies
    const poll = setInterval(async () => {
      try {
        const { session: s } = await LLAPI.session(sessionId);
        if (s?.scoreboard) {
          if (Array.isArray(s.scoreboard.rounds)) setRounds(s.scoreboard.rounds);
          if (typeof s.scoreboard.elapsed === 'number') setElapsed(s.scoreboard.elapsed);
          if (typeof s.scoreboard.running === 'boolean') setRunning(s.scoreboard.running);
        }
      } catch (_) {}
    }, 3000);
    return () => { unsub(); clearInterval(poll); };
  }, [isHost, sessionId]);

  React.useEffect(() => {
    if (!running) return;
    startedAt.current = Date.now() - elapsed;
    const id = setInterval(() => {
      const e = Date.now() - startedAt.current;
      setElapsed(e);
      if (isHost) pushScore(rounds, true, e);
    }, 1000);
    return () => clearInterval(id);
  }, [running]);

  function setCell(r, p, v) {
    if (!isHost) return;
    const n = v === '' ? null : parseInt(v, 10);
    const next = rounds.map((row, i) => i === r
      ? row.map((x, j) => j === p ? (isNaN(n) ? null : n) : x)
      : row);
    setRounds(next);
    pushScore(next);
  }
  function addRound() {
    if (!isHost || rounds.length >= 20) return;
    const next = [...rounds, players.map(() => null)];
    setRounds(next);
    pushScore(next);
  }
  function removeRound(r) {
    if (!isHost) return;
    const next = rounds.length === 1
      ? [players.map(() => null)]
      : rounds.filter((_, i) => i !== r);
    setRounds(next);
    pushScore(next);
  }
  async function endGame() {
    if (!isHost) return;
    setEndConfirm(false);
    setRunning(false);
    setSaving(true);

    // Compute winner + totals
    const totals = players.map((_, p) => rounds.reduce((s, row) => s + (row[p] || 0), 0));
    const max = Math.max(...totals);
    const winnerIdx = totals.findIndex(t => t === max);
    const winner = players[winnerIdx];
    const ranking = players
      .map((p, i) => ({ name: p.name, color: p.color, total: totals[i] }))
      .sort((a, b) => b.total - a.total);
    const duration = Math.round(elapsed / 60000) || null;

    // Persist to history (game played, winner, scores, duration in minutes)
    try {
      await LLAPI.recordHistory({
        game_id:  game?.id,
        players:  players.length,
        duration,
        winner:   winner?.name || '—',
        score:    JSON.stringify({
          totals: totals.reduce((acc, t, i) => ({ ...acc, [players[i].name]: t }), {}),
          rounds: rounds.map(r => r.map(v => v == null ? 0 : v)),
        }),
      });
    } catch (err) {
      console.warn('save history:', err);
    }

    // Broadcast end to every participant so they all see the celebration
    if (sessionId) {
      try { await LLAPI.endScore(sessionId, { winner, ranking, duration }); } catch (_) {}
    }
    savedRef.current = true;
    setSaving(false);
    setCelebration({ winner, ranking });
  }

  const totals = players.map((_, p) =>
    rounds.reduce((sum, row) => sum + (row[p] || 0), 0)
  );
  const maxTotal = Math.max(...totals);
  const leaders = totals.map((t, i) => t === maxTotal && t !== 0 ? i : -1).filter(i => i >= 0);

  const fmt = (ms) => {
    const s = Math.floor(ms / 1000);
    const mm = String(Math.floor(s / 60)).padStart(2, '0');
    const ss = String(s % 60).padStart(2, '0');
    return `${mm}:${ss}`;
  };

  // Column width — make it fit all players on the device
  const labelW = 80;
  const cellW = `minmax(52px, 1fr)`;
  const gridCols = `${labelW}px repeat(${players.length}, ${cellW})`;

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={() => setQuitStep(1)} title={game?.name || 'Partie'} />
      <ScreenBody padTop={44} padBottom={30}>
        <div style={{ padding: '0 22px' }}>
          {/* Chrono card */}
          <FoldedCard fold={20} color={LL.navy} bg={LL.cream} stroke={2} radius={16}
            style={{ padding: 14, marginBottom: 16 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <div style={{
                width: 38, height: 38, borderRadius: 10,
                background: LL.navy, color: LL.cream,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                flexShrink: 0,
              }}>
                <ClockIcon/>
              </div>
              <div style={{ flex: 1 }}>
                <div style={{
                  fontFamily: LL.fontBody, fontSize: 10, color: LL.muted,
                  letterSpacing: 1.2, textTransform: 'uppercase', fontWeight: 600,
                }}>chronomètre</div>
                <div style={{
                  fontFamily: LL.fontMono, fontSize: 22, fontWeight: 700,
                  color: LL.navyInk, lineHeight: 1.1,
                }}>{fmt(elapsed)}</div>
              </div>
              {isHost && (
                <button onClick={() => setRunning(r => !r)} aria-label={running ? 'Pause' : 'Lecture'} style={{
                  width: 40, height: 40, borderRadius: '50%',
                  background: running ? LL.red : LL.green, color: LL.cream,
                  border: 'none', cursor: 'pointer',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  padding: 0,
                }}>
                  {running ? (
                    <svg width="12" height="12" viewBox="0 0 12 12"><rect x="2" y="1" width="3" height="10" fill={LL.cream}/><rect x="7" y="1" width="3" height="10" fill={LL.cream}/></svg>
                  ) : (
                    <svg width="12" height="12" viewBox="0 0 12 12"><path d="M2 1 L11 6 L2 11 Z" fill={LL.cream}/></svg>
                  )}
                </button>
              )}
            </div>
          </FoldedCard>

          {/* Scoreboard title */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div style={{
              fontFamily: LL.fontDisplay, fontSize: 15, fontWeight: 800,
              color: LL.navyInk, letterSpacing: 0.3,
            }}>Feuille de score</div>
            <div style={{
              fontFamily: LL.fontBody, fontSize: 11, color: LL.muted, fontWeight: 600,
            }}>{rounds.length} manche{rounds.length > 1 ? 's' : ''}</div>
          </div>

          {/* Spreadsheet */}
          <div style={{
            border: `1.5px solid ${LL.navy}`, borderRadius: 14,
            overflow: 'hidden', background: LL.cream, marginBottom: 12,
          }}>
            {/* Header row — player avatars */}
            <div style={{
              display: 'grid', gridTemplateColumns: gridCols,
              background: LL.navy,
            }}>
              <div style={headerCellStyle(true)}>&nbsp;</div>
              {players.map((p, i) => (
                <div key={p.name} style={{ ...headerCellStyle(false), position: 'relative' }}>
                  <div style={{
                    width: 26, height: 26, borderRadius: '50%',
                    background: p.color, color: LL.cream,
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 12,
                    border: `1.5px solid ${LL.cream}`,
                  }}>{p.name[0]}</div>
                  <div style={{
                    fontFamily: LL.fontBody, fontSize: 9, color: LL.cream, opacity: 0.85,
                    fontWeight: 600, marginTop: 2, overflow: 'hidden',
                    textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '100%',
                  }}>{p.name}</div>
                  {i === startPlayerIdx && (
                    <div style={{
                      position: 'absolute', top: 4, right: 4,
                      width: 12, height: 12, borderRadius: '50%', background: LL.gold,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <svg width="7" height="7" viewBox="0 0 10 10">
                        <path d="M5 0 L6.2 3.6 L10 3.8 L7 6 L8 9.8 L5 7.6 L2 9.8 L3 6 L0 3.8 L3.8 3.6 Z" fill={LL.navyDeep}/>
                      </svg>
                    </div>
                  )}
                </div>
              ))}
            </div>

            {/* Data rows */}
            {rounds.map((row, r) => (
              <div key={r} style={{
                display: 'grid', gridTemplateColumns: gridCols,
                borderTop: `1px solid rgba(23,70,141,0.18)`,
              }}>
                <div style={{
                  ...labelCellStyle,
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '8px 8px 8px 12px',
                }}>
                  <span style={{
                    fontFamily: LL.fontBody, fontSize: 12, fontWeight: 700,
                    color: LL.navyInk, letterSpacing: 0.2,
                  }}>Manche {r + 1}</span>
                  {isHost && (
                    <button
                      onClick={() => removeRound(r)}
                      aria-label={`Supprimer manche ${r + 1}`}
                      style={{
                        width: 18, height: 18, borderRadius: '50%',
                        background: 'transparent', border: 'none',
                        color: LL.muted, cursor: 'pointer',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        padding: 0, flexShrink: 0,
                      }}
                    >
                      <svg width="9" height="9" viewBox="0 0 10 10"><path d="M2 2 L8 8 M8 2 L2 8" stroke={LL.muted} strokeWidth="1.8" strokeLinecap="round"/></svg>
                    </button>
                  )}
                </div>
                {row.map((val, p) => (
                  <div key={p} style={dataCellStyle}>
                    <input
                      type="number" inputMode="numeric" pattern="[0-9]*"
                      value={val == null ? '' : val}
                      onChange={(e) => setCell(r, p, e.target.value)}
                      readOnly={!isHost}
                      disabled={!isHost}
                      placeholder="—"
                      style={{
                        width: '100%', height: '100%',
                        background: 'transparent', border: 'none', outline: 'none',
                        textAlign: 'center',
                        fontFamily: LL.fontMono, fontSize: 15, fontWeight: 600,
                        color: LL.navyInk,
                        cursor: isHost ? 'text' : 'default',
                        MozAppearance: 'textfield',
                        padding: '10px 2px',
                      }}
                    />
                  </div>
                ))}
              </div>
            ))}

            {/* Add round — only host can add */}
            {isHost && (
              <div style={{ borderTop: `1px solid rgba(23,70,141,0.18)` }}>
                <button onClick={addRound} style={{
                  width: '100%', padding: '10px 12px', border: 'none',
                  background: 'rgba(23,70,141,0.05)', color: LL.navy, cursor: 'pointer',
                  fontFamily: LL.fontBody, fontSize: 13, fontWeight: 700, letterSpacing: 0.2,
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                }}>
                  <svg width="12" height="12" viewBox="0 0 12 12">
                    <path d="M6 1 V11 M1 6 H11" stroke={LL.navy} strokeWidth="2" strokeLinecap="round"/>
                  </svg>
                  Ajouter une manche
                </button>
              </div>
            )}

            {/* Totals row */}
            <div style={{
              display: 'grid', gridTemplateColumns: gridCols,
              background: LL.navyDeep, color: LL.cream,
            }}>
              <div style={{
                ...headerCellStyle(true),
                textAlign: 'left', padding: '10px 12px',
                fontFamily: LL.fontDisplay, fontSize: 13, fontWeight: 800,
                letterSpacing: 0.4, textTransform: 'uppercase',
              }}>Total</div>
              {totals.map((t, i) => {
                const isLeader = leaders.includes(i);
                return (
                  <div key={i} style={{
                    ...headerCellStyle(false),
                    padding: '8px 2px',
                    background: isLeader ? LL.gold : 'transparent',
                    color: isLeader ? LL.navyDeep : LL.cream,
                  }}>
                    <div style={{
                      fontFamily: LL.fontMono, fontSize: 18, fontWeight: 800,
                    }}>{t}</div>
                  </div>
                );
              })}
            </div>
          </div>

          {/* Ambient music */}
          <div style={{ position: 'relative', marginBottom: 14 }}>
            <AudioPlayer src={game?.music_url} label={trackLabelFor(game?.music_url)} />
          </div>

          {isHost ? (
            <LLButton
              onClick={() => setEndConfirm(true)}
              size="lg"
              style={{ width: '100%', opacity: saving ? 0.6 : 1 }}
            >
              {saving ? 'Enregistrement…' : 'Fin de partie'}
            </LLButton>
          ) : (
            <div style={{
              textAlign: 'center', padding: 14,
              background: 'rgba(23,70,141,0.08)', borderRadius: 12,
              fontFamily: LL.fontBody, fontSize: 12.5, color: LL.mutedInk,
            }}>
              L'organisateur de la session gère les scores et la fin de partie.
            </div>
          )}
        </div>
      </ScreenBody>

      {endConfirm && (
        <ConfirmDialog
          title="Es-tu sûr que la partie est finie ?"
          message="L'application va calculer le gagnant et enregistrer les scores dans ton historique."
          confirmLabel="Oui, terminer"
          onConfirm={endGame}
          onCancel={() => setEndConfirm(false)}
        />
      )}

      {/* 2-step confirmation to quit a game in progress — prevents accidents */}
      {quitStep === 1 && (
        <ConfirmDialog
          title="Quitter la partie en cours ?"
          message="La partie est toujours active. Les scores en cours ne seront pas sauvegardés si tu quittes."
          confirmLabel="Je veux quitter"
          cancelLabel="Rester"
          danger
          onConfirm={() => setQuitStep(2)}
          onCancel={() => setQuitStep(0)}
        />
      )}
      {quitStep === 2 && (
        <ConfirmDialog
          title="Confirme une dernière fois"
          message="Tu vas quitter définitivement. Continue la partie pour enregistrer les scores."
          confirmLabel="Oui, quitter"
          cancelLabel="Annuler"
          danger
          onConfirm={() => { setQuitStep(0); onBack && onBack(); }}
          onCancel={() => setQuitStep(0)}
        />
      )}

      {celebration && (
        <WinnerCelebration
          game={game}
          winner={celebration.winner}
          ranking={celebration.ranking}
          duration={Math.round(elapsed / 60000)}
          onClose={onBack}
        />
      )}
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// WinnerCelebration — overlay confettis + classement final
// ──────────────────────────────────────────────────────────────
function WinnerCelebration({ game, winner, ranking, duration, onClose }) {
  const [scale, setScale] = React.useState(0.4);
  const [rating, setRating] = React.useState(0);
  const [rated, setRated] = React.useState(false);
  const [sending, setSending] = React.useState(false);
  const [souvenirOpen, setSouvenirOpen] = React.useState(false);
  React.useEffect(() => { const t = setTimeout(() => setScale(1), 30); return () => clearTimeout(t); }, []);

  async function submitRating() {
    if (rating < 1 || sending) return;
    setSending(true);
    try {
      await LLAPI.submitReview(game?.id, rating, '');
      setRated(true);
    } catch (err) {
      alert('Envoi échoué : ' + err.message);
    } finally { setSending(false); }
  }

  const confetti = React.useMemo(() => Array.from({ length: 50 }).map((_, i) => ({
    x: Math.random() * 100,
    delay: Math.random() * 700,
    dur: 1800 + Math.random() * 1400,
    color: [LL.gold, LL.red, LL.cream, LL.green, LL.navy][i % 5],
    rot: Math.random() * 360,
    shape: i % 3,
  })), []);

  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 300,
      background: `radial-gradient(circle at 50% 35%, #1D5AB8 0%, ${LL.navyDeep} 80%)`,
      display: 'flex', flexDirection: 'column',
      animation: 'll-fade-in .25s ease',
    }}>
      <style>{`
        @keyframes ll-confetti-fall {
          0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
          100% { transform: translateY(900px) rotate(720deg); opacity: 0; }
        }
        @keyframes ll-fade-in { from { opacity: 0 } to { opacity: 1 } }
      `}</style>

      {confetti.map((c, i) => (
        <div key={i} style={{
          position: 'absolute', left: `${c.x}%`, top: -20,
          width: c.shape === 0 ? 10 : 8,
          height: c.shape === 2 ? 4 : 12,
          background: c.color,
          borderRadius: c.shape === 1 ? '50%' : 2,
          transform: `rotate(${c.rot}deg)`,
          animation: `ll-confetti-fall ${c.dur}ms ${c.delay}ms linear forwards`,
          pointerEvents: 'none',
        }}/>
      ))}

      <div style={{
        flex: 1, display: 'flex', flexDirection: 'column',
        alignItems: 'center', justifyContent: 'center',
        padding: '40px 24px', textAlign: 'center',
      }}>
        <div style={{
          fontFamily: LL.fontBody, fontSize: 11, letterSpacing: 3,
          textTransform: 'uppercase', fontWeight: 600,
          color: 'rgba(242,229,202,0.7)', marginBottom: 4,
        }}>
          {game?.name || ''} · Partie terminée
        </div>
        <div style={{
          fontFamily: LL.fontDisplay, fontSize: 42, fontWeight: 900,
          color: LL.cream, lineHeight: 1, letterSpacing: -1,
          textShadow: '0 4px 20px rgba(0,0,0,0.4)',
          transform: `scale(${scale})`, transition: 'transform .55s cubic-bezier(.18,1.4,.4,1)',
          marginBottom: 6,
        }}>BRAVO !</div>
        <div style={{
          fontFamily: LL.fontDisplay, fontSize: 22, fontWeight: 700,
          color: LL.gold, marginBottom: 24,
        }}>
          {winner?.name || ''} l'emporte
        </div>

        {/* Trophy + winner avatar */}
        <div style={{
          width: 120, height: 120, borderRadius: '50%',
          background: winner?.color || LL.cream, color: LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: LL.fontDisplay, fontSize: 56, fontWeight: 900,
          border: `4px solid ${LL.gold}`,
          boxShadow: `0 0 40px ${LL.gold}, 0 8px 20px rgba(0,0,0,0.4)`,
          transform: `scale(${scale})`,
          transition: 'transform .55s cubic-bezier(.18,1.4,.4,1)',
          marginBottom: 30,
        }}>
          {(winner?.name || '?')[0]}
        </div>

        {/* Ranking */}
        <div style={{
          width: '100%', maxWidth: 320,
          background: 'rgba(242,229,202,0.08)', borderRadius: 18,
          border: `1px solid rgba(242,229,202,0.18)`,
          padding: 14, marginBottom: 20,
        }}>
          <div style={{
            fontFamily: LL.fontBody, fontSize: 10, letterSpacing: 1.6,
            textTransform: 'uppercase', fontWeight: 700,
            color: 'rgba(242,229,202,0.6)', marginBottom: 8, textAlign: 'left',
          }}>Classement</div>
          {ranking.map((r, i) => (
            <div key={r.name} style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '8px 4px',
              borderTop: i > 0 ? `1px solid rgba(242,229,202,0.12)` : 'none',
            }}>
              <div style={{
                width: 22, height: 22, borderRadius: '50%',
                background: i === 0 ? LL.gold : 'rgba(242,229,202,0.18)',
                color: i === 0 ? LL.navyDeep : LL.cream,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: LL.fontDisplay, fontSize: 12, fontWeight: 800,
                flexShrink: 0,
              }}>{i + 1}</div>
              <div style={{
                width: 26, height: 26, borderRadius: '50%',
                background: r.color, color: LL.cream,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: LL.fontDisplay, fontSize: 12, fontWeight: 800,
                flexShrink: 0,
              }}>{r.name[0]}</div>
              <div style={{
                flex: 1, fontFamily: LL.fontBody, fontSize: 14, fontWeight: 600,
                color: LL.cream, textAlign: 'left',
              }}>{r.name}</div>
              <div style={{
                fontFamily: LL.fontMono, fontSize: 16, fontWeight: 700,
                color: i === 0 ? LL.gold : LL.cream,
              }}>{r.total}</div>
            </div>
          ))}
        </div>

        {duration > 0 && (
          <div style={{
            fontFamily: LL.fontBody, fontSize: 12,
            color: 'rgba(242,229,202,0.65)', marginBottom: 18,
          }}>
            Durée de la partie : {duration} min
          </div>
        )}

        {/* 5-star rating prompt */}
        <div style={{
          width: '100%', maxWidth: 320, marginBottom: 14,
          padding: '14px 16px', borderRadius: 16,
          background: 'rgba(242,229,202,0.08)', border: `1px solid rgba(242,229,202,0.18)`,
          textAlign: 'center',
        }}>
          {rated ? (
            <div style={{ color: LL.cream, fontFamily: LL.fontBody, fontSize: 13, padding: '6px 0' }}>
              Merci pour ta note !
            </div>
          ) : (
            <>
              <div style={{
                fontFamily: LL.fontBody, fontSize: 11, letterSpacing: 1.4,
                textTransform: 'uppercase', fontWeight: 700,
                color: 'rgba(242,229,202,0.7)', marginBottom: 8,
              }}>Note le jeu</div>
              <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 10 }}>
                <StarsInput value={rating} onChange={setRating} size={32} />
              </div>
              <button onClick={submitRating} disabled={rating < 1 || sending} style={{
                padding: '8px 18px', borderRadius: 999,
                background: rating < 1 ? 'rgba(242,229,202,0.15)' : LL.gold,
                color: rating < 1 ? 'rgba(242,229,202,0.5)' : LL.navyDeep,
                border: 'none', cursor: rating < 1 ? 'default' : 'pointer',
                fontFamily: LL.fontBody, fontWeight: 700, fontSize: 13,
              }}>
                {sending ? 'Envoi…' : 'Envoyer la note'}
              </button>
            </>
          )}
        </div>

        <div style={{
          width: '100%', maxWidth: 320,
          display: 'flex', flexDirection: 'column', gap: 10,
        }}>
          <button onClick={() => setSouvenirOpen(true)} style={{
            width: '100%', height: 52, borderRadius: 16,
            background: LL.gold, color: LL.navyDeep, border: 'none', cursor: 'pointer',
            fontFamily: LL.fontBody, fontWeight: 700, fontSize: 15, letterSpacing: 0.3,
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            boxShadow: '0 6px 16px rgba(226,177,58,0.35)',
          }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
              <path d="M3 8 h5 l2 -3 h4 l2 3 h5 a1 1 0 0 1 1 1 v11 a1 1 0 0 1 -1 1 H3 a1 1 0 0 1 -1 -1 V9 a1 1 0 0 1 1 -1 z"
                stroke={LL.navyDeep} strokeWidth="1.8" fill="none"/>
              <circle cx="12" cy="13" r="4" stroke={LL.navyDeep} strokeWidth="1.8" fill="none"/>
            </svg>
            Photo souvenir
          </button>
          <LLButton onClick={onClose} size="lg" style={{
            width: '100%', background: LL.cream, color: LL.navyDeep,
          }}>
            Retour
          </LLButton>
        </div>
      </div>

      {souvenirOpen && (
        <SouvenirSheet
          game={game} winner={winner} ranking={ranking} duration={duration}
          onClose={() => setSouvenirOpen(false)}
        />
      )}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// SouvenirSheet — photo + overlay shareable Insta/WhatsApp/Facebook
// ──────────────────────────────────────────────────────────────
function SouvenirSheet({ game, winner, ranking, duration, onClose }) {
  const [photoUrl, setPhotoUrl] = React.useState(null);    // raw uploaded photo (object URL)
  const [composedUrl, setComposedUrl] = React.useState(null); // composed final image (object URL)
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState(null);
  const cameraRef = React.useRef(null);
  const galleryRef = React.useRef(null);

  // Cleanup
  React.useEffect(() => () => {
    if (photoUrl) URL.revokeObjectURL(photoUrl);
    if (composedUrl) URL.revokeObjectURL(composedUrl);
  }, [photoUrl, composedUrl]);

  function pickPhoto(file) {
    if (!file) return;
    if (photoUrl) URL.revokeObjectURL(photoUrl);
    if (composedUrl) URL.revokeObjectURL(composedUrl);
    setComposedUrl(null);
    setError(null);
    setPhotoUrl(URL.createObjectURL(file));
  }

  // Compose the final image when a photo is selected
  React.useEffect(() => {
    if (!photoUrl) return;
    setBusy(true);
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onerror = () => { setError('Impossible de charger la photo'); setBusy(false); };
    img.onload = () => {
      try {
        const canvas = document.createElement('canvas');
        const W = 1080, H = 1350;            // Instagram portrait 4:5
        canvas.width = W; canvas.height = H;
        const ctx = canvas.getContext('2d');

        // BG photo, cover-fit + slight darkening for legibility
        const ratio = Math.max(W / img.width, H / img.height);
        const dw = img.width * ratio;
        const dh = img.height * ratio;
        ctx.drawImage(img, (W - dw) / 2, (H - dh) / 2, dw, dh);
        // Subtle warm wash over the whole image
        ctx.fillStyle = 'rgba(14,47,99,0.18)';
        ctx.fillRect(0, 0, W, H);

        // Top gradient (game name + date)
        const topG = ctx.createLinearGradient(0, 0, 0, 280);
        topG.addColorStop(0, 'rgba(14,47,99,0.85)');
        topG.addColorStop(1, 'rgba(14,47,99,0)');
        ctx.fillStyle = topG;
        ctx.fillRect(0, 0, W, 280);

        // Bottom gradient (winner panel)
        const bottomStart = H - 480;
        const botG = ctx.createLinearGradient(0, bottomStart, 0, H);
        botG.addColorStop(0, 'rgba(14,47,99,0)');
        botG.addColorStop(0.45, 'rgba(14,47,99,0.85)');
        botG.addColorStop(1, 'rgba(14,47,99,0.97)');
        ctx.fillStyle = botG;
        ctx.fillRect(0, bottomStart, W, 480);

        // Top-left: game name + date
        ctx.fillStyle = '#F2E5CA';
        ctx.font = 'bold 44px "Space Grotesk", system-ui, sans-serif';
        ctx.textAlign = 'left';
        ctx.fillText((game?.name || '').toUpperCase(), 60, 110);
        ctx.font = '500 26px "Space Grotesk", system-ui, sans-serif';
        ctx.fillStyle = 'rgba(242,229,202,0.7)';
        const date = new Date().toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' });
        const subtitle = duration > 0 ? `${date} · ${duration} min` : date;
        ctx.fillText(subtitle, 60, 150);

        // BRAVO badge
        ctx.fillStyle = '#E2B13A';
        ctx.font = 'bold 26px "Space Grotesk", system-ui, sans-serif';
        ctx.textAlign = 'left';
        ctx.fillText('VAINQUEUR', 60, H - 360);

        // Winner avatar (left) + name + score (right)
        const cx = 130, cy = H - 260;
        ctx.beginPath();
        ctx.arc(cx, cy, 84, 0, Math.PI * 2);
        ctx.fillStyle = winner?.color || '#17468D';
        ctx.fill();
        ctx.lineWidth = 8;
        ctx.strokeStyle = '#E2B13A';
        ctx.stroke();
        ctx.fillStyle = '#F2E5CA';
        ctx.font = 'bold 80px "Space Grotesk", system-ui, sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText((winner?.name || '?')[0], cx, cy + 4);
        ctx.textBaseline = 'alphabetic';

        ctx.textAlign = 'left';
        ctx.fillStyle = '#F2E5CA';
        ctx.font = 'bold 64px "Space Grotesk", system-ui, sans-serif';
        ctx.fillText(winner?.name || '', 240, H - 280);
        ctx.font = 'bold 56px "JetBrains Mono", monospace';
        ctx.fillStyle = '#E2B13A';
        const winnerScore = ranking?.[0]?.total ?? 0;
        ctx.fillText(`${winnerScore} pts`, 240, H - 215);

        // Mini ranking strip (positions 2 & 3)
        const others = (ranking || []).slice(1, 3);
        let oy = H - 145;
        ctx.font = '500 24px "Space Grotesk", system-ui, sans-serif';
        ctx.fillStyle = 'rgba(242,229,202,0.85)';
        others.forEach((r, i) => {
          ctx.fillStyle = '#F2E5CA';
          ctx.fillText(`${i + 2}.  ${r.name}`, 60, oy);
          ctx.textAlign = 'right';
          ctx.fillText(`${r.total} pts`, W - 60, oy);
          ctx.textAlign = 'left';
          oy += 36;
        });

        // Watermark bottom-right
        ctx.fillStyle = 'rgba(242,229,202,0.55)';
        ctx.font = 'bold 22px "Space Grotesk", system-ui, sans-serif';
        ctx.textAlign = 'right';
        ctx.fillText('ludilove', W - 60, H - 38);

        canvas.toBlob((blob) => {
          if (!blob) { setError('Échec composition'); setBusy(false); return; }
          if (composedUrl) URL.revokeObjectURL(composedUrl);
          setComposedUrl(URL.createObjectURL(blob));
          setBusy(false);
        }, 'image/jpeg', 0.92);
      } catch (err) {
        setError(err.message);
        setBusy(false);
      }
    };
    img.src = photoUrl;
  }, [photoUrl, game, winner, ranking, duration]);

  async function shareImage() {
    if (!composedUrl) return;
    try {
      const blob = await fetch(composedUrl).then(r => r.blob());
      const file = new File([blob], `ludilove-${(game?.id || 'partie')}.jpg`, { type: 'image/jpeg' });
      const data = {
        files: [file],
        title: `${winner?.name} gagne à ${game?.name} !`,
        text: `${winner?.name} remporte ${ranking?.[0]?.total ?? 0} pts à ${game?.name} #ludilove`,
      };
      if (navigator.canShare && navigator.canShare(data)) {
        await navigator.share(data);
      } else if (navigator.share) {
        await navigator.share({ title: data.title, text: data.text });
      } else {
        downloadImage();
      }
    } catch (err) {
      if (err.name !== 'AbortError') console.warn('share:', err);
    }
  }

  const [published, setPublished] = React.useState(false);
  const [publishing, setPublishing] = React.useState(false);
  async function publishToFeed() {
    if (!composedUrl || publishing) return;
    setPublishing(true);
    try {
      const blob = await fetch(composedUrl).then(r => r.blob());
      const file = new File([blob], `souvenir-${(game?.id || 'partie')}.jpg`, { type: 'image/jpeg' });
      await LLAPI.publishPost(file, {
        game_id:      game?.id,
        winner:       winner?.name,
        winner_score: ranking?.[0]?.total ?? 0,
        players:      ranking?.length,
        duration,
      });
      setPublished(true);
    } catch (err) {
      alert('Publication échouée : ' + err.message);
    } finally { setPublishing(false); }
  }

  function downloadImage() {
    if (!composedUrl) return;
    const a = document.createElement('a');
    a.href = composedUrl;
    a.download = `ludilove-${(game?.id || 'partie')}-${Date.now()}.jpg`;
    document.body.appendChild(a);
    a.click();
    a.remove();
  }

  function reset() {
    if (photoUrl) URL.revokeObjectURL(photoUrl);
    if (composedUrl) URL.revokeObjectURL(composedUrl);
    setPhotoUrl(null); setComposedUrl(null); setError(null);
  }

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 400,
      background: 'rgba(14,47,99,0.85)', backdropFilter: 'blur(6px)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      animation: 'll-fade-in .2s ease',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: LL.cream, width: '100%', maxWidth: 480,
        borderTopLeftRadius: 28, borderTopRightRadius: 28,
        border: `2px solid ${LL.navy}`, borderBottom: 'none',
        padding: '18px 22px 28px', maxHeight: '92%', overflowY: 'auto',
        animation: 'll-slide-up .25s cubic-bezier(.2,.8,.2,1)',
      }}>
        <div style={{ width: 44, height: 4, background: 'rgba(23,70,141,0.22)', borderRadius: 2, margin: '0 auto 14px' }}/>
        <h2 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 22,
          color: LL.navyInk, margin: '0 0 4px',
        }}>Photo souvenir</h2>
        <p style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, margin: '0 0 16px' }}>
          Capture le moment, partage sur Instagram, WhatsApp ou Facebook.
        </p>

        {/* Picker */}
        {!photoUrl && (
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 }}>
            <ScanActionCard icon={<CameraIcon/>} title="Caméra" subtitle="Prendre la photo"
              onClick={() => cameraRef.current && cameraRef.current.click()}/>
            <ScanActionCard icon={<GalleryIcon/>} title="Galerie" subtitle="Choisir une photo"
              onClick={() => galleryRef.current && galleryRef.current.click()}/>
          </div>
        )}

        {/* Composing / preview */}
        {photoUrl && (
          <div style={{
            border: `2px solid ${LL.navy}`, borderRadius: 18,
            overflow: 'hidden', marginBottom: 14, position: 'relative',
            aspectRatio: '4 / 5', background: '#000',
          }}>
            {composedUrl
              ? <img src={composedUrl} alt="Souvenir" style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }}/>
              : <img src={photoUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>}
            {busy && (
              <div style={{
                position: 'absolute', inset: 0, background: 'rgba(14,47,99,0.5)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                color: LL.cream, fontFamily: LL.fontBody, fontSize: 14, fontWeight: 600,
              }}>
                Composition…
              </div>
            )}
          </div>
        )}

        {error && (
          <div style={{
            padding: 12, borderRadius: 12, marginBottom: 12,
            background: 'rgba(201,74,58,0.1)', border: `1px solid ${LL.red}`,
            color: LL.red, fontFamily: LL.fontBody, fontSize: 13,
          }}>{error}</div>
        )}

        {/* Actions */}
        {composedUrl && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <LLButton
              onClick={publishToFeed}
              size="lg"
              style={{
                width: '100%',
                background: published ? LL.green : LL.gold,
                color: LL.navyDeep, opacity: publishing ? 0.6 : 1,
              }}
            >
              {published ? '✓ Publié sur Ludilove' : (publishing ? 'Publication…' : 'Publier sur Ludilove')}
            </LLButton>
            <LLButton onClick={shareImage} size="md" style={{ width: '100%' }}>
              Partager (Insta, WhatsApp…)
            </LLButton>
            <div style={{ display: 'flex', gap: 8 }}>
              <LLButton onClick={downloadImage} variant="outline" size="md" style={{ flex: 1 }}>
                Télécharger
              </LLButton>
              <LLButton onClick={reset} variant="ghost" size="md" style={{ flex: 1 }}>
                Reprendre
              </LLButton>
            </div>
          </div>
        )}

        {!composedUrl && photoUrl && !busy && (
          <LLButton onClick={reset} variant="outline" size="md" style={{ width: '100%' }}>
            Annuler
          </LLButton>
        )}

        {!photoUrl && (
          <LLButton onClick={onClose} variant="ghost" size="md" style={{ width: '100%' }}>
            Fermer
          </LLButton>
        )}

        <input ref={cameraRef} type="file" accept="image/*" capture="environment"
          style={{ display: 'none' }}
          onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) pickPhoto(f); e.target.value = ''; }}/>
        <input ref={galleryRef} type="file" accept="image/*"
          style={{ display: 'none' }}
          onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) pickPhoto(f); e.target.value = ''; }}/>
      </div>
    </div>
  );
}

function headerCellStyle(isLabel) {
  return {
    padding: '8px 4px', textAlign: 'center',
    borderLeft: isLabel ? 'none' : `1px solid rgba(242,229,202,0.18)`,
    display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
  };
}
const labelCellStyle = {
  borderRight: `1px solid rgba(23,70,141,0.18)`,
  background: 'rgba(23,70,141,0.03)',
};
const dataCellStyle = {
  borderLeft: `1px solid rgba(23,70,141,0.12)`,
  display: 'flex', alignItems: 'stretch', padding: 0,
};

function ClockIcon() {
  return (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
      <circle cx="12" cy="12" r="9" stroke={LL.cream} strokeWidth="2"/>
      <path d="M12 6 V12 L16 14" stroke={LL.cream} strokeWidth="2" strokeLinecap="round"/>
    </svg>
  );
}

function TrophyIcon2() {
  return (
    <svg width="52" height="52" viewBox="0 0 24 24" fill="none">
      <path d="M7 4 h10 v4 a5 5 0 0 1 -10 0 z" fill={LL.gold}/>
      <path d="M7 5 Q 3 5 3 8 Q 3 11 7 10" stroke={LL.gold} strokeWidth="1.6" fill="none"/>
      <path d="M17 5 Q 21 5 21 8 Q 21 11 17 10" stroke={LL.gold} strokeWidth="1.6" fill="none"/>
      <rect x="10.5" y="12" width="3" height="4" fill={LL.gold}/>
      <rect x="7" y="16" width="10" height="2.5" rx="1" fill={LL.gold}/>
      <rect x="5" y="18.5" width="14" height="2" rx="1" fill={LL.gold}/>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// VIDEO SCREEN — embed YouTube tutorial in-app via youtube-nocookie iframe
// ──────────────────────────────────────────────────────────────
function VideoScreen({ game, onBack }) {
  const id = game?.tutorial_video_id;
  const searchUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent('comment jouer à ' + (game?.name || '') + ' tutoriel français')}`;

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} title="Tutoriel vidéo" />
      <ScreenBody padTop={44} padBottom={30}>
        <div style={{ padding: '0 22px' }}>
          <div style={{
            fontFamily: LL.fontBody, fontSize: 11, letterSpacing: 1.6,
            color: LL.muted, fontWeight: 600, textTransform: 'uppercase', marginBottom: 4,
          }}>Tutoriel · {game?.name || 'Jeu'}</div>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontSize: 26, fontWeight: 800,
            color: LL.navyInk, margin: '0 0 16px',
          }}>Apprendre les règles en vidéo</h1>

          {id ? (
            <div style={{
              borderRadius: 18, overflow: 'hidden',
              border: `2px solid ${LL.navy}`,
              background: '#000',
              aspectRatio: '16 / 9', marginBottom: 16,
              boxShadow: '0 6px 18px rgba(14,47,99,0.25)',
            }}>
              <iframe
                title={`Tutoriel ${game?.name}`}
                src={`https://www.youtube-nocookie.com/embed/${id}?rel=0&modestbranding=1&playsinline=1`}
                width="100%" height="100%"
                style={{ border: 'none', display: 'block' }}
                allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                allowFullScreen
              />
            </div>
          ) : (
            <FoldedCard fold={20} color={LL.navy} bg={LL.cream} stroke={2} radius={18}
              style={{ padding: 18, marginBottom: 16, textAlign: 'center' }}>
              <div style={{
                fontFamily: LL.fontBody, fontSize: 14, color: LL.navyInk, lineHeight: 1.5, marginBottom: 14,
              }}>
                Pas de tutoriel pré-sélectionné pour ce jeu.<br/>
                Lance une recherche YouTube directement.
              </div>
              <LLButton onClick={() => window.open(searchUrl, '_blank', 'noopener')} size="md" style={{ width: '100%' }}>
                Chercher sur YouTube
              </LLButton>
            </FoldedCard>
          )}

          <div style={{
            fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, marginBottom: 14,
            lineHeight: 1.5,
          }}>
            La vidéo est jouée sur YouTube via le mode confidentialité renforcée.
            Aucun cookie de tracking n'est posé tant que vous ne lancez pas la lecture.
          </div>

          {id && (
            <LLButton onClick={() => window.open(`https://www.youtube.com/watch?v=${id}`, '_blank', 'noopener')}
              variant="outline" size="md" style={{ width: '100%' }}>
              Ouvrir sur YouTube
            </LLButton>
          )}
        </div>
      </ScreenBody>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// FEED SCREEN — Instagram-like : header + stories row + posts
// Palette conservée : navy/cream. Anneau de story = dégradé navy→red.
// ──────────────────────────────────────────────────────────────
function FeedScreen({ onBack, onNav, onNotifications, onMessages, onStartWithIdea, onStartSwipe }) {
  const [posts, setPosts] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [selectedPost, setSelectedPost] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  // Page d'accueil (nav home) : pas de bouton retour si onNav est fourni
  const isHomeMode = !!onNav;

  async function load() {
    setLoading(true);
    try {
      const r = await LLAPI.feed(30);
      setPosts(r.posts || []);
    } catch (err) {
      console.warn('feed:', err);
      setPosts([]);
    } finally { setLoading(false); }
  }
  React.useEffect(() => { load(); }, []);

  function flash(msg) { setToast(msg); setTimeout(() => setToast(null), 2200); }

  async function toggleLike(postId) {
    setPosts(arr => arr.map(p => p.id === postId
      ? { ...p, my_like: !p.my_like, likes_count: p.likes_count + (p.my_like ? -1 : 1) }
      : p));
    try { await LLAPI.likePost(postId); }
    catch {
      flash('Erreur — réessaye');
      setPosts(arr => arr.map(p => p.id === postId
        ? { ...p, my_like: !p.my_like, likes_count: p.likes_count + (p.my_like ? -1 : 1) }
        : p));
    }
  }

  return (
    <Screen>
      <StatusBar />
      {/* Insta-style header — cloche (notifs) gauche · logo centre · chat droite */}
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0, zIndex: 5,
        background: LL.cream, borderBottom: `1px solid rgba(23,70,141,0.12)`,
        paddingTop: 'calc(env(safe-area-inset-top) + 10px)',
        paddingBottom: 10, paddingLeft: 16, paddingRight: 16,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        {isHomeMode ? (
          <button onClick={onNotifications} aria-label="Notifications" style={{
            background: 'transparent', border: 'none', cursor: 'pointer',
            padding: 4, color: LL.navyInk, display: 'flex', position: 'relative',
          }}>
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={LL.navyInk} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
              <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
            </svg>
          </button>
        ) : (
          <button onClick={onBack} aria-label="Retour" style={{
            background: 'transparent', border: 'none', cursor: 'pointer',
            padding: 4, color: LL.navyInk, display: 'flex',
          }}>
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 18l-6-6 6-6"/>
            </svg>
          </button>
        )}
        <div style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 22,
          color: LL.navyInk, letterSpacing: -0.5,
        }}>ludilove</div>
        {isHomeMode ? (
          <button onClick={onMessages} aria-label="Messages" style={{
            background: 'transparent', border: 'none', cursor: 'pointer',
            padding: 4, color: LL.navyInk, display: 'flex',
          }}>
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={LL.navyInk} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
            </svg>
          </button>
        ) : (
          <div style={{ width: 30 }} />
        )}
      </div>

      <ScreenBody padTop={64} padBottom={isHomeMode ? 110 : 20}>
        {/* CTA grand — lancer une nouvelle session (home uniquement) */}
        {isHomeMode && (
          <div style={{
            margin: '14px 16px 20px', padding: '22px 20px',
            background: LL.navyDeep, color: LL.cream, borderRadius: 24,
            boxShadow: '0 8px 22px rgba(14,47,99,0.25)',
          }}>
            <div style={{
              fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 26,
              lineHeight: 1.15, letterSpacing: -0.3, marginBottom: 16,
            }}>
              Lancer un nouveau jeu !
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <button onClick={onStartWithIdea} style={{
                width: '100%', height: 52, borderRadius: 14,
                background: LL.cream, color: LL.navyDeep, border: 'none', cursor: 'pointer',
                fontFamily: LL.fontBody, fontSize: 15, fontWeight: 700,
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
              }}>
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={LL.navyDeep} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M9 18h6M10 22h4M12 2a7 7 0 0 0-4 12.74V17a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-2.26A7 7 0 0 0 12 2z"/>
                </svg>
                J'ai une idée de jeu
              </button>
              <button onClick={onStartSwipe} style={{
                width: '100%', height: 52, borderRadius: 14,
                background: 'transparent', color: LL.cream,
                border: `2px solid ${LL.cream}`, cursor: 'pointer',
                fontFamily: LL.fontBody, fontSize: 15, fontWeight: 700,
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
              }}>
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={LL.cream} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="12" cy="12" r="10"/>
                  <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3M12 17h.01"/>
                </svg>
                Je n'ai pas d'idée de jeu
              </button>
            </div>
          </div>
        )}

        {/* Posts */}
        {loading && (
          <div style={{ textAlign: 'center', color: LL.muted, padding: 40 }}>Chargement…</div>
        )}
        {!loading && posts && posts.length === 0 && (
          <div style={{
            margin: '16px 18px', padding: 24, textAlign: 'center',
            background: 'rgba(23,70,141,0.06)', borderRadius: 16,
            fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk, lineHeight: 1.5,
          }}>
            Aucun souvenir pour le moment.<br/>Appuie sur <b>＋</b> en haut à droite pour publier le tien.
          </div>
        )}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          {(posts || []).map(p => (
            <FeedPostCard key={p.id} post={p} onLike={() => toggleLike(p.id)} />
          ))}
        </div>
      </ScreenBody>

      {selectedPost && (
        <FeedPostDetail
          post={selectedPost}
          onClose={() => setSelectedPost(null)}
          onLike={() => { toggleLike(selectedPost.id); setSelectedPost(p => ({ ...p, my_like: !p.my_like, likes_count: p.likes_count + (p.my_like ? -1 : 1) })); }}
        />
      )}
      {toast && (
        <div style={{
          position: 'absolute', bottom: 28, left: '50%', transform: 'translateX(-50%)',
          background: LL.navyDeep, color: LL.cream,
          padding: '10px 18px', borderRadius: 999, zIndex: 300,
          fontFamily: LL.fontBody, fontSize: 13, fontWeight: 600,
          boxShadow: '0 6px 18px rgba(0,0,0,0.3)',
        }}>{toast}</div>
      )}

      {/* Nav du bas - home uniquement */}
      {isHomeMode && <BottomNav current="home" onNav={onNav} />}
    </Screen>
  );
}

function FeedPostCard({ post, onLike }) {
  const author = post.author || {};
  const date = new Date(post.created_at);
  const ago = relativeTime(date);
  const [showAllCaption, setShowAllCaption] = React.useState(false);
  const captionTooLong = (post.caption || '').length > 120;

  function handleDouble() {
    if (!post.my_like) onLike();
  }

  return (
    <div style={{
      background: LL.cream,
      borderTop: `1px solid rgba(23,70,141,0.12)`,
      borderBottom: `1px solid rgba(23,70,141,0.12)`,
      paddingBottom: 6,
    }}>
      {/* Author row */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: '10px 14px',
      }}>
        <div style={{
          width: 34, height: 34, borderRadius: '50%',
          background: author.avatar_color || LL.navy, color: LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: LL.fontDisplay, fontSize: 14, fontWeight: 800,
          overflow: 'hidden', flexShrink: 0,
        }}>
          {author.avatar_url
            ? <img src={author.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
            : (author.name || '?')[0]}
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            fontFamily: LL.fontBody, fontWeight: 700, fontSize: 13.5,
            color: LL.navyInk, lineHeight: 1.2,
          }}>{author.name}</div>
          <div style={{ fontFamily: LL.fontBody, fontSize: 11.5, color: LL.muted, marginTop: 1 }}>
            {post.game?.name || 'Partie'}
          </div>
        </div>
        <button aria-label="Plus" style={{
          background: 'transparent', border: 'none', cursor: 'pointer',
          color: LL.navyInk, padding: 4, display: 'flex',
        }}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
            <circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/>
          </svg>
        </button>
      </div>

      {/* Image — 4:5 portrait (format photo souvenir) */}
      <div
        onDoubleClick={handleDouble}
        style={{ background: LL.navyDeep, aspectRatio: '4 / 5', overflow: 'hidden', cursor: 'pointer' }}
      >
        <img src={post.image_url} alt="souvenir" style={{
          width: '100%', height: '100%', objectFit: 'cover', display: 'block',
        }}/>
      </div>

      {/* Action bar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 14,
        padding: '8px 12px 2px',
      }}>
        <button onClick={onLike} aria-label="J'aime" style={{
          background: 'transparent', border: 'none', cursor: 'pointer',
          padding: 4, display: 'flex', color: post.my_like ? LL.red : LL.navyInk,
        }}>
          <svg width="26" height="26" viewBox="0 0 24 24"
            fill={post.my_like ? LL.red : 'none'}
            stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
          </svg>
        </button>
      </div>

      {/* Likes count */}
      <div style={{
        padding: '2px 14px 2px',
        fontFamily: LL.fontBody, fontSize: 13, fontWeight: 700, color: LL.navyInk,
      }}>
        {post.likes_count} j'aime{post.likes_count > 1 ? 's' : ''}
      </div>

      {/* Caption (author + text, inline) */}
      {(post.caption || post.winner) && (
        <div style={{
          padding: '2px 14px',
          fontFamily: LL.fontBody, fontSize: 13, color: LL.navyInk, lineHeight: 1.4,
        }}>
          <span style={{ fontWeight: 700 }}>{author.name}</span>{' '}
          {post.winner && (
            <span style={{ fontWeight: 600 }}>{post.winner}{post.winner_score != null ? ` · ${post.winner_score} pts` : ''}{post.caption ? ' — ' : ''}</span>
          )}
          {post.caption && (!captionTooLong || showAllCaption
            ? post.caption
            : (
              <>
                {post.caption.slice(0, 120)}…{' '}
                <button onClick={() => setShowAllCaption(true)} style={{
                  background: 'transparent', border: 'none', cursor: 'pointer',
                  padding: 0, color: LL.muted, fontFamily: LL.fontBody, fontSize: 13,
                }}>plus</button>
              </>
            ))}
        </div>
      )}

      {/* Time */}
      <div style={{
        padding: '4px 14px 8px',
        fontFamily: LL.fontBody, fontSize: 10.5, color: LL.muted,
        textTransform: 'uppercase', letterSpacing: 0.5,
      }}>{ago}</div>
    </div>
  );
}

// Tuile grille 2 colonnes — portrait 4:5, overlay likes
function FeedGridTile({ post, onClick }) {
  const author = post.author || {};
  return (
    <button onClick={onClick} style={{
      position: 'relative', aspectRatio: '4 / 5',
      background: LL.navyDeep, border: 'none', padding: 0,
      cursor: 'pointer', overflow: 'hidden', display: 'block', width: '100%',
    }}>
      <img src={post.image_url} alt="souvenir" style={{
        width: '100%', height: '100%', objectFit: 'cover', display: 'block',
      }}/>
      {/* Overlay bas : avatar auteur + likes */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0,
        background: 'linear-gradient(transparent, rgba(14,47,99,0.72))',
        padding: '18px 8px 8px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <div style={{
          width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
          background: author.avatar_color || LL.navy, color: LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 11,
          border: '1.5px solid rgba(242,229,202,0.6)', overflow: 'hidden',
        }}>
          {author.avatar_url
            ? <img src={author.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
            : (author.name || '?')[0]}
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 4,
          fontFamily: LL.fontBody, fontSize: 12, fontWeight: 700, color: LL.cream,
        }}>
          <svg width="13" height="13" viewBox="0 0 24 24"
            fill={post.my_like ? LL.red : 'rgba(242,229,202,0.8)'}
            stroke="none">
            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
          </svg>
          {post.likes_count > 0 && post.likes_count}
        </div>
      </div>
      {/* Jeu en petit badge haut */}
      {post.game?.name && (
        <div style={{
          position: 'absolute', top: 7, left: 7, right: 7,
          fontFamily: LL.fontBody, fontSize: 10, fontWeight: 700,
          color: 'rgba(242,229,202,0.9)', textTransform: 'uppercase',
          letterSpacing: 0.4, overflow: 'hidden', whiteSpace: 'nowrap',
          textOverflow: 'ellipsis', textShadow: '0 1px 3px rgba(0,0,0,0.5)',
        }}>{post.game.name}</div>
      )}
    </button>
  );
}

// Modal de détail d'un post — slide depuis le bas
function FeedPostDetail({ post, onClose, onLike }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, background: 'rgba(14,47,99,0.55)',
      zIndex: 200, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
    }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{
        background: LL.cream, borderRadius: '20px 20px 0 0',
        maxHeight: '92%', overflowY: 'auto',
        boxShadow: '0 -8px 32px rgba(14,47,99,0.18)',
      }}>
        {/* Handle */}
        <div style={{ display: 'flex', justifyContent: 'center', padding: '10px 0 4px' }}>
          <div style={{ width: 40, height: 4, borderRadius: 99, background: 'rgba(23,70,141,0.2)' }}/>
        </div>
        <FeedPostCard post={post} onLike={onLike} />
      </div>
    </div>
  );
}


function relativeTime(d) {
  const ms = Date.now() - d.getTime();
  const m = Math.floor(ms / 60000);
  if (m < 1) return "à l'instant";
  if (m < 60) return `il y a ${m} min`;
  const h = Math.floor(m / 60);
  if (h < 24) return `il y a ${h} h`;
  const days = Math.floor(h / 24);
  if (days < 7) return `il y a ${days} j`;
  return d.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
}

// Page Abonnés / Abonnements — liste avec boutons "Suivre / Abonné"
function FollowListScreen({ mode, userId, onBack }) {
  const [users, setUsers] = React.useState(null);
  const [subState, setSubState] = React.useState({});
  const title = mode === 'followers' ? 'Abonnés' : 'Abonnements';

  React.useEffect(() => {
    (async () => {
      try {
        const r = mode === 'followers' ? await LLAPI.followers(userId) : await LLAPI.following(userId);
        setUsers(r.users || []);
        const seed = {};
        (r.users || []).forEach(u => { seed[u.id] = !!u.is_subscribed; });
        setSubState(seed);
      } catch (err) { console.warn(mode, err); setUsers([]); }
    })();
  }, [mode, userId]);

  async function toggleFollow(id) {
    setSubState(s => ({ ...s, [id]: !s[id] }));
    try { await LLAPI.toggleSubscribe(id); }
    catch { setSubState(s => ({ ...s, [id]: !s[id] })); }
  }

  return (
    <Screen>
      <StatusBar/>
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0, zIndex: 5,
        background: LL.cream, borderBottom: `1px solid rgba(23,70,141,0.12)`,
        paddingTop: 'calc(env(safe-area-inset-top) + 10px)',
        paddingBottom: 10, paddingLeft: 16, paddingRight: 16,
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <button onClick={onBack} aria-label="Retour" style={{
          background: 'transparent', border: 'none', cursor: 'pointer',
          padding: 4, color: LL.navyInk, display: 'flex',
        }}>
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
        </button>
        <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 20, color: LL.navyInk }}>{title}</div>
      </div>

      <ScreenBody padTop={60} padBottom={20}>
        {users == null && (
          <div style={{ textAlign: 'center', color: LL.muted, padding: 40 }}>Chargement…</div>
        )}
        {users && users.length === 0 && (
          <div style={{
            margin: '16px 18px', padding: 24, textAlign: 'center',
            background: 'rgba(23,70,141,0.06)', borderRadius: 16,
            fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk, lineHeight: 1.5,
          }}>
            {mode === 'followers'
              ? 'Personne ne te suit encore.'
              : 'Tu ne suis personne pour le moment.'}
          </div>
        )}
        {mode === 'following' && (
          <div style={{ padding: '0 16px' }}>
            <FollowSection currentUserId={userId} />
          </div>
        )}

        <div style={{ padding: '4px 16px' }}>
          {(users || []).map(u => {
            const sub = subState[u.id];
            return (
              <div key={u.id} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 0', borderBottom: `1px solid rgba(23,70,141,0.1)`,
              }}>
                <div style={{
                  width: 44, height: 44, borderRadius: '50%',
                  background: u.avatar_color || LL.navy, color: LL.cream,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 800,
                  overflow: 'hidden', flexShrink: 0,
                }}>
                  {u.avatar_url
                    ? <img src={u.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                    : (u.name || '?')[0]}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: LL.fontBody, fontWeight: 700, fontSize: 14,
                    color: LL.navyInk, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{u.name}</div>
                  <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted }}>@{u.handle}</div>
                </div>
                {u.id !== userId && (
                  <button onClick={() => toggleFollow(u.id)} style={{
                    background: sub ? LL.cream : LL.navy,
                    color: sub ? LL.navyInk : LL.cream,
                    border: `1.5px solid ${LL.navy}`,
                    borderRadius: 999, padding: '6px 14px',
                    fontFamily: LL.fontBody, fontWeight: 700, fontSize: 12,
                    cursor: 'pointer',
                  }}>{sub ? 'Abonné ✓' : 'Suivre'}</button>
                )}
              </div>
            );
          })}
        </div>
      </ScreenBody>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// Placeholders — Notifications + Messagerie (Vague 2 à venir)
// ──────────────────────────────────────────────────────────────
function NotificationsScreen({ onBack }) {
  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={0} padBottom={20}>
        <BackHeader onBack={onBack} />
        <div style={{ padding: '8px 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
            color: LL.navyInk, margin: '4px 0 18px',
          }}>Notifications</h1>
          <div style={{
            padding: '28px 22px', textAlign: 'center',
            background: 'rgba(23,70,141,0.05)', borderRadius: 18,
            fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk, lineHeight: 1.55,
          }}>
            <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke={LL.navy} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: 0.6, marginBottom: 10 }}>
              <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
              <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
            </svg>
            <div style={{ fontWeight: 700, color: LL.navyInk, marginBottom: 4 }}>
              Aucune notification pour le moment
            </div>
            <div>Les demandes d'abonnement et invitations à jouer apparaîtront ici.</div>
          </div>
        </div>
      </ScreenBody>
    </Screen>
  );
}

function MessagesScreen({ onBack }) {
  return (
    <Screen>
      <StatusBar />
      <ScreenBody padTop={0} padBottom={20}>
        <BackHeader onBack={onBack} />
        <div style={{ padding: '8px 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
            color: LL.navyInk, margin: '4px 0 18px',
          }}>Messages</h1>
          <div style={{
            padding: '28px 22px', textAlign: 'center',
            background: 'rgba(23,70,141,0.05)', borderRadius: 18,
            fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk, lineHeight: 1.55,
          }}>
            <svg width="44" height="44" viewBox="0 0 24 24" fill="none" stroke={LL.navy} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" style={{ opacity: 0.6, marginBottom: 10 }}>
              <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
            </svg>
            <div style={{ fontWeight: 700, color: LL.navyInk, marginBottom: 4 }}>
              Aucune discussion
            </div>
            <div>Bientôt : discute avec tes partenaires de jeu.</div>
          </div>
        </div>
      </ScreenBody>
    </Screen>
  );
}

Object.assign(window, {
  LibraryScreen, SelectedGameScreen, ProductScreen, ProductShopScreen, DiscoverScreen,
  ScanScreen, ProfileScreen, HistoryScreen, FirstPlayerScreen, VideoScreen, FeedScreen,
  FollowListScreen, NotificationsScreen, MessagesScreen,
  SectionHeader, HScroller,
});
