// desktop-screens.jsx — composants pour écrans ≥ 768 px (design PC natif)
;(function () {
  'use strict';

  const { useState, useEffect, useContext, useRef, useCallback } = React;
  const AppCtx  = window.AppCtx;
  const API     = window.API;
  const C       = window.__club || {};
  const Ic      = window.SimpleHelpers.Ic;
  const MiniQR  = window.SimpleHelpers.MiniQR;

  function useCtx() { return useContext(AppCtx) || {}; }

  function handleAuthErr(err, setAppState, goTo) {
    if (err && err.status === 401) {
      localStorage.removeItem('token');
      setAppState(s => ({ ...s, token: null, user: null }));
      goTo('login');
      return true;
    }
    return false;
  }

  // ── Palette ────────────────────────────────────────────────────
  const navy   = 'var(--navy, #133478)';
  const yellow = 'var(--yellow, #FFCF21)';
  const bg     = '#F4F6FB';
  const line   = '#E4E8F4';

  // ── Nav header ─────────────────────────────────────────────────
  function DeskNav({ showBack = false }) {
    const { back, appState, goTo } = useCtx();
    return (
      <header style={{
        background: '#fff', borderBottom: `1px solid ${line}`,
        padding: '0 40px', height: 64,
        display: 'flex', alignItems: 'center', gap: 20,
        position: 'sticky', top: 0, zIndex: 100,
        boxShadow: '0 1px 8px rgba(0,0,0,0.04)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, flex: 1, cursor: 'pointer' }}
          onClick={() => goTo('home')}>
          {C.logo && <img src={C.logo} alt="" style={{ height: 36, width: 36, objectFit: 'contain' }} />}
          <div>
            <div style={{ fontSize: 15, fontWeight: 800, color: navy, lineHeight: 1.1 }}>{C.name || 'Billetterie'}</div>
            <div style={{ fontSize: 11, color: '#8899BB', fontWeight: 500 }}>{C.billetterieLabel || 'Billetterie officielle'}</div>
          </div>
        </div>

        {showBack && (
          <button onClick={back} style={{
            display: 'flex', alignItems: 'center', gap: 6,
            background: '#F4F6FB', border: `1px solid ${line}`,
            color: navy, fontSize: 14, fontWeight: 600,
            padding: '7px 14px', borderRadius: 9, cursor: 'pointer',
          }}>
            <Ic name="back" size={17} stroke={2.2} /> Retour
          </button>
        )}

        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          {appState.token ? (
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{
                width: 34, height: 34, borderRadius: '50%',
                background: navy, color: '#fff',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 13, fontWeight: 800,
              }}>{(appState.user?.name || 'U')[0].toUpperCase()}</div>
              <span style={{ fontSize: 14, fontWeight: 600, color: navy }}>
                {appState.user?.name?.split(' ')[0] || 'Mon compte'}
              </span>
            </div>
          ) : (
            <button onClick={() => goTo('login')} style={{
              display: 'flex', alignItems: 'center', gap: 7,
              background: 'transparent', border: `1.5px solid ${navy}`,
              color: navy, borderRadius: 9, padding: '7px 16px',
              fontSize: 13, fontWeight: 700, cursor: 'pointer',
            }}>
              <Ic name="user" size={16} /> Se connecter
            </button>
          )}
          <button style={{
            background: 'transparent', border: 0, cursor: 'pointer',
            color: '#A0ABC0', padding: '6px 8px', borderRadius: 8,
            display: 'flex', alignItems: 'center',
          }}>
            <Ic name="help" size={20} />
          </button>
        </div>
      </header>
    );
  }

  // ── Checkout progress bar ──────────────────────────────────────
  function DeskSteps({ step }) {
    const labels = ['Match', 'Tribune', 'Places', 'Paiement'];
    return (
      <div style={{ display: 'flex', alignItems: 'center', marginBottom: 32 }}>
        {labels.map((label, i) => {
          const done   = i < step - 1;
          const active = i === step - 1;
          return (
            <React.Fragment key={i}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <div style={{
                  width: 28, height: 28, borderRadius: '50%', flexShrink: 0,
                  background: done ? '#16A34A' : active ? navy : '#E4E8F4',
                  color: (done || active) ? '#fff' : '#A0ABC0',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 12, fontWeight: 800,
                }}>
                  {done ? '✓' : i + 1}
                </div>
                <span style={{
                  fontSize: 13, fontWeight: active ? 700 : 500,
                  color: active ? navy : done ? '#16A34A' : '#A0ABC0',
                  whiteSpace: 'nowrap',
                }}>{label}</span>
              </div>
              {i < labels.length - 1 && (
                <div style={{ flex: 1, height: 2, background: done ? '#16A34A' : '#E4E8F4', margin: '0 12px', minWidth: 20 }} />
              )}
            </React.Fragment>
          );
        })}
      </div>
    );
  }

  // ── Page shell ─────────────────────────────────────────────────
  function DeskPage({ children, nav, pageBg = bg }) {
    return (
      <div style={{ width: '100%', minHeight: '100vh', background: pageBg, display: 'flex', flexDirection: 'column' }}>
        {nav}
        <div style={{ flex: 1 }}>{children}</div>
      </div>
    );
  }

  function DeskContent({ children, maxWidth = 1100 }) {
    return (
      <div style={{ maxWidth, margin: '0 auto', padding: '40px 40px 80px', width: '100%' }}>
        {children}
      </div>
    );
  }

  // ── Card ───────────────────────────────────────────────────────
  function Card({ children, style = {} }) {
    return (
      <div style={{
        background: '#fff', borderRadius: 16,
        border: `1.5px solid ${line}`,
        ...style,
      }}>{children}</div>
    );
  }

  // ── Match card ─────────────────────────────────────────────────
  function DeskMatchCard({ match: m, featured = false, onBook }) {
    const sold    = m.status === 'sold';
    const limited = m.status === 'limited';
    return (
      <div
        onClick={() => !sold && onBook()}
        style={{
          background: featured ? navy : '#fff',
          border: `1.5px solid ${featured ? 'transparent' : line}`,
          borderRadius: 16, padding: '22px 24px',
          display: 'grid', gridTemplateColumns: '72px 1fr auto',
          gap: 20, alignItems: 'center',
          cursor: sold ? 'default' : 'pointer',
          transition: 'box-shadow 0.15s',
          boxShadow: featured ? '0 8px 32px rgba(19,52,120,0.18)' : 'none',
        }}
        onMouseEnter={e => { if (!sold) e.currentTarget.style.boxShadow = '0 4px 20px rgba(0,0,0,0.10)'; }}
        onMouseLeave={e => { e.currentTarget.style.boxShadow = featured ? '0 8px 32px rgba(19,52,120,0.18)' : 'none'; }}
      >
        {/* Date */}
        <div style={{
          textAlign: 'center',
          borderRight: `1.5px solid ${featured ? 'rgba(255,255,255,0.15)' : line}`,
          paddingRight: 20,
        }}>
          <div style={{ fontSize: 30, fontWeight: 900, lineHeight: 1, color: featured ? '#fff' : navy }}>{m.dayNum}</div>
          <div style={{ fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: featured ? 'rgba(255,255,255,0.55)' : '#A0ABC0', marginTop: 2 }}>{m.monthShort}</div>
          <div style={{ fontSize: 12, color: featured ? 'rgba(255,255,255,0.45)' : '#B0B8CC', marginTop: 3 }}>{m.timeText}</div>
        </div>

        {/* Info */}
        <div>
          {featured && (
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: yellow, marginBottom: 6 }}>★ PROCHAIN MATCH</div>
          )}
          <div style={{ fontSize: featured ? 19 : 16, fontWeight: 800, color: featured ? '#fff' : navy, marginBottom: 3 }}>
            {C.name || 'Club'} — {m.away}
          </div>
          <div style={{ fontSize: 13, color: featured ? 'rgba(255,255,255,0.55)' : '#8899BB' }}>
            {m.dayText} · {C.stadiumShort || C.stadium || 'Stade'} · {C.city || ''}
          </div>
          {limited && (
            <span style={{ marginTop: 6, display: 'inline-block', background: 'rgba(234,88,12,0.1)', color: '#EA580C', borderRadius: 6, padding: '3px 8px', fontSize: 12, fontWeight: 600 }}>
              ⚠ Dernières places
            </span>
          )}
        </div>

        {/* CTA */}
        <div style={{ textAlign: 'right', flexShrink: 0 }}>
          {!sold && m.price_from > 0 && (
            <div style={{ fontSize: 13, color: featured ? 'rgba(255,255,255,0.55)' : '#8899BB', marginBottom: 8 }}>
              à partir de <b style={{ fontSize: 17, fontWeight: 900, color: featured ? '#fff' : navy }}>{m.price_from} €</b>
            </div>
          )}
          {sold ? (
            <span style={{ background: '#F3F4F6', color: '#9CA3AF', borderRadius: 8, padding: '9px 18px', fontSize: 13, fontWeight: 600 }}>Complet</span>
          ) : (
            <button
              onClick={e => { e.stopPropagation(); onBook(); }}
              style={{
                background: featured ? yellow : navy, color: featured ? navy : '#fff',
                border: 0, borderRadius: 10, padding: '10px 20px',
                fontSize: 14, fontWeight: 700, cursor: 'pointer',
                display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap',
              }}
            >
              Réserver <Ic name="arrow" size={16} />
            </button>
          )}
        </div>
      </div>
    );
  }

  // ── Stadium plan SVG (interactive) ────────────────────────────
  function StadiumPlan({ highlighted = null, onZoneClick }) {
    const zoneColor  = (key) => highlighted === key ? yellow          : 'rgba(255,255,255,0.10)';
    const strokeCol  = (key) => highlighted === key ? '#FFCF21'       : 'rgba(255,255,255,0.20)';
    const labelColor = (key) => highlighted === key ? navy            : 'rgba(255,255,255,0.45)';
    const cur        = (key) => onZoneClick ? 'pointer' : 'default';
    const click      = (key) => onZoneClick && onZoneClick(key);

    return (
      <svg viewBox="0 0 320 200" xmlns="http://www.w3.org/2000/svg" style={{ width: '100%' }}>
        {/* Présidentielle (haut) */}
        <path d="M62 14 L258 14 Q278 14 286 30 L286 50 L34 50 L34 30 Q42 14 62 14 Z"
          fill={zoneColor('presi')} stroke={strokeCol('presi')} strokeWidth="1.2"
          style={{ cursor: cur('presi'), transition: 'fill 0.2s' }}
          onClick={() => click('presi')} />
        <text x="160" y="36" textAnchor="middle" fontSize="9" fontWeight="700" fill={labelColor('presi')} letterSpacing="0.08em" style={{ pointerEvents: 'none' }}>PRÉSIDENTIELLE</text>

        {/* Officielle (bas) */}
        <path d="M34 152 L286 152 L286 172 Q278 188 258 188 L62 188 Q42 188 34 172 Z"
          fill={zoneColor('offi')} stroke={strokeCol('offi')} strokeWidth="1.2"
          style={{ cursor: cur('offi'), transition: 'fill 0.2s' }}
          onClick={() => click('offi')} />
        <text x="160" y="174" textAnchor="middle" fontSize="9" fontWeight="700" fill={labelColor('offi')} letterSpacing="0.08em" style={{ pointerEvents: 'none' }}>OFFICIELLE</text>

        {/* Famille (gauche haut) */}
        <path d="M34 54 L96 54 L96 96 L34 96 Z"
          fill={zoneColor('fam')} stroke={strokeCol('fam')} strokeWidth="1.2"
          style={{ cursor: cur('fam'), transition: 'fill 0.2s' }}
          onClick={() => click('fam')} />
        <text x="65" y="80" textAnchor="middle" fontSize="8.5" fontWeight="700" fill={labelColor('fam')} style={{ pointerEvents: 'none' }}>FAMILLE</text>

        {/* Ouest (gauche bas) */}
        <path d="M34 100 L96 100 L96 148 L34 148 Z"
          fill={zoneColor('ouest')} stroke={strokeCol('ouest')} strokeWidth="1.2"
          style={{ cursor: cur('ouest'), transition: 'fill 0.2s' }}
          onClick={() => click('ouest')} />
        <text x="65" y="125" textAnchor="middle" fontSize="8.5" fontWeight="700" fill={labelColor('ouest')} style={{ pointerEvents: 'none' }}>V. OUEST</text>

        {/* Kop (droite) */}
        <path d="M224 54 L286 54 L286 148 L224 148 Z"
          fill={zoneColor('kop')} stroke={strokeCol('kop')} strokeWidth="1.2"
          style={{ cursor: cur('kop'), transition: 'fill 0.2s' }}
          onClick={() => click('kop')} />
        <text x="255" y="97" textAnchor="middle" fontSize="9" fontWeight="700" fill={labelColor('kop')} style={{ pointerEvents: 'none' }}>VIRAGE EST</text>
        <text x="255" y="110" textAnchor="middle" fontSize="8.5" fontWeight="600" fill={labelColor('kop')} opacity="0.85" style={{ pointerEvents: 'none' }}>LE KOP</text>

        {/* Pelouse */}
        <rect x="104" y="58" width="116" height="86" rx="3" fill="rgba(255,255,255,0.06)" stroke="rgba(255,255,255,0.18)" strokeWidth="0.8"/>
        <line x1="162" y1="58" x2="162" y2="144" stroke="rgba(255,255,255,0.15)" strokeWidth="0.5"/>
        <circle cx="162" cy="101" r="10" fill="none" stroke="rgba(255,255,255,0.15)" strokeWidth="0.5"/>
        <circle cx="162" cy="101" r="1.5" fill="rgba(255,255,255,0.25)"/>
      </svg>
    );
  }

  // Map zone name → SVG key
  function svgKey(name) {
    const n = (name || '').toLowerCase();
    if (n.includes('présidentielle') || n.includes('presidentielle')) return 'presi';
    if (n.includes('officielle') && !n.includes('pmr'))               return 'offi';
    if (n.includes('famille'))                                         return 'fam';
    if (n.includes('kop') || (n.includes('virage') && n.includes('est'))) return 'kop';
    if (n.includes('ouest'))                                           return 'ouest';
    return null;
  }

  // ── Auth helpers ───────────────────────────────────────────────
  function AuthCard({ title, subtitle, children }) {
    const { goTo } = useCtx();
    return (
      <div style={{ width: '100%', minHeight: '100vh', display: 'flex' }}>
        {/* Gauche : branding */}
        <div style={{
          width: '42%', flexShrink: 0,
          background: navy,
          display: 'flex', flexDirection: 'column',
          justifyContent: 'space-between',
          padding: '40px 48px',
        }}>
          {/* Logo en haut */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer' }} onClick={() => goTo('welcome')}>
            {C.logo && <img src={C.logo} alt="" style={{ height: 36, objectFit: 'contain' }} />}
            <span style={{ color: '#fff', fontSize: 16, fontWeight: 800 }}>{C.name || 'Billetterie'}</span>
          </div>

          {/* Centre : pitch */}
          <div>
            <h2 style={{ color: '#fff', fontSize: 36, fontWeight: 900, letterSpacing: '-0.03em', margin: '0 0 16px', lineHeight: 1.1 }}>
              Votre club,<br/>
              <span style={{ color: yellow }}>vos places.</span>
            </h2>
            <p style={{ color: 'rgba(255,255,255,0.55)', fontSize: 15, lineHeight: 1.6, margin: '0 0 36px', maxWidth: 300 }}>
              Achetez vos billets, gérez votre abonnement et retrouvez toutes vos places au même endroit.
            </p>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              {['Paiement 100 % sécurisé', 'Billet envoyé par email', 'Échangeable jusqu\'à J−1'].map(t => (
                <div key={t} style={{ display: 'flex', alignItems: 'center', gap: 10, color: 'rgba(255,255,255,0.60)', fontSize: 14 }}>
                  <span style={{ color: yellow, fontSize: 8, flexShrink: 0 }}>●</span>{t}
                </div>
              ))}
            </div>
          </div>

          {/* Bas : saison */}
          <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.30)', letterSpacing: '0.06em' }}>
            {C.billetterieLabel || 'BILLETTERIE OFFICIELLE'}
          </div>
        </div>

        {/* Droite : formulaire */}
        <div style={{
          flex: 1,
          background: '#fff',
          display: 'flex', flexDirection: 'column',
          justifyContent: 'center', alignItems: 'center',
          padding: '60px 48px',
        }}>
          <div style={{ width: '100%', maxWidth: 400 }}>
            <h1 style={{ margin: '0 0 6px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>{title}</h1>
            {subtitle && <p style={{ margin: '0 0 28px', color: '#8899BB', fontSize: 15 }}>{subtitle}</p>}
            {children}
          </div>
        </div>
      </div>
    );
  }

  function Field({ label, type = 'text', value, onChange, placeholder, autoComplete }) {
    return (
      <div style={{ marginBottom: 18 }}>
        <label style={{ display: 'block', fontSize: 13, fontWeight: 700, color: navy, marginBottom: 7 }}>{label}</label>
        <input
          type={type} value={value} onChange={e => onChange(e.target.value)}
          placeholder={placeholder} autoComplete={autoComplete}
          style={{
            width: '100%', padding: '11px 14px', borderRadius: 10,
            border: `1.5px solid ${line}`, fontSize: 15, color: navy,
            fontFamily: 'inherit', outline: 'none', background: '#fff',
            boxSizing: 'border-box',
          }}
          onFocus={e => e.target.style.borderColor = navy}
          onBlur={e  => e.target.style.borderColor = line}
        />
      </div>
    );
  }

  function ErrMsg({ msg }) {
    if (!msg) return null;
    return (
      <p style={{
        color: '#DC2626', fontSize: 14, margin: '0 0 16px',
        padding: '10px 14px', background: 'rgba(220,38,38,0.07)',
        borderRadius: 10, lineHeight: 1.45,
      }}>{msg}</p>
    );
  }

  function PrimaryBtn({ onClick, disabled, busy, children, style = {} }) {
    return (
      <button onClick={onClick} disabled={disabled || busy} style={{
        width: '100%', padding: '14px 24px',
        background: yellow, color: navy,
        border: 0, borderRadius: 12, fontSize: 15, fontWeight: 800, cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        opacity: (disabled || busy) ? 0.55 : 1,
        ...style,
      }}>
        {busy ? 'Chargement…' : children}
      </button>
    );
  }

  // ══════════════════════════════════════════════════════════════
  // SCREENS
  // ══════════════════════════════════════════════════════════════

  // ── Welcome ────────────────────────────────────────────────────
  function DeskScrWelcome() {
    const { goTo } = useCtx();
    return (
      <div style={{ width: '100%', minHeight: '100vh', background: navy, display: 'flex', flexDirection: 'column' }}>
        {/* Top bar */}
        <header style={{ padding: '20px 40px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            {C.logo && <img src={C.logo} alt="" style={{ height: 44, width: 44, objectFit: 'contain' }} />}
            <div style={{ color: '#fff' }}>
              <div style={{ fontSize: 18, fontWeight: 900, lineHeight: 1.1 }}>{C.name || 'Billetterie'}</div>
              <div style={{ fontSize: 12, opacity: 0.55 }}>{C.billetterieLabel || 'Billetterie officielle'}</div>
            </div>
          </div>
          <button onClick={() => goTo('login')} style={{
            background: 'rgba(255,255,255,0.10)', border: '1px solid rgba(255,255,255,0.22)',
            color: '#fff', borderRadius: 10, padding: '9px 22px',
            fontSize: 14, fontWeight: 600, cursor: 'pointer',
          }}>
            Se connecter
          </button>
        </header>

        {/* Hero */}
        <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '60px 40px', flexDirection: 'column', textAlign: 'center' }}>
          {C.logo && <img src={C.logo} alt="" style={{ height: 90, width: 90, objectFit: 'contain', marginBottom: 28 }} />}
          <h1 style={{ color: '#fff', margin: '0 0 18px', fontSize: 56, fontWeight: 900, lineHeight: 1.08, letterSpacing: '-0.03em' }}>
            Bienvenue<br/>
            <span style={{ color: yellow }}>{C.name || 'Billetterie officielle'}</span>
          </h1>
          <p style={{ color: 'rgba(255,255,255,0.60)', fontSize: 18, maxWidth: 480, margin: '0 auto 44px', lineHeight: 1.6 }}>
            Achetez vos places en quelques minutes, recevez vos billets directement par email.
          </p>
          <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', justifyContent: 'center' }}>
            <button onClick={() => goTo('list')} style={{
              background: yellow, color: navy, border: 0, borderRadius: 12,
              padding: '15px 36px', fontSize: 16, fontWeight: 800, cursor: 'pointer',
              display: 'flex', alignItems: 'center', gap: 8,
            }}>
              Voir les matchs <Ic name="arrow" size={20} />
            </button>
            <button onClick={() => goTo('login')} style={{
              background: 'rgba(255,255,255,0.10)', border: '1px solid rgba(255,255,255,0.22)',
              color: '#fff', borderRadius: 12, padding: '15px 36px',
              fontSize: 16, fontWeight: 600, cursor: 'pointer',
            }}>
              Je suis abonné
            </button>
          </div>
          <div style={{ marginTop: 22, fontSize: 14, color: 'rgba(255,255,255,0.45)' }}>
            Déjà un compte ?{' '}
            <a href="#" onClick={e => { e.preventDefault(); goTo('login'); }}
              style={{ color: yellow, fontWeight: 600, textDecoration: 'none' }}>
              Se connecter
            </a>
          </div>
        </div>

        {/* Trust bar */}
        <div style={{ borderTop: '1px solid rgba(255,255,255,0.10)', padding: '18px 40px', display: 'flex', justifyContent: 'center', gap: 48, flexWrap: 'wrap' }}>
          {['Paiement 100 % sécurisé', 'Billet envoyé par email', 'Échangeable jusqu\'à J−1'].map(t => (
            <div key={t} style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'rgba(255,255,255,0.50)', fontSize: 13 }}>
              <span style={{ color: yellow, fontSize: 8 }}>●</span> {t}
            </div>
          ))}
        </div>
      </div>
    );
  }

  // ── Home ───────────────────────────────────────────────────────
  function DeskScrHome() {
    const { appState, setAppState, goTo } = useCtx();
    const [matches,      setMatches]      = useState([]);
    const [subscription, setSubscription] = useState(null);
    const [loading,      setLoading]      = useState(true);

    useEffect(() => {
      (async () => {
        try { setMatches(await API.matches('upcoming')); } catch {}
        if (appState.token) {
          try {
            const s = await API.subscription();
            setSubscription(s.subscription || null);
          } catch {}
        }
        setLoading(false);
      })();
    }, []);

    const goToMatch = m => { setAppState(s => ({ ...s, match: m })); goTo('zones'); };

    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 32, alignItems: 'start' }}>
            {/* Main */}
            <div>
              <h1 style={{ margin: '0 0 4px', fontSize: 30, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>
                {appState.user?.name ? `Bonjour, ${appState.user.name.split(' ')[0]} 👋` : 'Prochains matchs'}
              </h1>
              <p style={{ color: '#8899BB', margin: '0 0 28px', fontSize: 15 }}>
                À domicile · {C.stadium || 'Stade'}
                {!loading && ` · ${matches.length} match${matches.length !== 1 ? 's' : ''} à venir`}
              </p>
              {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                  {matches.map((m, i) => <DeskMatchCard key={m.id} match={m} featured={i === 0} onBook={() => goToMatch(m)} />)}
                  {matches.length === 0 && <p style={{ color: '#A0ABC0' }}>Aucun match à venir pour le moment.</p>}
                </div>
              )}
            </div>

            {/* Sidebar */}
            <div style={{ position: 'sticky', top: 80, display: 'flex', flexDirection: 'column', gap: 14 }}>
              {subscription ? (
                <Card style={{ padding: 24, background: navy, border: 'none', cursor: 'pointer' }} onClick={() => goTo('wallet')}>
                  <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'rgba(255,255,255,0.5)', marginBottom: 10 }}>MON ABONNEMENT</div>
                  <div style={{ fontSize: 19, fontWeight: 800, color: '#fff', marginBottom: 4 }}>Carte {subscription.season}</div>
                  <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.65)', marginBottom: 20 }}>
                    {subscription.zone}{subscription.seat_number ? ` · Place ${subscription.seat_number}` : ''}
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, fontWeight: 700, color: yellow }}>
                    Voir mon abonnement <Ic name="arrow" size={15} />
                  </div>
                </Card>
              ) : !appState.token && (
                <Card style={{ padding: 24 }}>
                  <div style={{ fontSize: 16, fontWeight: 800, color: navy, marginBottom: 8 }}>Mon compte</div>
                  <p style={{ fontSize: 13, color: '#8899BB', margin: '0 0 16px', lineHeight: 1.5 }}>
                    Connectez-vous pour retrouver vos billets et votre abonnement.
                  </p>
                  <button onClick={() => goTo('login')} style={{
                    width: '100%', padding: '12px', background: navy, color: '#fff',
                    border: 0, borderRadius: 10, fontSize: 14, fontWeight: 700, cursor: 'pointer', marginBottom: 8,
                  }}>Se connecter</button>
                  <button onClick={() => goTo('signup')} style={{
                    width: '100%', padding: '11px', background: 'transparent', color: navy,
                    border: `1.5px solid ${line}`, borderRadius: 10, fontSize: 14, fontWeight: 600, cursor: 'pointer',
                  }}>Créer un compte</button>
                </Card>
              )}

              {appState.token && (
                <Card style={{ padding: 20 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: navy, marginBottom: 6 }}>Mes billets</div>
                  <button onClick={() => goTo('history')} style={{
                    width: '100%', padding: '11px', background: '#F4F6FB', color: navy,
                    border: `1.5px solid ${line}`, borderRadius: 10, fontSize: 13, fontWeight: 600,
                    cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
                  }}>
                    <Ic name="ticket" size={16} /> Historique des achats
                  </button>
                </Card>
              )}

              {C.phone && (
                <Card style={{ padding: 20 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: navy, marginBottom: 4 }}>Une question ?</div>
                  <p style={{ fontSize: 13, color: '#8899BB', margin: '0 0 12px', lineHeight: 1.5 }}>Lun–Ven · 9h–18h</p>
                  <a href={`tel:${C.phone}`} style={{ display: 'flex', alignItems: 'center', gap: 8, color: navy, fontSize: 14, fontWeight: 600, textDecoration: 'none' }}>
                    <Ic name="phone" size={16} />{C.phone}
                  </a>
                </Card>
              )}
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── List ───────────────────────────────────────────────────────
  function DeskScrList() {
    const { setAppState, goTo } = useCtx();
    const [matches, setMatches] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error,   setError]   = useState(null);

    useEffect(() => {
      API.matches('upcoming')
        .then(ms => setMatches(ms))
        .catch(e => setError(e.message))
        .finally(() => setLoading(false));
    }, []);

    const byMonth = matches.reduce((acc, m) => {
      const key = m.monthText + ' ' + new Date(m.date.slice(0, 10) + 'T12:00:00').getFullYear();
      if (!acc[key]) acc[key] = [];
      acc[key].push(m);
      return acc;
    }, {});

    const goToMatch = m => { setAppState(s => ({ ...s, match: m })); goTo('zones'); };

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={860}>
          <DeskSteps step={1} />
          <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>
            Choisissez votre match
          </h1>
          <p style={{ color: '#8899BB', margin: '0 0 36px', fontSize: 15 }}>
            {loading ? 'Chargement…' : `${matches.length} match${matches.length !== 1 ? 's' : ''} à venir au ${C.stadiumShort || C.stadium || 'stade'}`}
          </p>

          {loading && <p style={{ color: '#A0ABC0' }}>Chargement…</p>}
          {error   && <p style={{ color: '#DC2626' }}>{error}</p>}

          {Object.entries(byMonth).map(([month, ms]) => (
            <div key={month} style={{ marginBottom: 36 }}>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: '#A0ABC0', marginBottom: 14 }}>{month}</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {ms.map(m => <DeskMatchCard key={m.id} match={m} onBook={() => goToMatch(m)} />)}
              </div>
            </div>
          ))}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Zones ──────────────────────────────────────────────────────
  function DeskScrZones() {
    const { appState, setAppState, goTo } = useCtx();
    const [zones,    setZones]    = useState([]);
    const [selected, setSelected] = useState(null);
    const [loading,  setLoading]  = useState(true);
    const match = appState.match;

    useEffect(() => {
      if (!match?.id) { setLoading(false); return; }
      API.zones(match.id)
        .then(zs => {
          setZones(zs);
          const first = zs.find(z => z.status !== 'closed' && z.status !== 'sold');
          if (first) setSelected(first.id);
        })
        .catch(() => {})
        .finally(() => setLoading(false));
    }, [match?.id]);

    const selectedZone = zones.find(z => z.id === selected) || null;

    const handleContinue = () => {
      if (!selectedZone) return;
      setAppState(s => ({ ...s, zone: selectedZone, selectedSeats: [], holdId: null }));
      goTo('seats');
    };

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={1000}>
          <DeskSteps step={2} />
          <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>
            Où souhaitez-vous vous asseoir ?
          </h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>
            {C.name || 'Club'} — {match?.away || '…'} · {match?.dateText || ''}{match?.timeText ? ` à ${match.timeText}` : ''}
          </p>

          {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 340px', gap: 28, alignItems: 'start' }}>
              {/* Zone list */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {zones.map(z => {
                  const sold = z.status === 'sold' || z.status === 'closed';
                  const on   = selected === z.id;
                  return (
                    <div
                      key={z.id}
                      onClick={() => !sold && setSelected(z.id)}
                      style={{
                        background: on ? navy : '#fff',
                        border: `2px solid ${on ? navy : line}`,
                        borderRadius: 14, padding: '18px 22px',
                        display: 'grid', gridTemplateColumns: '1fr auto auto',
                        gap: 18, alignItems: 'center',
                        cursor: sold ? 'not-allowed' : 'pointer',
                        opacity: sold ? 0.6 : 1, transition: 'all 0.15s',
                      }}
                    >
                      <div>
                        <div style={{ fontSize: 16, fontWeight: 700, color: on ? '#fff' : navy, marginBottom: 3 }}>{z.name}</div>
                        <div style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.60)' : '#8899BB', lineHeight: 1.4 }}>
                          {z.description || (z.standing ? 'Tribune debout' : 'Tribune assise')}
                          {!sold && !z.standing && z.seats_free != null && (
                            <span style={{ marginLeft: 8, fontWeight: 600, color: z.seats_free < 20 ? '#EA580C' : '#16A34A' }}>
                              · {z.seats_free} place{z.seats_free > 1 ? 's' : ''}
                            </span>
                          )}
                        </div>
                      </div>
                      <div style={{ textAlign: 'right' }}>
                        {sold ? (
                          <span style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.45)' : '#A0ABC0' }}>Complet</span>
                        ) : (
                          <>
                            <div style={{ fontSize: 22, fontWeight: 900, color: on ? '#fff' : navy, lineHeight: 1 }}>{z.price} €</div>
                            <div style={{ fontSize: 12, color: on ? 'rgba(255,255,255,0.50)' : '#A0ABC0' }}>par place</div>
                          </>
                        )}
                      </div>
                      <div style={{
                        width: 22, height: 22, borderRadius: '50%', flexShrink: 0,
                        border: `2px solid ${on ? '#fff' : '#D1D5DB'}`,
                        background: on ? '#fff' : 'transparent',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                      }}>
                        {on && <div style={{ width: 10, height: 10, borderRadius: '50%', background: navy }} />}
                      </div>
                    </div>
                  );
                })}

                {/* Tip */}
                <div style={{
                  background: '#EFF6FF', border: '1.5px solid #DBEAFE',
                  borderRadius: 12, padding: '14px 18px',
                  display: 'flex', gap: 12, alignItems: 'flex-start', marginTop: 4,
                }}>
                  <span style={{ color: '#3B82F6', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
                  <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                    Le <b>Virage Est</b> est la tribune ambiance. La <b>Tribune Famille</b> est calme et adaptée aux enfants.
                  </p>
                </div>
              </div>

              {/* Right: stadium plan + CTA */}
              <div style={{ position: 'sticky', top: 80 }}>
                <Card style={{ padding: '20px 16px 16px', background: navy, border: 'none', marginBottom: 16 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'rgba(255,255,255,0.45)', marginBottom: 12 }}>
                    PLAN DU STADE
                  </div>
                  <StadiumPlan highlighted={selectedZone ? svgKey(selectedZone.name) : null} />
                </Card>

                {selectedZone && (
                  <Card style={{ padding: 22 }}>
                    <div style={{ fontSize: 15, fontWeight: 800, color: navy, marginBottom: 3 }}>{selectedZone.name}</div>
                    <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 18 }}>
                      {selectedZone.description || (selectedZone.standing ? 'Tribune debout' : 'Tribune assise')}
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 20 }}>
                      <span style={{ fontSize: 14, color: '#8899BB' }}>Prix par place</span>
                      <span style={{ fontSize: 24, fontWeight: 900, color: navy }}>{selectedZone.price} €</span>
                    </div>
                    <PrimaryBtn onClick={handleContinue}>
                      Choisir mes places <Ic name="arrow" size={18} />
                    </PrimaryBtn>
                  </Card>
                )}
              </div>
            </div>
          )}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Seats ──────────────────────────────────────────────────────
  function DeskScrSeats() {
    const { appState, setAppState, goTo } = useCtx();
    const [seatData, setSeatData] = useState(null);
    const [picked,   setPicked]   = useState([]);
    const [qty,      setQty]      = useState(1);
    const [loading,  setLoading]  = useState(true);
    const [busy,     setBusy]     = useState(false);
    const [error,    setError]    = useState(null);
    const MAX = 6;

    const { match, zone, token } = appState;
    const isStanding = zone?.standing;
    const pricePer   = zone?.price || 0;
    const total      = pricePer * (isStanding ? qty : picked.length);

    useEffect(() => {
      if (!match?.id || !zone?.id) { setLoading(false); return; }
      if (isStanding) { setLoading(false); return; }
      API.seats(match.id, zone.id)
        .then(d => setSeatData(d))
        .catch(() => {})
        .finally(() => setLoading(false));
    }, [match?.id, zone?.id]);

    const toggle = seat => {
      if (seat.status !== 'free') return;
      setPicked(p =>
        p.find(s => s.id === seat.id) ? p.filter(s => s.id !== seat.id)
          : p.length < MAX ? [...p, seat] : p
      );
    };

    const handleContinue = async () => {
      if (busy) return;
      if (!token) { setAppState(s => ({ ...s, returnAfterAuth: 'seats' })); goTo('login'); return; }
      setBusy(true); setError(null);
      try {
        const result = await API.createHold(
          match.id, zone.id,
          isStanding ? [] : picked.map(s => s.id),
          isStanding ? qty : undefined
        );
        setAppState(s => ({
          ...s,
          holdId:        result.hold_id,
          holdExpiresAt: result.expires_at,
          selectedSeats: isStanding ? [] : picked,
          qty:           isStanding ? qty : picked.length,
        }));
        goTo('pay');
      } catch (err) {
        if (handleAuthErr(err, setAppState, goTo)) return;
        if (err.code === 'SEATS_UNAVAILABLE') { goTo('errSold'); return; }
        if (err.code === 'HOLD_EXPIRED' || err.code === 'HOLD_NOT_FOUND') { goTo('errSess'); return; }
        setError(err.message || 'Erreur lors de la réservation.');
      } finally { setBusy(false); }
    };

    const rows         = seatData?.rows || [];
    const canContinue  = isStanding ? qty >= 1 : picked.length >= 1;

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={1000}>
          <DeskSteps step={3} />
          <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>
            {isStanding ? 'Combien de places ?' : 'Choisissez vos places'}
          </h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>
            {zone?.name}{pricePer ? ` · ${pricePer} € par place` : ''}
          </p>

          {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 300px', gap: 28, alignItems: 'start' }}>
              {/* Seat area */}
              <div>
                {isStanding ? (
                  <Card style={{ padding: 40, textAlign: 'center' }}>
                    <p style={{ color: '#8899BB', marginBottom: 28, fontSize: 15, lineHeight: 1.5 }}>
                      Tribune debout — aucun numéro de siège attribué.
                    </p>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 20, marginBottom: 28 }}>
                      <button onClick={() => setQty(q => Math.max(1, q - 1))} disabled={qty <= 1} style={{
                        width: 52, height: 52, borderRadius: '50%', border: `2px solid ${line}`,
                        background: '#fff', fontSize: 26, fontWeight: 700, cursor: 'pointer', color: navy,
                        opacity: qty <= 1 ? 0.4 : 1,
                      }}>−</button>
                      <span style={{ fontSize: 56, fontWeight: 900, color: navy, minWidth: 70, textAlign: 'center' }}>{qty}</span>
                      <button onClick={() => setQty(q => Math.min(MAX, q + 1))} disabled={qty >= MAX} style={{
                        width: 52, height: 52, borderRadius: '50%', border: `2px solid ${line}`,
                        background: '#fff', fontSize: 26, fontWeight: 700, cursor: 'pointer', color: navy,
                        opacity: qty >= MAX ? 0.4 : 1,
                      }}>+</button>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'center', gap: 8, flexWrap: 'wrap' }}>
                      {[1,2,3,4,5,6].map(n => (
                        <button key={n} onClick={() => setQty(n)} style={{
                          width: 48, height: 48, borderRadius: 10,
                          border: qty === n ? `2px solid ${navy}` : `1.5px solid ${line}`,
                          background: qty === n ? navy : '#fff',
                          color: qty === n ? '#fff' : navy,
                          fontWeight: 700, fontSize: 17, cursor: 'pointer',
                        }}>{n}</button>
                      ))}
                    </div>
                  </Card>
                ) : (
                  <Card style={{ padding: 24, overflowX: 'auto' }}>
                    <div style={{ fontSize: 12, textAlign: 'center', color: '#A0ABC0', marginBottom: 16, fontWeight: 600, letterSpacing: '0.06em' }}>↑ VERS LA PELOUSE ↑</div>
                    <div style={{ overflowX: 'auto' }}>
                      {rows.map(({ row, seats }) => (
                        <div key={row} style={{ display: 'flex', alignItems: 'center', gap: 4, marginBottom: 4 }}>
                          <span style={{ fontSize: 11, fontWeight: 600, color: '#A0ABC0', width: 22, flexShrink: 0, textAlign: 'right' }}>{row}</span>
                          <div style={{ display: 'flex', gap: 3, flexWrap: 'nowrap' }}>
                            {seats.map(seat => {
                              const taken = seat.status !== 'free';
                              const on    = picked.some(s => s.id === seat.id);
                              return (
                                <button
                                  key={seat.id}
                                  onClick={() => toggle({ id: seat.id, row, number: seat.number, status: seat.status })}
                                  title={`Rang ${row} · Place ${seat.number}${taken ? ' — occupée' : ''}`}
                                  style={{
                                    width: 28, height: 26, borderRadius: 5, border: 0,
                                    fontSize: 9, fontWeight: 700, cursor: taken ? 'not-allowed' : 'pointer',
                                    background: taken ? '#F0F0F0' : on ? navy : '#E8EBF4',
                                    color: taken ? '#C0C0C0' : on ? '#fff' : navy,
                                    transition: 'all 0.12s', flexShrink: 0,
                                  }}
                                >{seat.number}</button>
                              );
                            })}
                          </div>
                        </div>
                      ))}
                    </div>
                    <div style={{ display: 'flex', gap: 16, marginTop: 16, justifyContent: 'center' }}>
                      {[['#E8EBF4', navy, 'Libre'], [navy, '#fff', 'Sélectionné'], ['#F0F0F0', '#C0C0C0', 'Occupé']].map(([bg2, col, lbl]) => (
                        <div key={lbl} style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: '#8899BB' }}>
                          <div style={{ width: 14, height: 14, borderRadius: 3, background: bg2 }} />
                          {lbl}
                        </div>
                      ))}
                    </div>
                  </Card>
                )}

                {error && <p style={{ color: '#DC2626', marginTop: 12, fontSize: 14 }}>{error}</p>}

                <div style={{ marginTop: 12, background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '13px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                  <span style={{ color: '#3B82F6', flexShrink: 0 }}><Ic name="info" size={17} /></span>
                  <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                    {isStanding
                      ? 'Tribune debout — accès libre dans votre zone, sans numéro de siège attribué.'
                      : 'Choisissez des places côte à côte sur la même rangée pour rester ensemble.'}
                  </p>
                </div>
              </div>

              {/* Summary sidebar */}
              <div style={{ position: 'sticky', top: 80, display: 'flex', flexDirection: 'column', gap: 14 }}>
                <Card style={{ padding: 22 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 14 }}>RÉCAPITULATIF</div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: navy, marginBottom: 4 }}>
                    {C.name || 'Club'} — {match?.away}
                  </div>
                  <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 16 }}>
                    {match?.dateText}{match?.timeText ? ` · ${match.timeText}` : ''}
                  </div>
                  <div style={{ borderTop: `1px solid ${line}`, paddingTop: 14 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                      <span style={{ fontSize: 13, color: '#8899BB' }}>{zone?.name}</span>
                      <span style={{ fontSize: 13, color: navy }}>{pricePer} € / pl.</span>
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                      <span style={{ fontSize: 13, color: '#8899BB' }}>Nombre</span>
                      <span style={{ fontSize: 13, color: navy, fontWeight: 600 }}>
                        {isStanding ? qty : picked.length} place{(isStanding ? qty : picked.length) > 1 ? 's' : ''}
                      </span>
                    </div>
                    {!isStanding && picked.length > 0 && (
                      <div style={{ fontSize: 12, color: '#8899BB', marginBottom: 6 }}>
                        {picked.map(s => `R.${s.row} P.${s.number}`).join(' · ')}
                      </div>
                    )}
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 12, paddingTop: 12, borderTop: `1px solid ${line}` }}>
                      <span style={{ fontSize: 15, fontWeight: 700, color: navy }}>Total</span>
                      <span style={{ fontSize: 22, fontWeight: 900, color: navy }}>{total} €</span>
                    </div>
                  </div>
                </Card>
                <PrimaryBtn onClick={handleContinue} disabled={!canContinue} busy={busy}>
                  {busy ? 'Chargement…' : 'Continuer'} {!busy && <Ic name="arrow" size={18} />}
                </PrimaryBtn>
              </div>
            </div>
          )}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Pay ────────────────────────────────────────────────────────
  function DeskScrPay() {
    const { appState, setAppState, goTo } = useCtx();
    const { match, zone, holdId, selectedSeats, qty } = appState;
    const effectiveQty = selectedSeats?.length || qty || 1;
    const pricePer     = zone?.price || 0;
    const total        = pricePer * effectiveQty;

    const [holderNames, setHolderNames] = useState(() => Array.from({ length: effectiveQty }, () => ''));
    const [step,        setStep]        = useState('names');
    const [orderData,   setOrderData]   = useState(null);
    const [error,       setError]       = useState(null);
    const [busy,        setBusy]        = useState(false);

    const stripeRef   = useRef(null);
    const elementsRef = useRef(null);
    const payElRef    = useRef(null);

    const namesValid = holderNames.every(n => (n || '').trim().length >= 2);

    const handleNamesContinue = async () => {
      if (!namesValid || busy) return;
      setBusy(true); setError(null);
      try {
        const result = await API.createOrder(holdId, holderNames.map(n => n.trim()));
        setOrderData(result);
        setAppState(s => ({ ...s, orderId: result.order_id }));
        setStep('stripe');
      } catch (err) {
        if (handleAuthErr(err, setAppState, goTo)) return;
        if (err.code === 'HOLD_EXPIRED') { goTo('errSess'); return; }
        setError(err.message || 'Erreur lors de la commande.');
      } finally { setBusy(false); }
    };

    useEffect(() => {
      if (step !== 'stripe' || !orderData || !payElRef.current || stripeRef.current) return;
      try {
        const stripe   = window.Stripe(orderData.stripe_publishable_key);
        const elements = stripe.elements({ clientSecret: orderData.client_secret });
        const payEl    = elements.create('payment');
        payEl.mount(payElRef.current);
        stripeRef.current   = stripe;
        elementsRef.current = elements;
      } catch {}
    }, [step, orderData]);

    const handlePay = async () => {
      if (!stripeRef.current || !elementsRef.current || busy) return;
      setBusy(true); setError(null);
      try {
        const { error: se } = await stripeRef.current.confirmPayment({
          elements: elementsRef.current, redirect: 'if_required',
        });
        if (se) { setError(se.message); goTo('errPay'); return; }
        const orderFull = await API.order(orderData.order_id);
        setAppState(s => ({
          ...s, order: orderFull, orderId: orderData.order_id,
          downloadCtx: { type: 'order', orderId: orderData.order_id },
        }));
        goTo('done');
      } catch (err) {
        setError(err.message || 'Paiement refusé.'); goTo('errPay');
      } finally { setBusy(false); }
    };

    // Summary card (sticky)
    const Summary = () => (
      <Card style={{ padding: 24 }}>
        <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 16 }}>VOTRE COMMANDE</div>
        <div style={{ fontSize: 15, fontWeight: 800, color: navy, marginBottom: 4 }}>
          {C.name || 'Club'} — {match?.away}
        </div>
        <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 16 }}>{match?.dateText}{match?.timeText ? ` · ${match.timeText}` : ''}</div>
        <div style={{ borderTop: `1px solid ${line}`, paddingTop: 14 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
            <span style={{ fontSize: 13, color: '#8899BB' }}>{zone?.name}</span>
          </div>
          {!zone?.standing && selectedSeats?.length > 0 && (
            <div style={{ fontSize: 12, color: '#A0ABC0', marginBottom: 6 }}>
              {selectedSeats.map(s => `R.${s.row} P.${s.number}`).join(' · ')}
            </div>
          )}
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
            <span style={{ fontSize: 13, color: '#8899BB' }}>{effectiveQty} × {pricePer} €</span>
            <span style={{ fontSize: 13, color: navy }}>{total} €</span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 14, paddingTop: 14, borderTop: `1px solid ${line}` }}>
            <span style={{ fontSize: 16, fontWeight: 800, color: navy }}>Total TTC</span>
            <span style={{ fontSize: 24, fontWeight: 900, color: navy }}>{total} €</span>
          </div>
        </div>
        <div style={{ marginTop: 20, padding: '12px 14px', background: '#F0FDF4', borderRadius: 10, border: '1px solid #BBF7D0', display: 'flex', gap: 10, alignItems: 'flex-start' }}>
          <span style={{ color: '#16A34A', flexShrink: 0 }}><Ic name="lock" size={16} /></span>
          <p style={{ margin: 0, fontSize: 12, color: '#15803D', lineHeight: 1.5 }}>
            Paiement sécurisé · Vos coordonnées bancaires ne sont jamais conservées par le club.
          </p>
        </div>
      </Card>
    );

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={960}>
          <DeskSteps step={4} />
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 28, alignItems: 'start' }}>
            {/* Left: form */}
            <div>
              {step === 'names' ? (
                <Card style={{ padding: 32 }}>
                  <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 900, color: navy, letterSpacing: '-0.01em' }}>
                    Vérifiez votre commande
                  </h2>
                  <p style={{ color: '#8899BB', margin: '0 0 28px', fontSize: 15 }}>
                    Renseignez le nom de chaque personne. Ce nom sera imprimé sur le billet.
                  </p>
                  <ErrMsg msg={error} />
                  {holderNames.map((name, i) => (
                    <Field
                      key={i}
                      label={effectiveQty > 1 ? `Nom — billet ${i + 1}` : 'Nom du porteur'}
                      value={name}
                      onChange={v => setHolderNames(ns => { const n = [...ns]; n[i] = v; return n; })}
                      placeholder="Prénom Nom"
                      autoComplete="name"
                    />
                  ))}
                  <PrimaryBtn onClick={handleNamesContinue} disabled={!namesValid} busy={busy} style={{ marginTop: 8 }}>
                    Passer au paiement <Ic name="arrow" size={18} />
                  </PrimaryBtn>
                </Card>
              ) : (
                <Card style={{ padding: 32 }}>
                  <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 900, color: navy, letterSpacing: '-0.01em' }}>
                    Paiement sécurisé
                  </h2>
                  <p style={{ color: '#8899BB', margin: '0 0 24px', fontSize: 15 }}>Entrez vos coordonnées bancaires ci-dessous.</p>
                  <ErrMsg msg={error} />
                  <div ref={payElRef} style={{ marginBottom: 24 }} />
                  <PrimaryBtn onClick={handlePay} busy={busy} style={{ fontSize: 16 }}>
                    <Ic name="lock" size={18} /> Payer {total} €
                  </PrimaryBtn>
                </Card>
              )}
            </div>

            {/* Right: sticky summary */}
            <div style={{ position: 'sticky', top: 80 }}><Summary /></div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Done ───────────────────────────────────────────────────────
  function DeskScrDone() {
    const { appState, goTo } = useCtx();
    const { match, zone, selectedSeats, qty, orderId } = appState;
    const effectiveQty = selectedSeats?.length || qty || 1;

    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={680}>
          <div style={{ textAlign: 'center', marginBottom: 40 }}>
            <div style={{
              width: 72, height: 72, borderRadius: '50%',
              background: '#F0FDF4', border: '3px solid #16A34A',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              margin: '0 auto 20px', fontSize: 32,
            }}>✓</div>
            <h1 style={{ margin: '0 0 10px', fontSize: 32, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>
              Paiement confirmé !
            </h1>
            <p style={{ color: '#8899BB', fontSize: 16, maxWidth: 440, margin: '0 auto' }}>
              Vos billets ont été envoyés par email. Présentez-les à l'entrée du stade.
            </p>
          </div>

          <Card style={{ padding: 28, marginBottom: 20 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 20, textAlign: 'center' }}>
              <div>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 6 }}>MATCH</div>
                <div style={{ fontSize: 14, fontWeight: 700, color: navy }}>{C.name || 'Club'} — {match?.away}</div>
                <div style={{ fontSize: 13, color: '#8899BB', marginTop: 2 }}>{match?.dateText}</div>
              </div>
              <div style={{ borderLeft: `1px solid ${line}`, borderRight: `1px solid ${line}` }}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 6 }}>TRIBUNE</div>
                <div style={{ fontSize: 14, fontWeight: 700, color: navy }}>{zone?.name}</div>
                <div style={{ fontSize: 13, color: '#8899BB', marginTop: 2 }}>{effectiveQty} place{effectiveQty > 1 ? 's' : ''}</div>
              </div>
              <div>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 6 }}>STADE</div>
                <div style={{ fontSize: 14, fontWeight: 700, color: navy }}>{C.stadiumShort || C.stadium || 'Stade'}</div>
                <div style={{ fontSize: 13, color: '#8899BB', marginTop: 2 }}>{match?.timeText ? `Coup d'envoi ${match.timeText}` : ''}</div>
              </div>
            </div>
          </Card>

          <Card style={{ padding: 24, marginBottom: 20 }}>
            <div style={{ fontSize: 13, fontWeight: 700, color: navy, marginBottom: 14 }}>Télécharger ou envoyer vos billets</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {[
                { label: 'Télécharger en PDF', icon: 'download-pdf', color: '#FFE7E7' },
                { label: 'Renvoyer par email', icon: 'mail',         color: '#E0F2FE' },
              ].map(({ label, icon, color }) => (
                <button key={label} style={{
                  display: 'flex', alignItems: 'center', gap: 14,
                  padding: '14px 18px', background: '#F4F6FB',
                  border: `1.5px solid ${line}`, borderRadius: 12,
                  cursor: 'pointer', textAlign: 'left',
                }}>
                  <div style={{ width: 40, height: 40, borderRadius: 10, background: color, display: 'flex', alignItems: 'center', justifyContent: 'center', color: navy, flexShrink: 0 }}>
                    <Ic name={icon} size={20} />
                  </div>
                  <span style={{ fontSize: 14, fontWeight: 600, color: navy }}>{label}</span>
                  <Ic name="arrow" size={18} style={{ marginLeft: 'auto', color: '#A0ABC0' }} />
                </button>
              ))}
            </div>
          </Card>

          <div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
            <button onClick={() => goTo('list')} style={{
              padding: '12px 28px', background: navy, color: '#fff',
              border: 0, borderRadius: 11, fontSize: 14, fontWeight: 700, cursor: 'pointer',
            }}>Acheter d'autres places</button>
            <button onClick={() => goTo('home')} style={{
              padding: '12px 28px', background: 'transparent', color: navy,
              border: `1.5px solid ${line}`, borderRadius: 11, fontSize: 14, fontWeight: 600, cursor: 'pointer',
            }}>Retour à l'accueil</button>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Login ──────────────────────────────────────────────────────
  function DeskScrLogin() {
    const { goTo, appState, setAppState } = useCtx();
    const [email,   setEmail]   = useState('');
    const [pw,      setPw]      = useState('');
    const [loading, setLoading] = useState(false);
    const [err,     setErr]     = useState(null);

    function afterAuth(token, user) {
      localStorage.setItem('token', token);
      setAppState(s => ({ ...s, token, user: user || null }));
      const dest = appState.returnAfterAuth || 'home';
      setAppState(s => ({ ...s, returnAfterAuth: null }));
      goTo(dest);
    }

    async function submit(e) {
      if (e) e.preventDefault();
      if (!email.trim() || !pw) { setErr('Remplis tous les champs.'); return; }
      setLoading(true); setErr(null);
      try {
        const d = await API.login(email.trim(), pw);
        afterAuth(d.token, d.user);
      } catch (ex) {
        setErr(ex.message || 'Identifiants incorrects.');
        setLoading(false);
      }
    }

    return (
      <AuthCard title="Se connecter" subtitle="Accédez à vos billets et votre abonnement.">
        <form onSubmit={submit}>
          <ErrMsg msg={err} />
          <Field label="Adresse email" type="email" value={email} onChange={setEmail} placeholder="vous@exemple.fr" autoComplete="email" />
          <Field label="Mot de passe"  type="password" value={pw} onChange={setPw} placeholder="••••••••" autoComplete="current-password" />
          <div style={{ textAlign: 'right', marginBottom: 22 }}>
            <a href="#" onClick={e => { e.preventDefault(); goTo('pwdReset'); }}
              style={{ fontSize: 13, color: navy, fontWeight: 600, textDecoration: 'none' }}>
              Mot de passe oublié ?
            </a>
          </div>
          <PrimaryBtn onClick={submit} busy={loading}>Se connecter</PrimaryBtn>
        </form>
        <div style={{ textAlign: 'center', marginTop: 20, fontSize: 14, color: '#8899BB' }}>
          Pas encore de compte ?{' '}
          <a href="#" onClick={e => { e.preventDefault(); goTo('signup'); }}
            style={{ color: navy, fontWeight: 700, textDecoration: 'none' }}>
            Créer un compte
          </a>
        </div>
      </AuthCard>
    );
  }

  // ── Signup ─────────────────────────────────────────────────────
  function DeskScrSignup() {
    const { goTo, appState, setAppState } = useCtx();
    const [name,    setName]    = useState('');
    const [email,   setEmail]   = useState('');
    const [pw,      setPw]      = useState('');
    const [loading, setLoading] = useState(false);
    const [err,     setErr]     = useState(null);

    function afterAuth(token, user) {
      localStorage.setItem('token', token);
      setAppState(s => ({ ...s, token, user: user || null }));
      const dest = appState.returnAfterAuth || 'home';
      setAppState(s => ({ ...s, returnAfterAuth: null }));
      goTo(dest);
    }

    async function submit(e) {
      if (e) e.preventDefault();
      if (!name.trim() || !email.trim() || pw.length < 8) {
        setErr('Remplis tous les champs (mot de passe : 8 caractères min.)');
        return;
      }
      setLoading(true); setErr(null);
      try {
        const d = await API.register(name.trim(), email.trim(), pw);
        afterAuth(d.token, d.user);
      } catch (ex) {
        setErr(ex.message || 'Erreur lors de la création du compte.');
        setLoading(false);
      }
    }

    return (
      <AuthCard title="Créer un compte" subtitle="Pour recevoir et gérer vos billets.">
        <form onSubmit={submit}>
          <ErrMsg msg={err} />
          <Field label="Votre nom complet" value={name}  onChange={setName}  placeholder="Prénom Nom"       autoComplete="name" />
          <Field label="Adresse email"     type="email"    value={email} onChange={setEmail} placeholder="vous@exemple.fr" autoComplete="email" />
          <Field label="Mot de passe"      type="password" value={pw}    onChange={setPw}    placeholder="8 caractères min." autoComplete="new-password" />
          <PrimaryBtn onClick={submit} busy={loading}>Créer mon compte</PrimaryBtn>
        </form>
        <div style={{ textAlign: 'center', marginTop: 20, fontSize: 14, color: '#8899BB' }}>
          Déjà un compte ?{' '}
          <a href="#" onClick={e => { e.preventDefault(); goTo('login'); }}
            style={{ color: navy, fontWeight: 700, textDecoration: 'none' }}>
            Se connecter
          </a>
        </div>
        <p style={{ textAlign: 'center', fontSize: 12, color: '#B0B8CC', marginTop: 16, lineHeight: 1.5 }}>
          En créant un compte, vous acceptez les conditions générales de vente et la politique de confidentialité du club.
        </p>
      </AuthCard>
    );
  }

  // ── PwdReset ───────────────────────────────────────────────────
  function DeskScrPwdReset() {
    const { goTo } = useCtx();
    const [email,   setEmail]   = useState('');
    const [sent,    setSent]    = useState(false);
    const [loading, setLoading] = useState(false);
    const [err,     setErr]     = useState(null);

    async function submit(e) {
      if (e) e.preventDefault();
      if (!email.trim()) { setErr('Renseignez votre adresse email.'); return; }
      setLoading(true); setErr(null);
      try {
        await API.passwordReset(email.trim());
        setSent(true);
      } catch (ex) {
        setErr(ex.message || 'Erreur lors de la demande.');
        setLoading(false);
      }
    }

    return (
      <AuthCard title="Mot de passe oublié" subtitle="Nous vous enverrons un lien de réinitialisation.">
        {sent ? (
          <div style={{ textAlign: 'center', padding: '8px 0 16px' }}>
            <div style={{ fontSize: 40, marginBottom: 16 }}>📬</div>
            <p style={{ fontSize: 15, color: navy, fontWeight: 600, marginBottom: 8 }}>Email envoyé !</p>
            <p style={{ fontSize: 14, color: '#8899BB', marginBottom: 24 }}>Vérifiez votre boîte mail et suivez le lien pour réinitialiser votre mot de passe.</p>
            <button onClick={() => goTo('login')} style={{ color: navy, fontWeight: 700, background: 'transparent', border: 0, cursor: 'pointer', fontSize: 14 }}>
              ← Retour à la connexion
            </button>
          </div>
        ) : (
          <form onSubmit={submit}>
            <ErrMsg msg={err} />
            <Field label="Votre adresse email" type="email" value={email} onChange={setEmail} placeholder="vous@exemple.fr" autoComplete="email" />
            <PrimaryBtn onClick={submit} busy={loading}>Envoyer le lien</PrimaryBtn>
            <button onClick={() => goTo('login')} style={{
              width: '100%', marginTop: 12, padding: '12px', background: 'transparent',
              border: `1.5px solid ${line}`, borderRadius: 12, color: navy,
              fontSize: 14, fontWeight: 600, cursor: 'pointer',
            }}>Annuler</button>
          </form>
        )}
      </AuthCard>
    );
  }

  // ── Wallet abonné ──────────────────────────────────────────────
  function DeskScrWallet() {
    const { appState, setAppState, goTo } = useCtx();
    const [sub,     setSub]     = useState(appState.subscription || null);
    const [loading, setLoading] = useState(!appState.subscription);

    useEffect(() => {
      if (appState.subscription) return;
      if (!appState.token) { goTo('login'); return; }
      API.subscription()
        .then(d => { setSub(d.subscription); setAppState(s => ({ ...s, subscription: d.subscription })); })
        .catch(err => handleAuthErr(err, setAppState, goTo))
        .finally(() => setLoading(false));
    }, []);

    if (loading) return <DeskPage nav={<DeskNav />}><DeskContent><p style={{ color: '#A0ABC0' }}>Chargement…</p></DeskContent></DeskPage>;
    if (!sub) return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={600}>
          <Card style={{ padding: 40, textAlign: 'center' }}>
            <p style={{ color: '#8899BB', fontSize: 16, marginBottom: 24 }}>Vous n'avez pas d'abonnement actif.</p>
            <PrimaryBtn onClick={() => goTo('aboPick')} style={{ width: 'auto', display: 'inline-flex', padding: '12px 28px' }}>
              Découvrir les formules <Ic name="arrow" size={18} />
            </PrimaryBtn>
          </Card>
        </DeskContent>
      </DeskPage>
    );

    const nextMatch = sub.next_match || null;
    const diff      = nextMatch ? Math.ceil((new Date(nextMatch.date + 'T00:00:00') - new Date()) / 86400000) : null;
    const diffText  = diff === null ? null : diff <= 0 ? "Aujourd'hui" : `Dans ${diff} jour${diff > 1 ? 's' : ''}`;

    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent>
          <div style={{ display: 'grid', gridTemplateColumns: '380px 1fr', gap: 32, alignItems: 'start' }}>
            {/* Left: card */}
            <div style={{ position: 'sticky', top: 80 }}>
              <div style={{ background: navy, borderRadius: 20, padding: '28px 24px', marginBottom: 14 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20 }}>
                  <div>
                    <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: 'rgba(255,255,255,0.5)', marginBottom: 4 }}>CARTE ABONNÉ</div>
                    <div style={{ fontSize: 18, fontWeight: 800, color: '#fff' }}>Saison {sub.season}</div>
                  </div>
                  {C.logo && <img src={C.logo} alt="" style={{ height: 40, objectFit: 'contain' }} />}
                </div>
                <div style={{ fontSize: 22, fontWeight: 900, color: '#fff', marginBottom: 4 }}>{sub.holder_name}</div>
                <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.55)', marginBottom: 24 }}>
                  Abonné depuis {sub.seasons_count} saison{sub.seasons_count > 1 ? 's' : ''} · Merci ♥
                </div>
                <div style={{ background: '#fff', borderRadius: 14, padding: 20, textAlign: 'center', marginBottom: 20 }}>
                  <MiniQR size={160} fg="#133478" token={sub.qr_token} />
                  <div style={{ fontSize: 12, color: '#8899BB', marginTop: 10, fontWeight: 600 }}>Présenter à l'entrée</div>
                </div>
                <div style={{ display: 'flex', gap: 16, justifyContent: 'center', flexWrap: 'wrap' }}>
                  {[['Tribune', sub.zone], sub.row && ['Rang', sub.row], sub.seat_number && ['Place', sub.seat_number], sub.number && ['N° abonné', sub.number]].filter(Boolean).map(([lbl, val]) => (
                    <div key={lbl} style={{ textAlign: 'center' }}>
                      <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', marginBottom: 3 }}>{lbl}</div>
                      <div style={{ fontSize: 14, fontWeight: 700, color: '#fff' }}>{val}</div>
                    </div>
                  ))}
                </div>
              </div>
              <div style={{ display: 'flex', gap: 10 }}>
                <button style={{ flex: 1, padding: '12px', background: '#000', color: '#fff', border: 0, borderRadius: 12, fontSize: 13, fontWeight: 700, cursor: 'pointer' }}>Apple Wallet</button>
                <button style={{ flex: 1, padding: '12px', background: '#fff', color: '#1F1F1F', border: '1.5px solid #DADCE0', borderRadius: 12, fontSize: 13, fontWeight: 700, cursor: 'pointer' }}>Google Wallet</button>
              </div>
            </div>

            {/* Right */}
            <div>
              <h1 style={{ margin: '0 0 4px', fontSize: 30, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Mon abonnement</h1>
              <p style={{ color: '#8899BB', margin: '0 0 28px', fontSize: 15 }}>Saison {sub.season} · {C.stadium || 'Stade'}</p>

              {nextMatch && (
                <Card style={{ padding: 28, marginBottom: 16 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: navy }}>Prochain match</div>
                    {diffText && (
                      <span style={{ background: diff <= 1 ? '#F0FDF4' : '#EFF6FF', color: diff <= 1 ? '#16A34A' : '#3B82F6', borderRadius: 20, padding: '4px 12px', fontSize: 12, fontWeight: 700 }}>
                        {diffText}
                      </span>
                    )}
                  </div>
                  <div style={{ fontSize: 20, fontWeight: 900, color: navy, marginBottom: 4 }}>{C.name || 'Club'} — {nextMatch.away}</div>
                  <div style={{ fontSize: 14, color: '#8899BB', marginBottom: 20 }}>
                    {API.fmtDate(nextMatch.date)} · {(nextMatch.kickoff || '').slice(0, 5).replace(':', 'h')} · {C.stadiumShort || C.stadium || 'Stade'}
                  </div>
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: 12, background: '#F4F6FB', borderRadius: 12, padding: '14px 18px', marginBottom: 20 }}>
                    {[
                      sub.row && sub.seat_number && ['Ta place', `Rang ${sub.row} · ${sub.seat_number}`],
                      sub.entrance_gate && ["Porte d'entrée", sub.entrance_gate],
                      nextMatch.doors_open && ['Ouverture', (nextMatch.doors_open || '').slice(0, 5).replace(':', 'h')],
                      ['Coup d\'envoi', (nextMatch.kickoff || '').slice(0, 5).replace(':', 'h')],
                    ].filter(Boolean).map(([lbl, val]) => (
                      <div key={lbl}>
                        <div style={{ fontSize: 11, color: '#A0ABC0', marginBottom: 3 }}>{lbl}</div>
                        <div style={{ fontSize: 14, fontWeight: 700, color: navy }}>{val}</div>
                      </div>
                    ))}
                  </div>
                  <button onClick={() => goTo('transfer')} style={{
                    width: '100%', padding: '13px 18px', background: '#F4F6FB',
                    border: `1.5px solid ${line}`, borderRadius: 12,
                    color: navy, fontSize: 14, fontWeight: 600, cursor: 'pointer',
                    display: 'flex', alignItems: 'center', gap: 10,
                  }}>
                    <Ic name="users" size={18} /> Je ne peux pas y aller — céder ma place
                  </button>
                </Card>
              )}

              <Card style={{ padding: 22, marginBottom: 16 }}>
                <button onClick={() => goTo('season')} style={{
                  width: '100%', padding: '13px 18px', background: '#F4F6FB',
                  border: `1.5px solid ${line}`, borderRadius: 12, color: navy, fontSize: 14, fontWeight: 600, cursor: 'pointer',
                  display: 'flex', alignItems: 'center', gap: 10,
                }}>
                  <Ic name="calendar" size={18} /> Voir tous mes matchs de la saison
                  <span style={{ marginLeft: 'auto', color: '#A0ABC0' }}><Ic name="arrow" size={16} /></span>
                </button>
              </Card>

              <div style={{ background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '14px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                <span style={{ color: '#3B82F6', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
                <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                  <b>Pas de billet à télécharger.</b> Votre carte abonné fonctionne pour tous les matchs de la saison. Scannez votre QR à l'entrée.
                </p>
              </div>
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Saison ─────────────────────────────────────────────────────
  function DeskScrSeason() {
    const { appState, setAppState, goTo } = useCtx();
    const [data,    setData]    = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      if (!appState.token) { goTo('login'); return; }
      API.seasonMatches()
        .then(d => setData(d))
        .catch(err => handleAuthErr(err, setAppState, goTo))
        .finally(() => setLoading(false));
    }, []);

    const matches  = data?.matches || [];
    const upcoming = matches.filter(m => m.status !== 'past');
    const past     = matches.filter(m => m.status === 'past');
    const sub      = appState.subscription;

    const MatchRow = ({ m, isPast }) => {
      const d            = new Date(m.match.date + 'T00:00:00');
      const isNext       = !isPast && upcoming[0]?.slot_id === m.slot_id;
      const isTransferred = m.status === 'transferred';
      const statusLabel  = isTransferred ? 'Cédé' : isNext ? 'Prochain' : isPast ? 'Joué' : 'À venir';
      const statusColor  = isTransferred ? '#EA580C' : isNext ? '#16A34A' : isPast ? '#9CA3AF' : '#3B82F6';
      const statusBg     = isTransferred ? 'rgba(234,88,12,0.1)' : isNext ? 'rgba(22,163,74,0.1)' : isPast ? '#F3F4F6' : 'rgba(59,130,246,0.1)';
      return (
        <div
          onClick={() => { setAppState(s => ({ ...s, downloadCtx: { type: 'subscription' } })); goTo('wallet'); }}
          style={{ display: 'grid', gridTemplateColumns: '60px 1fr auto auto', gap: 20, alignItems: 'center', padding: '16px 24px', borderBottom: `1px solid ${line}`, cursor: 'pointer', opacity: isPast ? 0.65 : 1, transition: 'background 0.12s' }}
          onMouseEnter={e => { e.currentTarget.style.background = '#F4F6FB'; }}
          onMouseLeave={e => { e.currentTarget.style.background = ''; }}
        >
          <div style={{ textAlign: 'center' }}>
            <div style={{ fontSize: 26, fontWeight: 900, color: isPast ? '#A0ABC0' : navy, lineHeight: 1 }}>{d.getDate()}</div>
            <div style={{ fontSize: 12, fontWeight: 700, color: '#8899BB', textTransform: 'uppercase' }}>{API.FR_MONTHS_SHORT[d.getMonth()]}</div>
          </div>
          <div>
            <div style={{ fontSize: 15, fontWeight: 700, color: isPast ? '#6B7280' : navy }}>
              {C.name || 'Club'} — {m.match.away}
            </div>
            <div style={{ fontSize: 13, color: '#8899BB', marginTop: 2 }}>
              {isTransferred ? 'Place cédée · QR transmis à votre invité' :
               sub?.row ? `Rang ${sub.row}, place ${sub.seat_number} · Scannez votre carte` :
               'Scannez votre carte abonné'}
            </div>
          </div>
          <div style={{ fontSize: 13, color: '#8899BB' }}>{(m.match.kickoff || '').slice(0, 5).replace(':', 'h')}</div>
          <span style={{ background: statusBg, color: statusColor, borderRadius: 20, padding: '4px 12px', fontSize: 12, fontWeight: 700, whiteSpace: 'nowrap' }}>{statusLabel}</span>
        </div>
      );
    };

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent>
          <h1 style={{ margin: '0 0 4px', fontSize: 30, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Tous mes matchs</h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>
            {data ? `Saison ${data.season} · ${matches.length} matchs · ${upcoming.length} à venir` : 'Chargement…'}
          </p>
          {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : (
            <Card>
              {upcoming.length > 0 && (
                <>
                  <div style={{ padding: '12px 24px', background: '#F4F6FB', borderBottom: `1px solid ${line}`, fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0' }}>À VENIR</div>
                  {upcoming.map(m => <MatchRow key={m.slot_id} m={m} isPast={false} />)}
                </>
              )}
              {past.length > 0 && (
                <>
                  <div style={{ padding: '12px 24px', background: '#F4F6FB', borderBottom: `1px solid ${line}`, borderTop: upcoming.length > 0 ? `1px solid ${line}` : 0, fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0' }}>MATCHS JOUÉS</div>
                  {past.map(m => <MatchRow key={m.slot_id} m={m} isPast={true} />)}
                </>
              )}
            </Card>
          )}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Céder une place ────────────────────────────────────────────
  function DeskScrTransfer() {
    const { appState, setAppState, goTo } = useCtx();
    const [recipientName,  setRecipientName]  = useState('');
    const [recipientEmail, setRecipientEmail] = useState('');
    const [message,        setMessage]        = useState('');
    const [busy,           setBusy]           = useState(false);
    const [error,          setError]          = useState(null);

    const sub       = appState.subscription;
    const nextMatch = sub?.next_match;

    if (!sub || !nextMatch) return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={600}>
          <Card style={{ padding: 40, textAlign: 'center' }}><p style={{ color: '#8899BB', fontSize: 16 }}>Aucun prochain match trouvé.</p></Card>
        </DeskContent>
      </DeskPage>
    );

    const handleSubmit = async () => {
      if (busy || !recipientName.trim() || !recipientEmail.trim()) return;
      setBusy(true); setError(null);
      try {
        const result = await API.transfer(nextMatch.id, recipientName.trim(), recipientEmail.trim(), message.trim() || undefined);
        setAppState(s => ({ ...s, transferResult: result }));
        goTo('transferDone');
      } catch (err) {
        if (handleAuthErr(err, setAppState, goTo)) return;
        setError(err.message || 'Erreur lors de la cession.');
      } finally { setBusy(false); }
    };

    const d = new Date(nextMatch.date + 'T00:00:00');

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={900}>
          <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Céder ma place</h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>
            Donnez votre place à un proche pour ce match. Votre carte abonné reste active pour les prochains matchs.
          </p>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 28, alignItems: 'start' }}>
            <Card style={{ padding: 32 }}>
              <ErrMsg msg={error} />
              <Field label="À qui donnez-vous votre place ?" value={recipientName} onChange={setRecipientName} placeholder="Prénom Nom" />
              <Field label="Son adresse email" type="email" value={recipientEmail} onChange={setRecipientEmail} placeholder="exemple@email.fr" />
              <div style={{ marginBottom: 18 }}>
                <label style={{ display: 'block', fontSize: 13, fontWeight: 700, color: navy, marginBottom: 7 }}>Un message (facultatif)</label>
                <textarea value={message} onChange={e => setMessage(e.target.value)} rows={3} placeholder="Bon match !"
                  style={{ width: '100%', padding: '11px 14px', borderRadius: 10, border: `1.5px solid ${line}`, fontSize: 15, color: navy, fontFamily: 'inherit', outline: 'none', background: '#fff', boxSizing: 'border-box', resize: 'vertical' }}
                  onFocus={e => e.target.style.borderColor = navy}
                  onBlur={e => e.target.style.borderColor = line}
                />
              </div>
              <PrimaryBtn onClick={handleSubmit} disabled={!recipientName.trim() || !recipientEmail.trim()} busy={busy}>
                {!busy && `Envoyer la place à ${recipientName || '…'}`} {!busy && <Ic name="arrow" size={18} />}
              </PrimaryBtn>
            </Card>

            <div style={{ position: 'sticky', top: 80, display: 'flex', flexDirection: 'column', gap: 14 }}>
              <Card style={{ padding: 24 }}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 16 }}>MATCH CONCERNÉ</div>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center', marginBottom: 16 }}>
                  <div style={{ width: 52, height: 52, background: '#F4F6FB', border: `1px solid ${line}`, borderRadius: 12, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                    <b style={{ fontSize: 22, fontWeight: 800, color: navy, lineHeight: 1 }}>{d.getDate()}</b>
                    <span style={{ fontSize: 10, fontWeight: 600, color: '#8899BB', marginTop: 2 }}>{(API.FR_MONTHS_SHORT[d.getMonth()] || '').toUpperCase().replace('.', '')}</span>
                  </div>
                  <div>
                    <div style={{ fontSize: 15, fontWeight: 800, color: navy }}>{C.name || 'Club'} — {nextMatch.away}</div>
                    <div style={{ fontSize: 13, color: '#8899BB' }}>{API.fmtDate(nextMatch.date)} · {(nextMatch.kickoff || '').slice(0, 5).replace(':', 'h')}</div>
                  </div>
                </div>
                {sub.row && (
                  <div style={{ background: '#F4F6FB', borderRadius: 10, padding: '12px 14px', display: 'flex', justifyContent: 'space-between' }}>
                    <span style={{ fontSize: 13, color: '#8899BB' }}>Votre place</span>
                    <span style={{ fontSize: 13, fontWeight: 700, color: navy }}>Rang {sub.row} · {sub.seat_number}</span>
                  </div>
                )}
              </Card>
              <div style={{ background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '14px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                <span style={{ color: '#3B82F6', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
                <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                  Un QR unique est généré au nom de votre invité. Votre carte est désactivée juste pour ce match, puis réactivée automatiquement.
                </p>
              </div>
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Cession confirmée ──────────────────────────────────────────
  function DeskScrTransferDone() {
    const { appState, setAppState, goTo } = useCtx();
    const result = appState.transferResult;

    const cancelTransfer = async () => {
      if (!result?.transfer_id) return;
      try { await API.cancelTransfer(result.transfer_id); setAppState(s => ({ ...s, transferResult: null })); goTo('wallet'); } catch {}
    };

    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={600}>
          <div style={{ textAlign: 'center', marginBottom: 32 }}>
            <div style={{ width: 72, height: 72, borderRadius: '50%', background: '#F0FDF4', border: '3px solid #16A34A', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 20px', fontSize: 32 }}>✓</div>
            <h1 style={{ margin: '0 0 10px', fontSize: 32, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>C'est envoyé !</h1>
            <p style={{ color: '#8899BB', fontSize: 16, maxWidth: 400, margin: '0 auto' }}>
              {result?.recipient_name || 'Votre invité'} a reçu le billet par email. Votre carte abonné reste active pour les prochains matchs.
            </p>
          </div>

          {result && (
            <Card style={{ padding: 28, marginBottom: 20 }}>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 14 }}>CESSION ACTIVE</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
                <div style={{ width: 44, height: 44, borderRadius: '50%', background: navy, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 17, fontWeight: 700, flexShrink: 0 }}>
                  {(result.recipient_name || '?')[0].toUpperCase()}
                </div>
                <div>
                  <div style={{ fontSize: 16, fontWeight: 700, color: navy }}>{result.recipient_name}</div>
                  <div style={{ fontSize: 13, color: '#8899BB' }}>
                    {result.recipient_email} · {C.name || 'Club'} — {result.match?.away || '…'} · {API.fmtDate(result.match?.date)}
                  </div>
                </div>
              </div>
              <button onClick={cancelTransfer} style={{ background: 'transparent', border: 0, color: '#DC2626', fontFamily: 'inherit', fontSize: 14, fontWeight: 600, padding: 0, textDecoration: 'underline', textUnderlineOffset: 4, cursor: 'pointer' }}>
                Annuler cette cession
              </button>
            </Card>
          )}

          <p style={{ textAlign: 'center', fontSize: 14, color: '#8899BB', marginBottom: 24 }}>Votre place vous revient automatiquement <b>au match suivant</b>.</p>
          <div style={{ display: 'flex', justifyContent: 'center' }}>
            <PrimaryBtn onClick={() => goTo('wallet')} style={{ width: 'auto', padding: '12px 28px', display: 'inline-flex' }}>
              Retour à mon abonnement <Ic name="arrow" size={18} />
            </PrimaryBtn>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Historique ─────────────────────────────────────────────────
  function DeskScrHistory() {
    const { appState, setAppState, goTo } = useCtx();
    const [orders,  setOrders]  = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      if (!appState.token) { goTo('login'); return; }
      API.orders()
        .then(os => setOrders(os))
        .catch(err => handleAuthErr(err, setAppState, goTo))
        .finally(() => setLoading(false));
    }, []);

    const upcoming = orders.filter(o => o.match && new Date(o.match.date + 'T00:00:00') >= new Date());
    const past     = orders.filter(o => !o.match || new Date(o.match.date + 'T00:00:00') < new Date());

    const openOrder = o => { setAppState(s => ({ ...s, downloadCtx: { type: 'order', orderId: o.id } })); goTo('download'); };

    const OrderRow = ({ o }) => (
      <div
        onClick={() => openOrder(o)}
        style={{ display: 'grid', gridTemplateColumns: '44px 1fr auto auto', gap: 18, alignItems: 'center', padding: '16px 24px', cursor: 'pointer', borderBottom: `1px solid ${line}`, transition: 'background 0.12s' }}
        onMouseEnter={e => e.currentTarget.style.background = '#F4F6FB'}
        onMouseLeave={e => e.currentTarget.style.background = ''}
      >
        <div style={{ width: 40, height: 40, borderRadius: 10, background: '#EFF6FF', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, color: '#3B82F6' }}>
          <Ic name="ticket" size={20} />
        </div>
        <div>
          <div style={{ fontSize: 15, fontWeight: 700, color: navy }}>{o.match ? `${C.name || 'Club'} — ${o.match.away}` : 'Commande'}</div>
          <div style={{ fontSize: 13, color: '#8899BB', marginTop: 2 }}>{o.match ? `${API.fmtDate(o.match.date)} · ` : ''}{o.ticket_count} billet{o.ticket_count > 1 ? 's' : ''}</div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 16, fontWeight: 800, color: navy }}>{o.total} €</div>
          <div style={{ fontSize: 12, color: '#A0ABC0', marginTop: 2 }}>Le {API.fmtDate(o.created_at)}</div>
        </div>
        <div style={{ color: '#A0ABC0' }}><Ic name="arrow" size={18} /></div>
      </div>
    );

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent>
          <h1 style={{ margin: '0 0 4px', fontSize: 30, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Mes achats</h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>Retrouvez tous vos billets et commandes.</p>

          {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : orders.length === 0 ? (
            <Card style={{ padding: 40, textAlign: 'center' }}><p style={{ color: '#8899BB', fontSize: 16 }}>Aucun achat pour le moment.</p></Card>
          ) : (
            <Card>
              {upcoming.length > 0 && (
                <>
                  <div style={{ padding: '12px 24px', background: '#F4F6FB', borderBottom: `1px solid ${line}`, fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0' }}>À VENIR</div>
                  {upcoming.map(o => <OrderRow key={o.id} o={o} />)}
                </>
              )}
              {past.length > 0 && (
                <>
                  <div style={{ padding: '12px 24px', background: '#F4F6FB', borderBottom: `1px solid ${line}`, borderTop: upcoming.length > 0 ? `1px solid ${line}` : 0, fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0' }}>MATCHS PASSÉS</div>
                  {past.map(o => <OrderRow key={o.id} o={o} />)}
                </>
              )}
            </Card>
          )}

          {!loading && orders.length > 0 && (
            <div style={{ marginTop: 16, background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '14px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
              <span style={{ color: '#3B82F6', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
              <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                <b>Factures et reçus</b> — Toutes vos factures sont disponibles au format PDF.
                {C.phone && <> Une question ? Appelez-nous au <a href={`tel:${C.phone}`} style={{ color: '#1D4ED8', fontWeight: 600 }}>{C.phone}</a>.</>}
              </p>
            </div>
          )}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Téléchargement ─────────────────────────────────────────────
  function DeskScrDownload() {
    const { appState } = useCtx();
    const [tickets, setTickets] = useState([]);
    const [active,  setActive]  = useState(0);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      const ctx = appState.downloadCtx;
      if (!ctx) { setLoading(false); return; }

      if (ctx.type === 'subscription') {
        const sub = appState.subscription;
        if (sub) {
          setTickets([{ holder_name: sub.holder_name, qr_token: sub.qr_token, zone: sub.zone, seat: sub.row ? { row: sub.row, number: sub.seat_number } : null, match: sub.next_match || null, isSubscription: true }]);
        }
        setLoading(false);
        return;
      }

      if (ctx.type === 'order' && ctx.orderId) {
        API.order(ctx.orderId)
          .then(async o => {
            const raw = (o.tickets || []).filter(t => t && t.id);
            if (!raw.length) { setLoading(false); return; }
            const full = await Promise.all(raw.map(t => API.ticket(t.id).catch(() => t)));
            setTickets(full);
          })
          .catch(() => {})
          .finally(() => setLoading(false));
      } else { setLoading(false); }
    }, []);

    const t     = tickets[active] || null;
    const match = t?.match ? API.fmtMatch({ ...t.match, kickoff: t.match.kickoff || '' }) : null;

    const dlActions = [
      { label: 'Ajouter à Apple Wallet',  sub: 'Le billet apparaît dans votre iPhone', iconBg: '#000',     iconColor: '#fff' },
      { label: 'Ajouter à Google Wallet', sub: 'Pour les téléphones Android',           iconBg: '#fff',     iconColor: '#1F1F1F', border: '#DADCE0' },
      { label: 'Télécharger en PDF',      sub: 'Pour imprimer ou enregistrer',          iconBg: '#FFE7E7',  iconColor: '#C44141' },
      { label: 'Renvoyer par email',       sub: 'À votre adresse email',                iconBg: '#EFF6FF',  iconColor: '#3B82F6' },
    ];

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={900}>
          <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Mes places</h1>
          <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>Présentez le QR à l'entrée du stade ou téléchargez-le.</p>

          {loading ? <p style={{ color: '#A0ABC0' }}>Chargement…</p> : (
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 28, alignItems: 'start' }}>
              <div>
                {tickets.length > 1 && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
                    <button onClick={() => setActive(Math.max(0, active - 1))} disabled={active === 0} style={{ width: 36, height: 36, borderRadius: '50%', border: `1.5px solid ${line}`, background: '#fff', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: active === 0 ? 0.4 : 1 }}>
                      <Ic name="back" size={16} stroke={2.2} />
                    </button>
                    <span style={{ fontSize: 14, color: navy, fontWeight: 600 }}>Billet <b>{active + 1}</b> sur <b>{tickets.length}</b></span>
                    <button onClick={() => setActive(Math.min(tickets.length - 1, active + 1))} disabled={active === tickets.length - 1} style={{ width: 36, height: 36, borderRadius: '50%', border: `1.5px solid ${line}`, background: '#fff', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: active === tickets.length - 1 ? 0.4 : 1, transform: 'rotate(180deg)' }}>
                      <Ic name="back" size={16} stroke={2.2} />
                    </button>
                  </div>
                )}
                {t ? (
                  <Card style={{ padding: 32 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 14 }}>BILLET OFFICIEL · {C.abbreviation || C.name || 'Club'}</div>
                    <div style={{ fontSize: 22, fontWeight: 900, color: navy, marginBottom: 4 }}>{C.name || 'Club'}{match ? ` — ${match.away}` : ''}</div>
                    {match && <div style={{ fontSize: 14, color: '#8899BB', marginBottom: 28 }}>{match.dateText} · {match.timeText} · {C.stadiumShort || C.stadium || 'Stade'}</div>}
                    <div style={{ textAlign: 'center', marginBottom: 28 }}>
                      <div style={{ background: '#F4F6FB', borderRadius: 14, padding: 24, display: 'inline-block' }}>
                        <MiniQR size={200} token={t.qr_token} />
                      </div>
                      <div style={{ fontSize: 13, color: '#8899BB', marginTop: 10, fontWeight: 600 }}>
                        N° {(C.abbreviation || 'TK')}–{(t.id || t.qr_token || '').slice(0, 8).toUpperCase()}
                      </div>
                    </div>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                      {[
                        ['Au nom de', t.holder_name],
                        ['Tribune', t.zone || '—'],
                        t.seat ? ['Rang & place', `Rang ${t.seat.row} · place ${t.seat.number}`] : null,
                        match?.doors_open ? ['Ouverture portes', (match.doors_open || '').slice(0, 5).replace(':', 'h')] : null,
                      ].filter(Boolean).map(([lbl, val]) => (
                        <div key={lbl} style={{ background: '#F4F6FB', borderRadius: 10, padding: '12px 14px' }}>
                          <div style={{ fontSize: 11, color: '#A0ABC0', marginBottom: 3 }}>{lbl}</div>
                          <div style={{ fontSize: 14, fontWeight: 700, color: navy }}>{val}</div>
                        </div>
                      ))}
                    </div>
                  </Card>
                ) : (
                  <Card style={{ padding: 40, textAlign: 'center' }}><p style={{ color: '#8899BB' }}>Aucun billet trouvé.</p></Card>
                )}
              </div>

              <div style={{ position: 'sticky', top: 80 }}>
                <Card style={{ padding: 24 }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: navy, marginBottom: 16 }}>Télécharger ou envoyer</div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                    {dlActions.map(({ label, sub: s, iconBg, iconColor, border: bd }) => (
                      <button key={label} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '13px 14px', background: '#F4F6FB', border: `1.5px solid ${line}`, borderRadius: 12, cursor: 'pointer', textAlign: 'left', transition: 'background 0.12s' }}
                        onMouseEnter={e => e.currentTarget.style.background = '#ECEEF5'}
                        onMouseLeave={e => e.currentTarget.style.background = '#F4F6FB'}
                      >
                        <div style={{ width: 34, height: 34, borderRadius: 9, background: iconBg, border: bd ? `1px solid ${bd}` : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, color: iconColor }}>
                          <Ic name="download-pdf" size={17} />
                        </div>
                        <div style={{ flex: 1 }}>
                          <div style={{ fontSize: 13, fontWeight: 700, color: navy }}>{label}</div>
                          <div style={{ fontSize: 12, color: '#8899BB', marginTop: 1 }}>{s}</div>
                        </div>
                        <span style={{ color: '#A0ABC0' }}><Ic name="arrow" size={16} /></span>
                      </button>
                    ))}
                  </div>
                </Card>
              </div>
            </div>
          )}
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Erreur paiement ────────────────────────────────────────────
  function DeskScrErrPay() {
    const { back, goTo, appState } = useCtx();
    const [secs, setSecs] = useState(() =>
      appState.holdExpiresAt ? Math.max(0, Math.floor((new Date(appState.holdExpiresAt) - Date.now()) / 1000)) : 0
    );
    useEffect(() => {
      if (!secs) return;
      const t = setInterval(() => setSecs(s => Math.max(0, s - 1)), 1000);
      return () => clearInterval(t);
    }, []);
    const mins   = Math.floor(secs / 60);
    const secStr = String(secs % 60).padStart(2, '0');

    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={560}>
          <div style={{ textAlign: 'center', marginBottom: 32 }}>
            <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'rgba(220,38,38,0.10)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 20px' }}>
              <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#DC2626" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M6 6l12 12M18 6 6 18"/></svg>
            </div>
            <h1 style={{ margin: '0 0 10px', fontSize: 32, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Paiement refusé</h1>
            <p style={{ color: '#8899BB', fontSize: 16, maxWidth: 420, margin: '0 auto' }}>
              Votre banque a refusé le paiement. Aucun montant n'a été débité.
            </p>
          </div>
          {secs > 0 && (
            <div style={{ background: '#FFFBEB', border: '1.5px solid #FDE68A', borderRadius: 12, padding: '14px 18px', marginBottom: 20, display: 'flex', gap: 12, alignItems: 'flex-start' }}>
              <span style={{ color: '#F59E0B', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
              <p style={{ margin: 0, fontSize: 13, color: '#92400E', lineHeight: 1.5 }}>
                <b>Vos places ne sont pas perdues.</b> On les garde encore {mins} min {secStr} s.
              </p>
            </div>
          )}
          <Card style={{ padding: 28 }}>
            <PrimaryBtn onClick={() => back()} style={{ marginBottom: 12 }}>Réessayer le paiement</PrimaryBtn>
            <button onClick={() => back()} style={{ width: '100%', padding: '13px', background: 'transparent', color: navy, border: `1.5px solid ${line}`, borderRadius: 12, fontSize: 15, fontWeight: 600, cursor: 'pointer', marginBottom: 8 }}>Changer de carte</button>
            {C.phone && <button onClick={() => goTo('home')} style={{ width: '100%', padding: '11px', background: 'transparent', color: '#8899BB', border: 0, fontSize: 14, fontWeight: 600, cursor: 'pointer' }}>Appeler le club — {C.phone}</button>}
          </Card>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Session expirée ────────────────────────────────────────────
  function DeskScrErrSess() {
    const { goTo } = useCtx();
    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={520}>
          <div style={{ textAlign: 'center', marginBottom: 32 }}>
            <div style={{ width: 72, height: 72, borderRadius: '50%', background: '#FFF7ED', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 20px' }}>
              <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#EA580C" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
            </div>
            <h1 style={{ margin: '0 0 10px', fontSize: 32, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Réservation expirée</h1>
            <p style={{ color: '#8899BB', fontSize: 16, maxWidth: 400, margin: '0 auto' }}>
              Vous avez 15 minutes pour finaliser votre commande. Vos places ont été remises en vente.
            </p>
          </div>
          <Card style={{ padding: 28 }}>
            <PrimaryBtn onClick={() => goTo('zones')} style={{ marginBottom: 12 }}>Choisir à nouveau mes places</PrimaryBtn>
            <button onClick={() => goTo('home')} style={{ width: '100%', padding: '12px', background: 'transparent', color: '#8899BB', border: 0, fontSize: 14, fontWeight: 600, cursor: 'pointer' }}>Retour à l'accueil</button>
          </Card>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Place épuisée ──────────────────────────────────────────────
  function DeskScrErrSold() {
    const { back, goTo } = useCtx();
    return (
      <DeskPage nav={<DeskNav />}>
        <DeskContent maxWidth={520}>
          <div style={{ textAlign: 'center', marginBottom: 32 }}>
            <div style={{ width: 72, height: 72, borderRadius: '50%', background: '#FFF7ED', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 20px' }}>
              <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#EA580C" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 8v4M12 16v.01"/><circle cx="12" cy="12" r="9"/></svg>
            </div>
            <h1 style={{ margin: '0 0 10px', fontSize: 32, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Place plus disponible</h1>
            <p style={{ color: '#8899BB', fontSize: 16, maxWidth: 420, margin: '0 auto' }}>
              La place sélectionnée vient d'être prise par un autre supporter. Choisissez-en une autre.
            </p>
          </div>
          <Card style={{ padding: 28 }}>
            <PrimaryBtn onClick={() => back()} style={{ marginBottom: 12 }}>Choisir une autre place</PrimaryBtn>
            <button onClick={() => goTo('zones')} style={{ width: '100%', padding: '12px', background: 'transparent', color: '#8899BB', border: 0, fontSize: 14, fontWeight: 600, cursor: 'pointer' }}>Revenir au choix de tribune</button>
          </Card>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── PMR ────────────────────────────────────────────────────────
  function DeskScrPMR() {
    const pmrZones = [
      { id: 'pmr-offi', name: 'Tribune Officielle — Espace PMR', description: 'Accès direct · vue dégagée · 4 places restantes', price: 15 },
      { id: 'pmr-fam',  name: 'Tribune Famille — Espace PMR',    description: 'Environnement calme · 6 places restantes',         price: 12 },
    ];
    const [selected, setSelected] = useState(pmrZones[0].id);
    const selectedZone = pmrZones.find(z => z.id === selected);

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={900}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 28, alignItems: 'start' }}>
            <div>
              <div style={{ background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '16px 20px', marginBottom: 28, display: 'flex', gap: 14, alignItems: 'flex-start' }}>
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#3B82F6" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 1 }}>
                  <circle cx="10" cy="5" r="2"/><path d="M10 7v5h6l2 5"/><path d="M16 18a6 6 0 1 1-9-5"/>
                </svg>
                <div style={{ fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>
                  <b>Place + accompagnateur gratuit</b><br />Pour chaque place PMR, votre accompagnateur entre gratuitement. Accès direct sans escalier.
                </div>
              </div>
              <h2 style={{ margin: '0 0 16px', fontSize: 20, fontWeight: 800, color: navy }}>Tribunes adaptées</h2>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {pmrZones.map(z => {
                  const on = selected === z.id;
                  return (
                    <div key={z.id} onClick={() => setSelected(z.id)} style={{ background: on ? navy : '#fff', border: `2px solid ${on ? navy : line}`, borderRadius: 14, padding: '18px 22px', display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 18, alignItems: 'center', cursor: 'pointer', transition: 'all 0.15s' }}>
                      <div>
                        <div style={{ fontSize: 15, fontWeight: 700, color: on ? '#fff' : navy, marginBottom: 3 }}>{z.name}</div>
                        <div style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.60)' : '#8899BB' }}>
                          {z.description} · <span style={{ color: on ? 'rgba(255,255,255,0.7)' : '#16A34A', fontWeight: 600 }}>+ accompagnateur gratuit</span>
                        </div>
                      </div>
                      <div style={{ textAlign: 'right' }}>
                        <div style={{ fontSize: 22, fontWeight: 900, color: on ? '#fff' : navy, lineHeight: 1 }}>{z.price} €</div>
                        <div style={{ fontSize: 12, color: on ? 'rgba(255,255,255,0.50)' : '#A0ABC0' }}>par place</div>
                      </div>
                      <div style={{ width: 22, height: 22, borderRadius: '50%', flexShrink: 0, border: `2px solid ${on ? '#fff' : '#D1D5DB'}`, background: on ? '#fff' : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        {on && <div style={{ width: 10, height: 10, borderRadius: '50%', background: navy }} />}
                      </div>
                    </div>
                  );
                })}
              </div>
              {C.phoneAccessibility && (
                <div style={{ marginTop: 20, background: '#F0FDF4', border: '1.5px solid #BBF7D0', borderRadius: 12, padding: '14px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                  <span style={{ color: '#16A34A', flexShrink: 0 }}><Ic name="phone" size={17} /></span>
                  <p style={{ margin: 0, fontSize: 13, color: '#15803D', lineHeight: 1.5 }}>
                    <b>Besoin d'aide ?</b> Notre équipe accessibilité vous rappelle au{' '}
                    <a href={`tel:${C.phoneAccessibility}`} style={{ color: '#15803D', fontWeight: 700 }}>{C.phoneAccessibility}</a>.
                  </p>
                </div>
              )}
            </div>
            <div style={{ position: 'sticky', top: 80 }}>
              {selectedZone && (
                <Card style={{ padding: 24 }}>
                  <div style={{ fontSize: 15, fontWeight: 800, color: navy, marginBottom: 4 }}>{selectedZone.name}</div>
                  <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 20 }}>{selectedZone.description}</div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 20 }}>
                    <span style={{ fontSize: 14, color: '#8899BB' }}>Prix par place</span>
                    <span style={{ fontSize: 24, fontWeight: 900, color: navy }}>{selectedZone.price} €</span>
                  </div>
                  <PrimaryBtn onClick={() => {}}>Continuer <Ic name="arrow" size={18} /></PrimaryBtn>
                </Card>
              )}
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Groupes & CE ───────────────────────────────────────────────
  function DeskScrGroup() {
    const { appState } = useCtx();
    const [groupName,   setGroupName]   = useState('');
    const [contactName, setContactName] = useState('');
    const [phone,       setPhone]       = useState('');
    const [email,       setEmail]       = useState('');
    const [qty,         setQty]         = useState('');
    const [loading,     setLoading]     = useState(false);
    const [done,        setDone]        = useState(false);
    const [err,         setErr]         = useState(null);

    async function submit() {
      const qtyNum = parseInt(qty, 10);
      if (!groupName.trim() || !email.trim() || !qtyNum || qtyNum < 10) {
        setErr('Remplissez le nom du groupe, votre email et un nombre de personnes ≥ 10.');
        return;
      }
      setLoading(true); setErr(null);
      try {
        await API.groupRequest(appState.match?.id || null, groupName.trim(), contactName.trim() || null, email.trim(), phone.trim() || null, qtyNum, null);
        setDone(true);
      } catch (ex) { setErr(ex.message || "Erreur lors de l'envoi"); }
      finally { setLoading(false); }
    }

    const leftPanel = (
      <div style={{ width: '42%', flexShrink: 0, background: navy, display: 'flex', flexDirection: 'column', justifyContent: 'space-between', padding: '40px 48px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          {C.logo && <img src={C.logo} alt="" style={{ height: 36, objectFit: 'contain' }} />}
          <span style={{ color: '#fff', fontSize: 16, fontWeight: 800 }}>{C.name || 'Billetterie'}</span>
        </div>
        <div>
          <h2 style={{ color: '#fff', fontSize: 36, fontWeight: 900, letterSpacing: '-0.03em', margin: '0 0 16px', lineHeight: 1.1 }}>
            Groupes &amp; CE<br /><span style={{ color: yellow }}>au meilleur prix.</span>
          </h2>
          <p style={{ color: 'rgba(255,255,255,0.55)', fontSize: 15, lineHeight: 1.6, margin: '0 0 36px', maxWidth: 300 }}>
            À partir de 10 personnes — tarif réduit, accueil dédié, paiement différé adapté aux comités d'entreprise.
          </p>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {['Tarif préférentiel dès 10 places', 'Accueil dédié à l\'entrée', 'Devis personnalisé sous 48h ouvrées'].map(t => (
              <div key={t} style={{ display: 'flex', alignItems: 'center', gap: 10, color: 'rgba(255,255,255,0.60)', fontSize: 14 }}>
                <span style={{ color: yellow, fontSize: 8, flexShrink: 0 }}>●</span>{t}
              </div>
            ))}
          </div>
        </div>
        <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.30)', letterSpacing: '0.06em' }}>{C.billetterieLabel || 'BILLETTERIE OFFICIELLE'}</div>
      </div>
    );

    if (done) return (
      <div style={{ width: '100%', minHeight: '100vh', display: 'flex' }}>
        {leftPanel}
        <div style={{ flex: 1, background: '#fff', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '60px 48px' }}>
          <div style={{ width: '100%', maxWidth: 400, textAlign: 'center' }}>
            <div style={{ width: 72, height: 72, borderRadius: '50%', background: '#F0FDF4', border: '3px solid #16A34A', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 20px', fontSize: 32 }}>✓</div>
            <h1 style={{ margin: '0 0 10px', fontSize: 28, fontWeight: 900, color: navy }}>Demande envoyée !</h1>
            <p style={{ color: '#8899BB', fontSize: 15 }}>Notre équipe vous recontacte sous 48h ouvrées à <b>{email}</b> avec un devis personnalisé.</p>
          </div>
        </div>
      </div>
    );

    return (
      <div style={{ width: '100%', minHeight: '100vh', display: 'flex' }}>
        {leftPanel}
        <div style={{ flex: 1, background: '#fff', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '60px 48px', overflowY: 'auto' }}>
          <div style={{ width: '100%', maxWidth: 420 }}>
            <h1 style={{ margin: '0 0 6px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Demande de groupe</h1>
            <p style={{ margin: '0 0 28px', color: '#8899BB', fontSize: 15 }}>Notre équipe vous recontacte sous 48h avec un devis personnalisé.</p>
            <ErrMsg msg={err} />
            <Field label="Nom du groupe ou de l'entreprise" value={groupName} onChange={setGroupName} placeholder="CE Peugeot Montbéliard" />
            <Field label="Votre prénom et nom" value={contactName} onChange={setContactName} placeholder="Maxime Dubois" />
            <div style={{ marginBottom: 18 }}>
              <label style={{ display: 'block', fontSize: 13, fontWeight: 700, color: navy, marginBottom: 7 }}>Nombre de personnes</label>
              <input type="number" min="10" value={qty} onChange={e => setQty(e.target.value)} placeholder="10 minimum"
                style={{ width: '100%', padding: '11px 14px', borderRadius: 10, border: `1.5px solid ${line}`, fontSize: 15, color: navy, fontFamily: 'inherit', outline: 'none', background: '#fff', boxSizing: 'border-box' }}
                onFocus={e => e.target.style.borderColor = navy} onBlur={e => e.target.style.borderColor = line}
              />
            </div>
            <Field label="Téléphone" type="tel" value={phone} onChange={setPhone} placeholder="06 00 00 00 00" />
            <Field label="Adresse email" type="email" value={email} onChange={setEmail} placeholder="exemple@email.fr" />
            <PrimaryBtn onClick={submit} busy={loading}>Envoyer ma demande</PrimaryBtn>
          </div>
        </div>
      </div>
    );
  }

  // ── Choix abonnement ───────────────────────────────────────────
  function DeskScrAboPick() {
    const { goTo, setAppState } = useCtx();
    const [plans,   setPlans]   = useState([]);
    const [pick,    setPick]    = useState(null);
    const [loading, setLoading] = useState(true);
    const [err,     setErr]     = useState(null);

    useEffect(() => {
      API.subscriptionPlans()
        .then(p => { setPlans(p); if (p.length) setPick(p.find(x => x.featured) || p[0]); setLoading(false); })
        .catch(ex => { setErr(ex.message || 'Erreur'); setLoading(false); });
    }, []);

    function next() { if (!pick) return; setAppState(s => ({ ...s, selectedPlan: pick })); goTo('aboPay'); }

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={900}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 32, alignItems: 'start' }}>
            <div>
              <h1 style={{ margin: '0 0 4px', fontSize: 30, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Une saison, plus économique</h1>
              <p style={{ color: '#8899BB', margin: '0 0 32px', fontSize: 15 }}>Tous les matchs à domicile. Choisissez votre formule.</p>
              {loading && <p style={{ color: '#A0ABC0' }}>Chargement…</p>}
              {err && <ErrMsg msg={err} />}
              {!loading && !err && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                  {plans.map(p => {
                    const on = pick && pick.id === p.id;
                    return (
                      <div key={p.id} onClick={() => setPick(p)} style={{ background: on ? navy : '#fff', border: `2px solid ${on ? navy : line}`, borderRadius: 16, padding: '22px 24px', cursor: 'pointer', transition: 'all 0.15s', display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 18, alignItems: 'center', position: 'relative' }}>
                        {p.featured && (
                          <div style={{ position: 'absolute', top: -10, left: 20, background: yellow, color: navy, fontSize: 11, fontWeight: 800, padding: '3px 10px', borderRadius: 20 }}>RECOMMANDÉ</div>
                        )}
                        <div>
                          <div style={{ fontSize: 17, fontWeight: 800, color: on ? '#fff' : navy, marginBottom: 3 }}>{p.name}</div>
                          <div style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.65)' : '#8899BB' }}>{p.description || p.zone}</div>
                        </div>
                        <div style={{ textAlign: 'right' }}>
                          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                            <span style={{ fontSize: 26, fontWeight: 900, color: on ? '#fff' : navy }}>{Number(p.price).toFixed(0)} €</span>
                            {p.price_full > p.price && <del style={{ fontSize: 14, color: on ? 'rgba(255,255,255,0.45)' : '#A0ABC0' }}>{Number(p.price_full).toFixed(0)} €</del>}
                          </div>
                          {p.match_count > 0 && <div style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.55)' : '#8899BB' }}>= {Number(p.price_per_match).toFixed(0)} €/match</div>}
                        </div>
                        <div style={{ width: 22, height: 22, borderRadius: '50%', flexShrink: 0, border: `2px solid ${on ? '#fff' : '#D1D5DB'}`, background: on ? '#fff' : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                          {on && <div style={{ width: 10, height: 10, borderRadius: '50%', background: navy }} />}
                        </div>
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
            <div style={{ position: 'sticky', top: 80 }}>
              {pick && !loading && (
                <Card style={{ padding: 24, marginBottom: 14 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 14 }}>FORMULE CHOISIE</div>
                  <div style={{ fontSize: 17, fontWeight: 800, color: navy, marginBottom: 4 }}>{pick.name}</div>
                  <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 18 }}>{pick.description || pick.zone}</div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', paddingBottom: 18, marginBottom: 18, borderBottom: `1px solid ${line}` }}>
                    <span style={{ fontSize: 14, color: '#8899BB' }}>Prix saison</span>
                    <span style={{ fontSize: 26, fontWeight: 900, color: navy }}>{Number(pick.price).toFixed(0)} €</span>
                  </div>
                  {pick.match_count > 0 && (
                    <div style={{ background: '#F0FDF4', borderRadius: 10, padding: '10px 14px', marginBottom: 18 }}>
                      <div style={{ fontSize: 13, color: '#16A34A', fontWeight: 700 }}>= {Number(pick.price_per_match).toFixed(0)} € par match</div>
                    </div>
                  )}
                  <PrimaryBtn onClick={next}>Continuer <Ic name="arrow" size={18} /></PrimaryBtn>
                </Card>
              )}
              <div style={{ background: '#EFF6FF', border: '1.5px solid #DBEAFE', borderRadius: 12, padding: '14px 18px', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                <span style={{ color: '#3B82F6', flexShrink: 0, marginTop: 1 }}><Ic name="info" size={17} /></span>
                <p style={{ margin: 0, fontSize: 13, color: '#1E40AF', lineHeight: 1.5 }}>Paiement en 4 fois sans frais disponible. Annulation possible sous 14 jours.</p>
              </div>
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ── Paiement abonnement ────────────────────────────────────────
  function DeskScrAboPay() {
    const { goTo, appState } = useCtx();
    const plan = appState.selectedPlan || {};

    const [installments, setInstallments] = useState(4);
    const [step,         setStep]         = useState('pick');
    const [loading,      setLoading]      = useState(false);
    const [err,          setErr]          = useState(null);
    const [payData,      setPayData]      = useState(null);

    const stripeRef   = useRef(null);
    const elementsRef = useRef(null);
    const mountRef    = useRef(null);

    const fmtEur = v => Number(v).toFixed(2).replace('.', ',') + ' €';
    const todayAmount = payData
      ? (payData.installment_amount ?? payData.total ?? 0)
      : (plan.price ? (installments === 4 ? Math.ceil(plan.price / 4 * 100) / 100 : plan.price) : 0);

    const today  = new Date();
    const months = Array.from({ length: installments }, (_, i) => {
      const d = new Date(today);
      d.setMonth(d.getMonth() + i);
      return d.getDate() + ' ' + API.FR_MONTHS_SHORT[d.getMonth()] + ' ' + d.getFullYear();
    });

    async function proceedToStripe() {
      if (!window.__stripeKey) { setErr('Clé Stripe non chargée.'); return; }
      setLoading(true); setErr(null);
      try {
        const d = await API.subscribe(plan.id, installments);
        setPayData(d);
        stripeRef.current   = Stripe(window.__stripeKey);
        elementsRef.current = stripeRef.current.elements({ clientSecret: d.client_secret, appearance: { theme: 'flat', variables: { colorPrimary: '#133478' } } });
        setStep('stripe');
      } catch (ex) { setErr(ex.message || 'Erreur'); }
      finally { setLoading(false); }
    }

    useEffect(() => {
      if (step !== 'stripe' || !elementsRef.current || !mountRef.current) return;
      const pe = elementsRef.current.create('payment');
      pe.mount(mountRef.current);
      return () => { try { pe.unmount(); } catch(_) {} };
    }, [step]);

    async function pay() {
      if (!stripeRef.current || !elementsRef.current) return;
      setLoading(true); setErr(null);
      try {
        const { error } = await stripeRef.current.confirmPayment({ elements: elementsRef.current, redirect: 'if_required' });
        if (error) { setErr(error.message); setLoading(false); return; }
        goTo('wallet');
      } catch (ex) { setErr(ex.message || 'Erreur de paiement'); setLoading(false); }
    }

    return (
      <DeskPage nav={<DeskNav showBack />}>
        <DeskContent maxWidth={900}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 300px', gap: 28, alignItems: 'start' }}>
            <div>
              <h1 style={{ margin: '0 0 4px', fontSize: 28, fontWeight: 900, color: navy, letterSpacing: '-0.02em' }}>Comment souhaitez-vous payer ?</h1>
              <p style={{ color: '#8899BB', margin: '0 0 28px', fontSize: 15 }}>{plan.name || 'Abonnement'}{plan.description ? ' · ' + plan.description : ''}</p>

              {step === 'pick' ? (
                <Card style={{ padding: 32 }}>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 24 }}>
                    {[
                      { val: 4, label: 'En 4 fois sans frais',     desc: 'Étalez votre abonnement sur 4 mensualités.' },
                      { val: 1, label: 'Tout payer maintenant',     desc: plan.price ? `${fmtEur(plan.price)} prélevés aujourd'hui.` : '—' },
                    ].map(({ val, label, desc }) => {
                      const on = installments === val;
                      return (
                        <div key={val} onClick={() => setInstallments(val)} style={{ background: on ? navy : '#F4F6FB', border: `2px solid ${on ? navy : line}`, borderRadius: 12, padding: '16px 20px', cursor: 'pointer', transition: 'all 0.15s', display: 'flex', alignItems: 'center', gap: 14 }}>
                          <div style={{ width: 22, height: 22, borderRadius: '50%', flexShrink: 0, border: `2px solid ${on ? '#fff' : '#D1D5DB'}`, background: on ? '#fff' : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                            {on && <div style={{ width: 10, height: 10, borderRadius: '50%', background: navy }} />}
                          </div>
                          <div>
                            <div style={{ fontSize: 15, fontWeight: 700, color: on ? '#fff' : navy }}>{label}</div>
                            <div style={{ fontSize: 13, color: on ? 'rgba(255,255,255,0.65)' : '#8899BB' }}>{desc}</div>
                          </div>
                        </div>
                      );
                    })}
                  </div>

                  {installments === 4 && plan.price && (
                    <div style={{ background: '#F4F6FB', borderRadius: 12, padding: '16px 20px', marginBottom: 24 }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }}>
                        <div style={{ fontSize: 13, fontWeight: 700, color: navy }}>Calendrier de paiement</div>
                        <span style={{ fontSize: 13, color: '#8899BB' }}>4 × {fmtEur(todayAmount)}</span>
                      </div>
                      {months.map((m, i) => (
                        <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0', borderTop: i > 0 ? `1px solid ${line}` : 0 }}>
                          <div>
                            <div style={{ fontSize: 13, fontWeight: i === 0 ? 700 : 500, color: navy }}>{i === 0 ? "Aujourd'hui" : `${i + 1}ᵉ versement`}</div>
                            <div style={{ fontSize: 12, color: '#8899BB' }}>{m}</div>
                          </div>
                          <div style={{ fontSize: 14, fontWeight: i === 0 ? 800 : 500, color: navy }}>{fmtEur(todayAmount)}</div>
                        </div>
                      ))}
                    </div>
                  )}

                  <div style={{ background: '#F0FDF4', border: '1.5px solid #BBF7D0', borderRadius: 10, padding: '12px 14px', marginBottom: 24, display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                    <span style={{ color: '#16A34A', flexShrink: 0, marginTop: 1 }}><Ic name="lock" size={16} /></span>
                    <p style={{ margin: 0, fontSize: 13, color: '#15803D', lineHeight: 1.5 }}>Le 4× sans frais est offert par le club. Annulation possible sous 14 jours.</p>
                  </div>
                  <ErrMsg msg={err} />
                  <PrimaryBtn onClick={proceedToStripe} disabled={!plan.id} busy={loading}>
                    <Ic name="lock" size={18} /> Passer au paiement
                  </PrimaryBtn>
                </Card>
              ) : (
                <Card style={{ padding: 32 }}>
                  <h2 style={{ margin: '0 0 20px', fontSize: 20, fontWeight: 800, color: navy }}>Paiement sécurisé</h2>
                  <div ref={mountRef} style={{ minHeight: 200, marginBottom: 24 }} />
                  <ErrMsg msg={err} />
                  <PrimaryBtn onClick={pay} busy={loading}>
                    <Ic name="lock" size={18} /> {loading ? 'Traitement…' : `Payer ${fmtEur(todayAmount)}`}
                  </PrimaryBtn>
                </Card>
              )}
            </div>

            <div style={{ position: 'sticky', top: 80 }}>
              <Card style={{ padding: 24 }}>
                <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.1em', color: '#A0ABC0', marginBottom: 14 }}>VOTRE ABONNEMENT</div>
                <div style={{ fontSize: 16, fontWeight: 800, color: navy, marginBottom: 4 }}>{plan.name || '—'}</div>
                <div style={{ fontSize: 13, color: '#8899BB', marginBottom: 18 }}>{plan.description || '—'}</div>
                <div style={{ borderTop: `1px solid ${line}`, paddingTop: 14 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
                    <span style={{ fontSize: 13, color: '#8899BB' }}>Mode</span>
                    <span style={{ fontSize: 13, color: navy, fontWeight: 600 }}>{installments === 1 ? 'Intégral' : 'En 4 fois'}</span>
                  </div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
                    <span style={{ fontSize: 13, color: '#8899BB' }}>Aujourd'hui</span>
                    <span style={{ fontSize: 13, color: navy, fontWeight: 700 }}>{fmtEur(todayAmount)}</span>
                  </div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', paddingTop: 12, borderTop: `1px solid ${line}` }}>
                    <span style={{ fontSize: 15, fontWeight: 700, color: navy }}>Total saison</span>
                    <span style={{ fontSize: 22, fontWeight: 900, color: navy }}>{plan.price ? fmtEur(plan.price) : '—'}</span>
                  </div>
                </div>
              </Card>
            </div>
          </div>
        </DeskContent>
      </DeskPage>
    );
  }

  // ══════════════════════════════════════════════════════════════
  window.DesktopScreens = {
    welcome:      DeskScrWelcome,
    home:         DeskScrHome,
    list:         DeskScrList,
    zones:        DeskScrZones,
    seats:        DeskScrSeats,
    pay:          DeskScrPay,
    done:         DeskScrDone,
    login:        DeskScrLogin,
    signup:       DeskScrSignup,
    pwdReset:     DeskScrPwdReset,
    wallet:       DeskScrWallet,
    season:       DeskScrSeason,
    transfer:     DeskScrTransfer,
    transferDone: DeskScrTransferDone,
    history:      DeskScrHistory,
    download:     DeskScrDownload,
    errPay:       DeskScrErrPay,
    errSess:      DeskScrErrSess,
    errSold:      DeskScrErrSold,
    pmr:          DeskScrPMR,
    group:        DeskScrGroup,
    aboPick:      DeskScrAboPick,
    aboPay:       DeskScrAboPay,
  };
})();
