// screens-home.jsx — Home screen with 3 hero layout variants. Exports: HomeScreen // Per-letter multicolor (uses the real product colors), same font. const RAINBOW = ["#277E2F", "#B51D1A", "#042199", "#09AEBD", "#EA6A9C"]; function rainbowSpans(str) { let ci = 0; return str.split("").map((ch, i) => { if (ch === " ") return ch; const c = RAINBOW[ci % RAINBOW.length]; ci++; return React.createElement("span", { key: i, style: { color: c } }, ch); }); } function TrustStrip({ t }) { const items = [t("home_trust_1"), t("home_trust_2"), t("home_trust_3")]; return React.createElement( "div", { style: { display: "flex", flexWrap: "wrap", gap: "10px 22px", marginTop: 26 } }, items.map((it, i) => React.createElement("span", { key: i, className: "pill" }, React.createElement("span", { className: "dot" }), it) ) ); } function HeroVisual({ big }) { // live 3D tag preview (kept for centered/split hero layouts) return React.createElement(window.Tag3DPreview, { text: "BEGIN:VCARD\nVERSION:3.0\nFN:Qlé\nTEL;TYPE=CELL:+33600000000\nEMAIL:hello@qleqle.com\nURL:https://qleqle.com\nEND:VCARD", plateId: "blue", plateHex: "#042199", codeHex: "#FFFFFF", size: big ? 380 : 320, }); } function HeroCentered({ t, onStart }) { return React.createElement( "div", { style: { textAlign: "center", maxWidth: 760, margin: "0 auto" } }, React.createElement("div", { className: "kicker", style: { marginBottom: 18 } }, t("tagline")), React.createElement("h1", { className: "display" }, t("home_title")), React.createElement("p", { className: "lead", style: { margin: "20px auto 0", maxWidth: 540 } }, t("home_subtitle")), React.createElement("div", { style: { display: "flex", gap: 12, justifyContent: "center", marginTop: 30, flexWrap: "wrap" } }, React.createElement("button", { className: "btn btn--primary btn--lg", onClick: onStart }, t("home_cta")), ), React.createElement("div", { className: "faint", style: { fontSize: 13, marginTop: 14 } }, t("home_cta_sub")), React.createElement("div", { style: { display: "grid", placeItems: "center", marginTop: 56 } }, React.createElement(HeroVisual, { big: true }) ), ); } function HeroSplit({ t, onStart }) { return React.createElement( "div", { className: "hero-split" }, React.createElement("div", { style: { flex: "1 1 380px", minWidth: 300 } }, React.createElement("div", { className: "kicker", style: { marginBottom: 18 } }, t("tagline")), React.createElement("h1", { className: "display" }, t("home_title")), React.createElement("p", { className: "lead", style: { margin: "20px 0 0", maxWidth: 460 } }, t("home_subtitle")), React.createElement("div", { style: { display: "flex", gap: 12, marginTop: 30, flexWrap: "wrap" } }, React.createElement("button", { className: "btn btn--primary btn--lg", onClick: onStart }, t("home_cta")), ), React.createElement(TrustStrip, { t }), ), React.createElement("div", { style: { flex: "1 1 320px", display: "grid", placeItems: "center", minWidth: 280 } }, React.createElement(HeroVisual, { big: false }) ), ); } // QR overlay positions on the band photo (calibrated to assets/band.png, % of image). const BAND_QR = [ { left: 1.73, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(39,126,47)", code: "#FFFFFF" }, { left: 11.55, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(181,29,26)", code: "#FFFFFF" }, { left: 21.39, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(4,33,153)", code: "#FFFFFF" }, { left: 31.21, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(9,174,189)", code: "#FFFFFF" }, { left: 41.11, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(31,30,30)", code: "#FFFFFF" }, { left: 50.96, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(233,234,233)", code: "#15161A" }, { left: 60.8, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(242,242,243)", code: "#15161A" }, { left: 70.66, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(238,123,25)", code: "#FFFFFF" }, { left: 80.52, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(244,194,13)", code: "#15161A" }, { left: 90.39, top: 27.88, w: 7.88, h: 60.4, plate: "rgb(234,106,156)", code: "#FFFFFF" }, ]; // One self-contained copy of the band (photo + QR overlays positioned relative to it). function BandUnit({ ariaHidden }) { return React.createElement( "div", { className: "band-unit", "aria-hidden": ariaHidden ? "true" : undefined }, React.createElement("img", { src: "assets/band-tags.png", alt: "", loading: "eager", fetchpriority: "high", decoding: "async", style: { width: "100%", height: "auto", display: "block" }, }), BAND_QR.map((q, i) => React.createElement("div", { key: i, className: "band-qr", style: { left: q.left + "%", top: q.top + "%", width: q.w + "%", height: q.h + "%" } }, React.createElement(window.QRPreview, { text: "https://qleqle.com", plate: q.plate, code: q.code, size: 120, pad: 2, showPlate: false, rounded: true, style: { width: "100%", height: "100%", borderRadius: 0 } }) ) ) ); } function HeroShowcase({ t, onStart }) { return React.createElement( "div", null, React.createElement("div", { style: { textAlign: "center", maxWidth: 720, margin: "0 auto" } }, React.createElement("div", { className: "kicker", style: { marginBottom: 16 } }, t("tagline")), React.createElement("h1", { className: "display" }, t("home_title")), React.createElement("p", { className: "lead", style: { margin: "18px auto 0", maxWidth: 520 } }, t("home_subtitle")), React.createElement("div", { style: { marginTop: 28 } }, React.createElement("button", { className: "btn btn--primary btn--lg", onClick: onStart }, t("home_cta")), ), ), React.createElement("div", { className: "band-marquee" }, React.createElement("div", { className: "band-track" }, React.createElement(BandUnit, { ariaHidden: false }), React.createElement(BandUnit, { ariaHidden: true }), ) ), React.createElement("p", { className: "h2 band-caption" }, rainbowSpans(t("band_caption")) ), ); } // 3D previews in the pack cards: distinct colors per card so each pack looks different. const PACK_3D_SETS = [ [{ plateId: "teal", plateHex: "#0499A4", codeHex: "#FFFFFF" }], [{ plateId: "yellow", plateHex: "#F3B604", codeHex: "#15161A" }, { plateId: "pink", plateHex: "#E96E9E", codeHex: "#15161A" }], [{ plateId: "orange", plateHex: "#FE4C02", codeHex: "#FFFFFF" }, { plateId: "blue", plateHex: "#1E3CD0", codeHex: "#FFFFFF" }, { plateId: "applegreen", plateHex: "#5FBB03", codeHex: "#15161A" }], ]; function PackMedia3D({ count, packLum, packCon }) { const size = count === 1 ? 210 : count === 2 ? 128 : 86; const set = PACK_3D_SETS[count - 1] || PACK_3D_SETS[0]; return React.createElement( "div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 4, width: "100%", height: "100%", background: "#ffffff" } }, set.map((c, i) => React.createElement(window.Tag3DPreview, { key: i, text: "https://qleqle.com", plateId: c.plateId, plateHex: c.plateHex, codeHex: c.codeHex, size, brightness: packLum, contrast: packCon, }) ) ); } function ProductCard({ p, lang, t, packLum, packCon, onChoose }) { const name = lang === "en" ? p.name_en : p.name_fr; const desc = lang === "en" ? p.desc_en : p.desc_fr; const dims = lang === "en" ? p.dims_en : p.dims_fr; const ph = p.count === 1 ? (lang === "en" ? "Drop the keychain photo" : "Glissez la photo du porte-clé") : (lang === "en" ? `Drop a photo of ${p.count} aligned keychains` : `Glissez la photo de ${p.count} porte-clés alignés`); const badge = p.badge ? (p.badge === "popular" ? t("badge_popular") : t("badge_best")) : null; return React.createElement( "div", { className: "card product-card" + (p.badge ? " product-card--badged" : "") }, badge ? React.createElement("div", { className: "product-badge product-badge--" + p.badge }, badge) : null, React.createElement("div", { className: "product-card__media" }, React.createElement(PackMedia3D, { count: p.count, packLum, packCon }) ), React.createElement("div", { className: "product-card__body" }, React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 12 } }, React.createElement("h3", { style: { margin: 0, fontSize: 19, fontWeight: 600, letterSpacing: "-.01em", lineHeight: 1.2 } }, name), React.createElement("div", { style: { fontSize: 19, fontWeight: 600, whiteSpace: "nowrap" } }, window.fmtPrice(p.price, lang)), ), React.createElement("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 14, lineHeight: 1.5, flex: 1, whiteSpace: "pre-line" } }, desc), React.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px 8px", marginTop: 14 } }, React.createElement("div", { className: "pill" }, React.createElement("span", { className: "dot" }), dims), React.createElement("div", { className: "pill" }, React.createElement("span", { className: "dot" }), t("ship_free")), ), React.createElement("button", { className: "btn btn--primary btn--block", style: { marginTop: 18 }, onClick: () => onChoose(p) }, t("home_choose")), ), ); } function StepCard({ n, title, desc, delay }) { return React.createElement( "div", { className: "step-card" }, React.createElement("div", { className: "step-num" }, n), React.createElement("div", { style: { fontWeight: 600, fontSize: 16, marginTop: 14 } }, title), React.createElement("div", { className: "muted", style: { fontSize: 14, marginTop: 5, lineHeight: 1.45 } }, desc), delay ? React.createElement("div", { className: "step-delay" }, delay) : null, ); } // Rotating photo background for the final CTA const CTA_SLIDES = ["assets/cta-bleu.png", "assets/cta-caniche2.png", "assets/cta-rouge.png", "assets/cta-chat.png"]; function FinalCtaSlides({ framing, onFrame }) { const [idx, setIdx] = React.useState(0); const dragRef = React.useRef(null); const holdRef = React.useRef(false); React.useEffect(function () { const id = setInterval(function () { if (!holdRef.current) setIdx(function (i) { return (i + 1) % CTA_SLIDES.length; }); }, 4500); return function () { clearInterval(id); }; }, []); // drag directly on the photo to reframe it (commits to tweaks on release) function onPointerDown(e) { if (!onFrame) return; const f = (framing && framing[idx]) || { x: 50, y: 35, z: 1 }; dragRef.current = { n: idx + 1, startX: e.clientX, startY: e.clientY, base: f, w: e.currentTarget.clientWidth, h: e.currentTarget.clientHeight, last: f }; holdRef.current = true; e.currentTarget.setPointerCapture(e.pointerId); e.preventDefault(); } function onPointerMove(e) { const d = dragRef.current; if (!d) return; const x = Math.max(0, Math.min(100, d.base.x - ((e.clientX - d.startX) / d.w) * 100)); const y = Math.max(0, Math.min(100, d.base.y - ((e.clientY - d.startY) / d.h) * 100)); d.last = { x, y }; onFrame(d.n, x, y); } function onPointerUp() { dragRef.current = null; holdRef.current = false; } return React.createElement("div", { className: "final-cta__slides" + (onFrame ? " is-draggable" : ""), onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, title: "Glissez la photo pour la recadrer", }, CTA_SLIDES.map(function (src, i) { const f = (framing && framing[i]) || { x: 50, y: 35, z: 1 }; return React.createElement("div", { key: src, className: "final-cta__slide" + (i === idx ? " is-active" : ""), style: { backgroundImage: "url('" + src + "')", backgroundPosition: f.x + "% " + f.y + "%", backgroundSize: (f.z && f.z !== 1 ? (f.z * 100) + "%" : "cover"), }, }); })); } function HomeScreen({ t, lang, heroLayout, packLum, packCon, ctaFraming, onCtaFrame, onStart, onChoose }) { const Hero = heroLayout === "split" ? HeroSplit : heroLayout === "showcase" ? HeroShowcase : HeroCentered; return React.createElement( "div", { className: "screen" }, // hero React.createElement("section", { className: "section" + (heroLayout === "showcase" ? " section--bandhero" : ""), style: { paddingBottom: heroLayout === "showcase" ? 30 : undefined } }, React.createElement("div", { className: "container" }, React.createElement(Hero, { t, onStart }) ) ), // shaded zone — from "Nos packs" down to the bottom: light gray bg, white cards React.createElement("div", { className: "home-shaded" }, // products React.createElement("section", { className: "section--tight", id: "products" }, React.createElement("div", { className: "container" }, React.createElement("div", { className: "section-head", style: { marginBottom: 34 } }, React.createElement("div", { className: "kicker" }, t("home_products_kicker")), React.createElement("h2", { className: "h2" }, t("home_products_title")), ), React.createElement("div", { className: "product-grid" }, window.PRODUCTS.map((p) => React.createElement(ProductCard, { key: p.id, p, lang, t, packLum, packCon, onChoose }) ) ), ) ), // how it works React.createElement("section", { className: "section" }, React.createElement("div", { className: "container" }, React.createElement("div", { className: "section-head", style: { marginBottom: 34 } }, React.createElement("div", { className: "kicker" }, t("home_how_kicker")), React.createElement("h2", { className: "h2" }, t("home_how_title")), ), React.createElement("div", { className: "steps-grid" }, React.createElement(StepCard, { n: "1", title: t("step1_t"), desc: t("step1_d"), delay: t("step1_delay") }), React.createElement(StepCard, { n: "2", title: t("step2_t"), desc: t("step2_d"), delay: t("step2_delay") }), React.createElement(StepCard, { n: "3", title: t("step3_t"), desc: t("step3_d"), delay: t("step3_delay") }), ), ) ), // final CTA React.createElement("section", { className: "section--tight", style: { paddingBottom: 90 } }, React.createElement("div", { className: "container" }, React.createElement("div", { className: "final-cta" }, React.createElement(FinalCtaSlides, { framing: ctaFraming, onFrame: onCtaFrame }), React.createElement("h2", { className: "h2", style: { margin: 0, color: "#fff" } }, t("home_final_title")), React.createElement("button", { className: "btn btn--lg", style: { marginTop: 22, background: "#fff", color: "var(--ink)" }, onClick: onStart }, t("home_final_cta")), ) ) ), ), // /home-shaded ); } Object.assign(window, { HomeScreen });