const { useState, useEffect, useRef, useCallback } = React;

const STUDIOS_SITE = "https://www.capartenlivestudios.fr";
const STUDIOS_TEL = "+33374721295";
const STUDIOS_TEL_DISPLAY = "03 74 72 12 95";
const STUDIOS_MAIL = "contact@capartenlivestudios.fr";

/* RDV config */
const WD = ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"];
const WD_LONG = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"];
const MO = ["janv.", "f\u00e9vr.", "mars", "avr.", "mai", "juin", "juil.", "ao\u00fbt", "sept.", "oct.", "nov.", "d\u00e9c."];
const MO_LONG = ["janvier", "f\u00e9vrier", "mars", "avril", "mai", "juin", "juillet", "ao\u00fbt", "septembre", "octobre", "novembre", "d\u00e9cembre"];
const SLOTS_AM = ["09:00", "09:45", "10:30", "11:15"];
const SLOTS_PM = ["14:00", "14:45", "15:30", "16:15", "17:00"];

function buildDays() {
  const out = [];
  const d = new Date();
  d.setHours(0, 0, 0, 0);
  d.setDate(d.getDate() + 1);
  let guard = 0;
  while (out.length < 20 && guard < 80) {
    const wd = d.getDay();
    if (wd >= 1 && wd <= 5) out.push(new Date(d));
    d.setDate(d.getDate() + 1);
    guard++;
  }
  return out;
}

/* icons */
const I = {
  video: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 10l4.55-2.28A1 1 0 0121 8.62v6.76a1 1 0 01-1.45.9L15 14M4 6h9a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2z" /></svg>,
  grid: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" /></svg>,
  strat: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20V10M18 20V4M6 20v-4" /></svg>,
  motion: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" /></svg>,
  mic: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10a7 7 0 0014 0M12 17v4" /></svg>,
  team: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M9 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" /></svg>,
  check: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>,
  arrow: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
};

/* ============================== DATA ============================== */
const PACKS = {
  starter: {
    name: "Starter", now: "690", old: "890", tag: "Structurer ta communication et produire du contenu régulier.",
    ribbon: "200€ offerts",
    feat: [
    { t: <><b>4 vidéos</b> format court</> },
    { t: <><b>4 carrousels</b> ou posts fixes</> },
    { t: <>Stratégie de contenu & ligne éditoriale</> },
    { t: <>Hooks, sujets & scripts</> },
    { t: <>Accompagnement dédié</> },
    { t: <>Motion design</>, no: true }]

  },
  expert: {
    name: "Expert", now: "1190", old: "1390", tag: "Une présence régulière et impactante qui te positionne au-dessus.",
    ribbon: "Le + choisi", feat: [
    { t: <><b>8 vidéos</b> format court</> },
    { t: <><b>6 carrousels</b> ou posts fixes</> },
    { t: <>Stratégie & direction de contenu avancée</> },
    { t: <>Hooks, sujets & scripts</> },
    { t: <><b>Motion design inclus</b> (textes animés, effets)</> },
    { t: <>Accompagnement à 100% + planning éditorial</> }]

  },
  ultime: {
    name: "Ultime", now: "2490", old: "2690", tag: "Une solution globale avec une équipe marketing qui t'est dédiée.",
    ribbon: "Tout inclus", feat: [
    { t: <><b>16 vidéos</b> format court</> },
    { t: <><b>10 carrousels</b> ou posts fixes</> },
    { t: <>Stratégie & direction de contenu avancée</> },
    { t: <>Hooks, sujets & scripts + motion design</> },
    { t: <>Équipe marketing dédiée · accompagnement 100%</> },
    { t: <><b>Podcast format long</b> (studio pro)</> }]

  }
};

const CARDS = [
{ kind: "cover" },
{ kind: "eco" },
{ kind: "caps" },
{ kind: "method" },
{ kind: "why" },
{ kind: "tiers" },
{ kind: "pack", id: "starter" },
{ kind: "pack", id: "expert" },
{ kind: "pack", id: "ultime" },
{ kind: "quotes" },
{ kind: "booking" }];


