// Ludilove — Screens part 1: Onboarding, Inscription, Nouvelle session,
// Attente, Swipe, Match

// ──────────────────────────────────────────────────────────────
// ONBOARDING
// ──────────────────────────────────────────────────────────────
function OnboardingScreen({ onContinue, onSignup }) {
  return (
    <Screen>
      <StatusBar />
      {/* Flex layout — header / cards / CTA with no overlap on any screen size */}
      <div style={{
        position: 'absolute', inset: 0,
        padding: '60px 28px calc(env(safe-area-inset-bottom) + 28px)',
        boxSizing: 'border-box',
        display: 'flex', flexDirection: 'column', alignItems: 'center',
      }}>
        <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 56, color: LL.navy, letterSpacing: -1 }}>
          ludilove
        </div>
        <div style={{ fontFamily: LL.fontBody, fontSize: 14, color: LL.mutedInk, marginTop: -2, letterSpacing: 2, textTransform: 'uppercase' }}>
          swipe · match · play
        </div>

        {/* Cards — fill remaining space, clipped to avoid touching the CTA */}
        <div style={{
          flex: 1, minHeight: 0, width: '100%',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          position: 'relative', overflow: 'hidden',
          padding: '8px 0',
        }}>
          <div style={{ position: 'relative', width: 240, height: 320 }}>
            {[
              { rot: -12, x: -44, y: 12, game: GAMES[0] },
              { rot: 6,   x: 0,   y: 4,  game: GAMES[2] },
              { rot: -3,  x: 22,  y: 0,  game: GAMES[1] },
            ].map((s, i) => (
              <div key={i} style={{
                position: 'absolute', left: '50%', top: 0,
                transform: `translateX(-50%) translate(${s.x}px, ${s.y}px) rotate(${s.rot}deg)`,
                zIndex: i,
              }}>
                <GameTile game={s.game} size="xl" />
              </div>
            ))}
          </div>
        </div>

        {/* CTA stack — always at bottom, no overlap */}
        <div style={{
          width: '100%', display: 'flex', flexDirection: 'column', gap: 12,
          flexShrink: 0,
        }}>
          <div style={{
            textAlign: 'center', fontFamily: LL.fontDisplay, fontSize: 22,
            color: LL.navyInk, fontWeight: 700, marginBottom: 4, lineHeight: 1.2,
          }}>
            Trouvez le jeu parfait<br/>pour votre groupe.
          </div>
          <LLButton onClick={onSignup} size="lg" style={{ width: '100%' }}>
            Créer un compte
          </LLButton>
          <LLButton onClick={onContinue} size="md" variant="ghost" style={{ width: '100%' }}>
            J'ai déjà un compte
          </LLButton>
        </div>
      </div>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// INSCRIPTION
// ──────────────────────────────────────────────────────────────
function SignupScreen({ onBack, onDone, flash }) {
  const [data, setData] = React.useState({ nom: '', email: '', pwd: '', pwd2: '' });
  const [c1, setC1] = React.useState(false);
  const [c2, setC2] = React.useState(true);
  const [submitting, setSubmitting] = React.useState(false);
  const set = (k) => (e) => setData({ ...data, [k]: e.target.value });

  async function submit() {
    if (submitting) return;
    if (!data.nom || !data.email || !data.pwd) return (flash||alert)('Remplissez tous les champs');
    if (data.pwd.length < 6) return (flash||alert)('Mot de passe trop court (6 caractères minimum)');
    if (data.pwd !== data.pwd2) return (flash||alert)('Les mots de passe ne correspondent pas');
    if (!c2) return (flash||alert)('Acceptez les conditions pour continuer');
    setSubmitting(true);
    try {
      const u = await LLAPI.signup(data.nom, data.email, data.pwd);
      flash && flash(`Bienvenue ${u.name} !`);
      onDone && onDone(u);
    } catch (err) {
      (flash||alert)('Inscription échouée: ' + err.message);
      setSubmitting(false);
    }
  }
  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} title="" />
      <ScreenBody padTop={0} padBottom={32}>
        <div style={{ padding: '0 22px' }}>
          <h1 style={{
            fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 38,
            color: LL.navyInk, margin: '8px 0 22px',
          }}>
            Inscription
          </h1>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <OutlineField label="Nom :" value={data.nom} onChange={set('nom')} rounded={26}/>
            <OutlineField label="Adresse mail :" value={data.email} onChange={set('email')} rounded={26}/>
            <OutlineField label="Mot de passe :" type="password" value={data.pwd} onChange={set('pwd')} rounded={26}/>
            <OutlineField label="Vérification mot de passe :" type="password" value={data.pwd2} onChange={set('pwd2')} rounded={26}/>
          </div>

          <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>
            <Checkbox checked={c1} onChange={setC1}>
              Je souhaite recevoir par e-mail des offres personnalisées et les toutes dernières nouveautés de Ludilove.
            </Checkbox>
            <Checkbox checked={c2} onChange={setC2}>
              En m'inscrivant, je confirme accepter les <b>Conditions Générales</b> de Ludilove, avoir lu la <b>Politique de confidentialité</b>.
            </Checkbox>
          </div>

          <LLButton onClick={submit} size="lg" style={{ width: '100%', marginTop: 22, opacity: submitting ? 0.6 : 1 }}>
            {submitting ? 'Inscription…' : "S'inscrire"}
          </LLButton>
        </div>
      </ScreenBody>
    </Screen>
  );
}

// ──────────────────────────────────────────────────────────────
// LOGIN — connexion à un compte existant
// ──────────────────────────────────────────────────────────────
function LoginScreen({ onBack, onDone, onSignup, flash }) {
  const [data, setData] = React.useState({ email: '', pwd: '' });
  const [submitting, setSubmitting] = React.useState(false);
  const set = (k) => (e) => setData({ ...data, [k]: e.target.value });

  async function submit() {
    if (submitting) return;
    if (!data.email || !data.pwd) return (flash || alert)('Email et mot de passe requis');
    setSubmitting(true);
    try {
      const u = await LLAPI.login(data.email, data.pwd);
      flash && flash(`Bienvenue ${u.name} !`);
      onDone && onDone(u);
    } catch (err) {
      (flash || alert)('Connexion échouée : ' + err.message);
      setSubmitting(false);
    }
  }

  return (
    <Screen>
      <StatusBar />
      <BackHeader onBack={onBack} title="" />
      <div style={{ padding: '0 22px' }}>
        <h1 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 38,
          color: LL.navyInk, margin: '8px 0 8px',
        }}>
          Connexion
        </h1>
        <div style={{
          fontFamily: LL.fontBody, fontSize: 13, color: LL.mutedInk, marginBottom: 22,
        }}>
          Reconnecte-toi pour retrouver ta ludothèque et tes amis.
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <OutlineField label="Adresse mail :" value={data.email} onChange={set('email')} rounded={26}/>
          <OutlineField label="Mot de passe :" type="password" value={data.pwd} onChange={set('pwd')} rounded={26}/>
        </div>

        <LLButton onClick={submit} size="lg" style={{ width: '100%', marginTop: 22, opacity: submitting ? 0.6 : 1 }}>
          {submitting ? 'Connexion…' : 'Se connecter'}
        </LLButton>

        <div style={{
          textAlign: 'center', marginTop: 16,
          fontFamily: LL.fontBody, fontSize: 13, color: LL.mutedInk,
        }}>
          Pas encore de compte ?{' '}
          <span onClick={onSignup} style={{
            color: LL.navy, fontWeight: 700, cursor: 'pointer', textDecoration: 'underline',
          }}>
            S'inscrire
          </span>
        </div>

      </div>
    </Screen>
  );
}

