// app-media.jsx — capturas reais do app (hero + seção das duas interfaces). // // Todo o site puxa imagem/vídeo do produto por aqui. Enquanto não houver // captura, cada slot cai no seu fallback e o layout já fica no lugar certo — // trocar depois é preencher `src`/`poster` em APP_MEDIA e mais nada. const { useState: _mUseState, useEffect: _mUseEffect, useRef: _mUseRef } = React; // ───────────────────────────────────────────────────────────── // PONTO ÚNICO DE TROCA // ───────────────────────────────────────────────────────────── // Para plugar a captura real, preencha `src` (vídeo .mp4/.webm) e/ou // `poster` (imagem .png/.webp). Com só `poster`, vira imagem estática — // que já é melhor que mockup. Com os dois, vira vídeo com poster nítido. // // vendedorDesktop: { src: 'assets/app-vendedor.mp4', poster: 'assets/app-vendedor.png' } // // Proporção alvo: 16/10 (as capturas de desktop devem sair em 1440×900 ou // 1600×1000 pra não borrar no retina). // As capturas reais estão em `assets/`, mas ficam DESLIGADAS de propósito na // seção "As duas pontas". A captura é uma tela cheia de dado miúdo: reduzida // pra caber em meia coluna, nada nela se lê, e ela não faz o que a seção // promete — mostrar a venda saindo do app do vendedor e chegando no painel da // empresa. O painel encenado faz: o cursor clica em "Minhas vendas", abre a // venda já preenchida pelo link, registra, e a pastinha atravessa até o // ranking do outro lado. É cena, não screenshot. // // Pra voltar a usar a captura em algum slot, basta repor o `poster` (e o // `src`, se houver vídeo) — o resto do layout não muda. const APP_MEDIA = { vendedorDesktop: { src: null, poster: null, // assets/app-vendedor.webp ratio: '1568 / 777', alt: 'Dashboard do app do vendedor vendas, comissão e meta do mês', }, empresaDesktop: { src: null, poster: null, // assets/app-empresa.webp ratio: '1568 / 777', alt: 'Painel do app da empresa equipe, produtos e comissões a pagar', }, }; // Etiqueta de "captura pendente" só aparece rodando local. Em produção o // slot fica limpo mesmo sem mídia — nunca vaza aviso de obra pro visitante. // // Desligada: nos dois slots das "duas pontas" o painel encenado é a escolha, // não uma captura que ainda falta — a etiqueta dizia o contrário e ainda // tapava a primeira linha de KPIs justo na hora de revisar a cena. Volte pra // `true` quando estiver esperando uma captura de verdade em algum slot. const MEDIA_DEV = false; const temMidia = (m) => !!(m && (m.src || m.poster)); window.APP_MEDIA = APP_MEDIA; // ───────────────────────────────────────────────────────────── // AppMedia — vídeo/imagem do produto, com fallback enquanto não existe // ───────────────────────────────────────────────────────────── // O vídeo só baixa quando chega perto da viewport (mesma tática do // AmbientVideo do FinalCTA): captura de app é pesada e não pode entrar no // caminho crítico do primeiro paint. function AppMedia({ media, fallback, ratio = '16 / 10', radius = 0, label }) { const ref = _mUseRef(null); const [perto, setPerto] = _mUseState(false); // Se a captura declarada em APP_MEDIA não existir no servidor (arquivo ainda // não subiu, caminho errado, deploy sem a pasta assets), o falha e a // seção inteira vira um retângulo branco — justamente a seção que existe pra // mostrar o produto. Aqui a falha cai no mock animado, que é melhor do que // nada e melhor do que um buraco. const [falhou, setFalhou] = _mUseState(false); _mUseEffect(() => { if (!media || !media.src) return; const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { setPerto(true); io.disconnect(); } }); }, { rootMargin: '300px 0px' }); io.observe(el); return () => io.disconnect(); }, [media && media.src]); // Autoplay mudo é sempre liberado, mas o play() falha se ainda não houver // dado decodificado — por isso dispara nos dois eventos. const tocar = (e) => { const p = e.currentTarget.play(); if (p && p.catch) p.catch(() => {}); }; if (!temMidia(media) || falhou) { return (
{fallback || } {MEDIA_DEV && fallback ? : null}
); } // A captura traz a própria proporção; o `ratio` do chamador é só o padrão // pra quando ela não disser nada (evita a moldura pular de altura na troca). const prop = media.ratio || ratio; const base = { display: 'block', width: '100%', height: '100%', objectFit: 'cover', borderRadius: radius, background: 'var(--surface)', }; // Screenshot é parado. O movimento vem daqui: deriva lenta + brilho // atravessando o vidro, dentro de um recorte que não deixa a escala // vazar pra fora da moldura. const palco = (miolo) => (
{miolo}
); if (!media.src) { return palco( {media.alt setFalhou(true)} style={{ ...base, position: 'absolute', inset: 0 }} /> ); } return palco(