/* ============================== QR ============================== */
function QR({ value }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current || typeof qrcode === "undefined") return;
    try {
      const q = qrcode(0, "M");q.addData(value);q.make();
      ref.current.innerHTML = q.createSvgTag({ cellSize: 4, margin: 0, scalable: true });
    } catch (e) {}
  }, [value]);
  return <div className="qr"><div ref={ref} style={{ width: "100%", height: "100%" }} /></div>;
}

function Kicker({ num, children }) {
  return (
    <div className="kicker anim">
      {num && <span className="num">{num}</span>}<span className="ln" />{children}
    </div>);

}

function parseNum(str) {
  const m = String(str).match(/^(\D*)(\d[\d\s\u00A0\u202f.,]*\d|\d)([\s\S]*)$/);
  if (!m) return null;
  const cleaned = m[2].replace(/[\s\u00A0\u202f]/g, "");
  let target, decimals = 0, sep = /[\s\u00A0\u202f]/.test(m[2]);
  if (/^\d+[.,]\d+$/.test(cleaned)) { decimals = cleaned.split(/[.,]/)[1].length; target = parseFloat(cleaned.replace(",", ".")); }
  else { target = parseInt(cleaned, 10); }
  return { prefix: m[1], suffix: m[3], target, decimals, sep };
}

function CountUp({ children, duration = 1400 }) {
  const str = String(children);
  const parsed = React.useMemo(() => parseNum(str), [str]);
  const ref = useRef(null);
  const [val, setVal] = useState(parsed ? parsed.target : null);
  useEffect(() => {
    if (!parsed) return;
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) { setVal(parsed.target); return; }
    const el = ref.current; let raf = null, safety = null, animating = false;
    const run = () => {
      animating = true; setVal(0); const t0 = performance.now();
      const tick = (t) => {
        const p = Math.min(1, (t - t0) / duration);
        setVal(parsed.target * (1 - Math.pow(1 - p, 3)));
        if (p < 1) raf = requestAnimationFrame(tick); else { setVal(parsed.target); animating = false; }
      };
      raf = requestAnimationFrame(tick);
      safety = setTimeout(() => { setVal(parsed.target); animating = false; }, duration + 400);
    };
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting && !animating) run(); });
    }, { threshold: 0.2 });
    io.observe(el);
    return () => { io.disconnect(); if (raf) cancelAnimationFrame(raf); if (safety) clearTimeout(safety); };
  }, [parsed, duration]);
  if (!parsed) return <span ref={ref}>{str}</span>;
  const n = parsed.decimals ? val.toFixed(parsed.decimals) : String(Math.round(val));
  let [int, dec] = n.split(".");
  if (parsed.sep) int = int.replace(/\B(?=(\d{3})+(?!\d))/g, "\u202f");
  return <span ref={ref}>{parsed.prefix}{dec ? int + "," + dec : int}{parsed.suffix}</span>;
}

/* ============================== CARDS ============================== */
function Cover() {
  return (
    <div className="inner cover-in">
      <div className="logo-tile anim"><img src="assets/logo-studios-2.jpeg" alt="Ça Part en Live Studios" /></div>
      <div className="founder anim d1">Ça Part en Live · Studios</div>
      <h1 className="display anim d2">Studios</h1>
      <p className="tagline anim d3">La nouvelle façon<br />de <em>créer…</em> et de <em>vendre.</em></p>
      <p className="sub anim d4" style={{ maxWidth: "32ch" }}>Production de contenu, stratégie et image de marque pour les professionnels.</p>
      <div className="swipe-hint anim d5">
        <span className="dot">{I.arrow}</span> Découvrir nos solutions
      </div>
    </div>);

}

function Eco() {
  return (
    <div className="inner">
      <Kicker num="01">Ta vitrine digitale</Kicker>
      <h2 className="title anim d1">On te découvre d'abord en ligne</h2>
      <p className="lead anim d2">Avant même de pousser ta porte, tes clients te jugent sur les réseaux. Une <span className="hl">vitrine digitale soignée</span> travaille pour toi 24h/24.</p>
      <ul className="bens anim d3">
        <li><b>Plus de visibilité.</b> Tu apparais là où ton audience passe ses journées.</li>
        <li><b>Plus de crédibilité.</b> Une image pro et cohérente qui rassure dès le premier regard.</li>
        <li><b>Plus de clients.</b> Du contenu qui capte l'attention et transforme les curieux en acheteurs.</li>
      </ul>
      <div className="badge-line anim d4">{I.check} 9 professionnels sur 10 utilisent déjà les réseaux pour vendre</div>
    </div>);

}

