// Fallbacks para rodar tanto no app local (com API/TAB_H2) quanto em preview isolado
const MB_API = (typeof API !== "undefined" && API) ? API : null;
const MB_TAB_H = (typeof TAB_H2 !== "undefined") ? TAB_H2 : 96;
// mb-home.jsx — Home tab (tela inicial do app)
const GUIDE_EDITIONS = [
{ n: 14, m: "Jun 2026", c: ["#1B2A6B", "#0E1330"] },
{ n: 13, m: "Mar 2026", c: ["#214E7E", "#0E1B30"] },
{ n: 12, m: "Dez 2025", c: ["#2A4D5E", "#0E1d24"] },
{ n: 11, m: "Set 2025", c: ["#3A3370", "#15122E"] },
{ n: 10, m: "Jun 2025", c: ["#6E3B4E", "#2E1722"] },
];
function GuideCover({ ed, w = 150 }) {
const h = Math.round(w * 1.4);
const grad = ed.c || ["#1B2A6B", "#0E1330"];
return (
{editions.map((ed, i) => {
const dist = ((i - cur + n + Math.floor(n / 2)) % n) - Math.floor(n / 2);
if (Math.abs(dist) > 1) return null;
const scale = dist === 0 ? 1 : 0.68;
const tx = dist * (cardW * 0.82 + 14);
return (
dist !== 0 && setCur(i)} style={{
position: "absolute", left: "50%", top: "50%",
transform: `translate(calc(-50% + ${tx}px), -50%) scale(${scale})`,
opacity: dist === 0 ? 1 : 0.55, zIndex: dist === 0 ? 10 : 3,
transition: "all .42s cubic-bezier(.4,0,.2,1)",
cursor: dist !== 0 ? "pointer" : "default",
}}>
);
})}
{editions.map((_, i) => (
setCur(i)} style={{ width: i === cur ? 16 : 5, height: 5, borderRadius: 999, background: i === cur ? "var(--accent)" : "var(--line)", transition: "all .3s", cursor: "pointer" }} />
))}
);
}
// ── Blog preview inline (independente de mb-blog.jsx) ────────
function HomeBlogPreview({ onOpenBlog }) {
const MB_API_OBJ = (typeof API !== "undefined" && API) ? API : null;
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
const mock = (typeof BLOG_MOCK_POSTS !== "undefined") ? BLOG_MOCK_POSTS.slice(0, 6) : [];
if (!MB_API_OBJ) { setPosts(mock); return; }
const bail = new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 7000));
Promise.race([MB_API_OBJ.blog.posts(), bail])
.then(d => {
const list = (d?.posts || []).filter(p => p.published !== false).slice(0, 6);
setPosts(list.length ? list : mock);
})
.catch(() => setPosts(mock));
}, []);
if (!posts.length) return null;
return (
{/* Cabeçalho com fundo no título Blog */}
{/* Badge "Blog" com fundo */}
Blog
Inspiração e informação
{/* Cards menores e mais arredondados */}
{posts.map(p => {
const grad = p.cover_gradient || ["#1B2A6B", "#0E1330"];
const catColor = p.category_color || "#051E52";
return (
);
})}
);
}
function HomeIntro({ user, onOpenClub, canInstallPwa, onInstallApp, onDescobrir, onNovoLar, onServices, onShop, onOpenBlog, updateReady, updateStatus, onUpdate }) {
const [homeData, setHomeData] = React.useState(null);
const [imgSlides, setImgSlides] = React.useState(null);
const [slideIdx, setSlideIdx] = React.useState(0);
const [partnerFormOpen, setPartnerFormOpen] = React.useState(false);
const [cardGrad, setCardGrad] = React.useState({ c1: "#3B0764", c2: "#6D28D9" });
React.useEffect(() => {
if (!MB_API) { setImgSlides([]); return; }
MB_API.home().then(d => setHomeData(d)).catch(() => {});
MB_API.designAssets()
.then(d => {
setImgSlides((d.login_slides || []).filter(s => s && s.startsWith("http")));
if (d.home_card_color1) setCardGrad({ c1: d.home_card_color1, c2: d.home_card_color2 || "#6D28D9" });
})
.catch(() => setImgSlides([]));
}, []);
const slides = imgSlides && imgSlides.length > 0 ? imgSlides : null;
React.useEffect(() => {
const count = slides ? slides.length : 1;
const t = setInterval(() => setSlideIdx(x => (x + 1) % count), 4500);
return () => clearInterval(t);
}, [slides?.length]);
const title = homeData?.content?.title || "O guia impresso que você já conhece — agora também no digital.";
const subtitle = homeData?.content?.subtitle || "Mapa, gastronomia, imóveis e muito mais na palma da sua mão.";
const editions = homeData?.editions?.length ? homeData.editions : GUIDE_EDITIONS.map(e => ({
id: e.id, number: e.n, month_label: e.m, cover_gradient: e.c, status: "publicada",
}));
const firstName = user?.first || user?.name?.split(" ")[0];
// Auto-shrink do título quando a fonte do sistema está aumentada (acessibilidade)
const titleRef = React.useRef(null);
React.useLayoutEffect(() => {
const el = titleRef.current;
if (!el) return;
el.style.fontSize = "20px";
// scrollHeight > clientHeight+3 indica que o texto está sendo cortado
if (el.scrollHeight <= el.clientHeight + 3) return;
for (let s = 18; s >= 12; s -= 2) {
el.style.fontSize = s + "px";
if (el.scrollHeight <= el.clientHeight + 3) break;
}
}, [title]);
return (
{/* ── Chip flutuante de atualização — só aparece quando SW detecta nova versão ── */}
{updateReady && updateStatus !== "done" && (
Nova versão disponível
{updateStatus === "checking"
?
:
}
)}
{/* ── Hero ── */}
{slides && slides.map((url, i) => (
))}
{/* Logo + botão de update no topo direito */}
{firstName && (
Olá, {firstName}! 👋
)}
{/* Título com mais destaque — font auto-shrink via useLayoutEffect */}
{title}
{/* Subtítulo */}
{subtitle}
{canInstallPwa && (
)}

{ e.target.style.display = "none"; }} style={{ position: "absolute", right: 0, bottom: -8, height: 280, width: "auto", zIndex: 2, pointerEvents: "none", userSelect: "none" }} />
{/* Dots removidos — transição das fotos acontece de forma silenciosa */}
{/* ── Atalhos ── */}
{[
{ ic: "home", t: "Novo Lar", d: "Sua nova casa", c: "#0EA371", c2: "#37D39D", fn: onNovoLar },
{ ic: "bolt", t: "Serviços", d: "Tudo para o lar", c: "#E0852B", c2: "#F4B24C", fn: onServices },
{ ic: "store", t: "Produtos", d: "Lojas e delivery", c: "#8B5CF6", c2: "#A78BFA", fn: onShop },
{ ic: "map", t: "Descubra no mapa", d: "Parceiros por perto", c: "#2563EB", c2: "#5B8DEF", fn: onDescobrir },
].map((f, i) => (
))}
{/* ── Blog preview — Inspiração e informação ── */}
{onOpenBlog &&
}
{/* ── Edições do guia impresso — carousel spotlight ── */}
Edições do guia impresso
Anos levando o melhor da sua cidade até você
{/* ── Seja Parceiro ── */}
{/* Topo colorido */}
Leve seu negócio ao próximo nível
Seja um parceiro Morar Bem
{["📍 Apareça no mapa", "📲 Mais clientes", "📊 Gestão simples"].map(t => (
{t}
))}
{/* Rodapé claro */}
Centenas de negócios já estão no guia. Junte-se a nós.
Cadastro gratuito · Sem mensalidade inicial
{/* ── Muito mais vem por aí ── */}
Muito mais vem por aí
Em breve disponível para você
EM BREVE
{["🛍️ Loja & Delivery", "📅 Agendamento", "💰 BemCash & Cashback", "⭐ Clube de Benefícios"].map(t => (
{t}
))}
{/* ── Formulário de Lead (sobrepõe a tela) ── */}
{partnerFormOpen && (
setPartnerFormOpen(false)} />
)}
);
}
Object.assign(window, { GUIDE_EDITIONS, GuideCover, HomeIntro, HomeBlogPreview, PartnerLeadScreen });