function Checkbox({ checked, onChange, children }) {
  return (
    <label style={{ display: 'flex', gap: 10, alignItems: 'flex-start', cursor: 'pointer' }}>
      <div
        onClick={() => onChange(!checked)}
        style={{
          width: 18, height: 18, flexShrink: 0, marginTop: 2,
          border: `2px solid ${LL.navy}`, borderRadius: 4,
          background: checked ? LL.navy : LL.cream,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}
      >
        {checked && (
          <svg width="12" height="10" viewBox="0 0 12 10">
            <path d="M1 5 L5 9 L11 1" stroke={LL.cream} strokeWidth="2" fill="none" strokeLinecap="round"/>
          </svg>
        )}
      </div>
      <div style={{ fontFamily: LL.fontBody, fontSize: 12.5, color: LL.navyInk, lineHeight: 1.4 }}>
        {children}
      </div>
    </label>
  );
}

function BackHeader({ onBack, title, right }) {
  return (
    <div style={{
      height: 48, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '0 16px', marginTop: 4,
    }}>
      <button onClick={onBack} style={{
        width: 40, height: 40, background: 'transparent', border: 'none',
        cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg width="12" height="20" viewBox="0 0 12 20">
          <path d="M10 2L2 10l8 8" stroke={LL.navy} strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </button>
      {title && (
        <div style={{ fontFamily: LL.fontDisplay, fontWeight: 700, color: LL.navyInk, fontSize: 18 }}>
          {title}
        </div>
      )}
      <div style={{ width: 40 }}>{right}</div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// NOUVELLE SESSION
// ──────────────────────────────────────────────────────────────
const LUDOTHEQUES = [
  { id: 'aug',   name: 'Augustin', count: 24, initial: 'A', me: true },
  { id: 'marie', name: 'Marie',    count: 31, initial: 'M' },
  { id: 'leo',   name: 'Léo',      count: 12, initial: 'L' },
  { id: 'ines',  name: 'Inès',     count: 18, initial: 'I' },
  { id: 'theo',  name: 'Théo',     count: 9,  initial: 'T' },
];

function NewSessionScreen({ onNav, onStart, onHistory, onFeed, user }) {
  const [duration, setDuration] = React.useState(30);
  const [peers, setPeers] = React.useState(LUDOTHEQUES);
  const [selectedLibs, setSelectedLibs] = React.useState([]);
  const [pickerOpen, setPickerOpen] = React.useState(false);
  const [starting, setStarting] = React.useState(false);
  const [invitedIds, setInvitedIds] = React.useState([]);

  React.useEffect(() => {
    (async () => {
      try {
        if (!LLAPI.isLoggedIn()) return;
        const { peers: list } = await LLAPI.peers();
        if (list && list.length) {
          setPeers(list);
          const me = list.find(p => p.me);
          setSelectedLibs(me ? [me.id] : [list[0].id]);
        }
      } catch (err) { console.warn('peers:', err); }
    })();
  }, []);

  const libs = peers.filter(l => selectedLibs.includes(l.id));
  const libLabel = libs.length === 0
    ? 'aucune ludothèque'
    : libs.length === 1
      ? (libs[0].me ? 'Ma ludothèque' : `ludothèque de ${libs[0].name}`)
      : `${libs.length} ludothèques · ${libs.reduce((s,l)=>s+(l.count||0),0)} jeux`;

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

  async function start() {
    if (starting) return;
    setStarting(true);
    try {
      await onStart({
        duration_target: duration,
        competition: 1,
        moods: [],
        library_ids: selectedLibs,
        invited_user_ids: invitedIds,
      });
    } finally {
      setStarting(false);
    }
  }

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

          {/* Ludothèque source */}
          <FoldedCard fold={30} style={{ height: 66, marginBottom: 14 }} onClick={() => setPickerOpen(true)}>
            <div style={{
              height: '100%', display: 'flex', alignItems: 'center', gap: 14,
              padding: '0 14px 0 10px',
            }}>
              {libs.length <= 1 ? (
                <Avatar size={46} 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 }}>
                  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>

          {/* Durée (full width, slider) */}
          <FoldedCard fold={22} style={{ minHeight: 108, marginBottom: 14 }}>
            <div style={{ padding: '14px 18px 16px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
                <HourglassIcon />
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, fontWeight: 600 }}>
                    temps de jeu
                  </div>
                  <div style={{ fontFamily: LL.fontDisplay, fontSize: 22, color: LL.navyInk, fontWeight: 700, lineHeight: 1 }}>
                    {duration} min
                  </div>
                </div>
              </div>
              <SessionSlider value={duration} onChange={setDuration} min={15} max={180} step={15}/>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 4 }}>
                <span style={{ fontSize: 10, color: LL.muted, fontFamily: LL.fontBody }}>15 min</span>
                <span style={{ fontSize: 10, color: LL.muted, fontFamily: LL.fontBody }}>3 h</span>
              </div>
            </div>
          </FoldedCard>

          <LLButton onClick={start} size="lg" style={{ width: '100%', height: 64, fontSize: 18, letterSpacing: 0.4, opacity: starting ? 0.6 : 1 }}>
            {starting ? 'création…' : 'lancer la session'}
          </LLButton>
        </div>
      </ScreenBody>
      <BottomNav current="session" onNav={onNav} />
      {pickerOpen && (
        <LudothequePicker
          peers={peers}
          selected={selectedLibs}
          onToggle={toggleLib}
          onClose={() => setPickerOpen(false)}
        />
      )}
    </Screen>
  );
}

function InviteSection({ user, onChange }) {
  const [friends, setFriends] = React.useState([]);
  const [groups, setGroups] = React.useState([]);
  const [selected, setSelected] = React.useState(new Set());
  const [groupSheet, setGroupSheet] = React.useState(false);
  const [groupName, setGroupName] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (!user?.id || !LLAPI.isLoggedIn()) return;
    Promise.all([
      LLAPI.following(user.id).catch(() => ({ users: [] })),
      LLAPI.myGroups().catch(() => ({ groups: [] })),
    ]).then(([f, g]) => {
      setFriends(f.users || []);
      setGroups(g.groups || []);
    });
  }, [user?.id]);

  function toggle(id) {
    setSelected(s => {
      const next = new Set(s);
      if (next.has(id)) next.delete(id); else next.add(id);
      onChange([...next]);
      return next;
    });
  }

  function applyGroup(g) {
    const ids = g.member_ids || [];
    const next = new Set(ids);
    setSelected(next);
    onChange(ids);
  }

  async function saveGroup() {
    if (!groupName.trim() || selected.size < 2) return;
    setSaving(true);
    try {
      const { group } = await LLAPI.createGroup(groupName.trim(), [...selected]);
      setGroups(gs => [group, ...gs]);
      setGroupSheet(false);
      setGroupName('');
    } catch (_) {}
    setSaving(false);
  }

  async function removeGroup(gid, e) {
    e.stopPropagation();
    await LLAPI.deleteGroup(gid).catch(() => {});
    setGroups(gs => gs.filter(g => g.id !== gid));
  }

  if (!user || friends.length === 0) return null;

  return (
    <FoldedCard fold={22} style={{ marginBottom: 14 }}>
      <div style={{ padding: '14px 14px 12px' }}>
        <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, fontWeight: 600, marginBottom: 10 }}>
          inviter des amis
        </div>

        {groups.length > 0 && (
          <div style={{ display: 'flex', gap: 6, overflowX: 'auto', marginBottom: 10, paddingBottom: 2 }}>
            {groups.map(g => (
              <div key={g.id} onClick={() => applyGroup(g)} style={{
                display: 'flex', alignItems: 'center', gap: 5,
                background: LL.navy, color: LL.cream,
                borderRadius: 20, padding: '5px 10px 5px 10px',
                fontSize: 12, fontWeight: 600, fontFamily: LL.fontBody,
                cursor: 'pointer', flexShrink: 0, whiteSpace: 'nowrap',
              }}>
                <span>{g.name}</span>
                <span onClick={(e) => removeGroup(g.id, e)} style={{
                  marginLeft: 4, opacity: 0.6, fontSize: 11,
                }}>✕</span>
              </div>
            ))}
          </div>
        )}

        <div style={{ display: 'flex', gap: 10, overflowX: 'auto', paddingBottom: 4 }}>
          {friends.map(f => {
            const sel = selected.has(f.id);
            return (
              <div key={f.id} onClick={() => toggle(f.id)} style={{
                display: 'flex', flexDirection: 'column', alignItems: 'center',
                gap: 4, cursor: 'pointer', flexShrink: 0, width: 52,
              }}>
                <div style={{ position: 'relative' }}>
                  <Avatar size={44} name={(f.name || '?')[0].toUpperCase()} />
                  {sel && (
                    <div style={{
                      position: 'absolute', inset: 0, borderRadius: '50%',
                      background: 'rgba(23,70,141,0.6)',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                        <path d="M4 9l4 4 6-7" stroke={LL.cream} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>
                      </svg>
                    </div>
                  )}
                </div>
                <div style={{
                  fontFamily: LL.fontBody, fontSize: 10,
                  color: sel ? LL.navyDeep : LL.navyInk,
                  fontWeight: sel ? 700 : 500,
                  textAlign: 'center',
                  overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%',
                }}>{(f.name || '').split(' ')[0]}</div>
              </div>
            );
          })}
        </div>

        {selected.size >= 2 && (
          <div onClick={() => setGroupSheet(true)} style={{
            marginTop: 10, fontFamily: LL.fontBody, fontSize: 12,
            color: LL.navy, fontWeight: 600, cursor: 'pointer',
            textDecoration: 'underline', display: 'inline-block',
          }}>
            + enregistrer comme groupe
          </div>
        )}
      </div>

      {groupSheet && (
        <div style={{
          position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 300,
          display: 'flex', alignItems: 'flex-end',
        }} onClick={() => setGroupSheet(false)}>
          <div onClick={e => e.stopPropagation()} style={{
            background: LL.cream, borderRadius: '20px 20px 0 0',
            padding: '22px 22px 44px', width: '100%', boxSizing: 'border-box',
          }}>
            <div style={{
              fontFamily: LL.fontDisplay, fontWeight: 700, fontSize: 20,
              color: LL.navyInk, marginBottom: 18,
            }}>Nom du groupe</div>
            <input
              autoFocus
              value={groupName}
              onChange={e => setGroupName(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && saveGroup()}
              placeholder="Ex : Soirée jeux, Les amis…"
              style={{
                width: '100%', boxSizing: 'border-box',
                border: `1.5px solid ${LL.navy}`, borderRadius: 10,
                padding: '12px 14px', fontSize: 15, fontFamily: LL.fontBody,
                background: 'white', color: LL.navyInk, outline: 'none',
              }}
            />
            <LLButton
              onClick={saveGroup}
              size="lg"
              style={{
                width: '100%', marginTop: 14,
                opacity: (!groupName.trim() || saving) ? 0.5 : 1,
              }}
            >
              {saving ? 'Création…' : 'Créer le groupe'}
            </LLButton>
          </div>
        </div>
      )}
    </FoldedCard>
  );
}

function StackedAvatars({ libs }) {
  return (
    <div style={{ display: 'flex', position: 'relative', width: 46 + (libs.length-1)*20, height: 46, flexShrink: 0 }}>
      {libs.map((l, i) => (
        <div key={l.id} style={{
          position: 'absolute', left: i * 20, top: 0,
          border: `2px solid ${LL.cream}`, borderRadius: '50%',
          zIndex: libs.length - i,
        }}>
          <Avatar size={42} name={l.initial}/>
        </div>
      ))}
    </div>
  );
}

// CommunityCard — entry point to the social feed (souvenirs des amis)
function CommunityCard({ onOpen }) {
  const [posts, setPosts] = React.useState([]);
  React.useEffect(() => {
    (async () => {
      try {
        if (!LLAPI.isLoggedIn()) return;
        const r = await LLAPI.feed(6);
        setPosts(r.posts || []);
      } catch (err) { console.warn('community:', err); }
    })();
  }, []);

  return (
    <div onClick={onOpen} style={{
      marginBottom: 14, cursor: 'pointer',
      background: LL.cream, border: `2px solid ${LL.navy}`, borderRadius: 22,
      padding: '14px 16px', position: 'relative', overflow: 'hidden',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
        <div style={{
          width: 42, height: 42, borderRadius: 12,
          background: LL.navy, display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
        }}>
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
            <circle cx="9" cy="8" r="3" stroke={LL.gold} strokeWidth="1.7"/>
            <circle cx="17" cy="8" r="3" stroke={LL.gold} strokeWidth="1.7"/>
            <path d="M3 20 c0 -3 3 -5 6 -5 s6 2 6 5" stroke={LL.gold} strokeWidth="1.7" fill="none" strokeLinecap="round"/>
            <path d="M11 20 c0 -3 3 -5 6 -5 s6 2 6 5" stroke={LL.gold} strokeWidth="1.7" fill="none" strokeLinecap="round"/>
          </svg>
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 800, color: LL.navyInk, lineHeight: 1.1 }}>
            Communauté
          </div>
          <div style={{
            fontFamily: LL.fontBody, fontSize: 11.5, color: LL.muted,
            marginTop: 2, fontWeight: 500,
          }}>
            {posts.length > 0
              ? `${posts.length} souvenir${posts.length > 1 ? 's' : ''} récents · voir le mur`
              : "Aucun post pour l'instant — sois le premier !"}
          </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>

      {posts.length > 0 && (
        <div style={{ display: 'flex', gap: 8 }}>
          {posts.slice(0, 3).map(p => (
            <div key={p.id} style={{
              flex: 1, position: 'relative',
              borderRadius: 10, overflow: 'hidden',
              aspectRatio: '4 / 5',
              border: `1.5px solid ${LL.navy}`,
              background: '#000',
            }}>
              <img src={p.image_url} alt="" loading="lazy"
                style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>
              <div style={{
                position: 'absolute', left: 0, right: 0, bottom: 0,
                padding: '8px 6px 4px',
                background: 'linear-gradient(180deg, transparent, rgba(14,47,99,0.92))',
                color: LL.cream, fontFamily: LL.fontBody, fontSize: 9.5,
                fontWeight: 600, textAlign: 'center',
              }}>{p.author?.name || ''}</div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// WinsCard — shortcut to history from Nouvelle session
const WINS = [
  { gameId: 'catan',  when: 'hier',         score: '12 pts', trophy: 'or' },
  { gameId: 'dixit',  when: 'il y a 3 j',   score: '30 pts', trophy: 'argent' },
  { gameId: 'trio',   when: 'la semaine dernière', score: '3 trios', trophy: 'or' },
];

function WinsCard({ onHistory }) {
  return (
    <div
      onClick={onHistory}
      style={{
        marginBottom: 14, cursor: 'pointer',
        background: LL.cream,
        border: `2px solid ${LL.navy}`,
        borderRadius: 22,
        padding: '14px 16px',
        position: 'relative', overflow: 'hidden',
      }}
    >
      {/* Header */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12,
      }}>
        <div style={{
          width: 42, height: 42, borderRadius: 12,
          background: LL.navy,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
        }}>
          <TrophyIcon color={LL.gold} size={22}/>
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 800,
            color: LL.navyInk, lineHeight: 1.1,
          }}>Dernières victoires</div>
          <div style={{
            fontFamily: LL.fontBody, fontSize: 11.5, color: LL.muted,
            marginTop: 2, fontWeight: 500,
          }}>
            {WINS.length} triomphes récents · voir l'historique
          </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>

      {/* Wins strip */}
      <div style={{ display: 'flex', gap: 10 }}>
        {WINS.map((w, i) => {
          const game = GAMES.find(g => g.id === w.gameId) || GAMES[0];
          return (
            <div key={i} style={{
              flex: 1, minWidth: 0, position: 'relative',
              borderRadius: 12, overflow: 'hidden',
              aspectRatio: '1 / 1',
              background: game.bg || '#E8D5B0',
              border: `1.5px solid ${LL.navy}`,
            }}>
              <GameCover game={game} />
              {/* Trophy overlay */}
              <div style={{
                position: 'absolute', top: 4, right: 4,
                width: 22, height: 22, borderRadius: '50%',
                background: w.trophy === 'or' ? LL.gold : '#C0C7D0',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                border: `1.5px solid ${LL.navyInk}`,
                boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
              }}>
                <TrophyIcon color={LL.navyInk} size={12}/>
              </div>
              {/* Bottom label */}
              <div style={{
                position: 'absolute', left: 0, right: 0, bottom: 0,
                background: 'linear-gradient(180deg, transparent, rgba(14,47,99,0.88) 55%)',
                padding: '10px 6px 5px',
                color: LL.cream,
                fontFamily: LL.fontBody, fontSize: 9.5, fontWeight: 600,
                textAlign: 'center', letterSpacing: 0.2,
              }}>
                {w.when}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function TrophyIcon({ color = LL.navy, size = 20 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
      {/* Cup body */}
      <path d="M7 4 h10 v4 a5 5 0 0 1 -10 0 z" fill={color}/>
      {/* Left handle */}
      <path d="M7 5 Q 3 5 3 8 Q 3 11 7 10" stroke={color} strokeWidth="1.5" fill="none"/>
      {/* Right handle */}
      <path d="M17 5 Q 21 5 21 8 Q 21 11 17 10" stroke={color} strokeWidth="1.5" fill="none"/>
      {/* Stem */}
      <rect x="10.5" y="12" width="3" height="4" fill={color}/>
      {/* Base */}
      <rect x="7" y="16" width="10" height="2.5" rx="1" fill={color}/>
      <rect x="5" y="18.5" width="14" height="2" rx="1" fill={color}/>
    </svg>
  );
}

function LudothequePicker({ selected, onToggle, onClose, peers }) {
  const list = (peers && peers.length) ? peers : LUDOTHEQUES;
  return (
    <>
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0, background: 'rgba(14,47,99,0.45)',
        zIndex: 50, animation: 'llFadeIn .2s ease',
      }}/>      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        background: LL.cream, borderTopLeftRadius: 28, borderTopRightRadius: 28,
        border: `2px solid ${LL.navy}`, borderBottom: 'none',
        padding: '18px 20px 28px',
        zIndex: 51, maxHeight: '70%', overflowY: 'auto',
        animation: 'llSlideUp .28s 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: 700, fontSize: 22,
          color: LL.navyInk, margin: '0 0 4px',
        }}>
          Choisir une ludothèque
        </h2>
        <p style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, margin: '0 0 16px' }}>
          Combinez les collections de votre groupe
        </p>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {list.map(l => {
            const on = selected.includes(l.id);
            const initial = l.initial || (l.name ? l.name[0].toUpperCase() : '?');
            return (
              <div key={l.id} onClick={() => onToggle(l.id)} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 14px', borderRadius: 16,
                border: `1.5px solid ${on ? LL.navy : 'rgba(23,70,141,0.18)'}`,
                background: on ? 'rgba(23,70,141,0.08)' : 'transparent',
                cursor: 'pointer', transition: 'all .15s',
              }}>
                <Avatar size={42} name={initial}/>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: LL.fontBody, fontWeight: 600, fontSize: 14, color: LL.navyInk }}>
                    {l.me ? 'Ma ludothèque' : `ludothèque de ${l.name}`}
                  </div>
                  <div style={{ fontFamily: LL.fontBody, fontSize: 11, color: LL.muted }}>
                    {l.count} jeux
                  </div>
                </div>
                <div style={{
                  width: 24, height: 24, borderRadius: 8,
                  border: `1.5px solid ${LL.navy}`,
                  background: on ? LL.navy : 'transparent',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  {on && (
                    <svg width="14" height="14" viewBox="0 0 14 14">
                      <path d="M2 7 L 6 11 L 12 3" stroke={LL.cream} strokeWidth="2.2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  )}
                </div>
              </div>
            );
          })}
        </div>

        <button onClick={onClose} style={{
          width: '100%', marginTop: 18, height: 52, borderRadius: 14,
          background: LL.navy, color: LL.cream, border: 'none', cursor: 'pointer',
          fontFamily: LL.fontBody, fontWeight: 700, fontSize: 15, letterSpacing: 0.4,
        }}>
          Valider ({selected.length})
        </button>
      </div>
      <style>{`
        @keyframes llFadeIn { from { opacity: 0 } to { opacity: 1 } }
        @keyframes llSlideUp { from { transform: translateY(100%) } to { transform: translateY(0) } }
      `}</style>
    </>
  );
}

function InviteOption({ icon, label }) {
  return (
    <div style={{
      border: `1.5px solid ${LL.navy}`, borderRadius: 14, height: 72,
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      gap: 4, background: LL.cream,
      cursor: 'pointer',
    }}>
      {icon}
      <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.navyInk, fontWeight: 600 }}>{label}</div>
    </div>
  );
}

function Avatar({ size = 46, name = '?' }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: `linear-gradient(135deg, ${LL.navy}, ${LL.navyDeep})`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      color: LL.cream, fontFamily: LL.fontDisplay, fontSize: size * 0.44, fontWeight: 700,
      flexShrink: 0,
    }}>
      {name}
    </div>
  );
}