function Caps() {
  const caps = [
  { ic: I.video, t: "Vidéos format court", d: "Tournage, montage, prêt à publier." },
  { ic: I.grid, t: "Carrousels & posts", d: "Statiques aux codes des réseaux." },
  { ic: I.strat, t: "Stratégie de contenu", d: "Ligne éditoriale, hooks, scripts." },
  { ic: I.motion, t: "Motion design", d: "Textes animés et effets visuels." },
  { ic: I.mic, t: "Podcast format long", d: "Enregistrement studio pro." },
  { ic: I.team, t: "Équipe dédiée", d: "Un accompagnement de A à Z." }];

  return (
    <div className="inner">
      <Kicker num="02">Nos capacités</Kicker>
      <h2 className="title anim d1">Tout ce qu'on produit pour toi</h2>
      <div className="caps anim d2">
        {caps.map((c, i) =>
        <div className="cap" key={i}>
            <div className="ci">{c.ic}</div>
            <div className="ct">{c.t}</div>
            <div className="cd">{c.d}</div>
          </div>
        )}
      </div>
    </div>);

}

function Method() {
  const steps = [
  { t: "Échange & compréhension", d: "On cerne ton activité, tes objectifs et ta cible." },
  { t: "Angles & messages", d: "On définit la ligne éditoriale, les hooks et les scripts." },
  { t: "Tournage", d: "En studio équipé ou directement chez toi." },
  { t: "Montage & motion design", d: "Un rendu rythmé, pensé pour capter l'attention." },
  { t: "Livraison prête à publier", d: "Des contenus finalisés, aux bons formats." }];

  return (
    <div className="inner">
      <Kicker num="03">La méthode</Kicker>
      <h2 className="title anim d1">De l'idée au contenu qui performe</h2>
      <div className="steps anim d2">
        {steps.map((s, i) =>
        <div className="step" key={i}>
            <div className="si">{String(i + 1).padStart(2, "0")}</div>
            <div><div className="stt">{s.t}</div><div className="std">{s.d}</div></div>
          </div>
        )}
      </div>
    </div>);

}

function Why() {
  return (
    <div className="inner">
      <Kicker num="04">Pourquoi Studios</Kicker>
      <h2 className="title anim d1">Une image qui inspire confiance et qui fait vendre</h2>
      <ul className="bens anim d2">
        <li><b>Tu fais plus pro.</b> Une image cohérente et soignée qui met en valeur ton activité.</li>
        <li><b>Tu captes l'attention.</b> Des contenus pensés pour les réseaux, qui marquent les esprits.</li>
        <li><b>Tu donnes du sens.</b> Une direction de contenu par des experts, qui convertit réellement.</li>
        <li><b>Tout au même endroit.</b> Formation, stratégie et production : un seul interlocuteur.</li>
      </ul>
    </div>);

}

function Tiers({ go }) {
  const order = ["starter", "expert", "ultime"];
  return (
    <div className="inner">
      <Kicker num="05">Nos formules</Kicker>
      <h2 className="title anim d1">Une offre pour chaque ambition</h2>
      <p className="sub anim d2">Sans engagement · HT / mois · 200€ offerts au lancement.</p>
      <div className="tiers anim d3">
        {order.map((k, i) => {
          const p = PACKS[k];
          return (
            <div className={"tier" + (k === "expert" ? " feat" : "")} key={k} onClick={() => go(6 + i)}>
              {k === "expert" && <span className="tier-badge">Le + choisi</span>}
              <div className="tn">
                <div className="tname">{p.name}</div>
                <div className="tdesc">{p.tag}</div>
              </div>
              <div className="tprice">
                <div className="tp-now"><CountUp>{p.now}</CountUp>€<small>/mois</small></div>
                <div className="tp-old">au lieu de {p.old}€</div>
              </div>
            </div>);

        })}
      </div>
    </div>);

}

