/* Pages: Home, Infos, Jeux, Admin */

const HomePage = ({ guest }) => {
  const { wedding, guests } = useStore();
  // On relit l'invité dans la liste serveur pour avoir son message à jour
  // (le profil mémorisé en localStorage peut être plus ancien).
  const fresh = (guests || []).find((g) => g.id === guest.id) || guest;
  const firstName = fresh.name || guest.name;
  const intro = (fresh.message && fresh.message.trim())
    ? fresh.message
    : wedding.welcomeText;
  return (
    <div className="container page">
      <div className="hero">
        <Crest initials={`${wedding.bride[0]} & ${wedding.groom[0]}`} />
        <div style={{ height: 28 }} />
        <div className="greeting">Bonjour, {firstName}.</div>
        <div className="couple">
          {wedding.bride}<span className="amp">&</span>{wedding.groom}
        </div>
        <div className="date-line">{wedding.dateShort} · {wedding.venue}</div>
      </div>

      <div className="welcome-body">
        <p className="dropcap">{intro}</p>
        {wedding.homeIntro2 && <p>{wedding.homeIntro2}</p>}
        <p style={{ marginTop: 28 }}>
          {wedding.signoff}
          <br />
          <span className="script" style={{
            fontSize: 36,
            color: "var(--burgundy)",
            display: "inline-block",
            marginTop: 4
          }}>{wedding.bride} & {wedding.groom}</span>
        </p>
      </div>

      <div style={{ height: 48 }} />
      <Divider>{wedding.programTitle}</Divider>
      <div style={{ height: 24 }} />

      <div style={{
        display: "grid",
        gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
        gap: 16,
        textAlign: "center"
      }}>
        {[
          { t: wedding.ceremonyTime, l: wedding.ceremonyLabel },
          { t: wedding.cocktailTime, l: wedding.cocktailLabel },
          { t: wedding.dinnerTime, l: wedding.dinnerLabel }
        ].map((m, i) => (
          <div key={i} className="card ornate" style={{ padding: "22px 18px" }}>
            <div style={{
              fontFamily: "'Cormorant Garamond', serif",
              fontSize: 32,
              color: "var(--burgundy)"
            }}>{m.t}</div>
            <div className="eyebrow" style={{ marginTop: 6 }}>{m.l}</div>
          </div>
        ))}
      </div>
    </div>
  );
};

/* Convertit un lien de partage Spotify en URL d'intégration (lecteur embarqué).
   Gère playlist / album / track / artist, avec ou sans préfixe « intl-xx/ ». */
const spotifyEmbedUrl = (url) => {
  const m = String(url || "").match(
    /open\.spotify\.com\/(?:intl-[a-z]+\/)?(playlist|album|track|artist|episode|show)\/([A-Za-z0-9]+)/
  );
  return m ? `https://open.spotify.com/embed/${m[1]}/${m[2]}` : "";
};