function IconPlus() {
  return (
    <svg width="32" height="32" viewBox="0 0 32 32">
      <circle cx="16" cy="16" r="14" stroke={LL.navy} strokeWidth="2" fill={LL.cream}/>
      <circle cx="16" cy="13" r="3.8" fill={LL.navy}/>
      <path d="M8 24 C 9 19.5 12 18 16 18 C 20 18 23 19.5 24 24 Z" fill={LL.navy}/>
    </svg>
  );
}
function HourglassIcon() {
  return (
    <svg width="34" height="38" viewBox="0 0 36 40">
      <path d="M6 2 H 30 L 22 16 L 30 30 H 6 L 14 16 Z M 10 6 H 26 L 19 15 H 17 Z"
        fill={LL.navy}/>
    </svg>
  );
}
function SessionSlider({ value, onChange, min, max, step = 1 }) {
  const pct = ((value - min) / (max - min)) * 100;
  return (
    <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: 6, borderRadius: 999,
        background: `linear-gradient(90deg, ${LL.navy} 0 ${pct}%, rgba(23,70,141,0.18) ${pct}% 100%)`,
        outline: 'none', cursor: 'pointer', margin: 0,
      }}
    />
  );
}
function ShareIcon() {
  return (
    <svg width="24" height="24" viewBox="0 0 24 24">
      <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>
  );
}
function QRIcon() {
  return (
    <svg width="24" height="24" viewBox="0 0 24 24" fill={LL.navy}>
      <rect x="1" y="1" width="9" height="9" rx="1" stroke={LL.navy} strokeWidth="2" fill="none"/>
      <rect x="4" y="4" width="3" height="3"/>
      <rect x="14" y="1" width="9" height="9" rx="1" stroke={LL.navy} strokeWidth="2" fill="none"/>
      <rect x="17" y="4" width="3" height="3"/>
      <rect x="1" y="14" width="9" height="9" rx="1" stroke={LL.navy} strokeWidth="2" fill="none"/>
      <rect x="4" y="17" width="3" height="3"/>
      <rect x="14" y="14" width="3" height="3"/>
      <rect x="20" y="14" width="3" height="3"/>
      <rect x="14" y="20" width="3" height="3"/>
      <rect x="20" y="20" width="3" height="3"/>
      <rect x="17" y="17" width="3" height="3"/>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────
// SALLE D'ATTENTE (session lobby)
// ──────────────────────────────────────────────────────────────
function LobbyScreen({ onBack, onStart, session, setSession, user }) {
  const [liveSession, setLiveSession] = React.useState(session);
  const [qrOpen, setQrOpen] = React.useState(false);
  const [shareToast, setShareToast] = React.useState(null);

  React.useEffect(() => {
    setLiveSession(session);
    if (!session?.id) return;
    const unsub = LLAPI.subscribe(session.id, (evt) => {
      if (evt.session) {
        setLiveSession(evt.session);
        setSession && setSession(evt.session);
      }
    });
    // Polling fallback — SSE can fail silently through some proxies
    // (Cloudflare tunnels, mobile networks). We refetch every 2s while in the
    // lobby to guarantee the host sees new participants even if SSE is dead.
    const poll = setInterval(async () => {
      try {
        const { session: s } = await LLAPI.session(session.id);
        if (s) {
          setLiveSession(prev => {
            // Only update if something actually changed to avoid re-render churn
            const changed = !prev
              || prev.status !== s.status
              || (prev.participants?.length || 0) !== (s.participants?.length || 0)
              || JSON.stringify(prev.participants) !== JSON.stringify(s.participants);
            if (changed) {
              setSession && setSession(s);
              return s;
            }
            return prev;
          });
        }
      } catch (_) {}
    }, 2000);
    return () => { unsub(); clearInterval(poll); };
  }, [session?.id]);

  const participants = liveSession?.participants || [];
  const readyCount = participants.filter(p => p.ready).length;
  const canStart = participants.length >= 2;
  const code = liveSession?.code || '';
  const joinUrl = code ? `${window.location.origin}/?join=${encodeURIComponent(code)}` : '';

  async function shareInvite() {
    const data = {
      title: 'Rejoins ma session Ludilove',
      text: `Code de session : ${code}`,
      url: joinUrl,
    };
    if (navigator.share) {
      try { await navigator.share(data); return; }
      catch (err) { /* user cancelled or unsupported — fall through */ }
    }
    try {
      await navigator.clipboard.writeText(joinUrl);
      setShareToast('Lien copié !');
    } catch {
      setShareToast(joinUrl);
    }
    setTimeout(() => setShareToast(null), 2500);
  }

  async function copyCode() {
    try { await navigator.clipboard.writeText(code); setShareToast('Code copié !'); }
    catch { setShareToast(code); }
    setTimeout(() => setShareToast(null), 2200);
  }
  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',
        }}>
          Salle d'attente
        </h1>
        <div style={{ color: LL.mutedInk, fontSize: 14 }}>
          {readyCount}/{participants.length} joueurs prêts
        </div>

        <FoldedCard fold={24} style={{ marginTop: 18, padding: 0 }}>
          <div style={{ padding: 16 }}>
            <div style={{
              display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4,
            }}>
              <div style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, fontWeight: 600 }}>
                code de la partie
              </div>
              <button onClick={copyCode} aria-label="Copier le code" style={{
                background: 'transparent', border: `1.5px solid ${LL.navy}`,
                color: LL.navy, borderRadius: 999, padding: '4px 10px', cursor: 'pointer',
                fontFamily: LL.fontBody, fontSize: 11, fontWeight: 600,
                display: 'flex', alignItems: 'center', gap: 5,
              }}>
                <svg width="11" height="11" viewBox="0 0 14 14"><rect x="3" y="3" width="9" height="10" rx="1.5" stroke={LL.navy} strokeWidth="1.4" fill="none"/><rect x="1" y="1" width="9" height="10" rx="1.5" stroke={LL.navy} strokeWidth="1.4" fill={LL.cream}/></svg>
                Copier
              </button>
            </div>
            <div style={{
              fontFamily: LL.fontMono, fontSize: 28, letterSpacing: 4,
              color: LL.navyInk, fontWeight: 700,
            }}>
              {code}
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 14 }}>
              <button onClick={shareInvite} style={inviteBtnStyle()}>
                <ShareIcon/>
                <span>Partager</span>
              </button>
              <button onClick={() => setQrOpen(true)} style={inviteBtnStyle()}>
                <QRIcon/>
                <span>Code QR</span>
              </button>
            </div>
          </div>
        </FoldedCard>

        <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 10 }}>
          {participants.map((p, i) => (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 14,
              padding: '12px 16px',
              background: LL.white,
              border: `1.5px solid ${LL.navy}`,
              borderRadius: 16,
            }}>
              <Avatar name={p.name[0]} />
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 600, color: LL.navyInk }}>{p.name}</div>
                {p.host && <div style={{ fontSize: 11, color: LL.muted }}>hôte</div>}
              </div>
              <div style={{
                padding: '4px 10px', borderRadius: 999,
                fontSize: 11, fontWeight: 600,
                background: p.ready ? 'rgba(79,142,90,0.15)' : 'rgba(226,177,58,0.2)',
                color: p.ready ? LL.green : '#8B6510',
              }}>
                {p.ready ? '● prêt' : '○ en attente'}
              </div>
            </div>
          ))}
        </div>

        <div style={{ marginTop: 18 }}>
          <InviteSection user={user} onChange={() => {}} />
        </div>

        <LLButton
          onClick={canStart ? onStart : undefined} size="lg"
          style={{
            width: '100%', marginTop: 14, height: 58,
            opacity: canStart ? 1 : 0.45,
            cursor: canStart ? 'pointer' : 'not-allowed',
          }}
        >
          {canStart ? 'Démarrer le swipe →' : 'En attente d\'un 2e joueur…'}
        </LLButton>
        {!canStart && (
          <div style={{
            marginTop: 10, textAlign: 'center',
            fontFamily: LL.fontBody, fontSize: 12, color: LL.muted,
          }}>
            Il faut au minimum 2 joueurs. Partage le code ou le QR pour inviter.
          </div>
        )}
      </div>

      {qrOpen && (
        <QrSheet code={code} url={joinUrl} onClose={() => setQrOpen(false)} />
      )}

      {shareToast && (
        <div style={{
          position: 'absolute', bottom: 28, left: '50%', transform: 'translateX(-50%)',
          background: LL.navyDeep, color: LL.cream,
          padding: '10px 18px', borderRadius: 999, zIndex: 200,
          fontFamily: LL.fontBody, fontSize: 13, fontWeight: 600,
          boxShadow: '0 6px 18px rgba(0,0,0,0.3)',
          maxWidth: '90%', textAlign: 'center',
        }}>{shareToast}</div>
      )}
    </Screen>
  );
}