function Pack({ id }) {
  const p = PACKS[id];
  return (
    <div className="inner">
      <Kicker>Formule</Kicker>
      <div className="pcard anim d1">
        <div className="ph">
          <span className="pribbon">{p.ribbon}</span>
          <div className="pname">{p.name}</div>
          <div className="ptag">{p.tag}</div>
          <div className="pprice">
            <span className="pnow"><CountUp>{p.now}</CountUp>€<small> HT/mois</small></span>
            <span className="pold">au lieu de {p.old}€</span>
          </div>
          <div className="pengage">Sans engagement</div>
        </div>
        <div className="pbody">
          <ul className="pfeat">
            {p.feat.map((f, i) =>
            <li key={i} className={f.no ? "no" : ""}>{f.no && <span className="x">×</span>}{f.t}{f.no ? " (non inclus)" : ""}</li>
            )}
          </ul>
        </div>
      </div>
    </div>);

}

function Quotes() {
  const q = [
  { t: "J'ai tourné un podcast au sein de Ça Part en Live et le résultat est sans appel ! L'accompagnement pour la ligne directrice, les conseils, les séquences vidéos... juste super. Ça plaît énormément sur les réseaux. Je recommande vivement ce studio.", m: "Emma Vincent", r: "Avis Google" },
  { t: "Excellente expérience ! Un vrai travail de fond en amont, puis un tournage fluide, carré et très professionnel. Le format podcast apporte une vraie dynamique. Merci à toute l'équipe !", m: "Merino", r: "Avis Google" },
  { t: "Une équipe vraiment pro, au top.", m: "Marcus Fenix", r: "Avis Google" },
  { t: "Excellente équipe, locaux professionnels.", m: "Vrej Arm", r: "Avis Google" }];

  return (
    <div className="inner">
      <Kicker num="06">Ils parlent de nous</Kicker>
      <h2 className="title anim d1">Des résultats concrets</h2>
      <div className="quotes anim d2">
        {q.map((x, i) =>
        <div className="quote" key={i}>
            <div className="stars">★★★★★</div>
            <div className="qt">« {x.t} »</div>
            <div className="qm">{x.m}</div>
            <div className="qref">{x.r}</div>
          </div>
        )}
      </div>
    </div>);

}