const InfoPage = ({ guest }) => {
  const { wedding, info, guests, setRsvp, setAddress, addressEcho } = useStore();
  const schedule = info.schedule || [];
  const lodging = info.lodging || [];
  const access = info.access || [];
  // Invité réel (présent dans la liste serveur) -> peut répondre présent.
  const me = (guests || []).find((g) => g.id === guest?.id);
  const [rsvpBusy, setRsvpBusy] = React.useState(false);
  const answerRsvp = async (value) => {
    if (!me) return;
    setRsvpBusy(true);
    try { await setRsvp(me.id, value); }
    catch { /* silencieux : on garde l'état serveur */ }
    finally { setRsvpBusy(false); }
  };

  // Adresse pour le faire-part (pré-remplie depuis l'écho local si déjà saisie).
  const [addr, setAddr] = React.useState("");
  const [addrSaved, setAddrSaved] = React.useState(false);
  const [addrBusy, setAddrBusy] = React.useState(false);
  React.useEffect(() => {
    if (me) setAddr((addressEcho && addressEcho[me.id]) || "");
  }, [me, addressEcho]);
  const saveAddress = async () => {
    if (!me) return;
    setAddrBusy(true);
    setAddrSaved(false);
    try { await setAddress(me.id, addr.trim()); setAddrSaved(true); }
    catch { /* silencieux */ }
    finally { setAddrBusy(false); }
  };

  return (
    <div className="container page">
      <div className="center">
        <div className="eyebrow">Tout ce qu'il faut savoir</div>
        <h1 style={{ marginTop: 12 }}>Informations</h1>
        <div style={{ height: 18 }} />
        <Flourish width={180} />
      </div>

      <div className="info-grid">
        <div className="card ornate info-card">
          <div className="label">Le lieu</div>
          <div className="big">{wedding.venue}</div>
          <p className="sub">{wedding.region}</p>
          <p className="sub">{wedding.address}</p>
          <p style={{ marginTop: 16, fontSize: 15 }}>{info.venueDesc}</p>
        </div>

        <div className="card ornate info-card">
          <div className="label">La date</div>
          <div className="big">{wedding.date}</div>
          <p className="sub">{wedding.dateShort}</p>
          <div className="timeline">
            {schedule.map((row, i) => (
              <div className="timeline-row" key={i}>
                <div className="t">{row.time}</div>
                <div className="l">{row.label}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="card ornate info-card">
          <div className="label">Tenue</div>
          <div className="big">{info.dressCodeTitle}</div>
          <p className="sub">{wedding.dressCode}</p>
          <p style={{ fontSize: 15, marginTop: 12 }}>{info.dressCodeDesc}</p>
        </div>

        <div className="card ornate info-card">
          <div className="label">Hébergement</div>
          <div className="big">Où dormir</div>
          <p className="sub" style={{ marginBottom: 12 }}>{info.lodgingIntro}</p>
          <ul style={{
            margin: 0, padding: 0, listStyle: "none",
            display: "flex", flexDirection: "column", gap: 10
          }}>
            {lodging.map((h, i) => (
              <li style={{ fontSize: 15 }} key={i}>
                <strong style={{ color: "var(--burgundy)" }}>{h.name}</strong>
                {h.distance ? ` — ${h.distance}` : ""}
                {h.note && <div style={{ color: "var(--ink-muted)", fontSize: 13 }}>{h.note}</div>}
              </li>
            ))}
          </ul>
        </div>

        <div className="card ornate info-card">
          <div className="label">Accès</div>
          <div className="big">Comment venir</div>
          {access.map((a, i) => (
            <p style={{ fontSize: 15 }} key={i}>
              <strong>{a.mode} :</strong> {a.text}
            </p>
          ))}
        </div>

        <div className="card ornate info-card">
          <div className="label">Liste de mariage</div>
          <div className="big">{info.registryTitle}</div>
          <p style={{ fontSize: 15 }}>{info.registryText}</p>
          {info.registryUrl ? (
            <a className="btn gold" style={{ marginTop: 12 }} href={info.registryUrl} target="_blank" rel="noopener noreferrer">
              {info.registryButton || "Voir la cagnotte"}
            </a>
          ) : (
            <button className="btn gold" style={{ marginTop: 12 }}>{info.registryButton || "Voir la cagnotte"}</button>
          )}
        </div>

        {/* Blocs personnalisés ajoutés depuis l'admin */}
        {(info.customBlocks || []).map((block, i) => (
          (block.title || block.text || block.label) ? (
            <div className="card ornate info-card" key={i}>
              {block.label && <div className="label">{block.label}</div>}
              {block.title && <div className="big">{block.title}</div>}
              {block.text && <p style={{ fontSize: 15, marginTop: block.title ? 12 : 0, whiteSpace: "pre-line" }}>{block.text}</p>}
            </div>
          ) : null
        ))}
      </div>

      {spotifyEmbedUrl(info.playlistUrl) && (
        <>
          <div style={{ height: 48 }} />
          <div className="card ornate" style={{ padding: "32px 24px" }}>
            <div className="center" style={{ marginBottom: 18 }}>
              <div className="eyebrow">Ambiance sonore</div>
              <div className="big" style={{ marginTop: 6 }}>{info.playlistTitle}</div>
              {info.playlistText && (
                <p className="sub" style={{ maxWidth: 520, margin: "8px auto 0" }}>{info.playlistText}</p>
              )}
            </div>
            <iframe
              title="Playlist Spotify"
              src={spotifyEmbedUrl(info.playlistUrl)}
              width="100%"
              height="380"
              style={{ border: 0, borderRadius: 12, display: "block" }}
              loading="lazy"
              allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
            />
            <div className="center" style={{ marginTop: 14 }}>
              <a className="btn gold" href={info.playlistUrl} target="_blank" rel="noopener noreferrer">
                Ouvrir dans Spotify
              </a>
            </div>
          </div>
        </>
      )}

      <div style={{ height: 48 }} />
      <div className="card ornate" style={{ textAlign: "center", padding: "32px 24px" }}>
        <div className="eyebrow">Réponse souhaitée avant le</div>
        <div style={{
          fontFamily: "'Cormorant Garamond', serif",
          fontSize: 32, color: "var(--burgundy)", margin: "8px 0"
        }}>{wedding.rsvpDate}</div>
        <p className="sub" style={{ maxWidth: 480, margin: "0 auto 16px" }}>{info.rsvpNote}</p>

        {me ? (
          <div className="rsvp-choice">
            <label className={"rsvp-opt" + (me.rsvp === "yes" ? " on" : "")}>
              <input
                type="radio"
                name="rsvp"
                checked={me.rsvp === "yes"}
                disabled={rsvpBusy}
                onChange={() => answerRsvp("yes")}
              />
              <span>Oui, je serai présent·e</span>
            </label>
            <label className={"rsvp-opt no" + (me.rsvp === "no" ? " on" : "")}>
              <input
                type="radio"
                name="rsvp"
                checked={me.rsvp === "no"}
                disabled={rsvpBusy}
                onChange={() => answerRsvp("no")}
              />
              <span>Non, je ne pourrai pas venir</span>
            </label>
            {me.rsvp && (
              <div className="muted" style={{ fontSize: 13, marginTop: 10 }}>
                Merci, ta réponse est enregistrée ✦
                {" "}
                <button className="link-btn" disabled={rsvpBusy} onClick={() => answerRsvp("")}>
                  modifier
                </button>
              </div>
            )}
          </div>
        ) : (
          <p className="muted" style={{ fontSize: 13 }}>
            Connecte-toi en tant qu'invité pour indiquer ta présence.
          </p>
        )}

        {me && (
          <div className="rsvp-address">
            <label htmlFor="faire-part">Adresse pour le faire-part</label>
            <p className="muted" style={{ fontSize: 13, margin: "2px 0 10px" }}>
              Indique-nous l'adresse postale à laquelle envoyer ton faire-part.
            </p>
            <textarea
              id="faire-part"
              value={addr}
              rows={3}
              placeholder="12 rue des Lilas, 07150 Vallon-Pont-d'Arc"
              onChange={e => { setAddr(e.target.value); setAddrSaved(false); }}
            />
            <div style={{ marginTop: 10 }}>
              <button className="btn" disabled={addrBusy} onClick={saveAddress}>
                {addrBusy ? "Enregistrement…" : "Enregistrer mon adresse"}
              </button>
              {addrSaved && (
                <span className="muted" style={{ fontSize: 13, marginLeft: 12 }}>
                  Adresse enregistrée ✦
                </span>
              )}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

const GamePage = ({ guest }) => {
  // On transmet l'identité de l'invité à l'iframe via l'URL : plus fiable que de
  // compter sur le partage du localStorage entre la page et le jeu embarqué.
  const gameSrc = guest && guest.id
    ? `/jeu/?guestId=${encodeURIComponent(guest.id)}&guestName=${encodeURIComponent(guest.name || "")}`
    : "/jeu/";
  return (
    <div className="container page">
      <div className="center" style={{ marginBottom: 32 }}>
        <div className="eyebrow">Pour patienter jusqu'au grand jour</div>
        <h1 style={{ marginTop: 12 }}>Aidez Patapon &amp; Poutchi</h1>
        <div style={{ height: 16 }} />
        <Flourish width={180} />
        <p className="muted" style={{ maxWidth: 540, margin: "16px auto 0", fontSize: 15 }}>
          Faites bondir Patapon ou Poutchi à travers les portails du château.
          Clic, écran tactile, <kbd style={{
            padding: "2px 6px", border: "1px solid var(--rule)",
            background: "#fbf3df", fontFamily: "inherit", fontSize: 13
          }}>Espace</kbd> ou flèche du haut pour décoller.
        </p>
      </div>

      <div className="game-embed">
        <iframe
          src={gameSrc}
          title="Aidez Patapon et Poutchi à venir au mariage"
          loading="lazy"
          allow="fullscreen"
        />
      </div>
    </div>
  );
};

/* ===== Admin Page ====================================================== */

const slug = (s) => (s || "")
  .toLowerCase()
  .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
  .replace(/[^a-z0-9]+/g, "-")
  .replace(/^-+|-+$/g, "");

const AdminField = ({ label, value, onChange, onCommit, multiline, helper }) => (
  <div className="field" style={{ marginBottom: 14 }}>
    <label>{label}</label>
    {multiline ? (
      <textarea
        value={value || ""}
        onChange={e => onChange(e.target.value)}
        onBlur={onCommit ? () => onCommit() : undefined}
        rows={4}
        style={{
          font: "inherit", fontSize: 16, padding: "12px 14px",
          background: "#fbf3df", border: "1px solid var(--rule)",
          color: "var(--ink)", borderRadius: 0, outline: "none",
          resize: "vertical", lineHeight: 1.5
        }}
      />
    ) : (
      <input
        type="text"
        value={value || ""}
        onChange={e => onChange(e.target.value)}
        onBlur={onCommit ? () => onCommit() : undefined}
      />
    )}
    {helper && <div style={{ fontSize: 12, color: "var(--ink-muted)", marginTop: 2 }}>{helper}</div>}
  </div>
);

const AdminPage = () => {
  const { wedding, setWedding, info, setInfo, saveSite, guests, adminToken, adminAddGuest, adminSaveGuest, adminDeleteGuest } = useStore();
  const [savedFlash, setSavedFlash] = React.useState(false);
  const [exportOpen, setExportOpen] = React.useState(false);

  // Liste éditable complète (dashboard admin : inclut e-mail, adresse, présence…).
  const [rows, setRows] = React.useState([]);
  React.useEffect(() => {
    if (!adminToken) return;
    api.get("/api/admin/dashboard", adminToken)
      .then((d) => setRows((d.guests || []).slice().sort((a, b) => a.name.localeCompare(b.name))))
      .catch(() => {});
  }, [adminToken]);

  // Invité dont le message personnel est en cours d'édition.
  const [msgOpenId, setMsgOpenId] = React.useState(null);

  const flashSaved = () => {
    setSavedFlash(true);
    setTimeout(() => setSavedFlash(false), 1400);
  };

  const handleErr = (e) => {
    if (e && e.status === 401) {
      alert("Session des mariés expirée. Déconnectez-vous puis reconnectez-vous.");
    } else {
      alert(e && e.offline ? "Serveur injoignable." : ((e && e.message) || "Une erreur est survenue."));
    }
  };

  // Contenu du site (infos + page Informations), partagé sur le serveur.
  // Édition locale instantanée, persistance serveur au « commit » (blur).
  const persistSite = async () => {
    try { await saveSite(); flashSaved(); }
    catch (e) { handleErr(e); }
  };
  const update = (patch) => setWedding({ ...wedding, ...patch });        // infos couple/cérémonie
  const updateInfo = (patch) => setInfo({ ...info, ...patch });          // page Informations

  // Édition des listes de la page Informations (horaires, hébergements, accès).
  const setList = (key, list) => setInfo({ ...info, [key]: list });
  const listAdd = (key, blank) => { setList(key, [...(info[key] || []), blank]); };
  const listRemove = (key, i) => {
    const next = (info[key] || []).filter((_, idx) => idx !== i);
    // setInfo puis persistance (la suppression est immédiate).
    setInfo({ ...info, [key]: next });
    setTimeout(persistSite, 0);
  };
  const listEdit = (key, i, patch) => {
    const next = (info[key] || []).map((row, idx) => idx === i ? { ...row, ...patch } : row);
    setList(key, next);
  };

  // Guest CRUD (serveur, liste partagée)
  const editRow = (id, patch) => setRows(rows.map(r => r.id === id ? { ...r, ...patch } : r));

  const addGuest = async () => {
    try { const g = await adminAddGuest(); setRows(prev => [...prev, g]); flashSaved(); }
    catch (e) { handleErr(e); }
  };
  const removeGuest = async (id) => {
    if (!confirm("Supprimer cet invité de la liste ?")) return;
    try { await adminDeleteGuest(id); setRows(prev => prev.filter(r => r.id !== id)); flashSaved(); }
    catch (e) { handleErr(e); }
  };
  // Enregistre l'invité complet (toutes les colonnes) sur le serveur.
  const persistRow = async (row) => {
    try {
      const g = await adminSaveGuest(row.id, row);
      setRows(prev => prev.map(r => r.id === row.id ? { ...r, ...g } : r));
      flashSaved();
    } catch (e) { handleErr(e); }
  };

  // Export / Import
  const exportData = () => {
    const blob = { wedding, info, guests };
    const txt = JSON.stringify(blob, null, 2);
    navigator.clipboard?.writeText(txt);
    return txt;
  };

  const resetDefaults = () => {
    if (confirm("Réinitialiser les informations du couple et la page Informations aux valeurs par défaut ? (La liste d'invités, partagée sur le serveur, n'est pas touchée.)")) {
      setWedding(WEDDING_DEFAULTS);
      setInfo(INFO_DEFAULTS);
      setTimeout(persistSite, 0);
    }
  };

  return (
    <div className="container page">
      <div className="center" style={{ marginBottom: 24 }}>
        <div className="eyebrow">Espace des mariés</div>
        <h1 style={{ marginTop: 12 }}>Administration</h1>
        <div style={{ height: 16 }} />
        <Flourish width={180} />
        <p className="muted" style={{ maxWidth: 540, margin: "16px auto 0", fontSize: 15 }}>
          Toutes les modifications sont enregistrées automatiquement. Pensez à
          exporter régulièrement vos données pour les sauvegarder.
        </p>
      </div>

      {savedFlash && (
        <div style={{
          position: "fixed", top: 80, left: "50%", transform: "translateX(-50%)",
          background: "var(--burgundy)", color: "var(--cream)",
          padding: "8px 18px", fontSize: 13, letterSpacing: "0.22em",
          textTransform: "uppercase", zIndex: 50,
          boxShadow: "var(--shadow)"
        }}>Enregistré ✦</div>
      )}

      {/* ===== Wedding info ===== */}
      <h2 style={{ fontSize: 26, marginTop: 24, marginBottom: 8 }}>Le couple & la cérémonie</h2>
      <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>
        Ces informations apparaissent sur la page d'accueil et la page Informations.
      </p>

      <div className="card ornate" style={{ marginBottom: 32 }}>
        <div style={{
          display: "grid",
          gridTemplateColumns: "repeat(2, 1fr)",
          gap: "0 24px"
        }}>
          <AdminField label="Prénom (mariée)" value={wedding.bride} onChange={v => update({ bride: v })} onCommit={persistSite} />
          <AdminField label="Prénom (marié)" value={wedding.groom} onChange={v => update({ groom: v })} onCommit={persistSite} />
          <AdminField label="Nom complet (mariée)" value={wedding.brideFull} onChange={v => update({ brideFull: v })} onCommit={persistSite} />
          <AdminField label="Nom complet (marié)" value={wedding.groomFull} onChange={v => update({ groomFull: v })} onCommit={persistSite} />
          <AdminField label="Date (lisible)" value={wedding.date} onChange={v => update({ date: v })} onCommit={persistSite} helper="ex. « Vendredi 12 juin 2026 »" />
          <AdminField label="Date courte" value={wedding.dateShort} onChange={v => update({ dateShort: v })} onCommit={persistSite} helper="ex. « 12 . VI . MMXXVI »" />
          <AdminField label="Lieu" value={wedding.venue} onChange={v => update({ venue: v })} onCommit={persistSite} />
          <AdminField label="Région" value={wedding.region} onChange={v => update({ region: v })} onCommit={persistSite} />
          <AdminField label="Adresse" value={wedding.address} onChange={v => update({ address: v })} onCommit={persistSite} />
          <AdminField label="RSVP avant le" value={wedding.rsvpDate} onChange={v => update({ rsvpDate: v })} onCommit={persistSite} />
          <AdminField label="Heure cérémonie" value={wedding.ceremonyTime} onChange={v => update({ ceremonyTime: v })} onCommit={persistSite} />
          <AdminField label="Heure vin d'honneur" value={wedding.cocktailTime} onChange={v => update({ cocktailTime: v })} onCommit={persistSite} />
          <AdminField label="Heure dîner" value={wedding.dinnerTime} onChange={v => update({ dinnerTime: v })} onCommit={persistSite} />
          <AdminField label="Code vestimentaire" value={wedding.dressCode} onChange={v => update({ dressCode: v })} onCommit={persistSite} />
        </div>
        <AdminField
          label="Mot d'accueil"
          value={wedding.welcomeText}
          onChange={v => update({ welcomeText: v })}
          onCommit={persistSite}
          multiline
          helper="Texte personnel affiché sous la salutation sur la page d'accueil."
        />
        <AdminField
          label="Accueil — second paragraphe"
          value={wedding.homeIntro2}
          onChange={v => update({ homeIntro2: v })}
          onCommit={persistSite}
          multiline
          helper="Paragraphe sous le mot d'accueil. Laisser vide pour le masquer."
        />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: "0 24px" }}>
          <AdminField label="Accueil — formule de fin" value={wedding.signoff} onChange={v => update({ signoff: v })} onCommit={persistSite} helper="ex. « Avec toute notre affection, »" />
          <AdminField label="Accueil — titre du programme" value={wedding.programTitle} onChange={v => update({ programTitle: v })} onCommit={persistSite} />
          <AdminField label="Programme — libellé 1 (cérémonie)" value={wedding.ceremonyLabel} onChange={v => update({ ceremonyLabel: v })} onCommit={persistSite} />
          <AdminField label="Programme — libellé 2 (vin d'honneur)" value={wedding.cocktailLabel} onChange={v => update({ cocktailLabel: v })} onCommit={persistSite} />
          <AdminField label="Programme — libellé 3 (dîner)" value={wedding.dinnerLabel} onChange={v => update({ dinnerLabel: v })} onCommit={persistSite} />
        </div>
      </div>

      {/* ===== Page Informations ===== */}
      <h2 style={{ fontSize: 26, marginTop: 24, marginBottom: 8 }}>Page Informations</h2>
      <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>
        Le contenu des cartes de la page « Informations ». Ces textes sont partagés
        avec tous les invités.
      </p>

      <div className="card ornate" style={{ marginBottom: 32 }}>
        {/* Le lieu */}
        <AdminField
          label="Le lieu — description"
          value={info.venueDesc}
          onChange={v => updateInfo({ venueDesc: v })}
          onCommit={persistSite}
          multiline
          helper="Le titre, la région et l'adresse se règlent dans « Le couple & la cérémonie »."
        />

        {/* Horaires */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginTop: 8, marginBottom: 8 }}>
          <label style={{ fontWeight: 600 }}>Déroulé de la journée</label>
          <button className="link-btn" onClick={() => listAdd("schedule", { time: "", label: "" })}>+ Ajouter une étape</button>
        </div>
        {(info.schedule || []).map((row, i) => (
          <div key={i} style={{ display: "flex", gap: 8, marginBottom: 8, alignItems: "center" }}>
            <input
              type="text" value={row.time || ""} placeholder="20h00"
              style={{ width: 110, flex: "0 0 auto" }}
              onChange={e => listEdit("schedule", i, { time: e.target.value })}
              onBlur={persistSite}
            />
            <input
              type="text" value={row.label || ""} placeholder="Dîner dans la grande salle"
              style={{ flex: 1 }}
              onChange={e => listEdit("schedule", i, { label: e.target.value })}
              onBlur={persistSite}
            />
            <button className="row-del" onClick={() => listRemove("schedule", i)} title="Supprimer">✕</button>
          </div>
        ))}

        {/* Tenue */}
        <div style={{ marginTop: 16 }}>
          <AdminField label="Tenue — titre" value={info.dressCodeTitle} onChange={v => updateInfo({ dressCodeTitle: v })} onCommit={persistSite} />
          <AdminField label="Tenue — description" value={info.dressCodeDesc} onChange={v => updateInfo({ dressCodeDesc: v })} onCommit={persistSite} multiline />
        </div>

        {/* Hébergement */}
        <AdminField label="Hébergement — introduction" value={info.lodgingIntro} onChange={v => updateInfo({ lodgingIntro: v })} onCommit={persistSite} />
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8 }}>
          <label style={{ fontWeight: 600 }}>Adresses</label>
          <button className="link-btn" onClick={() => listAdd("lodging", { name: "", distance: "", note: "" })}>+ Ajouter une adresse</button>
        </div>
        {(info.lodging || []).map((row, i) => (
          <div key={i} style={{ display: "flex", gap: 8, marginBottom: 8, alignItems: "center", flexWrap: "wrap" }}>
            <input type="text" value={row.name || ""} placeholder="Nom" style={{ flex: "2 1 160px" }}
              onChange={e => listEdit("lodging", i, { name: e.target.value })} onBlur={persistSite} />
            <input type="text" value={row.distance || ""} placeholder="800 m" style={{ flex: "0 1 90px" }}
              onChange={e => listEdit("lodging", i, { distance: e.target.value })} onBlur={persistSite} />
            <input type="text" value={row.note || ""} placeholder="À partir de 95 € · navette" style={{ flex: "3 1 200px" }}
              onChange={e => listEdit("lodging", i, { note: e.target.value })} onBlur={persistSite} />
            <button className="row-del" onClick={() => listRemove("lodging", i)} title="Supprimer">✕</button>
          </div>
        ))}

        {/* Accès */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginTop: 16, marginBottom: 8 }}>
          <label style={{ fontWeight: 600 }}>Comment venir</label>
          <button className="link-btn" onClick={() => listAdd("access", { mode: "", text: "" })}>+ Ajouter un moyen</button>
        </div>
        {(info.access || []).map((row, i) => (
          <div key={i} style={{ display: "flex", gap: 8, marginBottom: 8, alignItems: "center", flexWrap: "wrap" }}>
            <input type="text" value={row.mode || ""} placeholder="En train" style={{ flex: "0 1 120px" }}
              onChange={e => listEdit("access", i, { mode: e.target.value })} onBlur={persistSite} />
            <input type="text" value={row.text || ""} placeholder="gare de Montélimar (40 min)…" style={{ flex: "1 1 240px" }}
              onChange={e => listEdit("access", i, { text: e.target.value })} onBlur={persistSite} />
            <button className="row-del" onClick={() => listRemove("access", i)} title="Supprimer">✕</button>
          </div>
        ))}

        {/* Cagnotte */}
        <div style={{ marginTop: 16 }}>
          <AdminField label="Cagnotte — titre" value={info.registryTitle} onChange={v => updateInfo({ registryTitle: v })} onCommit={persistSite} />
          <AdminField label="Cagnotte — description" value={info.registryText} onChange={v => updateInfo({ registryText: v })} onCommit={persistSite} multiline />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: "0 24px" }}>
            <AdminField label="Cagnotte — texte du bouton" value={info.registryButton} onChange={v => updateInfo({ registryButton: v })} onCommit={persistSite} />
            <AdminField label="Cagnotte — lien (URL)" value={info.registryUrl} onChange={v => updateInfo({ registryUrl: v })} onCommit={persistSite} helper="Laisser vide si pas encore de cagnotte." />
          </div>
        </div>

        {/* Playlist Spotify */}
        <div style={{ marginTop: 16 }}>
          <AdminField label="Playlist — titre" value={info.playlistTitle} onChange={v => updateInfo({ playlistTitle: v })} onCommit={persistSite} />
          <AdminField label="Playlist — description" value={info.playlistText} onChange={v => updateInfo({ playlistText: v })} onCommit={persistSite} multiline />
          <AdminField label="Playlist — lien Spotify" value={info.playlistUrl} onChange={v => updateInfo({ playlistUrl: v })} onCommit={persistSite} helper="Colle le lien de partage Spotify (open.spotify.com/playlist/…). Laisser vide pour masquer la section." />
        </div>

        {/* Blocs personnalisés */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginTop: 24, marginBottom: 8 }}>
          <label style={{ fontWeight: 600 }}>Blocs personnalisés</label>
          <button className="link-btn" onClick={() => listAdd("customBlocks", { label: "", title: "", text: "" })}>+ Ajouter un bloc</button>
        </div>
        <p className="muted" style={{ fontSize: 13, marginTop: 0, marginBottom: 12 }}>
          Cartes libres affichées sur la page Informations. Un bloc vide n'apparaît pas.
        </p>
        {(info.customBlocks || []).map((row, i) => (
          <div key={i} style={{
            border: "1px solid var(--rule)", padding: 12, marginBottom: 12,
            display: "flex", flexDirection: "column", gap: 8, background: "#fbf3df"
          }}>
            <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
              <input type="text" value={row.label || ""} placeholder="Sur-titre (ex. « Bon à savoir »)" style={{ flex: "1 1 160px" }}
                onChange={e => listEdit("customBlocks", i, { label: e.target.value })} onBlur={persistSite} />
              <input type="text" value={row.title || ""} placeholder="Titre" style={{ flex: "2 1 200px" }}
                onChange={e => listEdit("customBlocks", i, { title: e.target.value })} onBlur={persistSite} />
              <button className="row-del" onClick={() => listRemove("customBlocks", i)} title="Supprimer ce bloc">✕</button>
            </div>
            <textarea value={row.text || ""} placeholder="Texte du bloc" rows={3}
              style={{
                font: "inherit", fontSize: 16, padding: "10px 12px",
                background: "#fff5dc", border: "1px solid var(--rule)",
                color: "var(--ink)", borderRadius: 0, outline: "none",
                resize: "vertical", lineHeight: 1.5
              }}
              onChange={e => listEdit("customBlocks", i, { text: e.target.value })} onBlur={persistSite} />
          </div>
        ))}

        {/* RSVP */}
        <AdminField label="Message RSVP" value={info.rsvpNote} onChange={v => updateInfo({ rsvpNote: v })} onCommit={persistSite} multiline helper="La date limite se règle via « RSVP avant le » plus haut." />
      </div>

      {/* ===== Guests ===== */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8, flexWrap: "wrap", gap: 12 }}>
        <h2 style={{ fontSize: 26 }}>Liste des invités <span style={{ color: "var(--ink-muted)", fontSize: 16 }}>· {guests.length}</span></h2>
        <button className="btn gold" onClick={addGuest}>+ Ajouter un invité</button>
      </div>
      <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>
        Les noms saisis ici apparaissent sur l'écran de sélection après connexion.
      </p>

      <div className="card ornate" style={{ padding: 0, overflowX: "auto" }}>
        <div className="admin-table">
          <div className="admin-row admin-head">
            <div>Prénom</div>
            <div>Côté</div>
            <div>Table</div>
            <div>Présence</div>
            <div>E-mail</div>
            <div>Adresse faire-part</div>
            <div></div>
            <div></div>
          </div>
          {rows.map((g) => (
            <React.Fragment key={g.id}>
              <div className="admin-row">
                <input
                  type="text"
                  value={g.name}
                  onChange={e => editRow(g.id, { name: e.target.value })}
                  onBlur={e => persistRow({ ...g, name: e.target.value })}
                />
                <select
                  value={g.side}
                  onChange={e => { editRow(g.id, { side: e.target.value }); persistRow({ ...g, side: e.target.value }); }}
                >
                  <option value="">—</option>
                  <option value={wedding.bride}>{wedding.bride}</option>
                  <option value={wedding.groom}>{wedding.groom}</option>
                  <option value="Les deux">Les deux</option>
                </select>
                <input
                  type="text"
                  value={g.table || ""}
                  onChange={e => editRow(g.id, { table: e.target.value })}
                  onBlur={e => persistRow({ ...g, table: e.target.value })}
                  placeholder="—"
                />
                <select
                  className={"rsvp-select " + (g.rsvp || "none")}
                  value={g.rsvp || ""}
                  onChange={e => { editRow(g.id, { rsvp: e.target.value }); persistRow({ ...g, rsvp: e.target.value }); }}
                  title="Présence"
                >
                  <option value="">—</option>
                  <option value="yes">Oui</option>
                  <option value="no">Non</option>
                </select>
                <input
                  type="email"
                  value={g.email || ""}
                  onChange={e => editRow(g.id, { email: e.target.value })}
                  onBlur={e => persistRow({ ...g, email: e.target.value })}
                  placeholder="—"
                  title={g.email || ""}
                />
                <input
                  type="text"
                  value={g.address || ""}
                  onChange={e => editRow(g.id, { address: e.target.value })}
                  onBlur={e => persistRow({ ...g, address: e.target.value })}
                  placeholder="—"
                  title={g.address || ""}
                />
                <button
                  className={"row-del" + ((g.message && g.message.trim()) ? " has-msg" : "")}
                  onClick={() => setMsgOpenId(msgOpenId === g.id ? null : g.id)}
                  title="Message personnel"
                >✉</button>
                <button
                  className="row-del"
                  onClick={() => removeGuest(g.id)}
                  title="Supprimer cet invité"
                >✕</button>
              </div>
              {msgOpenId === g.id && (
                <div className="admin-msg">
                  <label>Message personnel pour {g.name || "cet invité"}</label>
                  <textarea
                    value={g.message || ""}
                    placeholder="Quelques mots rien que pour cette personne. S'affichera sur sa page d'accueil. Laisser vide pour utiliser le mot d'accueil général."
                    onChange={e => editRow(g.id, { message: e.target.value })}
                    onBlur={e => persistRow({ ...g, message: e.target.value })}
                    rows={3}
                  />
                </div>
              )}
            </React.Fragment>
          ))}
          {rows.length === 0 && (
            <div style={{ padding: 24, textAlign: "center", color: "var(--ink-muted)" }}>
              Aucun invité. Ajoutez le premier en cliquant ci-dessus.
            </div>
          )}
        </div>
      </div>

      {/* ===== Export / Reset ===== */}
      <h2 style={{ fontSize: 26, marginTop: 40, marginBottom: 8 }}>Sauvegarde</h2>
      <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>
        Les données sont enregistrées dans ce navigateur. Pour les conserver
        ailleurs ou les partager, exportez-les au format texte.
      </p>
      <div className="card ornate">
        <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
          <button className="btn" onClick={() => { exportData(); setExportOpen(true); }}>
            Exporter & copier
          </button>
          <button className="btn ghost" onClick={resetDefaults}>
            Réinitialiser
          </button>
        </div>
        {exportOpen && (
          <div style={{ marginTop: 16 }}>
            <div className="muted" style={{ fontSize: 13, marginBottom: 6 }}>
              ✓ Copié dans le presse-papier. Vous pouvez aussi sélectionner manuellement :
            </div>
            <textarea
              readOnly
              value={JSON.stringify({ wedding, guests }, null, 2)}
              rows={10}
              style={{
                width: "100%", font: "13px ui-monospace, monospace",
                padding: 12, background: "#f7ecd1", border: "1px solid var(--rule)",
                color: "var(--ink-soft)", borderRadius: 0
              }}
            />
          </div>
        )}
      </div>
    </div>
  );
};

Object.assign(window, { HomePage, InfoPage, GamePage, AdminPage });