function inviteBtnStyle() {
  return {
    display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
    padding: '10px 12px', borderRadius: 12, cursor: 'pointer',
    border: `1.5px solid ${LL.navy}`, background: LL.cream,
    color: LL.navyInk, fontFamily: LL.fontBody, fontSize: 13, fontWeight: 600,
  };
}

// Bottom-sheet showing a scannable QR code with the join URL.
function QrSheet({ code, url, onClose }) {
  const canvasRef = React.useRef(null);
  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !window.qrcode) return;
    try {
      const qr = window.qrcode(0, 'M');
      qr.addData(url);
      qr.make();
      const n = qr.getModuleCount();
      const margin = 2;
      const size = 240;
      const cell = size / (n + margin * 2);
      const ctx = canvas.getContext('2d');
      const dpr = window.devicePixelRatio || 1;
      canvas.width = size * dpr; canvas.height = size * dpr;
      canvas.style.width = size + 'px'; canvas.style.height = size + 'px';
      ctx.scale(dpr, dpr);
      ctx.fillStyle = '#F2E5CA';
      ctx.fillRect(0, 0, size, size);
      ctx.fillStyle = '#0E2F63';
      for (let r = 0; r < n; r++) {
        for (let c = 0; c < n; c++) {
          if (qr.isDark(r, c)) {
            ctx.fillRect((c + margin) * cell, (r + margin) * cell, cell + 0.5, cell + 0.5);
          }
        }
      }
    } catch (e) { console.warn('QR render', e); }
  }, [url]);
  return (
    <>
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0, background: 'rgba(14,47,99,0.5)',
        zIndex: 200, animation: 'llFadeIn .18s ease',
      }}/>
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        background: LL.cream, borderTopLeftRadius: 28, borderTopRightRadius: 28,
        border: `2px solid ${LL.navy}`, borderBottom: 'none',
        padding: '18px 22px 28px', zIndex: 201,
        animation: 'llSlideUp .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',
        }}>
          Scanner pour rejoindre
        </h2>
        <p style={{ fontFamily: LL.fontBody, fontSize: 12, color: LL.muted, margin: '0 0 16px' }}>
          Demandez à vos amis d'ouvrir leur appareil photo et de viser ce QR code.
        </p>
        <div style={{
          display: 'flex', justifyContent: 'center', alignItems: 'center',
          background: LL.cream, borderRadius: 18, padding: 12,
          border: `1.5px solid ${LL.navy}`, marginBottom: 14,
        }}>
          <canvas ref={canvasRef} width="240" height="240"
            aria-label={`QR code session ${code}`}
            style={{ display: 'block', borderRadius: 8, width: 240, height: 240 }}/>
        </div>
        <div style={{
          fontFamily: LL.fontMono, fontSize: 18, letterSpacing: 3,
          color: LL.navyInk, fontWeight: 700, textAlign: 'center', marginBottom: 16,
        }}>{code}</div>
        <button onClick={onClose} style={{
          width: '100%', height: 50, borderRadius: 14,
          background: LL.navy, color: LL.cream, border: 'none', cursor: 'pointer',
          fontFamily: LL.fontBody, fontWeight: 700, fontSize: 15, letterSpacing: 0.4,
        }}>
          Fermer
        </button>
      </div>
    </>
  );
}