function Booking() {
  const days = useRef(buildDays()).current;
  const [step, setStep] = useState("slot");
  const [dayI, setDayI] = useState(0);
  const [slot, setSlot] = useState(null);
  const [form, setForm] = useState({ entreprise: "", tel: "", email: "", vendeur: "" });
  const [err, setErr] = useState("");
  const [count, setCount] = useState(0);
  const [saved, setSaved] = useState(null);

  useEffect(() => {
    try {setCount(JSON.parse(localStorage.getItem("cpls_rdv") || "[]").length);} catch (e) {}
  }, []);

  // RDV confirmé → reset du parcours (index) + retour auto à l'accueil après un court délai.
  // Le timer est annulé si l'utilisateur agit avant ou quitte la page.
  useEffect(() => {
    if (step !== "done") return;
    try { localStorage.removeItem("cpls_idx"); } catch (e) {}
    const t = setTimeout(() => { window.location.href = "Accueil.html"; }, 7000);
    return () => clearTimeout(t);
  }, [step]);

  const day = days[dayI];
  const fmtLong = (d) => `${WD_LONG[d.getDay()]} ${d.getDate()} ${MO_LONG[d.getMonth()]}`;
  const scrollTop = () => {const v = document.querySelector(".card.booking");if (v) v.scrollTo({ top: 0, behavior: "smooth" });};

  const goForm = () => {
    if (!slot) {setErr("Choisis d'abord un créneau.");return;}
    setErr("");setStep("form");scrollTop();
  };

  const confirm = (e) => {
    e.preventDefault();
    if (!form.entreprise.trim() || !form.tel.trim()) {setErr("Le nom de l'entreprise et le téléphone sont nécessaires.");return;}
    const rec = {
      entreprise: form.entreprise.trim(), tel: form.tel.trim(), email: form.email.trim(),
      vendeur: form.vendeur, day: day.toISOString(), slot, ts: new Date().toISOString()
    };
    try {
      const all = JSON.parse(localStorage.getItem("cpls_rdv") || "[]");
      all.push(rec);localStorage.setItem("cpls_rdv", JSON.stringify(all));setCount(all.length);
    } catch (e) {}
    setSaved(rec);setErr("");setStep("done");scrollTop();
    try { window.cplSyncFoireRdv && window.cplSyncFoireRdv(rec, "studios"); } catch (e) {}
  };

  const reset = () => {
    setStep("slot");setSlot(null);setSaved(null);
    setForm({ entreprise: "", tel: "", email: "", vendeur: "" });setErr("");scrollTop();
  };

  const exportRdv = () => {
    try {
      const all = JSON.parse(localStorage.getItem("cpls_rdv") || "[]");
      const txt = all.map((r) => {
        const d = new Date(r.day);
        return [fmtLong(d), r.slot, r.entreprise, r.tel, r.email, r.vendeur === "oui" ? "Déjà en ligne" : r.vendeur === "non" ? "Pas encore en ligne" : ""].join(" \t ");
      }).join("\n");
      if (navigator.clipboard) navigator.clipboard.writeText(txt);
      alert(all.length + " RDV copié(s) dans le presse-papier.\nColle-les dans un mail ou un tableur.");
    } catch (e) {}
  };

  return (
    <div className="inner">
      {step === "slot" &&
      <React.Fragment>
          <Kicker num="07">Réserve ton rendez-vous</Kicker>
          <h2 className="title anim d1">On construit ta présence ensemble</h2>
          <p className="book-intro anim d1">45 min pour faire le point sur ta stratégie de contenu et ta vitrine digitale. On se déplace à ton bureau, choisis le moment qui t'arrange.</p>

          <div className="book-steps anim d2"><i className="on" /><i /><i /></div>

          <div className="anim d2">
            <div className="daystrip no-swipe">
              {days.map((d, i) =>
            <button key={i} className={"daypill" + (i === dayI ? " sel" : "")}
            onClick={() => {setDayI(i);setSlot(null);}}>
                  <span className="wd">{WD[d.getDay()]}</span>
                  <span className="dn">{d.getDate()}</span>
                  <span className="mo">{MO[d.getMonth()]}</span>
                </button>
            )}
            </div>
          </div>

          <div className="slot-sec anim d3">
            <h4>Matin · 9h à 12h</h4>
            <div className="slots">
              {SLOTS_AM.map((s) =>
            <button key={s} className={"slot" + (slot === s ? " sel" : "")} onClick={() => {setSlot(s);setErr("");}}>{s}</button>
            )}
            </div>
          </div>
          <div className="slot-sec anim d4">
            <h4 className="aft">Après-midi · 14h à 18h</h4>
            <div className="slots">
              {SLOTS_PM.map((s) =>
            <button key={s} className={"slot" + (slot === s ? " sel" : "")} onClick={() => {setSlot(s);setErr("");}}>{s}</button>
            )}
            </div>
          </div>

          {err && <div className="err">{err}</div>}

          <div className="anim d5" style={{ marginTop: 20 }}>
            <button className="submit" onClick={goForm}>
              {slot ? `Continuer · ${day.getDate()} ${MO[day.getMonth()]} à ${slot}` : "Continuer"}
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
            </button>
          </div>

          <div className="mini-contact anim d5">
            <div>
              <QR value={STUDIOS_SITE} />
              <div className="qr-cap">capartenlivestudios.fr</div>
            </div>
            <div className="cinfo">
              <span className="lbl">Une question ?</span>
              <a href={"tel:" + STUDIOS_TEL}>{STUDIOS_TEL_DISPLAY}</a>
              <a href={"mailto:" + STUDIOS_MAIL}>{STUDIOS_MAIL}</a>
            </div>
          </div>

          {count > 0 &&
        <div className="rdvcount">{count} RDV enregistré(s) sur cet appareil <button onClick={exportRdv}>exporter</button></div>
        }
        </React.Fragment>
      }

      {step === "form" &&
      <React.Fragment>
          <Kicker num="07">Tes coordonnées</Kicker>
          <h2 className="title anim d1">Presque terminé !</h2>

          <div className="book-steps anim d1"><i className="on" /><i className="on" /><i /></div>

          <div className="selbar anim d1">
            <div>
              <div className="s1">Ton créneau</div>
              <div className="s2">{fmtLong(day)} · {slot}</div>
            </div>
            <button onClick={() => setStep("slot")}>modifier</button>
          </div>

          <form onSubmit={confirm}>
            <div className="field anim d2">
              <span className="field-lbl">Entreprise <span className="req">*</span></span>
              <input placeholder="Nom de ton entreprise" value={form.entreprise} onChange={(e) => setForm({ ...form, entreprise: e.target.value })} />
            </div>
            <div className="field anim d2">
              <span className="field-lbl">Téléphone <span className="req">*</span></span>
              <input placeholder="06 12 34 56 78" inputMode="tel" value={form.tel} onChange={(e) => setForm({ ...form, tel: e.target.value })} />
            </div>
            <div className="field anim d3">
              <span className="field-lbl">E-mail</span>
              <input placeholder="toi@entreprise.fr" inputMode="email" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} />
            </div>
            <div className="field anim d3">
              <span className="field-lbl">Tu communiques déjà sur les réseaux ?</span>
              <div className="toggle2">
                <button type="button" className={form.vendeur === "oui" ? "on" : ""} onClick={() => setForm({ ...form, vendeur: "oui" })}>Oui</button>
                <button type="button" className={form.vendeur === "non" ? "on" : ""} onClick={() => setForm({ ...form, vendeur: "non" })}>Pas encore</button>
              </div>
            </div>

            {err && <div className="err">{err}</div>}

            <div className="anim d4" style={{ marginTop: 22 }}>
              <button className="submit" type="submit">
                Confirmer le rendez-vous
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
              </button>
            </div>
          </form>
        </React.Fragment>
      }

      {step === "done" && saved &&
      <React.Fragment>
          <div className="recap anim">
            <div className="chk">
              <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#F7B500" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
            </div>
            <div className="big">Rendez-vous noté !</div>
            <div className="when">{fmtLong(new Date(saved.day))}<br />à {saved.slot}</div>
            <div className="det">
              <b>{saved.entreprise}</b><br />
              {saved.tel}{saved.email ? " · " + saved.email : ""}<br />
              On t'appelle pour confirmer. À très vite !
            </div>
            <div className="actions">
              <button className="ghostbtn" onClick={reset}>Prendre un autre RDV</button>
              <button className="ghostbtn" onClick={() => { try { localStorage.removeItem("cpls_idx"); } catch (e) {} window.location.href = "Accueil.html"; }}>Retour à l'accueil</button>
            </div>
          </div>

          <div className="mini-contact anim d2">
            <div>
              <QR value={STUDIOS_SITE} />
              <div className="qr-cap">capartenlivestudios.fr</div>
            </div>
            <div className="cinfo">
              <span className="lbl">Ça Part en Live Studios</span>
              <a href={"tel:" + STUDIOS_TEL}>{STUDIOS_TEL_DISPLAY}</a>
              <a href={STUDIOS_SITE} target="_blank" rel="noopener">www.capartenlivestudios.fr</a>
            </div>
          </div>

          {count > 0 &&
        <div className="rdvcount">{count} RDV enregistré(s) sur cet appareil <button onClick={exportRdv}>exporter</button></div>
        }
        </React.Fragment>
      }
    </div>);

}