// ──────────────────────────────────────────────────────────────
// SWIPE
// ──────────────────────────────────────────────────────────────
function SwipeScreen({ onNav, onMatch, session }) {
  const [deck, setDeck] = React.useState([]); // array of game objects
  const [pos, setPos] = React.useState(0);    // top-of-deck index
  const [drag, setDrag] = React.useState({ x: 0, y: 0, dragging: false });
  const [flying, setFlying] = React.useState(null);
  const [infoOpen, setInfoOpen] = React.useState(false);
  const [loading, setLoading] = React.useState(true);
  const matchedRef = React.useRef(false);

  // Load deck — uniquement depuis une vraie session
  React.useEffect(() => {
    (async () => {
      try {
        if (session?.id) {
          const { deck } = await LLAPI.sessionDeck(session.id);
          // Shuffle the deck client-side so each player sees a different
          // random order — matches still work because matching only
          // requires everyone to like the same game, order-independent.
          const shuffled = [...(deck || [])];
          for (let i = shuffled.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
          }
          setDeck(shuffled);
        } else {
          setDeck([]);
        }
      } catch (err) {
        console.warn('deck:', err);
        setDeck([]);
      } finally {
        setLoading(false);
      }
    })();
  }, [session?.id]);

  // Subscribe to SSE for match notifications from other participants.
  // Also poll as a fallback — SSE can die through proxies (Cloudflare tunnel,
  // mobile networks). Without polling, a match by a peer would never reach
  // this client and the swipe would hang.
  React.useEffect(() => {
    if (!session?.id) return;
    const unsub = LLAPI.subscribe(session.id, (evt) => {
      if (evt.type === 'match' && evt.game && !matchedRef.current) {
        matchedRef.current = true;
        onMatch(evt.game);
      }
    });
    const poll = setInterval(async () => {
      if (matchedRef.current) return;
      try {
        const { session: s } = await LLAPI.session(session.id);
        if (s?.matched_game_id && !matchedRef.current) {
          const { game } = await LLAPI.game(s.matched_game_id);
          if (game && !matchedRef.current) {
            matchedRef.current = true;
            onMatch(game);
          }
        }
      } catch (_) {}
    }, 2500);
    return () => { unsub(); clearInterval(poll); };
  }, [session?.id]);

  const topGame = deck[pos];

  async function doSwipe(dir) {
    if (!topGame || matchedRef.current) return;
    setFlying(dir);
    const g = topGame;
    setTimeout(() => {
      setPos(p => p + 1);
      setDrag({ x: 0, y: 0, dragging: false });
      setFlying(null);
    }, 280);
    // Record swipe
    try {
      if (session?.id) {
        const r = await LLAPI.swipe(session.id, g.id, dir === 'right' ? 'like' : 'pass');
        if (r.matched && r.game && !matchedRef.current) {
          matchedRef.current = true;
          onMatch(r.game);
        }
      }
    } catch (err) {
      console.warn('swipe:', err);
    }
  }

  function onPointerDown(e) {
    setDrag({ x: 0, y: 0, dragging: true, startX: e.clientX, startY: e.clientY });
  }
  function onPointerMove(e) {
    if (!drag.dragging) return;
    setDrag({ ...drag, x: e.clientX - drag.startX, y: e.clientY - drag.startY });
  }
  function onPointerUp() {
    if (!drag.dragging) return;
    if (drag.x > 100) doSwipe('right');
    else if (drag.x < -100) doSwipe('left');
    else setDrag({ x: 0, y: 0, dragging: false });
  }

  // Build the top-3 cards (top first)
  const cards = [];
  for (let i = 0; i < 3; i++) {
    const g = deck[pos + i];
    if (g) cards.push(g);
  }
  const deckExhausted = !loading && deck.length > 0 && pos >= deck.length;
  const deckEmpty = !loading && deck.length === 0;
  return (
    <Screen>
      <StatusBar />
      <div style={{ padding: '2px 22px 0' }}>
        <h1 style={{
          fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 32,
          color: LL.navyInk, margin: 0,
        }}>
          Swipe
        </h1>
      </div>

      <div style={{
        position: 'absolute', top: 100, left: 0, right: 0, bottom: 200,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        touchAction: 'none', userSelect: 'none',
        padding: '0 22px',
      }}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        onPointerCancel={onPointerUp}
        onPointerLeave={onPointerUp}
      >
        {(deckEmpty || deckExhausted) && (
          <div style={{
            textAlign: 'center',
            fontFamily: LL.fontBody, color: LL.mutedInk, maxWidth: 280,
          }}>
            <div style={{ width: 56, height: 56, margin: '0 auto 10px' }}>
              {deckEmpty ? (
                <svg viewBox="0 0 48 48" width="100%" height="100%" fill="none" stroke={LL.navy} strokeWidth="2.4" strokeLinejoin="round" strokeLinecap="round">
                  <path d="M6 20 L24 10 L42 20 L42 40 L6 40 Z"/>
                  <path d="M6 20 L24 28 L42 20"/>
                  <path d="M24 28 L24 40"/>
                </svg>
              ) : (
                <LoadingDie color={LL.cream} bg={LL.navy}/>
              )}
            </div>
            <div style={{ fontFamily: LL.fontDisplay, fontWeight: 800, fontSize: 22, color: LL.navyInk, marginBottom: 6 }}>
              {deckEmpty ? 'Aucun jeu à proposer' : 'Fin du deck'}
            </div>
            <div style={{ fontSize: 13 }}>
              {deckEmpty
                ? 'Vos ludothèques combinées ne contiennent aucun jeu. Ajoutez-en via le scan.'
                : 'Vous avez vu tous les jeux. En attente des autres joueurs…'}
            </div>
          </div>
        )}
        {cards.slice().reverse().map((game, ri) => {
          const depth = cards.length - 1 - ri;
          const isTop = depth === 0;
          const offsetY = depth * 10;
          const scale = 1 - depth * 0.04;
          let tx = 0, ty = offsetY, rot = 0, opacity = 1;
          if (isTop) {
            if (flying === 'right') { tx = 420; rot = 22; opacity = 0; }
            else if (flying === 'left') { tx = -420; rot = -22; opacity = 0; }
            else if (drag.dragging) { tx = drag.x; ty = drag.y + offsetY; rot = drag.x / 20; }
          }
          return (
            <div key={`${game.id}-${ri}`} style={{
              position: 'absolute',
              transform: `translate(${tx}px, ${ty}px) rotate(${rot}deg) scale(${scale})`,
              transition: drag.dragging && isTop ? 'none' : 'transform .28s ease, opacity .28s ease',
              opacity,
            }}>
              <SwipeCard game={game} />
              {isTop && drag.dragging && drag.x > 30 && (
                <Stamp type="like" rot={-15} />
              )}
              {isTop && drag.dragging && drag.x < -30 && (
                <Stamp type="nope" rot={15} />
              )}
            </div>
          );
        })}
      </div>

      {/* action buttons */}
      <div style={{
        position: 'absolute', bottom: 110, left: 0, right: 0,
        display: 'flex', justifyContent: 'center', gap: 26, zIndex: 30,
      }}>
        <ActionButton onClick={() => doSwipe('left')} variant="reject"/>
        <ActionButton onClick={() => setInfoOpen(true)} variant="info"/>
        <ActionButton onClick={() => doSwipe('right')} variant="like"/>
      </div>

      {infoOpen && topGame && <InfoSheet game={topGame} onClose={() => setInfoOpen(false)} />}

      <BottomNav current="session" onNav={onNav} />
    </Screen>
  );
}

function SwipeCard({ game }) {
  return (
    <FoldedCard fold={52} bg={LL.cream} color={LL.navy} radius={22}
      style={{ width: 300, height: 460 }}
    >
      <div style={{ padding: 14, display: 'flex', flexDirection: 'column', height: '100%' }}>
        <div style={{
          flex: 1, borderRadius: 12, overflow: 'hidden', position: 'relative',
          border: `1.5px solid ${LL.navy}`,
        }}>
          <GameCover game={game} />
          {/* player/duration badges */}
          <div style={{
            position: 'absolute', left: 10, top: 10, display: 'flex', gap: 6,
          }}>
            <MiniBadge>{game.players}</MiniBadge>
            <MiniBadge>{game.duration}</MiniBadge>
          </div>
        </div>
        <div style={{
          fontFamily: LL.fontDisplay, fontSize: 26, fontWeight: 800,
          color: LL.navyInk, marginTop: 12, textAlign: 'center',
        }}>
          {game.name}
        </div>
      </div>
    </FoldedCard>
  );
}

function MiniBadge({ children }) {
  return (
    <div style={{
      padding: '4px 10px', borderRadius: 999,
      background: LL.cream, border: `1.5px solid ${LL.navy}`,
      fontSize: 11, color: LL.navyInk, fontWeight: 600,
    }}>{children}</div>
  );
}