function CardBody({ c, go }) {
  switch (c.kind) {
    case "cover":return <Cover />;
    case "eco":return <Eco />;
    case "caps":return <Caps />;
    case "method":return <Method />;
    case "why":return <Why />;
    case "tiers":return <Tiers go={go} />;
    case "pack":return <Pack id={c.id} />;
    case "quotes":return <Quotes />;
    case "booking":return <Booking />;
    default:return null;
  }
}

const SCROLLY = new Set(["eco", "caps", "method", "why", "tiers", "pack", "quotes", "booking"]);

/* ============================== APP ============================== */
function App() {
  const N = CARDS.length;
  const [idx, setIdx] = useState(() => {
    const s = parseInt(localStorage.getItem("cpls_idx") || "0", 10);
    return isNaN(s) || s < 0 || s >= N ? 0 : s;
  });
  const [drag, setDrag] = useState(0);
  const start = useRef(null),startY = useRef(null),horiz = useRef(false);

  useEffect(() => {try {localStorage.setItem("cpls_idx", String(idx));} catch (e) {}}, [idx]);

  const go = useCallback((n) => setIdx(Math.max(0, Math.min(N - 1, n))), [N]);
  const next = useCallback(() => setIdx((p) => Math.min(N - 1, p + 1)), [N]);
  const prev = useCallback(() => setIdx((p) => Math.max(0, p - 1)), []);

  useEffect(() => {
    const onKey = (e) => {
      if (e.key === "ArrowRight" || e.key === "PageDown") {e.preventDefault();next();} else
      if (e.key === "ArrowLeft" || e.key === "PageUp") {e.preventDefault();prev();} else
      if (e.key === "Home") go(0);else
      if (e.key === "End") go(N - 1);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [next, prev, go, N]);

  const onStart = (e) => {
    const tag = (e.target.tagName || "").toLowerCase();
    if (!["input", "button", "textarea", "a", "select"].includes(tag) && !e.target.closest("a, button, .no-swipe")) {
      const t = e.touches[0];
      start.current = t.clientX;startY.current = t.clientY;horiz.current = false;
      return;
    }
    start.current = null;
  };
  const onMove = (e) => {
    if (start.current == null) return;
    const t = e.touches[0];
    const dx = t.clientX - start.current,dy = t.clientY - startY.current;
    if (!horiz.current) {
      if (Math.abs(dx) > 8 || Math.abs(dy) > 8) horiz.current = Math.abs(dx) > Math.abs(dy);
      if (!horiz.current) return;
    }
    e.preventDefault();
    let d = dx;
    if (idx === 0 && dx > 0 || idx === N - 1 && dx < 0) d = dx * 0.32;
    setDrag(d);
  };
  const onEnd = () => {
    if (start.current == null) return;
    const th = Math.min(90, window.innerWidth * 0.18);
    if (drag < -th) next();else if (drag > th) prev();
    setDrag(0);start.current = null;horiz.current = false;
  };

  const trackStyle = {
    transform: `translateX(calc(${-(idx * 100)}% + ${drag}px))`,
    transition: drag ? "none" : "transform .5s var(--ease)"
  };

  return (
    <div className="stage">
      <a className="homebtn" href="Accueil.html" aria-label="Accueil" onClick={() => { try { localStorage.removeItem("cpls_idx"); } catch (e) {} }}>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 11l9-8 9 8M5 9v11h14V9" /></svg>
      </a>
      <div className="topbar">
        <div className="seg">
          {CARDS.map((_, i) => <span key={i} className={i < idx ? "done" : i === idx ? "active" : ""}><i /></span>)}
        </div>
        <div className="counter">{String(idx + 1).padStart(2, "0")} / {String(N).padStart(2, "0")}</div>
      </div>

      <div className="viewport" onTouchStart={onStart} onTouchMove={onMove} onTouchEnd={onEnd}>
        <div className="track" style={trackStyle}>
          {CARDS.map((c, i) =>
          <section
            className={"card " + c.kind + (SCROLLY.has(c.kind) ? " scrolly" : "") + (i === idx ? " live" : "")}
            data-screen-label={"Studios " + String(i + 1).padStart(2, "0")}
            key={i}>
            
              <CardBody c={c} go={go} />
            </section>
          )}
        </div>
        {idx > 0 && <div className="edge l" onClick={prev} />}
        {idx < N - 1 && <div className="edge r" onClick={next} />}
      </div>

      <div className="navbar">
        <button className="nav-btn" onClick={prev} disabled={idx === 0} aria-label="Précédent">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6" /></svg>
        </button>
        <div className="nav-label">{idx === 0 ? "Ça Part en Live Studios" : idx === N - 1 ? "Prends rendez-vous" : "Studios · Production de contenu"}</div>
        <button className="nav-btn navnext" onClick={next} disabled={idx === N - 1} aria-label="Suivant">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18l6-6-6-6" /></svg>
        </button>
      </div>
    </div>);

}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);