function Stamp({ type, rot }) {
  const color = type === 'like' ? LL.green : LL.red;
  const text = type === 'like' ? 'MATCH' : 'PASS';
  return (
    <div style={{
      position: 'absolute', top: 40, [type === 'like' ? 'left' : 'right']: 20,
      border: `4px solid ${color}`, color,
      padding: '8px 18px', borderRadius: 8,
      fontFamily: LL.fontDisplay, fontWeight: 900, fontSize: 36,
      transform: `rotate(${rot}deg)`,
      letterSpacing: 2,
    }}>
      {text}
    </div>
  );
}

function ActionButton({ onClick, variant }) {
  const styles = {
    reject: { color: LL.red, size: 56, icon: (
      <svg width="22" height="22" viewBox="0 0 22 22">
        <path d="M3 3 L 19 19 M 19 3 L 3 19" stroke={LL.red} strokeWidth="3" strokeLinecap="round"/>
      </svg>
    )},
    info: { color: LL.navy, size: 50, icon: (
      <svg width="22" height="22" viewBox="0 0 20 20">
        <circle cx="10" cy="4.5" r="1.6" fill={LL.navy}/>
        <rect x="8.4" y="8" width="3.2" height="9" rx="1.6" fill={LL.navy}/>
      </svg>
    )},
    like: { color: LL.green, size: 56, icon: (
      <svg width="24" height="24" viewBox="0 0 24 24">
        <path d="M2 12 L 9 19 L 22 5" stroke={LL.green} strokeWidth="3.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    )},
  }[variant];
  return (
    <button onClick={onClick} style={{
      width: styles.size, height: styles.size, borderRadius: '50%',
      background: LL.cream, border: `2px solid ${styles.color}`,
      cursor: 'pointer',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      boxShadow: '0 4px 8px rgba(0,0,0,0.15)',
    }}>
      {styles.icon}
    </button>
  );
}

// ──────────────────────────────────────────────────────────────
// INFO SHEET — aide à la compréhension d'un jeu pendant le swipe
// ──────────────────────────────────────────────────────────────
const GAME_INFO = {
  scythe:       { theme: 'Europe alternative · 1920', themeColor: '#6B4423', vibe: 'Stratégique · Asymétrique', tags: ['4X', 'Contrôle de zone', 'Méchas'], ageMin: 14, hype: "Gagne en épique sans sombrer dans le conflit : l'asymétrie invite à jouer différemment à chaque partie." },
  dixit:        { theme: 'Onirique · Illustré',        themeColor: '#A6926B', vibe: 'Ambiance · Narration',     tags: ['Créatif', 'Party', 'Indices'],     ageMin: 8,  hype: "Parfait pour un groupe varié : on jauge ses amis autant que les cartes." },
  trio:         { theme: 'Cartes · Déduction',         themeColor: '#B48234', vibe: 'Rapide · Malin',           tags: ['Filler', 'Mémoire', 'Bluff'],      ageMin: 6,  hype: "Un filler redoutablement malin — 15 min et on en relance une." },
  catan:        { theme: 'Île colonisée',              themeColor: '#C67A35', vibe: 'Négociation · Gestion',    tags: ['Ressources', 'Commerce', 'Dés'],   ageMin: 10, hype: "Le classique intemporel : échanges serrés, construction et une pointe de hasard." },
  darwin:       { theme: 'Expédition scientifique',    themeColor: '#556B82', vibe: 'Placement d\'ouvriers',    tags: ['Éducatif', 'Course', 'Progression'], ageMin: 12, hype: "Progression fine des ouvriers : chaque partie récompense la planification longue." },
  'dice-forge': { theme: 'Mythologie · Forge',         themeColor: '#8B6B3A', vibe: 'Dés · Deckbuilding',       tags: ['Builder', 'Chacun son tour', 'Pouvoirs'], ageMin: 10, hype: "Modifier ses propres dés est un plaisir rare — chaque manche change vos probabilités." },
  'blood-rage': { theme: 'Vikings · Ragnarök',         themeColor: '#7A1E1E', vibe: 'Ameritrash · Conquête',    tags: ['Draft', 'Combat', 'Gloire'],       ageMin: 14, hype: "Mourir peut rapporter plus que gagner — un retournement de paradigme jubilatoire." },
  odin:         { theme: 'Cartes nordiques',           themeColor: '#5D7E4A', vibe: 'Défausse · Rapide',        tags: ['Filler', 'Famille', 'Tour de table'], ageMin: 8, hype: "Décisions tranchantes en 20 minutes : accessible sans être simpliste." },
};

function InfoSheet({ game, onClose }) {
  const info = GAME_INFO[game.id] || { theme: 'Jeu de société', themeColor: LL.navy, vibe: game.mood || 'Ambiance', tags: [], ageMin: 10, hype: game.desc };
  return (
    <div
      onClick={onClose}
      style={{
        position: 'absolute', inset: 0, zIndex: 50,
        background: 'rgba(14,47,99,0.55)',
        display: 'flex', alignItems: 'flex-end',
        animation: 'll-fade-in .2s ease',
      }}
    >
      <style>{`
        @keyframes ll-fade-in { from { opacity: 0 } to { opacity: 1 } }
        @keyframes ll-slide-up { from { transform: translateY(100%) } to { transform: translateY(0) } }
      `}</style>
      <div
        onClick={e => e.stopPropagation()}
        style={{
          width: '100%', background: LL.cream,
          borderTopLeftRadius: 28, borderTopRightRadius: 28,
          padding: '10px 22px 28px',
          maxHeight: '82%', overflowY: 'auto',
          boxShadow: '0 -10px 30px rgba(14,47,99,0.25)',
          animation: 'll-slide-up .28s ease',
        }}
      >
        {/* grab handle */}
        <div style={{
          width: 44, height: 4, borderRadius: 2,
          background: 'rgba(23,70,141,0.25)',
          margin: '4px auto 18px',
        }}/>

        {/* header — name + theme chip */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14,
        }}>
          <div style={{
            width: 52, height: 52, borderRadius: 12,
            background: game.bg || '#E8D5B0',
            position: 'relative', overflow: 'hidden',
            flexShrink: 0,
            border: `1.5px solid ${LL.navy}`,
          }}>
            <GameCover game={game} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontFamily: LL.fontDisplay, fontSize: 22, fontWeight: 800,
              color: LL.navyInk, lineHeight: 1.1,
            }}>{game.name}</div>
            <div style={{
              fontFamily: LL.fontBody, fontSize: 12, color: LL.mutedInk,
              marginTop: 3, display: 'flex', alignItems: 'center', gap: 6,
            }}>
              <span style={{
                width: 8, height: 8, borderRadius: '50%',
                background: info.themeColor, flexShrink: 0,
              }}/>
              {info.theme}
            </div>
          </div>
          <button onClick={onClose} aria-label="Fermer" style={{
            width: 36, height: 36, borderRadius: '50%',
            background: 'transparent', border: 'none', cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0,
          }}>
            <svg width="14" height="14" viewBox="0 0 14 14">
              <path d="M1 1 L 13 13 M 13 1 L 1 13" stroke={LL.navyInk} strokeWidth="2" strokeLinecap="round"/>
            </svg>
          </button>
        </div>

        {/* stat row */}
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)',
          gap: 0, padding: '14px 0',
          borderTop: `1px solid rgba(23,70,141,0.18)`,
          borderBottom: `1px solid rgba(23,70,141,0.18)`,
          marginBottom: 18,
        }}>
          {[
            ['joueurs', game.players, (
              <svg width="16" height="16" viewBox="0 0 16 16"><circle cx="5" cy="5" r="2.2" fill="none" stroke={LL.navy} strokeWidth="1.5"/><circle cx="11" cy="5" r="2.2" fill="none" stroke={LL.navy} strokeWidth="1.5"/><path d="M1 13 c0-2 2-3.5 4-3.5 s4 1.5 4 3.5" fill="none" stroke={LL.navy} strokeWidth="1.5"/><path d="M7 13 c0-2 2-3.5 4-3.5 s4 1.5 4 3.5" fill="none" stroke={LL.navy} strokeWidth="1.5"/></svg>
            )],
            ['durée', game.duration, (
              <svg width="16" height="16" viewBox="0 0 16 16"><circle cx="8" cy="8" r="6" fill="none" stroke={LL.navy} strokeWidth="1.5"/><path d="M8 4 v4 l2.5 2" stroke={LL.navy} strokeWidth="1.5" fill="none" strokeLinecap="round"/></svg>
            )],
            ['complexité', <ComplexityDots w={game.weight} />, null],
            ['âge', `${info.ageMin}+`, (
              <svg width="16" height="16" viewBox="0 0 16 16"><path d="M2 13 L 8 3 L 14 13 M 4.5 9 L 11.5 9" stroke={LL.navy} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
            )],
          ].map(([label, value, icon], i) => (
            <div key={i} style={{
              display: 'flex', flexDirection: 'column', alignItems: 'center',
              gap: 4,
              borderLeft: i > 0 ? `1px solid rgba(23,70,141,0.12)` : 'none',
            }}>
              {icon && <div style={{ height: 16 }}>{icon}</div>}
              <div style={{
                fontFamily: LL.fontDisplay, fontSize: 14, fontWeight: 800,
                color: LL.navyInk, lineHeight: 1.1,
              }}>{value}</div>
              <div style={{
                fontFamily: LL.fontBody, fontSize: 9.5,
                color: LL.muted, textTransform: 'uppercase',
                letterSpacing: 1, fontWeight: 600,
              }}>{label}</div>
            </div>
          ))}
        </div>

        {/* ambiance */}
        <div style={{ marginBottom: 14 }}>
          <SheetLabel>Ambiance</SheetLabel>
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 17, fontWeight: 700,
            color: LL.navyInk,
          }}>{info.vibe}</div>
        </div>

        {/* tags */}
        {info.tags.length > 0 && (
          <div style={{ marginBottom: 18 }}>
            <SheetLabel>Mécaniques</SheetLabel>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {info.tags.map(t => (
                <span key={t} style={{
                  padding: '5px 11px', borderRadius: 999,
                  border: `1.5px solid ${LL.navy}`,
                  fontFamily: LL.fontBody, fontSize: 11.5, fontWeight: 600,
                  color: LL.navyInk,
                }}>{t}</span>
              ))}
            </div>
          </div>
        )}

        {/* description */}
        <div style={{ marginBottom: 20 }}>
          <SheetLabel>En quelques mots</SheetLabel>
          <p style={{
            margin: 0, fontFamily: LL.fontBody, fontSize: 13.5,
            color: LL.navyInk, lineHeight: 1.55,
          }}>{game.desc}</p>
        </div>

        {/* why — matching your session filters */}
        <div style={{
          padding: 14, borderRadius: 16,
          background: 'rgba(23,70,141,0.06)',
          border: `1px dashed rgba(23,70,141,0.3)`,
          display: 'flex', gap: 12, alignItems: 'flex-start',
        }}>
          <div style={{
            width: 28, height: 28, borderRadius: '50%',
            background: LL.navy, color: LL.cream,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0, fontSize: 14,
          }}>
            <svg width="14" height="14" viewBox="0 0 14 14">
              <path d="M7 1 L 8.8 5 L 13 5.5 L 9.9 8.5 L 10.6 13 L 7 10.8 L 3.4 13 L 4.1 8.5 L 1 5.5 L 5.2 5 Z" fill={LL.cream}/>
            </svg>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{
              fontFamily: LL.fontBody, fontSize: 11,
              color: LL.muted, letterSpacing: 1, textTransform: 'uppercase',
              fontWeight: 600, marginBottom: 3,
            }}>Pourquoi on vous le propose</div>
            <div style={{
              fontFamily: LL.fontBody, fontSize: 13, color: LL.navyInk, lineHeight: 1.55,
            }}>{info.hype}</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function SheetLabel({ children }) {
  return (
    <div style={{
      fontFamily: LL.fontBody, fontSize: 10,
      color: LL.muted, letterSpacing: 1.5, textTransform: 'uppercase',
      fontWeight: 700, marginBottom: 6,
    }}>{children}</div>
  );
}

function ComplexityDots({ w }) {
  // w ∈ 1..5 (from game.weight)
  const filled = Math.round(w);
  return (
    <div style={{ display: 'flex', gap: 3, alignItems: 'center', height: 14 }}>
      {[1,2,3,4,5].map(i => (
        <div key={i} style={{
          width: 6, height: 6, borderRadius: '50%',
          background: i <= filled ? LL.navy : 'transparent',
          border: `1.2px solid ${LL.navy}`,
        }}/>
      ))}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// MATCH screen — animated celebration
// ──────────────────────────────────────────────────────────────
function MatchScreen({ game, onClose, onPlay }) {
  if (!game) return null;
  const [scale, setScale] = React.useState(0.4);
  const [ringR, setRingR] = React.useState(0);
  React.useEffect(() => {
    const t1 = setTimeout(() => setScale(1), 20);
    let r = 0;
    const iv = setInterval(() => { r += 8; setRingR(r); if (r > 400) clearInterval(iv); }, 20);
    return () => clearInterval(iv);
  }, []);

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

  return (
    <Screen style={{ background: `radial-gradient(circle at 50% 40%, #1D5AB8 0%, ${LL.navyDeep} 70%)` }}>
      <StatusBar />

      {/* burst rings */}
      {[0, 1, 2].map(i => {
        const r = Math.max(0, ringR - i * 120);
        return (
          <div key={i} style={{
            position: 'absolute', left: '50%', top: '42%',
            width: r * 2, height: r * 2, borderRadius: '50%',
            transform: 'translate(-50%, -50%)',
            border: `2px solid rgba(242, 229, 202, ${Math.max(0, 0.5 - r/500)})`,
            pointerEvents: 'none',
          }}/>
        );
      })}

      {/* confetti */}
      {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 : 10,
          background: c.color,
          borderRadius: c.shape === 1 ? '50%' : 2,
          transform: `rotate(${c.rot}deg)`,
          animation: `confettiFall ${c.dur}ms ${c.delay}ms linear forwards`,
          pointerEvents: 'none',
        }}/>
      ))}

      {/* Responsive flex layout — header / cover / CTA stack, no overlap */}
      <div style={{
        position: 'absolute', inset: 0,
        display: 'flex', flexDirection: 'column',
        padding: '60px 22px calc(env(safe-area-inset-bottom) + 24px)',
        boxSizing: 'border-box', color: LL.cream,
      }}>
        <div style={{ textAlign: 'center', flexShrink: 0 }}>
          <div style={{
            fontFamily: LL.fontDisplay, fontSize: 64, fontWeight: 900,
            letterSpacing: -2, lineHeight: 0.9,
            textShadow: '0 4px 20px rgba(0,0,0,0.4)',
            transform: `scale(${scale})`, transition: 'transform .5s cubic-bezier(.18,1.4,.4,1)',
          }}>
            MATCH !
          </div>
          <div style={{ fontFamily: LL.fontBody, fontSize: 14, opacity: 0.85, marginTop: 6 }}>
            Tout le groupe a swipé ce jeu
          </div>
        </div>

        {/* Cover — full image visible (contain), scales with available space */}
        <div style={{
          flex: 1, minHeight: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          padding: '18px 0',
          transform: `scale(${scale})`,
          transition: 'transform .6s cubic-bezier(.18,1.4,.4,1)',
        }}>
          <div style={{
            width: '100%', maxWidth: 260, aspectRatio: '3 / 4',
            borderRadius: 18, overflow: 'hidden',
            background: game.bg || LL.navy,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            boxShadow: '0 12px 30px rgba(0,0,0,0.35)',
          }}>
            {game.cover_url ? (
              <img src={game.cover_url} alt={game.name}
                style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }}/>
            ) : (
              <GameTile game={game} size="xl" />
            )}
          </div>
        </div>

        <div style={{ flexShrink: 0 }}>
          <LLButton onClick={onPlay} size="lg" style={{
            width: '100%', background: LL.cream, color: LL.navyDeep,
          }}>
            On joue !
          </LLButton>
        </div>
      </div>

      <style>{`
        @keyframes confettiFall {
          0% { transform: translateY(0) rotate(0deg); }
          100% { transform: translateY(900px) rotate(720deg); }
        }
      `}</style>
    </Screen>
  );
}

Object.assign(window, {
  OnboardingScreen, SignupScreen, LoginScreen, BackHeader,
  NewSessionScreen, LobbyScreen, SwipeScreen, MatchScreen,
});
