<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Cactus Front Docs Blog</title>
        <link>https://docs.c4ctus.com/changelog</link>
        <description>Cactus Front Docs Blog</description>
        <lastBuildDate>Sun, 10 May 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>pt-BR</language>
        <item>
            <title><![CDATA[Changelog - 10/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/10/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/10/daily</guid>
            <pubDate>Sun, 10 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Remarketing first-party (rmkvera)]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="remarketing-first-party-rmkvera">Remarketing first-party (rmkvera)<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#remarketing-first-party-rmkvera" class="hash-link" aria-label="Link direto para Remarketing first-party (rmkvera)" title="Link direto para Remarketing first-party (rmkvera)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sistema de remarketing first-party com UUID v4 estável + audience tags do funil</strong>, brand-aware via feature flag (<code>app/config/features/remarketing.ts</code>). Vera ativa com cookie <code>rmkvera</code> (365 dias sliding TTL, <code>Domain=.vera.bet.br</code>, <code>SameSite=None</code>, <code>Secure</code>); demais brands fazem opt-in declarando seu próprio <code>cookieName</code> no override.</li>
<li class=""><strong>Cookie <code>rmkvera_aud</code></strong> armazena tags do funil em JSON (90 dias sliding TTL, cap de 40 tags com eviction de oldest, set-once semantics) — pra ser usado como <code>external_id</code> em Meta CAPI, Google Enhanced Conversions e TikTok Events API. Funciona mesmo sem cookies de terceiros (iOS 14+, ITP, Brave).</li>
<li class=""><strong>Tags emitidas automaticamente em três eixos</strong>: path-based via <code>useLocation</code> brand-aware no Route Registry (<code>viewed_casino</code>, <code>viewed_live_casino</code>, <code>viewed_game_detail</code>, <code>viewed_sports</code>, <code>viewed_promotions</code>), modal-based via Zustand (<code>clicked_register</code>, <code>registered</code>, <code>abandoned_register</code>, <code>viewed_deposit_modal</code>, <code>abandoned_deposit</code>) e FTD-based em <code>useAnalytics.trackDepositConfirmed</code> (<code>ftd_completed</code>, <code>multi_deposit</code>).</li>
<li class=""><strong>Captura server-side no root loader</strong> (<code>captureRemarketingIdFromRequest</code>) — sobrevive a redirects via <code>Set-Cookie</code> na própria response do 302 (mesmo padrão do tracking de UTMs). Client-side <code>RemarketingCapture</code> faz refresh de TTL, publica em <code>window.__rmk</code> e dispara <code>rmk_ready</code> + <code>rmk_audience_tag</code> no dataLayer.</li>
<li class=""><strong>Regras duras</strong>: nome do cookie rejeita termos internos da plataforma (<code>cactus</code>, <code>bluetec</code>) em runtime, bot traffic é skipado no server, validador strict UUID v4 RFC 4122 contra cookies adulterados. 39 unit tests + 16 server-side tests cobrindo capture, Set-Cookie, regenerate em tamper, eviction.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cache--deploy">Cache &amp; Deploy<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#cache--deploy" class="hash-link" aria-label="Link direto para Cache &amp; Deploy" title="Link direto para Cache &amp; Deploy" translate="no">​</a></h2>
<ul>
<li class=""><strong>Purge proxy worker em paralelo com platform-cache</strong> (<code>/api/cache/purge</code>). O endpoint agora forwarda um purge per-brand pro <code>front-service-api</code> via service binding <code>API_SERVICE</code>, em paralelo com o purge local do platform-cache. Antes, o post-deploy purge limpava só os caches do brand worker e o proxy worker (KV <code>API_CACHE_KV</code> + CF Cache API) continuava servindo BFF stale por até 1h — root cause do widget "Pagou Muito" ficando vazio após deploy em vera e 7k. Os dois purges rodam via <code>Promise.allSettled</code> e o <code>originDomain</code> é inferido do hostname pra escopar a limpeza por brand.</li>
<li class=""><strong>front-ops <code>deploy.yml</code> propaga <code>postDeployPurge</code> segments pro <code>/api/cache/purge</code></strong>. O step "Resolve segments" já computava a lista de recursos (topGames, homeRows, casinoRows, etc.) mas o curl ignorava o output e batia no endpoint nu, caindo em <code>engine.purgeAll()</code> que deixa snapshots de KV intactos. Agora os segments viajam como <code>?segments=&lt;csv&gt;</code> e <code>engine.purge(segment)</code> roda por recurso — o caminho que deleta tanto o primary tier quanto o snapshot KV.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="404-redirects-cf-analytics">404 Redirects (CF Analytics)<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#404-redirects-cf-analytics" class="hash-link" aria-label="Link direto para 404 Redirects (CF Analytics)" title="Link direto para 404 Redirects (CF Analytics)" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>/reward</code>, <code>/rewards</code>, <code>/game-error</code>, <code>/games/category/:slug/popular</code> e prefixos <code>/{locale}/*</code></strong> cobertos no <code>legacy-redirects.ts</code> a partir do relatório de 404 do Cloudflare. Smartico push e ad creatives usam a forma singular de reward; integrações de jogo (Endorphina e família) navegam o iframe pra <code>/game-error</code> quando o launch falha — agora a página existe fora do <code>_layout</code> (full-bleed <code>bg-error</code>, alert + reload em <code>window.top</code>, <code>noindex,nofollow</code>, listada em <code>robots.txt</code>). O catch-all <code>/{locale}/*</code> aceita <code>/en</code>, <code>/es</code>, <code>/pt-br</code>, <code>/hi</code>, <code>/de</code>, <code>/fr</code>, <code>/it</code>, <code>/es-mx</code> (legado Nuxt pré-listava todos esses buckets mesmo com <code>no_prefix</code> em produção).</li>
<li class=""><strong><code>/pagina/kyc-policy</code>, <code>/apostas-ao-vivo</code>, <code>/pending</code></strong> adicionados a partir do CF analytics da cassino-bet-br. <code>/pagina/kyc-policy</code> resolve pra <code>legal.page</code> com <code>slug: "kyc"</code> (a string <code>kyc-policy</code> é o <code>apiSlug</code> do BFF, não o slug da URL). <code>/apostas-ao-vivo</code> é alias PT-BR pro sports root brand-aware (<code>/esportes</code>, <code>/deportes</code>, <code>/sports</code>). <code>/pending</code> 301 pra <code>/redirect</code> (a Vue mountava verification iframe ali; o novo stack já trata verification em-app).</li>
<li class=""><strong>Fix de ordering no <code>legacy-redirect.ts</code></strong>: o handler agora ordena <code>legacyRedirects</code> por contagem de segmentos literais descendente antes de iterar, espelhando a precedência que o React Router usa pra registro de rotas. Sem isso, a entrada genérica <code>/pagina/:slug</code> matchava antes da literal <code>/pagina/kyc-policy</code> e gerava <code>/pagina/undefined</code>. Comportamento idempotente pras entradas que não compartilham prefixo.</li>
<li class=""><strong>Tail-handling fix</strong> em <code>legacy-redirect.ts</code>: <code>${baseDest}/${tail}</code> produzia barra dupla quando <code>baseDest</code> era exatamente <code>/</code> (destino home dos catch-alls de locale). Strip do trailing slash antes do append.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="landing-pages">Landing Pages<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#landing-pages" class="hash-link" aria-label="Link direto para Landing Pages" title="Link direto para Landing Pages" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>/download-app</code> portado do legado Vue pra cassino-bet-br</strong> (<code>overrides/cassino-bet-br/.../lps/download-app.tsx</code>). Espelha o LP do 7k-bet-br (banner + benefícios + instruções Android/iOS + FAQ accordion) com branding "Cassino.bet", filename <code>cassinopixbetbr.apk</code>, e Android download apontando pro Play Store (<code>id=cassino.bet.br.twa</code>, package confirmado pelo <code>assetlinks.json</code> da brand). Diferente do LP do 7k, todas as cores resolvem via theme tokens (<code>bg-bg-primary</code>, <code>text-titles</code>, <code>border-texts/20</code>, etc.) — o palette navy/cyan do <code>theme/colors.ts</code> é pego automaticamente. Wiring em <code>lps.ts</code> + <code>lp-redirects.ts</code> (visitantes em <code>/download-app</code> 301 pra <code>/lp/download-app</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>Vera (<code>vera-bet-br</code>)</strong>: opt-in completo no remarketing first-party com cookie <code>rmkvera</code> (<code>overrides/vera-bet-br/app/config/features/remarketing.ts</code>); decoração V do <code>legacy-grid</code> (variant exclusivo da vera) agora visível sem hover — opacity default subiu de <code>0.07</code> pra <code>0.18</code> no rest state e o boost de hover foi pra <code>0.28</code> pra preservar feedback de focused-tile.</li>
<li class=""><strong>Cassino.bet (<code>cassino-bet-br</code>)</strong>: ganhou o LP <code>/download-app</code> com assets brand-specific copiados byte-for-byte do legado (md5 verificado: <code>bg-banner-mobile.png</code>, <code>bg-banner-desktop.png</code>, <code>bg-experience.png</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/05/10/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Marketing Docs publicado</strong> — nova surface dedicada pra operadores de marketing/tráfego/CRM/BI, paralela à Internal Docs (dev) e Client Docs. Acessível pelo 4º card na home + navbar "Marketing Docs" + sidebar de 10 categorias. 30 arquivos cobrindo: intro + glossário operacional, atribuição (last-touch + 3 camadas, UTMs canonical/wildcard, 10 clicked-IDs com dual-format payload, affiliate nativo + Clever lastclick), storage (cookies completos da plataforma + headers <code>X-ORIGIN-*</code> e <code>X-LOG-INFO</code>), 13 plataformas (GTM, Facebook Pixel+CAPI roadmap, Google Ads, Kwai, Taboola, TikTok, Clarity, Webtrends com alerta de impacto LCP, Mixpanel, Hotjar, Pendo, AppsFlyer S2S TWA-only, Smartico), eventos GTM/GA4 mapeados por canal, remarketing first-party + audience tags, privacidade (LGPD + GCM v2 com diagrama Mermaid de mapping 4 categorias → 7 sinais), App Mode TWA, config map por brand (4 tabelas focadas com valores reais auditados em vera/7k/cassino/pb/betpontobet/state77), e 3 docs operacionais (testar-campanha passo-a-passo, debug com DevTools/wrangler tail, 30 anti-patterns numerados).</li>
<li class=""><strong><code>docs-internal/architecture/marketing-tracking.md</code></strong> (224 linhas desatualizadas — dizia "NÃO captura gclid/fbclid" e "TTL 30 dias" e mencionava <code>?EnableDebug=1</code> que foi removido) substituído por stub apontando pra Marketing Docs + reference de código pra dev.</li>
<li class=""><strong>CSS custom</strong>: tabelas com mais de ~5 colunas agora ganham scroll horizontal automático (<code>display:block</code> + <code>overflow-x:auto</code>) — evita overflow visual em config-map e equivalentes.</li>
<li class=""><strong><code>ci-deploy.yml</code></strong> (front-cactus-docs): <code>run-name</code> customizado em manual dispatches (<code>&lt;usuario&gt;: Manual deploy → cactus</code>), espelhando o mesmo ajuste já aplicado no <code>front-web-base</code> pra títulos consistentes na lista do GitHub Actions.</li>
</ul>]]></content:encoded>
            <category>remarketing</category>
            <category>cache</category>
            <category>redirects</category>
            <category>cassino</category>
            <category>marketing-docs</category>
            <category>vera</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 09/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/09/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/09/daily</guid>
            <pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Geo-SEO multi-país da rede 7k (BR / CL / NG / FI)]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="geo-seo-multi-país-da-rede-7k-br--cl--ng--fi">Geo-SEO multi-país da rede 7k (BR / CL / NG / FI)<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#geo-seo-multi-pa%C3%ADs-da-rede-7k-br--cl--ng--fi" class="hash-link" aria-label="Link direto para Geo-SEO multi-país da rede 7k (BR / CL / NG / FI)" title="Link direto para Geo-SEO multi-país da rede 7k (BR / CL / NG / FI)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cluster recíproco de hreflang sem <code>x-default</code></strong>: cada um dos quatro domínios da família 7k declara as outras três variantes como alternates explícitas — <code>7k.bet.br</code> (pt-BR, self) + <code>cl.bet7k.com</code> (es-CL) + <code>ng.7k.bet</code> (en-NG) + <code>fi.7k.bet</code> (fi-FI). Os três overrides novos (<code>overrides/{cl-bet7k-com,fi-7k-bet,ng-7k-bet}/app/config/seo/hreflang.ts</code>) e a atualização do 7k-bet-br removem o <code>x-default</code> que apontava pro Brasil — cada brand é geo-exclusiva (KYC, regulação e moeda locais), então não existe "world fallback" legítimo. Resultado: o Google só serve a variante quando o idioma/região do usuário matcha um alternate explícito (ex: russo ou alemão não vê nenhum domínio, em vez de cair no <code>7k.bet.br</code>).</li>
<li class=""><strong><code>Schema.org/Organization</code> enriquecida com sinais de país</strong>: <code>buildOrganizationJsonLd()</code> (<code>app/utils/seo.ts</code>) agora aceita <code>addressCountry</code> (ISO-3166 alpha-2) + <code>areaServedName</code> (nome do país em inglês) e emite <code>address.addressCountry</code> + <code>areaServed.@type=Country</code>. O <code>_layout.tsx</code> resolve ambos via <code>getCountryByAlpha3</code> de <code>@cactus-agents/country-config</code> — mesmo catálogo usado pelo api-client pra derivar geo headers, evita duplicar mapping alpha-3 → alpha-2 + nome do país.</li>
<li class=""><strong>Novo JSON-LD <code>WebSite</code> com <code>inLanguage</code></strong>: segundo blob de Schema.org reforçando o idioma da página de forma independente do <code>&lt;html lang&gt;</code>. Google parseia <code>inLanguage</code> separadamente, então emitir os dois dá ao algoritmo dois sinais redundantes de locale.</li>
<li class=""><strong>Header HTTP <code>Content-Language</code></strong> injetado em toda response HTML pelo <code>app/entry.server.tsx</code> (text/html only — JSON/JS unaffected). Sourced do define <code>__BRAND_LANGUAGE__</code> do Vite e mapeado pra forma BCP 47. Adiciona uma terceira camada de sinal de locale (header HTTP + JSON-LD + <code>&lt;html lang&gt;</code>).</li>
<li class=""><strong>Helper compartilhado <code>app/utils/locale.ts</code></strong> consolida os mapas <code>BRAND_LANGUAGE → BCP 47</code> (pra <code>&lt;html lang&gt;</code>, <code>Content-Language</code>, <code>inLanguage</code>, hreflang) e <code>BRAND_LANGUAGE → og:locale</code> (pra OpenGraph). Antes os dois mapas ficavam inline em <code>root.tsx</code> e <code>_layout.tsx</code> — havia risco de drift entre <code>&lt;html lang="pt-BR"&gt;</code> e <code>Content-Language: pt</code>. Cobertura: oito códigos ativos (<code>pt-br</code>, <code>pt</code>, <code>en</code>, <code>es</code>, <code>es-mx</code>, <code>es-cl</code>, <code>en-ng</code>, <code>fi-fi</code>) com fallback pra <code>pt-BR</code>.</li>
<li class=""><strong><code>useCasinoNomenclature</code> agora é guiado por i18n</strong>, não mais por regex country-aware. O hook colapsa qualquer string vinda do BFF (campo <code>casinoNomenclature</code>) num enum de 2 valores (<code>"casino" | "games"</code>) e resolve o substantivo traduzido via <code>t("layout:sidebar.casino")</code> / <code>t("layout:sidebar.nomenclature_games")</code>. Adicionar país novo virou uma linha em cada <code>layout.json</code> do core — zero mudança no front. Dependência de <code>useBrand().features.country?.code</code> removida.</li>
<li class=""><strong>Doc operacional fora-do-código</strong>: o sinal mais forte de geo-targeting pra gccTLDs (<code>.com</code>, <code>.bet</code>) continua sendo o <strong>Google Search Console → International Targeting → Country</strong>, que é manual. Walkthrough escrito em <code>docs/superpowers/specs/2026-05-09-google-search-console-geo-targeting.md</code> pro time de SEO/marketing.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="redirects-e-cobertura-de-404-legados">Redirects e cobertura de 404 legados<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#redirects-e-cobertura-de-404-legados" class="hash-link" aria-label="Link direto para Redirects e cobertura de 404 legados" title="Link direto para Redirects e cobertura de 404 legados" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cross-brand redirects pro grupo 7k</strong> (PR #782): novo arquivo base <code>app/config/routes/legacy-redirects-cross-brand.ts</code> (default vazio, brand-overridable) carrega o mapa de redirects entre os domínios 7k. Cada uma das quatro brands (7k-bet-br, cl-bet7k-com, fi-7k-bet, ng-7k-bet) ganha um override idêntico de 242 entradas com os patterns PT-BR / ES / EN — o filtro <code>activeLegacyRedirects</code> dropa entries <code>from === to</code> em runtime, então cada brand só consome o que aponta pra fora do seu próprio path. Resolve o cenário em que o Cloudflare roteia entre brands irmãs (geo mismatch) e o visitor caía num path foreign-shaped (ex: <code>/casino/juego/evolution/fan-tan</code> aterrissando em <code>7k.bet.br</code>).</li>
<li class=""><strong>Cobertura ampliada de redirects do legado Vue</strong> (PR #784): <code>app/config/routes/legacy-redirects.ts</code> cresceu de poucas dezenas pra cobrir o umbrella inteiro <code>/casino/...</code> (live, category/<!-- -->:slug<!-- -->, providers, providers/<!-- -->:slug<!-- -->, <!-- -->:provider<!-- -->/<!-- -->:game<!-- -->, <!-- -->:provider<!-- -->/<!-- -->:game<!-- -->/faq) — recriando o catchall <code>/casino/[...casino].vue</code> que o Nuxt tinha e o novo stack nunca reproduziu. Inclui ainda variantes localizadas (<code>/cassino/...</code> PT-BR, <code>/casino/{en-vivo,categoria,proveedores,juego/...}</code> ES, <code>/casino/play/...</code> EN), esportes (<code>/esportes</code>, <code>/deportes</code>), help (<code>/ajuda</code>, <code>/ayuda</code>, <code>/help</code>), gamification em todos os locales, área de usuário (<code>/usuario/*</code>, <code>/jugador/*</code>, <code>/player/*</code>), histórico legado (<code>/user/history/{casino,full,poker,sports}</code>), <code>/user/irpf</code> → <code>user.incomeReport</code>, e LPs estáticas (<code>/cassino-online</code>, <code>/cassino-pix</code>, <code>/casa-de-aposta</code>, <code>/poker</code>, etc.). Repro fixado: <code>/casino/live</code> → 301 <code>/cassino/ao-vivo</code>.</li>
<li class=""><strong>Mitigação dos top 404 do Cloudflare Analytics</strong> (PR #786) em três frentes:<!-- -->
<ul>
<li class=""><strong>Filtro de garbage paths no Worker</strong> (~30k 404s/dia eliminados): <code>handleMiddlewareRoute()</code> curto-circuita paths com URL/HTML injection (<code>[http://</code>, <code>[https://</code>, <code>://</code>, <code>&lt;</code>, <code>&gt;</code>, <code>%3a</code>, <code>%2f</code>, <code>%3c</code>, <code>%3e</code>) com 204 No Content + cache imutável de 24h. Bots e marketing tags com macros não-substituídas (ex: <code>/cassino/jogar/evolution/[https://bat.bing.net/bat.js]</code>) param de poluir o Observability — o CF cacheia o body vazio e hits repetidos nem chegam ao Worker. 42 unit tests fixam o regex.</li>
<li class=""><strong>Restaura <code>/twa.js</code> para 7k-bet-br e cassino-bet-br</strong> (~4.6k 404s/dia + bridge AppsFlyer corrigida): script restaurado byte-a-byte do legado Nuxt (md5 <code>799d7078</code>, idêntico nas três brands BR antigas) bootstrappa <code>window.twa.getPostMessageService()</code> consumido pelo <code>useAppsFlyer.ts</code> dentro do APK Android (<code>br.bet.k.twa</code>, <code>cassino.bet.br.twa</code>). Sem o script, o AppsFlyer caía silenciosamente pro path HTTP S2S.</li>
<li class=""><strong><code>/cassino/category/:slug</code> híbrido PT+EN</strong> (~5.9k 404s/dia): paths indexados como <code>/cassino/category/cassino-ao-vivo/popular</code> (PT-prefix + EN-segment + filtro <code>/popular</code> legado) agora redirecionam pra <code>casino.category</code> (<code>/cassino/categoria/:slug</code>).</li>
</ul>
</li>
<li class=""><strong>Combinado com PR #784, a plataforma mitiga ~46k 404s/dia</strong> entre todas as brands.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="404-dentro-do-layout">404 dentro do layout<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#404-dentro-do-layout" class="hash-link" aria-label="Link direto para 404 dentro do layout" title="Link direto para 404 dentro do layout" translate="no">​</a></h2>
<ul>
<li class=""><strong>Catch-all splat (<code>app/routes/$.tsx</code>)</strong> registrado como último filho de <code>_layout</code>, então URLs não-matched (ex: <code>/asda</code>) preservam header / sidebar / footer / topbar em vez de cair na ErrorBoundary do root com tela cheia "nua".</li>
<li class=""><strong><code>ErrorBoundary</code> de layout</strong> reusa o loader data via <code>useRouteLoaderData</code> e renderiza <code>&lt;ErrorPage variant="inline"&gt;</code> como children do layout. Aplica também a child routes que dão throw 404 (<code>promotions/$slug</code>, <code>lp/$slug</code>, <code>blog/$slug</code>, recents com flag desligada, etc.).</li>
<li class=""><strong><code>ErrorPage</code> ganha <code>variant: "standalone" | "inline"</code></strong> — standalone mantém o full-screen wrapper usado pelo <code>root.tsx</code> em falha catastrófica; inline drop-a o wrapper pra fluir dentro do main do layout. Removidos o CTA verde "Voltar ao início" (as 3 nav pills cobrem) e o label "OU NAVEGUE PARA". Pills encolhem em telas pequenas (gap, padding, font-size, icon size) pra caber em 320px sem wrap.</li>
<li class=""><strong>Novo bloco <code>ErrorDetails</code></strong> sempre visível abaixo das nav pills: payload copy-pasteable em texto puro (URL, user id, build, timestamp, referrer, full UA) pro suporte triar quando o cliente printar/filmar um 404. Auth-reactive via <code>useAccountsStore</code> com o pattern padrão <code>authHydrated</code> + SSR fallback.</li>
<li class=""><strong>Status HTTP agora retorna 404 real</strong> (era 200 em alguns casos bubbleados) com <code>Cache-Control: no-store</code> pra crawlers e monitoring verem o code certo.</li>
<li class="">Bump do <code>@cactus-agents/i18n</code> pra <code>^0.87.0</code> pelas novas chaves <code>error_page.details_*</code> (pt-br, pt, es, en).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sitemap-reorganizado">Sitemap reorganizado<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#sitemap-reorganizado" class="hash-link" aria-label="Link direto para Sitemap reorganizado" title="Link direto para Sitemap reorganizado" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sitemap de jogos splittado por provider</strong> sob namespace <code>/sitemap-games/</code> (PR #781). Estrutura nova:</li>
</ul>
<div class="language-text codeBlockContainer_ZLH_ theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_o30o"><pre tabindex="0" class="prism-code language-text codeBlock_kHW7 thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_O5QB"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/sitemap.xml                          (root index)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-static.xml</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-games/&lt;provider&gt;.xml     (NEW — um urlset por provider)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-games/categories.xml     (renomeado de /sitemap-categories.xml)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-games/providers.xml      (renomeado de /sitemap-providers.xml)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-promotions.xml</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-blog.xml</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── /sitemap-faq.xml</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── /sitemap-legal.xml                (NEW — incorpora PR #743)</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Por que splittar por provider</strong>: Search Console reporta indexing status por sitemap → granularidade por provider expõe qual está falhando crawl; invalidação de cache CDN isolada por família (game novo da PG Soft não bust-a o sitemap da Pragmatic); payloads naturalmente abaixo do limite de 50k URLs sem precisar shard. Formato directory-style (<code>/sitemap-games/&lt;provider&gt;.xml</code>) escolhido em vez do dash-style porque o <code>compilePath</code> do React Router 7 só matcha tokens <code>:param</code> precedidos de <code>/</code>.</li>
<li class=""><strong>Slugs reservados</strong>: <code>categories</code> e <code>providers</code> não podem ser claimados por provider real — as rotas literais irmãs vencem por especificidade no RR e o loop do index pula essas keys defensivamente. A rota legada <code>/sitemap-games.xml</code> flat foi removida (404). Recomendação pós-deploy: ressubmeter <code>/sitemap.xml</code> no Search Console pra acelerar reindex.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="api-e-classificação-de-erros">API e classificação de erros<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#api-e-classifica%C3%A7%C3%A3o-de-erros" class="hash-link" aria-label="Link direto para API e classificação de erros" title="Link direto para API e classificação de erros" translate="no">​</a></h2>
<ul>
<li class=""><strong>Helper <code>app/utils/proxy-error.server.ts</code></strong> (<code>proxyErrorResponse(err, fallback, init?)</code> + <code>unauthorizedNoToken()</code>) substituiu o envelope <code>{ ok: false, error, detail }</code> em <strong>60 rotas internas</strong> <code>/api/*</code> (wallet, payments, user, kyc, rewards, income-report, validation, sports, games, address, favorites, <code>auth/{profile,recheck-spa,refresh}</code>). Em 401/403 o body do BFF é passado intacto pro classifier de <code>@cactus-agents/api-client</code> — antes, o top-level <code>error: "wallet_fetch_failed"</code> lockava a lookup e a <code>reason</code> real do BFF (escondida em <code>detail</code>) nunca era avaliada. Resultado: todo 401 colapsava em <code>EM0005</code> (catch-all), todo 403 em <code>EM0011</code>, perdendo <code>EM0001 no_token</code>, <code>EM0003 expired</code>, <code>EM0006 ip_changed</code>, <code>EM0009 liveness_required</code>. Reportado em 05/2026: usuário cassino.bet.br tomou <code>#EP0710-EM0005</code> no <code>/api/wallet/refresh</code> mid-game enquanto o log do CF mostrava <code>reason: "wallet_fetch_failed"</code> (o envelope, não a causa).</li>
<li class="">Fluxos pre-auth (login, register, recovery, social, validate-document, logout) <strong>intencionalmente preservados</strong> — não disparam <code>handleUnauthorized</code> e dependem de <code>mapBff*Error</code> / <code>FormErrorCode</code> pra UX form-level.</li>
<li class=""><strong>32 testes</strong> novos no helper, mais 5 ponta-a-ponta contra o classifier real. Suite total: 71 files / 685 tests passando.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="fix-de-hidratação-dos-widgets-de-wins">Fix de hidratação dos widgets de Wins<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#fix-de-hidrata%C3%A7%C3%A3o-dos-widgets-de-wins" class="hash-link" aria-label="Link direto para Fix de hidratação dos widgets de Wins" title="Link direto para Fix de hidratação dos widgets de Wins" translate="no">​</a></h2>
<ul>
<li class=""><strong>Eliminada a flash de skeleton no F5</strong> dos widgets <code>BiggestWinsCarousel</code>, <code>LatestBetsCarousel</code>, <code>WinsCombinedWidget</code>, <code>BiggestWinsListWidget</code> e <code>HomeWinsRightColumn</code>. Duas causas combinadas:<!-- -->
<ul>
<li class="">Widgets liam <code>topWins</code> / <code>lastWins</code> da Zustand store gated em <code>_isHomeHydrated</code>, que só virava <code>true</code> num <code>useEffect</code> de rota — SSR + primeiro render mostravam skeleton até o mount.</li>
<li class="">Todos estavam atrás de <code>React.lazy()</code> + <code>&lt;Suspense fallback&gt;</code> — mesmo após corrigir #1 via props, o Suspense substituía brevemente o HTML SSR pelo <code>SectionSkeleton</code> enquanto o chunk carregava.</li>
</ul>
</li>
<li class="">Fix espelha o pattern do <code>TopGamesSection</code>: widgets recebem <code>topWins</code> / <code>lastWins</code> como <strong>props</strong> threaded de cada loader → <code>HubRowRenderer</code> → <code>renderWidget</code>. Reads internos da store + gates <code>_isHomeHydrated</code> + skeleton blocks removidos. Wins widgets convertidos de <code>lazy()</code> pra static imports (custo de bundle ~4-6KB gzip — deps compartilhadas como <code>Marquee</code>, <code>WinCard</code>, <code>SectionTitle</code> já residentes). <code>SectionSkeleton</code> mantido pros outros lazy widgets (<code>ProvidersCarousel</code>, <code>RecommendedBanners</code>, <code>FeaturedGame</code>, <code>Tournaments</code>, <code>Missions</code>, <code>MainLeagues</code>) que ficam deep below the fold.</li>
<li class="">Casos resolvidos: <code>cassino-bet-br</code> row 2 da home (<code>biggest-wins-today</code>, above-the-fold), todas as 4 brands no row 3 do casino hub &amp; casino live (<code>biggest-wins-and-latest-bets</code>, dentro de <code>EAGER_CASINO_ROW_COUNT=4</code>), e o aside desktop do <code>vera-bet-br</code> (<code>HomeWinsRightColumn</code>).</li>
<li class="">Slices <code>topWins</code> / <code>lastWins</code> da Zustand continuam populadas por <code>hydrateHome</code> por backward compat — sem readers agora, safe pra cleanup em follow-up.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>fi-7k-bet (Finlândia)</strong>: tradução completa pra finlandês das <strong>17 entries de SEO page-descriptions</strong> (home, casino, casino.live, casino.category, casino.provider, casino.providers, blog, promotions, vip + 8 sub-páginas VIP, casino.category<!-- -->:todos-os-jogos<!-- -->), usando terminologia domain-aware (<code>kasino</code>, <code>verkkovedonlyönti</code>, <code>kolikkopelit</code>, <code>live-kasino</code>, <code>krash-pelit</code>, <code>talletus</code>, <code>nosto</code>, <code>pelaa vastuullisesti</code>, <code>VIP-ohjelma</code>). Antes era cópia do EN — a brand já roda com <code>BRAND_LANGUAGE=fi-fi</code>, mas o copy above-the-footer ainda renderizava em inglês ("7K: Your trusted online sportsbook"). <strong>Recomendado um pass de revisão por nativo</strong> antes de qualquer copy regulada (responsible-gambling, KYC, payments) — o Finnish gambling regulation tem fraseologia específica que tradução automática não pega. Nomes de ligas no <code>sidebar-sports.ts</code> padronizados pra PT (<code>Brasileirão Série A</code>, <code>Liga Europa</code>, <code>Copa do Brasil</code>) — antes divergia do <code>home-leagues.ts</code> em três casos. Removido o fallback <code>label: "Store"</code> hardcoded no override de sidebar-buttons — agora <code>intent: "store"</code> resolve pra "Kauppa" via i18n core.</li>
<li class=""><strong>7k-bet-br</strong>: refactor dos 4 arquivos de analytics (<code>analytics.ts</code>, <code>appsflyer.ts</code>, <code>hotjar.ts</code>, <code>mixpanel.ts</code>) pro pattern <code>isDev() ? DEV_* : PROD_*</code> (consistente com o resto do arquivo). <strong>Webtrends desativado</strong> (<code>webtrendsAccountId: undefined</code> em dev e prod) — token preservado comentado pra referência caso precise reativar. CSS anti-flicker do Webtrends removido (estava causando deadlock no initial page load).</li>
<li class=""><strong>7k-bet-br + cassino-bet-br</strong>: <code>/twa.js</code> restaurado em <code>public/twa.js</code> + registro <code>&lt;script src="/twa.js" defer&gt;</code> em <code>app/config/scripts/scripts.ts</code> (parte do PR #786 — bridge AppsFlyer pro APK Android). Vera-bet-br já tinha sua variante e ficou intocada.</li>
<li class=""><strong>cl-bet7k-com, fi-7k-bet, ng-7k-bet</strong>: <strong>três brands ganharam override de <code>app/config/seo/hreflang.ts</code></strong> com o cluster recíproco da família 7k (auto-referência + outras 3 sister-domains, sem <code>x-default</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ops">Ops<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#ops" class="hash-link" aria-label="Link direto para Ops" title="Link direto para Ops" translate="no">​</a></h2>
<ul>
<li class=""><strong>Deploy configs (front-ops)</strong>: <code>deploy.yml</code> atualizado pra mudar a default branch de <code>cl-bet7k-com</code>, <code>fi-7k-bet</code> e <code>ng-7k-bet</code> pra <code>main-inter</code> e setar <code>BRAND_LANGUAGE</code> correto por env (<code>es-cl</code>, <code>fi-fi</code>, <code>en-ng</code> respectivamente). Mesmas mudanças de <code>BRAND_LANGUAGE</code> aplicadas aos staging equivalentes (<code>stagecl-bet7k-com</code>, <code>stagefi-7k-bet</code>, <code>stageng-7k-bet</code>). <code>stagecl-bet7k-com</code> também trocou default branch de <code>performance</code> pra <code>stage</code> pra alinhar com a strategy de branching atual.</li>
<li class="">Default branch do <code>prod-7k-bet-br</code> ajustada em deploy config separado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/i18n</code> bumped para <code>^0.88.0</code></strong> — ship das novas keys: <code>error_page.details_*</code> (pt-br, pt, es, en) usadas pelo bloco <code>ErrorDetails</code>, <code>layout:sidebar.nomenclature_games</code> consumida pelo <code>useCasinoNomenclature</code> refatorado, e <code>layout:sidebar-buttons.store</code> que destrava a remoção do fallback <code>"Store"</code> hardcoded no <code>fi-7k-bet</code>. Adicionar país novo agora é uma linha em cada <code>layout.json</code> do core, sem mudança no front.</li>
<li class=""><strong><code>@cactus-agents/api-client</code> bumped para <code>^0.14.1</code></strong>.</li>
<li class=""><strong>Dependências do core estabilizadas</strong> (PR #774 + #775): vários <code>@cactus-agents/*</code> saíram de tag beta pra stable em <code>package.json</code> do front-web-base; <code>@cactus-agents/i18n</code> e <code>@cactus-agents/payments</code> bumpados pra stable em commit separado.</li>
<li class=""><strong>Suite de regressão no core</strong> (commit <code>68d3728</code> mencionado no PR #779) com 11 cases pinned cobrindo o classifier real contra <code>/api/wallet/refresh</code>, <code>/api/kyc/status</code>, <code>/api/user/login-history</code> etc. — garante que mexer no <code>classifyApiError</code> não regrida a granularidade dos códigos <code>EM*</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/05/09/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>docs-internal/endpoints/error-codes.md</code></strong>: nova seção <strong>"Envelope das rotas internas — preservar reason em 401/403"</strong> documentando a regra dura, o helper <code>proxyErrorResponse</code> / <code>unauthorizedNoToken</code>, as exceções legítimas (fluxos pre-auth: login, register, recovery, social, validate-document) e o procedimento pra adicionar rotas novas. Worked example baseado no caso real <code>cassino.bet.br #EP0710-EM0005</code> reportado em 05/2026.</li>
</ul>]]></content:encoded>
            <category>seo</category>
            <category>multi-country</category>
            <category>redirects</category>
            <category>errors</category>
            <category>sitemap</category>
            <category>brand</category>
            <category>infra</category>
        </item>
        <item>
            <title><![CDATA[Changelog — 08/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/08/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/08/daily</guid>
            <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Cadastro — validações client-side mais rigorosas]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cadastro--validações-client-side-mais-rigorosas">Cadastro — validações client-side mais rigorosas<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#cadastro--valida%C3%A7%C3%B5es-client-side-mais-rigorosas" class="hash-link" aria-label="Link direto para Cadastro — validações client-side mais rigorosas" title="Link direto para Cadastro — validações client-side mais rigorosas" translate="no">​</a></h2>
<ul>
<li class=""><strong>Limite etário no cadastro (PR #654)</strong> — novo <code>app/utils/age.ts</code> calcula idade contra <code>now</code> e bloqueia data de nascimento fora da faixa <strong>18–99 anos</strong> direto no <code>RegisterModal</code> antes do POST. Antes o front aceitava qualquer data válida e só o BFF reclamava (round-trip + UX ruim). Erros aparecem inline com chaves i18n já existentes (<code>auth.errors.too_young</code> / <code>too_old</code>), e o <code>parseRegisterFormData</code> em <code>app/modules/register/flow.ts</code> ganhou o mesmo guard pra blindar o pipeline FormData → payload.</li>
<li class=""><strong>Validação de telefone contra máscaras do país (PR #683)</strong> — o <code>RegisterModal</code> agora deriva os digit lengths válidos a partir de <code>availableMasks</code> (ex: BR → <code>[10, 11]</code>) e marca o campo como <code>INVALID_PHONE</code> no blur quando a contagem não bate. Isso afeta automaticamente 7k-bet-br, cassino-bet-br, vera-bet-br (e qualquer brand futuro) sem mudança no override — a validação é mask-derived. Erro limpa assim que o user atinge um length válido; não dispara shake (esse fica reservado pra erros de submit), só border + mensagem inline.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="search--autofill-bloqueado--tecla-buscar-no-mobile">Search — autofill bloqueado + tecla "Buscar" no mobile<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#search--autofill-bloqueado--tecla-buscar-no-mobile" class="hash-link" aria-label="Link direto para Search — autofill bloqueado + tecla &quot;Buscar&quot; no mobile" title="Link direto para Search — autofill bloqueado + tecla &quot;Buscar&quot; no mobile" translate="no">​</a></h2>
<ul>
<li class=""><strong>Autofill leak corrigido (PR #754)</strong> — bug reportado em 2026-05-08: após finalizar saque com sucesso e clicar "Finalizar", o user era redirecionado pra <code>/buscar?q=&lt;email&gt;</code>. Causa raiz: <code>HeaderDormantInput</code> era <code>&lt;input type="text"&gt;</code> sem <code>autoComplete</code>, e o Chrome autofillou o campo com email salvo, disparando <code>onChange</code> → <code>navigate(...)</code>. Fix aplicado nos <strong>4 inputs de busca shared</strong> (header dormant, header active, search-pill, page input): <code>type="search"</code> + <code>name="site-search"</code> + <code>autoComplete/autoCorrect/autoCapitalize="off"</code> + <code>spellCheck={false}</code>.</li>
<li class=""><strong>Tecla "Buscar" no teclado mobile</strong> — mesmo PR adicionou <code>enterKeyHint="search"</code> em todos os inputs de busca (iOS 13.4+, Chrome/Firefox Android mostram lupa em vez do enter genérico) e suprimiu o <code>✕</code> nativo do <code>type="search"</code> via <code>[&amp;::-webkit-search-cancel-button]:appearance-none</code> pra não duplicar com o botão X custom.</li>
<li class=""><strong>Guards defensivos no <code>useSearchTriggerHandlers</code></strong> — <code>handleChange</code> agora descarta eventos cujo <code>nativeEvent.inputType</code> seja <code>undefined</code> ou <code>"insertReplacementText"</code> (padrões de autofill do Chrome/password managers; digitação real é <code>"insertText"</code>) e qualquer valor contendo <code>@</code> (cinto + suspensório). Suite de testes ganhou 4 cenários novos cobrindo autofill, password manager, valor com <code>@</code> e happy path.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--top-games-stacked--categorias-mobile">Cassino — top games stacked + categorias mobile<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#cassino--top-games-stacked--categorias-mobile" class="hash-link" aria-label="Link direto para Cassino — top games stacked + categorias mobile" title="Link direto para Cassino — top games stacked + categorias mobile" translate="no">​</a></h2>
<ul>
<li class=""><strong>Card "Pagou Muito" redesenhado para 228×80 (PR #709)</strong> — <code>TopGameCardCompactWatermark</code> da cassino-bet-br ganhou layout stacked: o botão <code>JOGAR</code> saiu da coluna lateral e virou terceira linha full-width abaixo de nome+payout. Padding 8px ao redor + 8px de gap entre thumb e stack. Refactor adicional expôs <code>gameNameText</code> no <code>TopGamesStyleOverrides</code>: brands que adotarem o variant <code>compact-watermark</code> no futuro herdam automaticamente o token <code>titles</code> da brand; cassino-bet-br setou <code>gameNameText: "#C7CCD1"</code> pra preservar o tom legado exato.</li>
<li class=""><strong>Mobile game card stat layout (PR #690)</strong> — <code>GameCardActions</code> reduziu padding lateral de <code>px-2</code> pra <code>px-1</code> no bloco de conteúdo + <code>px-1.5</code> no chip de stats (mais conteúdo cabe sem cortar). <code>GameGrid</code> na variante <code>compact</code> passou de <code>grid-cols-4</code> pra <code>grid-cols-3</code> no mobile e gap reduzido (<code>gap-1</code> em vez de <code>gap-1.5</code>). <code>BalloonIcon</code> shrinkado pra <code>w-3 h-3</code> em telas pequenas mantendo <code>w-5 h-5</code> em desktop. <code>GameCardStacked</code> reduziu <code>gap-1</code> → <code>gap-0.5</code> no divider mobile.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="stories--swipe-nav-com-close-nas-bordas-pr-711">Stories — swipe nav com close nas bordas (PR #711)<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#stories--swipe-nav-com-close-nas-bordas-pr-711" class="hash-link" aria-label="Link direto para Stories — swipe nav com close nas bordas (PR #711)" title="Link direto para Stories — swipe nav com close nas bordas (PR #711)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Drag-on-card e swipe horizontal navegam stories</strong> — o <code>StoriesModal</code> e <code>StoriesModalFullScreen</code> agora detectam swipe lateral pra avançar/voltar, e nas bordas da timeline o gesto fecha o modal em vez de tentar passar pra fora do range. Drag iniciado em qualquer ponto do card (não só nas setas) também dispara nav.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="blog--wp-post-card--aspect-ratio-configurável-pr-742">Blog / WP Post Card — aspect-ratio configurável (PR #742)<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#blog--wp-post-card--aspect-ratio-configur%C3%A1vel-pr-742" class="hash-link" aria-label="Link direto para Blog / WP Post Card — aspect-ratio configurável (PR #742)" title="Link direto para Blog / WP Post Card — aspect-ratio configurável (PR #742)" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>object-contain</code> em todos os presets fixos</strong> — <code>WpPostCard</code> substituiu <code>object-cover</code> por <code>object-contain</code> em todos os aspect-ratio presets, então thumbnails de blog/promoções nunca mais são cropadas. Imagens fora da proporção do container ganham letterbox/pillarbox contra <code>bg-bg-secondary</code> (neutro).</li>
<li class=""><strong>Novo aspect <code>5/3</code> + default <code>auto</code></strong> — adicionado preset <code>"5/3"</code> (≈1.67) ao <code>PostCardThumbAspect</code>. Default do base virou <code>"auto"</code> (sem aspect fixo, altura segue a imagem). Brand presets restaurados: 7k-bet-br + cl-bet7k-com + fi-7k-bet + ng-7k-bet → <code>cinema</code> (21:9); cassino-bet-br → <code>ultrawide</code> (5:2); vera-bet-br → novo override <code>5/3</code>.</li>
<li class=""><strong>Hardening em <code>blog.server.ts</code></strong> — todos os 4 helpers de fetch WP envelopam o <code>fetch()</code> em <code>.catch()</code>: <code>TypeError: fetch failed</code> (DNS, SSL, endpoint indisponível) degrada pra resultado vazio em vez de quebrar o loader. Cada catch emite <code>log.warn</code> com URL + erro pra diagnóstico.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="security-headers--adição-e-revert-no-mesmo-dia">Security Headers — adição e revert no mesmo dia<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#security-headers--adi%C3%A7%C3%A3o-e-revert-no-mesmo-dia" class="hash-link" aria-label="Link direto para Security Headers — adição e revert no mesmo dia" title="Link direto para Security Headers — adição e revert no mesmo dia" translate="no">​</a></h2>
<ul>
<li class=""><strong>Adição (PR #292, manhã)</strong> — <code>workers/middleware.ts</code> ganhou 5 headers novos no <code>applySecurityHeaders</code>: <code>X-Frame-Options: SAMEORIGIN</code>, <code>X-Content-Type-Options: nosniff</code>, <code>Referrer-Policy: strict-origin-when-cross-origin</code>, <code>Permissions-Policy: geolocation=(), microphone=(), camera=()</code> e <code>Content-Security-Policy-Report-Only</code> (Phase 2 — coletando violações sem bloquear; promove pra <code>Content-Security-Policy</code> enforced após validar em prod sem falsos-positivos).</li>
<li class=""><strong>Revert (PR #770, hotfix da tarde)</strong> — o <code>Permissions-Policy</code> quebrou a permissão de <strong>câmera no fluxo de KYC</strong> (selfie/liveness). Decisão: reverter os 30 linhas inteiras do <code>middleware.ts</code> pra desbloquear KYC em prod. Próxima iteração precisa relaxar <code>camera=()</code> pra <code>camera=(self)</code> ou whitelist do hostname antes de re-aplicar.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>casateste-com — enriquecimento pra paridade com prod (PR #761)</strong> — overrides <code>casateste-com/app/config/**</code> recebem espelho funcional do layout da betpontobet-bet-br pra que a casa de teste exponha as mesmas features que rodam em produção:<!-- -->
<ul>
<li class=""><code>sections/home-rows.legacy.ts</code>, <code>casino-rows.legacy.ts</code>, <code>casino-live-rows.legacy.ts</code> com favorites-row, recently-played, top-games ("Pagou muito"), main-leagues, biggest-wins-and-latest-bets, tournaments e missions.</li>
<li class=""><code>catalog/categories.personalize.ts</code> (orderBy + displayPriority por slug) e <code>catalog/game-thumbs.ts</code> (efeitos mp4 no hover).</li>
<li class=""><code>widgets/sidebar-buttons.ts</code> no variant <code>gradient</code> (brand-aware sem custom override).</li>
<li class=""><code>features/features.ts</code> ativando <code>favoriteGames</code>, <code>recentlyPlayedApiSeed</code>, <code>rowSuggestionsInEmptyStates</code>, <code>headerNotifications</code>, <code>internalFaq</code>, <code>incomeReport</code> e <code>eagerHomeRowCount=8</code>.</li>
<li class=""><code>features/account.ts</code> ativando <code>accountSetLimits</code>, <code>accountTimeoutLimits</code> e <code>userPreferences</code>.</li>
<li class=""><strong>Não</strong> replicado: analytics, FAQ/game-details, SEO, LPs, scripts, licenses, referral custom-v2, menu/sidebar com labels da brand. Indique-e-ganhe fica no variant default.</li>
</ul>
</li>
<li class=""><strong>7k-bet-br — Webtrends account ID (PR #722)</strong> — <code>overrides/7k-bet-br/app/config/analytics/analytics.ts</code> recebe <code>webtrendsAccountId: "251da524-89d9-43b5-a186-8309fea3b93f"</code>, portado de <code>front-old/front-web-7k-bet-br/plugins/webtrends.client.ts</code>. Habilita o A/B testing ativo do 7k.bet.br via <code>&lt;WebtrendsLoader /&gt;</code> carregado globalmente em <code>root.tsx</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="infra--ci--deploy">Infra / CI / Deploy<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#infra--ci--deploy" class="hash-link" aria-label="Link direto para Infra / CI / Deploy" title="Link direto para Infra / CI / Deploy" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>ci-deploy.yml</code> migrado pra matrix fan-out a partir do front-ops (PRs #728, #729, #733, #745, #750, #755)</strong> — sequência grande de refactors no workflow do <code>front-web-base</code>: 3 jobs hardcoded substituídos por (1) <code>quality</code> (lint/build, filtra drafts), (2) <code>config-lint</code> (compara dropdown + branch filters do workflow contra scan filesystem do front-ops, falha em drift), (3) <code>resolve-matrix</code> (scaneia <code>front-ops/config/web/environments/*/deploy.yml</code> em runtime, filtra por <code>auto_deploy</code> + <code>default_branch</code>, emite matrix + GH step summary com tabelas "Deploys fan-out" e "Other environments"), (4) <code>deploy</code> (matrix fan-out, um job paralelo por env). Workflow extra <code>list-environments.yml</code> adicionado pra listar inventário sob demanda. Triggers ajustados: <code>pull_request: branches=[main]</code> + filtro de drafts; <code>workflow_dispatch</code> com dropdown alfabético; heredoc fix no glob/anchors.</li>
<li class=""><strong>Deploy summary table polida + job <code>validate</code> renomeado pra <code>check</code> (front-ops)</strong> — UI da matrix do GH Actions ficou mais legível. Auto-deploy desligado em <code>betpontobet-bet-br</code>, <code>cassino-bet-br</code>, <code>prod-7k-bet-br</code> e <code>vera-bet-br</code> (controle manual via dispatch). <code>cl-bet7k-com</code> aponta default branch pra <code>stage-main-ana</code>. <code>react-c4ctus-com</code> com auto_deploy off até a conta CF Teste estar configurada. Cache TTLs do <code>react-casateste-com</code> apertados pra ambiente de teste (rows + wins).</li>
<li class=""><strong>Ações do GitHub bumpadas pra Node 24-ready (front-ops + front-cactus-docs)</strong> — GitHub vai forçar workflows em Node 20 → Node 24 a partir de <strong>02/06/2026</strong>. Versões mínimas com suporte explícito: <code>actions/checkout@v4 → @v5</code>, <code>actions/setup-node@v4 → @v5</code>, <code>pnpm/action-setup@v4 → @v5</code>. Aplicado em todos os workflows reutilizáveis do front-ops e no deploy do <code>front-cactus-docs</code> (que também ganhou matrix fan-out idêntica ao base). Config dos envs do docs movida pra <code>front-ops/config/docs/environments/&lt;env&gt;/deploy.yml</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>sdk/auth.md</code> — <code>TwoFactorPayload</code> corrigido (PR docs #27)</strong> — campos do payload do 2FA sincronizados com os nomes reais do BFF (<code>code</code> em vez de <code>otp</code>, etc); doc estava descrevendo um shape divergente do que o backend aceita.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sync">Sync<a href="https://docs.c4ctus.com/changelog/2026/05/08/daily#sync" class="hash-link" aria-label="Link direto para Sync" title="Link direto para Sync" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>main-vera</code> (PR #769)</strong> — merge de sincronização do branch <code>main</code> pro <code>vera-bet-br</code>, agregando todos os PRs acima + features que já estavam em <code>main</code> e ainda não tinham chegado na linha vera (Recover KYC steps, Cookie Consent banner, AppsFlyer/Pendo hooks reformados, <code>useRecheckSpa</code>, rotas internas <code>/api/auth/recheck-spa</code> + <code>/api/auth/refresh</code>, <code>RestrictedModeAlert</code>, <code>SessionExpiredBanner</code>, etc.). Conteúdo dessas features já documentado nos changelogs anteriores; este PR só aplica o cherry-pick em massa pro deploy da vera.</li>
</ul>]]></content:encoded>
            <category>register</category>
            <category>security-headers</category>
            <category>ci-deploy</category>
            <category>casateste</category>
            <category>search</category>
            <category>blog</category>
            <category>cassino</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 07/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/07/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/07/daily</guid>
            <pubDate>Thu, 07 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Segurança — remoção total de debug do bundle de produção]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="segurança--remoção-total-de-debug-do-bundle-de-produção">Segurança — remoção total de debug do bundle de produção<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#seguran%C3%A7a--remo%C3%A7%C3%A3o-total-de-debug-do-bundle-de-produ%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Segurança — remoção total de debug do bundle de produção" title="Link direto para Segurança — remoção total de debug do bundle de produção" translate="no">​</a></h2>
<ul>
<li class=""><strong>Toggle <code>?EnableDebug=1</code> removido em prod.</strong> O parâmetro de URL e a chave <code>sessionStorage["debug_enabled"]</code> foram completamente eliminados — não existe mais runtime gate que possa ser ligado por query param, cookie ou storage. O motivador foi um leak no <code>/api/games/start</code>: o endpoint passava <code>debug:true</code> ao <code>ApiClient</code> e ecoava <code>_debug.requestInfo</code> na response, expondo o header <code>cf-worker-key</code> que o Worker usa pra autenticar com o BFF — qualquer um clicando em "Play" em qualquer brand de prod conseguia ler do DevTools. Endpoint corrigido com 6 testes de regressão que travam o contrato (<code>debug:true</code> proibido, <code>_debug</code> ausente em sucesso e erro).</li>
<li class=""><strong>Painéis <code>DevApiDebug</code>/<code>DevApiExplorer</code> agora gated por <code>import.meta.env.DEV</code>.</strong> Novo wrapper <code>DevPanel</code> com <code>React.lazy</code> condicional — em prod, o dynamic <code>import('./DevApiDebug')</code> vira inalcançável e o Vite remove o chunk inteiro do build. Consumers (home, favorites, game detail, wallet, history) trocaram <code>&lt;DevApiDebug&gt;</code> por <code>&lt;DevPanel&gt;</code> envolto em <code>{import.meta.env.DEV &amp;&amp; (...)}</code>, garantindo que loader <code>_debug</code> data nunca serializa no SSR stream de prod.</li>
<li class=""><strong>Rotas <code>/api/dev/*</code>, <code>/dev/ftd-cashback</code>, <code>/debug</code> e <code>/debug/analytics</code> só existem em dev build.</strong> O registro em <code>app/router/routes.ts</code> é gated por <code>process.env.NODE_ENV !== 'production'</code> — em prod build esses arquivos são tree-shaken por completo (server e client). Eliminados também <code>app/utils/debug.server.ts</code> (com <code>isDebugRequest</code>/<code>?EnableDebug</code>) e a doc <code>docs/enable-debug/overview.md</code>.</li>
<li class=""><strong>CI guard <code>pnpm bundle:guard</code> falha se strings proibidas vazarem para o bundle de prod.</strong> Novo script <code>scripts/check-prod-bundle.mjs</code> faz grep no <code>build/</code> após <code>pnpm build</code> e detecta <code>DevApiDebug</code>, <code>DevApiExplorer</code>, <code>activateDebugFromUrl</code>, <code>isDebugActive</code>, <code>EnableDebug</code>, <code>debug_enabled</code>, <code>validation_debug</code>, <code>/api/dev/</code>, <code>/dev/ftd-cashback</code>, <code>debug/analytics</code> e <code>cf-worker-key</code> (no client bundle apenas). Wired via <code>pnpm build:check</code> (= build + bundle<!-- -->:guard<!-- -->). Adicionar nova string secret-adjacent exige atualizar <code>FORBIDDEN_STRINGS</code> no script.</li>
<li class=""><strong>Bumps no core que sustentam a remoção:</strong> <code>@cactus-agents/api-client</code> 0.12.0 (redação regex-based de headers <code>*-key</code>/<code>*-token</code>/<code>*-secret</code>/<code>cf-*</code>/<code>x-api-*</code> no <code>requestInfo</code> em modo debug) e <code>@cactus-agents/utils</code> 1.0.0 <strong>major</strong> (remove os exports <code>activateDebugFromUrl</code> e <code>isDebugActive</code> — não existe mais API pública pra ligar o toggle em runtime). Defense-in-depth: <code>console.*</code> em <code>ftd-cashback.client.ts</code>, <code>smartico-checkin.client.ts</code> e <code>useOnFirstScrollIntoView.ts</code> agora também passam por <code>import.meta.env.DEV</code>, então mesmo se um caller acidentalmente passar <code>debug:true</code> em prod o output some no build.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="auth--classificação-de-logout-401-e-bridge-de-beacon">Auth — classificação de logout 401 e bridge de beacon<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#auth--classifica%C3%A7%C3%A3o-de-logout-401-e-bridge-de-beacon" class="hash-link" aria-label="Link direto para Auth — classificação de logout 401 e bridge de beacon" title="Link direto para Auth — classificação de logout 401 e bridge de beacon" translate="no">​</a></h2>
<ul>
<li class=""><strong>Banner <code>SessionExpiredBanner</code> com código de referência <code>#EP0120-EM0003</code>.</strong> Após um 401 que força logout, o login modal abre por cima da página atual com o banner "Sua sessão expirou" e um código mono-spaced copiável. O email vem prefilled com o último login conhecido. Em rotas protegidas (<code>/user/*</code>, <code>/vip/*</code>) a saída acontece via SPA navigation, sem full-page reload. 15 novos error codes (<code>EM0001</code>–<code>EM0014</code>) com matchers derivados do BFF Laravel real (<code>wrong_ha</code>, <code>wrong_hf1</code>, <code>wrrong_hf2</code> typo preservado, <code>wrong_token</code>, <code>ip_changed</code>, <code>device_changed</code>, <code>location_changed</code>, <code>liveness_required</code>, <code>deprecated_version</code>, <code>user_blocked</code>, <code>security_error</code>).</li>
<li class=""><strong>Beacon estruturado <code>/api/logs/auth-logout</code> agora pousa de fato no Cloudflare Observability.</strong> O <code>navigator.sendBeacon()</code> só aceita content-types CORS-safelisted (<code>application/x-www-form-urlencoded</code>, <code>multipart/form-data</code>, <code>text/plain</code>) — passar <code>Blob({ type: "application/json" })</code> fazia Chrome/Firefox/Safari rejeitarem silenciosamente, perdendo 100% dos payloads em prod. Fix em duas pontas: client troca pra <code>text/plain;charset=UTF-8</code> e captura o boolean retornado pelo <code>sendBeacon</code> (caindo pra <code>fetch({ keepalive: true })</code> quando <code>false</code>); server promove o log principal de <code>log.warn</code> → <code>log.error</code> (sobrevive aos filtros default do CF) e adiciona <code>log.warn</code> estruturado em todos os early-returns.</li>
<li class=""><strong>Refresh-token retry opcional + recheck-spa.</strong> <code>createApiClient({ enableRefreshRetry: true, refreshTokenFn })</code> agora coalesce um refresh único antes do logout — wired nos 5 <code>*.client.ts</code> services com proxy <code>/api/auth/refresh</code> e timeout de 5s. <code>RestrictedModeAlert</code> ganha botão "Verificar meus limites" pra usuários em <code>spa_exclusion</code>, backed pelo novo <code>/api/auth/recheck-spa</code> com cookie rate-limit server-side de 30s (paridade com <code>AlertOnlyWithdraw.verifyLimits</code> do legado Vue). Interceptor global <code>window.fetch</code> 401 agora whitelista same-origin <code>/api/*</code> + origem do BFF — 401s de terceiros (WordPress, Connect, ad tech) não disparam mais logout.</li>
<li class=""><strong><code>@cactus-agents/accounts</code> 2.2.0</strong> para de mandar <code>?check_spa_again=1</code> em todo <code>/auth/user-profile</code> (era um SIGAP lookup síncrono em todo SSR loader + revalidation + profile sync). Agora é opt-in via <code>{ checkSpaAgain: true }</code>. Bumps complementares: <code>@cactus-agents/api-client</code> 0.13.1 (registries de erro + refresh retry) e <code>@cactus-agents/i18n</code> 0.86.2 (15 chaves novas em <code>auth:session_expired_banner.*</code> / <code>auth:session_error.*</code> + 5 em <code>user:protection.recheck_spa_*</code> nos 4 locales).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="recovery-de-senha--três-fixes-do-fluxo">Recovery de senha — três fixes do fluxo<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#recovery-de-senha--tr%C3%AAs-fixes-do-fluxo" class="hash-link" aria-label="Link direto para Recovery de senha — três fixes do fluxo" title="Link direto para Recovery de senha — três fixes do fluxo" translate="no">​</a></h2>
<ul>
<li class=""><strong>Envelope <code>{success:false}</code> do BFF agora vira erro de verdade.</strong> O proxy de <code>confirmReset</code>, <code>sendEmail</code>, <code>sendSms</code> e <code>validateCode</code> tratava qualquer 2xx como sucesso, então a UI renderizava "Senha atualizada com sucesso" mesmo quando o BFF rejeitava semanticamente (ex: KYC com <code>kyc_id</code> ausente/inválido no fluxo de recuperação por KYC). Agora o proxy detecta o envelope de falha e converte para HTTP 400 + mensagem de erro, preserva o cookie <code>recovery_session</code> em falha de <code>confirmReset</code> (usuário pode retentar sem refazer o KYC), e pula a persistência do cookie de código em falha de <code>validateCode</code>.</li>
<li class=""><strong>Typo <code>source: "recovery_password"</code> → <code>"password_recovery"</code>.</strong> O fluxo de recovery por KYC mandava <code>source=recovery_password</code> para <code>/bff/users/kyc/recovery</code> — ordem invertida vs todas as brands do legado Nuxt (<code>source=password_recovery</code>). O BFF aceitava o KYC mas nunca associava o <code>correlation_id</code> ao reset, fazendo o <code>/auth/passwords/reset/confirm</code> subsequente falhar com o envelope genérico. Adicionado também o gate em <code>saveKycId</code> pra omitir <code>kyc_id</code> quando vindo de <code>password_recovery_attempt_limit</code> (paridade com <code>ResetForm.vue</code> legado). Typo introduzido em <code>bb2859bc0</code> (Mar/25/2026, bootstrap do novo fluxo) e nunca pego porque <code>RecoverKycStep</code> não tinha testes.</li>
<li class=""><strong>Step <code>options</code> nunca mais auto-avança.</strong> O <code>useEffect</code> que disparava <code>setStep(options[0].step)</code> quando havia só um método ativo fazia o botão "Voltar" parecer quebrado: Betpontobet (só KYC) voltava pra KYC ao cancelar; Vera (só email) voltava pro código ao apertar "Voltar". Removido o auto-advance — usuário sempre vê a tela de opções e clica, mesmo com um único método. 5 testes novos cobrindo as 4 permutações de método ativo. Comentário do prefixo <code>t2:</code> do Turnstile atualizado: o hardcode é intencional (plataforma tem um único sitekey always-show provisionado), não TODO.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="performance-e-resiliência">Performance e resiliência<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#performance-e-resili%C3%AAncia" class="hash-link" aria-label="Link direto para Performance e resiliência" title="Link direto para Performance e resiliência" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cache <code>topGames</code> reaproveitado para o fetch de high-payers.</strong> O refactor <code>6867beda</code> renomeou o resource de <code>topGames</code> para <code>highPayers</code> mas o nome novo nunca foi adicionado ao <code>defaults.yml</code> do <code>front-ops</code>. Resultado: todo SSR de home/casino fazia bypass total do cache (sem TTL, SWR ou single-flight coalescing) e batia direto no BFF <code>/bff/games/game-high-payers-dl</code>, sobrecarregando upstream e o database de stats. Voltar a usar <code>topGames</code> (que já tem 1h TTL + KV snapshot + post-deploy purge declarados no defaults) restaura o cache imediatamente sem mudança coordenada no front-ops.</li>
<li class=""><strong>AppsFlyer TWA bridge restaurada para a 7k.</strong> Após a virada SSG → SSR, eventos AppsFlyer pararam de chegar pra 7k (Vera e Cassino sobreviveram por terem mais tráfego de mobile browser; 7k é praticamente 100% TWA). O legado <code>useAppsFlyerS2S</code> tinha dois paths: bridge nativa via <code>window.twa.getPostMessageService()</code> quando <code>APK &gt;= postMessageMinVersion</code> (path dominante em prod) e HTTP S2S como fallback. A primeira porta do hook só implementou o HTTP path — e ainda com gate invertido. Solução: dispatcher dual-path com <code>AppsFlyerConfig.postMessageMinVersion?: string</code> novo. TWA + APK no threshold + bridge presente → <code>postMessage</code>; tudo o resto → HTTP S2S. Thresholds legados respeitados: 7k 3.0.0, Cassino 4.0.20, Vera 5.0.0.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cassino.bet.br — Pendo habilitado com tracking manual.</strong> Override de brand novo ativando o Pendo (token + <code>accountId: "cassino-bet-br"</code>, paridade com o runtime config do legado <code>front-web-cassino-bet-br</code>). Hook <code>usePendo</code> estendido pra emitir manualmente os eventos que o agente no-code não consegue observar: <code>app_loaded</code> (uma vez por document load — primeira visita, F5, navegação externa), <code>login</code> (transição <code>isAuthenticated false → true</code>) e <code>logout</code> (<code>true → false</code>). Re-identifica o visitante nas transições via <code>pendo.identify()</code> (fallback para <code>initialize</code>), espelhando <code>stores/auth.ts</code> do legado Nuxt. <code>visitor.id</code> é o user id da plataforma Cactus (<code>AuthUser.id</code> do <code>@cactus-agents/accounts</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="infra-front-ops">Infra (front-ops)<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#infra-front-ops" class="hash-link" aria-label="Link direto para Infra (front-ops)" title="Link direto para Infra (front-ops)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Refactor de config por brand:</strong> <code>brand.yml</code> agora declara só identidade (<code>repo: &lt;owner&gt;/&lt;name&gt;</code>, singular — cada brand é dona de um repo) e <code>environments/&lt;env&gt;/deploy.yml</code> vira a source of truth para <code>worker_name</code>, <code>cf_account</code>, <code>default_branch</code>, <code>language</code>, <code>origin_domain</code>, <code>worker_url</code> e <code>cf_zone</code>. <code>repos.yml</code> removido das duas brands. Contrato do reusable workflow inalterado (<code>environment</code> + <code>ref</code>), então callers em outras branches continuam funcionando.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/05/07/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Referência EP/EM de códigos de erro adicionada ao <code>docs-internal</code>.</strong> Nova doc <code>docs-internal/endpoints/error-codes.md</code> lista todos os EP (endpoint) e EM (error kind) que o front emite ao classificar 401/403/429/5xx forçando logout — superfície compartilhada com o time de back-end pra triagem de tickets "fui deslogado com <code>#EP0120-EM0003</code>". 13 tabelas de EP por domínio (Auth, User, Wallet, Payments, KYC, Games, Sports, Brand, internal <code>/api/*</code>, Unknown) extraídas verbatim de <code>packages/api-client/src/errors/endpoint-codes.ts</code>; 5 tabelas de EM por status (401 com 13 matchers específicos, 403, 409/429, 5xx, transport, unknown) com cross-reference pro BFF Laravel (<code>User.php</code>, <code>CheckBlocked.php</code>, <code>CheckUserLoggedIn.php</code>). Inclui playbook de triagem com 2 exemplos end-to-end, checklist pra adicionar códigos novos, e schema do beacon estruturado que <code>app/routes/api/logs/auth-logout.ts</code> escreve no CF Workers Observability.</li>
</ul>]]></content:encoded>
            <category>security</category>
            <category>debug</category>
            <category>auth</category>
            <category>recovery</category>
            <category>beacon</category>
            <category>brand</category>
            <category>infra</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 06/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/06/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/06/daily</guid>
            <pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Pós-registro & Auth — fluxo regulatório e dedup de bursts]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pós-registro--auth--fluxo-regulatório-e-dedup-de-bursts">Pós-registro &amp; Auth — fluxo regulatório e dedup de bursts<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#p%C3%B3s-registro--auth--fluxo-regulat%C3%B3rio-e-dedup-de-bursts" class="hash-link" aria-label="Link direto para Pós-registro &amp; Auth — fluxo regulatório e dedup de bursts" title="Link direto para Pós-registro &amp; Auth — fluxo regulatório e dedup de bursts" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo hook <code>useAfterRegisterFlow</code></strong> (<code>app/hooks/useAfterRegisterFlow.ts</code>) orquestra o pós-registro: abre o modal de depósito uma vez, logo após o cadastro, <strong>bypassando o throttle de 12h</strong> do <code>useAutoDepositModal</code>. Replica o <code>handleOpenRegisterStrategy → openModal('deposit')</code> do <code>handleSuccessRegister</code> legado em Vue. Suporta o ramo regulatório (espera o <code>LimitsStep</code> do <code>ValidationBlockerOverlay</code> resolver antes de abrir o depósito) e suprime quando há <code>?deposit=&lt;campaign&gt;</code> na URL, conta restrita ou <code>featuresConfig.autoDepositAfterRegister === false</code>.</li>
<li class=""><strong>Store <code>app/store/onboarding.ts</code></strong> (Zustand, <strong>não persistida</strong>) carrega o sinal <code>justRegistered</code> + <code>lastPrudentialLimitActive</code> entre o sucesso do <code>RegisterModal</code> e o próximo render do <code>DefaultLayout</code>. Reset automático no logout.</li>
<li class=""><strong>Forward de <code>user_prudential_limit_active</code></strong> no payload de signup (<code>/bff/register-simplified</code>, <code>/auth/register</code>): sinaliza ao BFF qual botão regulatório o usuário clicou ("Quero definir meus limites agora" vs "Continuar sem limites"). Nova feature flag <code>autoDepositAfterRegister</code> propagada nos 13 overrides de brand.</li>
<li class=""><strong>Dedup cross-request de <code>/auth/user-profile</code></strong> em <code>app/services/auth.server.ts</code>: cache token-scoped com <strong>TTL de 10s</strong> + <strong>cap de 100 entradas</strong> (chave = SHA-256 hex do token, evita expor token cru em dumps de memória). Resolve o burst pós-login onde <code>POST /api/auth/login</code>, <code>useAuthProfileSync → GET /api/auth/profile</code> e <code>revalidator.revalidate() → _layout</code> loader pegavam <strong>três Requests distintos com o mesmo token</strong> — o <code>WeakMap&lt;Request&gt;</code> antigo não conseguia deduplicar.</li>
<li class=""><strong><code>/api/auth/profile</code> agora consome <code>getAuthForRequest</code></strong> em vez de chamar <code>createAuthFromClient</code> direto, herdando automaticamente o cache acima. PRs touch-up em 20+ componentes (UserSummaryHeader, ValidationContextGate, GameIframe, payments, protection, validation steps) que liam <code>useProfile()</code> ad-hoc.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos--refresh-de-profile-e-ux-pix">Pagamentos — refresh de profile e UX Pix<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#pagamentos--refresh-de-profile-e-ux-pix" class="hash-link" aria-label="Link direto para Pagamentos — refresh de profile e UX Pix" title="Link direto para Pagamentos — refresh de profile e UX Pix" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>refreshProfile()</code> após depósito</strong> (PR #674) — opt-in via flag <code>refreshProfileAfterDeposit</code> em <strong>7k-bet-br, cassino-bet-br, vera-bet-br</strong> apenas. Faz refetch do profile assim que o depósito fecha pra que <code>ftd_value</code>/<code>ftd_date</code> saiam de 0 imediatamente após o FTD, destravando features gateadas (minigames diários, FTD cashback) sem reload. Disparo do tracking acontece <strong>antes</strong> do refresh pra preservar o sinal de FTD. Demais brands não recebem a flag (ruído removido dos overrides).</li>
<li class=""><strong>Botão "Já paguei" oculto no resultado Pix</strong> (PR #677) — feature flag <code>hidePixAlreadyPaidButton</code> ativa em <strong>7k-bet-br, cassino-bet-br, vera-bet-br</strong>. Usuário fica preso no fluxo "aguardando confirmação real do BFF" em vez de poder forçar o <code>onApproved</code> manualmente.</li>
<li class=""><strong>Toggle de marketing sincronizado com store</strong> (PR #680) — <code>MarketingSection</code> agora deriva <code>accepted</code> direto de <code>useAccountsStore</code> (era <code>useState</code> local que só semeava no mount → mostrava valor stale ao navegar fora e voltar). Adiciona feedback de save via <code>sonner</code> toast e trata erro com <code>extractApiError</code>. Strings novas em <code>pt-br</code>/<code>es</code>/<code>en</code> no namespace <code>user</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--banner-regulamentado-promoções-layout-do-card">Cassino — banner regulamentado, promoções, layout do card<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#cassino--banner-regulamentado-promo%C3%A7%C3%B5es-layout-do-card" class="hash-link" aria-label="Link direto para Cassino — banner regulamentado, promoções, layout do card" title="Link direto para Cassino — banner regulamentado, promoções, layout do card" translate="no">​</a></h2>
<ul>
<li class=""><strong>Side CTA de "Plataforma Regulamentada" agora rola até o footer</strong> em vez de navegar (PR #670). Novo type <code>action: "scroll-to-footer"</code> em <code>HomeBannerSideCta</code> — mutualmente exclusivo com <code>link</code>. <code>SideCtaCard</code> usa <code>&lt;button&gt;</code> em vez de <code>&lt;Link&gt;</code>, faz <code>scrollIntoView({ behavior: "smooth" })</code> no <code>&lt;footer id="page-footer"&gt;</code> (fallback <code>window.scrollTo(bottom)</code>). Footer agora carrega o <code>id="page-footer"</code> em <code>FooterDefault</code> e <code>FooterStacked</code>. Aplicado em <strong>cassino-bet-br</strong> via override do <code>home-banner.ts</code>.</li>
<li class=""><strong>Cards de promoção sem crop</strong> em <strong>cassino-bet-br</strong> (PR #681) — novo override <code>widgets/post-card.ts</code> força <code>thumbAspect: "ultrawide"</code> (5:2), espelhando o precedente do <strong>7k-bet-br</strong>. Banners promocionais saem em ~2.44:1 com logo + disclaimer "Aposta não é investimento" no lado direito; o default 16:9 cropava essa região.</li>
<li class=""><strong>Game card mais denso em mobile</strong> (PR #673) — <code>GameCarousel</code> ajusta largura compacta (<code>w-[118px]</code> em mobile) e regular; <code>BalloonIcon</code> reduz pra <code>w-3.5 h-3.5</code> em mobile (mantém <code>sm:w-5 sm:h-5</code> em desktop); chip de stat ganha <code>gap-1</code> em mobile (<code>sm:gap-1.5</code> em desktop).</li>
<li class=""><strong><code>PageDescription</code> permite scroll</strong> (PR #682) — quando expandida, vira <code>max-h-[80vh] overflow-y-auto</code> (antes <code>max-h-max</code> sem clamp). Descrições SEO longas na home não estouram mais o viewport.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="tracking--affiliate">Tracking &amp; Affiliate<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#tracking--affiliate" class="hash-link" aria-label="Link direto para Tracking &amp; Affiliate" title="Link direto para Tracking &amp; Affiliate" translate="no">​</a></h2>
<ul>
<li class=""><strong>Clever-01 — paridade estrita 1:1 com <code>pages/clever.vue</code> legado</strong> (PR #672). Cookie <code>lastclick</code> agora:<!-- -->
<ul>
<li class="">Vai <strong>sem <code>sanitizeTrackingValue</code></strong> — <code>lastclick</code> é tratado como ID opaco do parceiro Clever, que pode usar formatos com strings da blocklist (<code>document</code>, <code>appendChild</code>, macros <code>{macro}</code>/<code>__CLICKID__</code>). Sanitizar mutilava IDs e quebrava atribuição.</li>
<li class="">Resolve por <code>params.getAll("utm_source")</code> pegando o <strong>último valor</strong> (handles <code>?utm_source=a&amp;utm_source=b</code>), em vez de <code>params.get()</code> que pega o primeiro.</li>
<li class="">Fallback usa <strong>último valor da primeira key</strong> (paridade com <code>Object.values(searchParams)[0]</code> + <code>Array.isArray ? .last : .</code> do legado).</li>
</ul>
</li>
<li class=""><strong>Clever-02 — Set-Cookie cross-site + cookies legacy individuais</strong> (PR #685, follow-up do clever-01 de 25/04). <code>captureTrackingFromRequest</code> agora retorna <code>setCookies: string[]</code> (plural) com o <code>cookie_tracking</code> <strong>+ cookies individuais legacy</strong> (<code>src</code>, <code>utm_source</code>, <code>utm_campaign</code>, <code>utm_content</code>, <code>utm_medium</code>, <code>gclid</code>, <code>fbclid</code>, etc.) emitidos como <code>Set-Cookie</code> separados. Em HTTPS, todos viram <code>SameSite=None; Secure</code> pra que validators de afiliado/anúncio enxerguem o cookie em chains cross-site (HTTP local cai em <code>SameSite=Lax</code> porque o browser rejeita <code>None</code> sem <code>Secure</code>). Loaders <code>app/root.tsx</code> e <code>app/routes/clever.ts</code> propagam <code>setCookies</code> em vez de <code>setCookie</code> único.</li>
<li class=""><strong>AppsFlyer S2S restrito a mobile browser fora de TWA</strong> (PR #675). Antes disparava em qualquer App Mode (<code>?app=true</code>); agora exige (1) UA de phone + (2) <strong>NÃO estar em TWA</strong>. Razão: as APKs Android do 7k/cassino/vera já reportam <code>lead</code>/<code>dep</code>/<code>rebill</code> via SDK nativo (<code>InAppEvents.logEvent</code>) — disparar S2S no WebView por cima dobrava todo evento no dashboard AppsFlyer. Captura de <code>appsflyer_id</code>/<code>advertising_id</code>/<code>app_version</code> da URL roda incondicional pra cobrir o caso "usuário sai da APK pro browser mid-session".</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="proteção--limites">Proteção / Limites<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#prote%C3%A7%C3%A3o--limites" class="hash-link" aria-label="Link direto para Proteção / Limites" title="Link direto para Proteção / Limites" translate="no">​</a></h2>
<ul>
<li class=""><strong>Autoexclusão permanente: <code>-1</code> (front) → <code>99</code> (BFF)</strong> (PR #671). Front novo adotou <code>-1</code> como convenção interna pra "permanente" (alinhado com <code>SELF_EXCLUSION_MONTHS_OPTIONS</code> em <code>@cactus-agents/accounts</code> e <code>isPermanentExclusion</code>); BFF rejeita <code>0</code>/negativos com "user exclusion deve ser pelo menos 1" e exige <code>99</code> (convenção do legado Vera). Boundary fica no proxy <code>app/routes/api/user/self-exclusion.ts</code> via <code>toBffExclusionValue()</code> — callers podem continuar passando <code>-1</code> sem precisar lembrar da regra.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>Privacy HTML legado pras brands ANA Gaming</strong> (PR #652) — <code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>vera-bet-br</code> ganham overrides próprios de <code>app/config/legal/pages.ts</code> com versões <code>terms=5.0</code>, <code>privacy=2.0</code>, demais=<code>4.0</code> (espelhando o <code>versionText</code> do <code>Terms.vue</code> legado). Brands não-ANA caem no fallback <code>"1.0"</code> do loader (<code>pageConfig.version ?? "1.0"</code>). Tabela de privacy renderiza responsiva em mobile (linhas empilham, sem scroll horizontal). Flag <code>useStaticPrivacyTerm</code> removida das 11 brands não-target (ruído).</li>
<li class=""><strong>Vera referral — copy persuasiva + CTA pra jogos elegíveis</strong> (PR #669). <code>overrides/vera-bet-br/app/config/referral/v1.ts</code> ganha <code>minBetAmount: 200</code>, <code>providerName: "PG Soft"</code>, <code>eligibleGamesPath: "/cassino/categoria/indique-amigos"</code>. Copy vinda do <code>pt-br.json:608</code> legado ("girar o mínimo de R$200,00 em jogos selecionados da PG Soft").</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ops">Ops<a href="https://docs.c4ctus.com/changelog/2026/05/06/daily#ops" class="hash-link" aria-label="Link direto para Ops" title="Link direto para Ops" translate="no">​</a></h2>
<ul>
<li class=""><strong>Default branch do <code>front-web-base</code></strong> alterado para <code>main-ana</code> na config dos workflows reusáveis (<code>front-ops</code>).</li>
</ul>]]></content:encoded>
            <category>auth</category>
            <category>payments</category>
            <category>tracking</category>
            <category>clever</category>
            <category>brand-overrides</category>
            <category>ana-gaming</category>
            <category>paridade-legado</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 05/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/05/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/05/daily</guid>
            <pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Analytics — Mixpanel Deposit + AppsFlyer S2S]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="analytics--mixpanel-deposit--appsflyer-s2s">Analytics — Mixpanel Deposit + AppsFlyer S2S<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#analytics--mixpanel-deposit--appsflyer-s2s" class="hash-link" aria-label="Link direto para Analytics — Mixpanel Deposit + AppsFlyer S2S" title="Link direto para Analytics — Mixpanel Deposit + AppsFlyer S2S" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo evento Mixpanel <code>Deposit Requested</code></strong> (<code>useAnalytics.trackDepositRequested</code>) disparado quando <code>/wallet/add-credit</code> retorna 200 OK. Espelha o legado <code>DefaultLayout.vue</code> das brands 7k/cassino/vera (props <code>Amount</code> em unidades inteiras + <code>DepositMethod</code>). Continua complementar ao evento de funil já existente (<code>pix_generated</code> no GTM e <code>PixGenerated</code> no Facebook).</li>
<li class=""><strong>Bugfix crítico nos eventos S2S do AppsFlyer (<code>sendRegister</code>, <code>sendFTD</code>, <code>sendRebill</code>)</strong>. Os três estavam dentro do guard <code>if (!shouldLoadTrackers) return</code> do <code>useAnalytics</code>, mas o gate é o <strong>inverso</strong> do gate do AppsFlyer (<code>shouldLoadTrackers = !isAppMode &amp;&amp; !isBot</code>, <code>useAppsFlyer.ready = enabled &amp;&amp; isAppMode</code>) — resultado: nenhuma das chamadas server-to-server disparava nem na web (gate externo bloqueia) nem dentro do APK (early return do gate externo cortava o caminho). Agora as três chamadas são disparadas <strong>antes</strong> do guard e o S2S funciona em produção. Coberto por <code>useAppsFlyer.test.ts</code> (202 linhas) e <code>useAppMode.test.ts</code> (72 linhas).</li>
<li class=""><strong><code>useAppMode</code> agora reconhece <code>?is_twa=true</code></strong> além de <code>?app=true</code>. Os APKs em produção da família 7k/cassino/vera (publicados antes do rename do query param) continuam injetando o legado em cada landing — sem isso <code>isAppMode</code> ficava <code>false</code> pra users do app e desativava todo o pipeline de tracking S2S silenciosamente.</li>
<li class=""><strong>Valor do <code>deposit</code> GTM corrigido pra unidades inteiras</strong> (<code>amountCents / 100</code>) ao invés de cents puros — paridade com o legado e com a expectativa dos dashboards de BI.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sitemap--split-temático-e-origem-dinâmica">Sitemap — Split Temático e Origem Dinâmica<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#sitemap--split-tem%C3%A1tico-e-origem-din%C3%A2mica" class="hash-link" aria-label="Link direto para Sitemap — Split Temático e Origem Dinâmica" title="Link direto para Sitemap — Split Temático e Origem Dinâmica" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sitemap monolítico fatiado em sub-sitemaps temáticos</strong> (<code>feat/sitemap-split-and-index</code>): <code>sitemap.xml</code> virou índice e aponta pra <code>sitemap-static.xml</code>, <code>sitemap-games.xml</code>, <code>sitemap-categories.xml</code>, <code>sitemap-providers.xml</code>, <code>sitemap-promotions.xml</code>, <code>sitemap-blog.xml</code> e <code>sitemap-faq.xml</code>. Sub-sitemaps só aparecem no índice quando a brand tem conteúdo correspondente — evita Googlebot descobrir filhos vazios. Sharding automático em <code>sitemap-games.xml</code> quando passa de 45k URLs (<code>computeShardCount</code>).</li>
<li class=""><strong>Origem do <code>&lt;loc&gt;</code> agora vem do <code>request.url</code> ao invés de <code>ORIGIN_DOMAIN</code></strong> (<code>fix/sitemap-origin-from-request-host</code>). A env aponta pro domínio canônico da brand (compartilhado entre prod e stage), então o sitemap de stage estava listando URLs de produção e ficava inútil pra QA. Agora prod aponta pra <code>https://7k.bet.br</code>, stage pra <code>https://stage.7k.bet.br</code>, dev pra <code>http://localhost:5173</code>. Fallback pra <code>ORIGIN_DOMAIN</code> apenas quando <code>request.url</code> falha — caso patológico em workers Cloudflare.</li>
<li class=""><strong><code>robots.txt</code> bloqueia indexação em hosts não-prod</strong> — stage e dev disparam <code>Disallow: /</code> enquanto o domínio canônico fica liberado.</li>
<li class=""><strong>Stylesheet XSL</strong> (<code>public/sitemap.xsl</code>, 391 linhas) adicionada pra renderizar o sitemap legível em browser.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="performance--listing-de-jogos-híbrido">Performance — Listing de Jogos Híbrido<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#performance--listing-de-jogos-h%C3%ADbrido" class="hash-link" aria-label="Link direto para Performance — Listing de Jogos Híbrido" title="Link direto para Performance — Listing de Jogos Híbrido" translate="no">​</a></h2>
<ul>
<li class=""><strong>Listing de categoria/provider agora é híbrido</strong> (<code>perf/games-listing-infinite-scroll-search-ux</code>): catálogos com <code>total &lt;= 120</code> jogos (90%+ dos casos) seguem 100% client-side com filter reativo sub-segundo; categorias maiores recebem só a primeira batch via SSR e completam por infinite-scroll usando os novos endpoints <code>/api/games/category/:slug</code> e <code>/api/games/provider/:slug</code> (offset/limit, default 48). Cutoff e batch size centralizados em <code>useHybridGamesList</code> (329 linhas, 287 linhas de teste).</li>
<li class=""><strong>Hook <code>useStatsBatchHydration</code> + endpoint <code>/api/games/stats-batch</code></strong> alimenta o store reativo de stats sem bloquear a primeira pintura.</li>
<li class=""><strong>Search com debounce de 250ms server-side</strong> quando em modo paginado; folding de diacríticos via <code>foldForSearch</code> do core mantém paridade entre filter local e server.</li>
<li class=""><strong>NavigationLoader/ProgressBar/Spinner</strong> ajustados pra refletir corretamente navegações dentro do mesmo route (filter, infinite-scroll) via novo hook <code>useIsRouteNavigation</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="seo--landing-all-games--page-descriptions">SEO — Landing All-Games + Page Descriptions<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#seo--landing-all-games--page-descriptions" class="hash-link" aria-label="Link direto para SEO — Landing All-Games + Page Descriptions" title="Link direto para SEO — Landing All-Games + Page Descriptions" translate="no">​</a></h2>
<ul>
<li class=""><strong>Página <code>/cassino/categoria/all</code> ganha <code>PageDescription</code></strong> específico via override <code>casino.category:todos-os-jogos</code> (<code>fix/all-games-landing-seo-and-canonical-slug</code>). Antes a SEO copy era pulada (<code>pageDescription = null</code>) só pra "all", deixando a landing órfã. Agora o resolver usa o slug canônico <code>todos-os-jogos</code> como chave do override, valendo tanto pro alias legado <code>/cassino/categoria/all</code> quanto pra URL canônica <code>/cassino/categoria/todos-os-jogos</code>.</li>
<li class=""><strong>Helpers <code>CANONICAL_ALL_GAMES_SLUG</code>, <code>isAllGamesSlug</code> e <code>resolveCategorySlug</code></strong> centralizados em <code>app/utils/casino-categories.ts</code> — qualquer caller que precise resolver "all games" passa a usar o mesmo trio (filter bar, breadcrumbs, SEO) e os dois slugs (<code>all</code> e <code>todos-os-jogos</code>) ficam cobertos.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--sidebar--footer">Cassino / Sidebar / Footer<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#cassino--sidebar--footer" class="hash-link" aria-label="Link direto para Cassino / Sidebar / Footer" title="Link direto para Cassino / Sidebar / Footer" translate="no">​</a></h2>
<ul>
<li class=""><strong>Item "Regras de Apostas" na seção esportes da sidebar</strong> (<code>feat/sidebar-betting-rules-7k-cassino-vera</code>). Aparece somente quando <code>showSidebarBettingRules: true</code>, o user está em rota <code>/sports</code> e o provider ativo é <code>first</code> (CactusSportsbook). Click dispara o evento <code>cactus:show-betting-rules</code> que o <code>SportsIframeFirst</code> escuta e reescreve o <code>src</code> do iframe com <code>#betting-rules</code> — espelha o legado <code>CactusSportsbook.showBettingRules</code>. Ativado em <code>7k-bet-br</code>, <code>cassino-bet-br</code> e <code>vera-bet-br</code>; flag adicionada (default <code>false</code>) em <strong>todas as 14 brands com override</strong> pra evitar drift do file-replacement.</li>
<li class=""><strong>Sidebar variants ganham suporte a <code>actionKey</code></strong> em <code>SidebarMenuItem</code> — quando preenchido, o item renderiza como botão com side-effect handler ao invés de link de navegação. Primeiro consumer é o <code>betting-rules</code> acima.</li>
<li class=""><strong>Footer com row opcional de links extras</strong> (<code>feat/footer-extra-legal-links-7k-cassino-vera</code>): novo tipo <code>ExtraLegalLinksConfig</code> com 3 posições (<code>footer-stacked-bottom-bar</code>, <code>footer-default-after-seals</code>, <code>footer-default-after-legal</code>). 7k, cassino e vera ganham os links Denúncias / Privacidade / Ouvidoria; demais brands continuam com <code>null</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos--conta">Pagamentos / Conta<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#pagamentos--conta" class="hash-link" aria-label="Link direto para Pagamentos / Conta" title="Link direto para Pagamentos / Conta" translate="no">​</a></h2>
<ul>
<li class=""><strong>Métodos <code>pixtopay-global</code> e <code>pixtopay-global-webpay</code> (Payku/CHL) suportados no checkout</strong> (<code>feat/chl-pixtopay-global-methods</code>). Render via iframe (altura 750px, mesmo padrão do <code>bank-transfer</code>/<code>khipu</code>). Bump do <code>@cactus-agents/payments</code> na lockfile.</li>
<li class=""><strong>Valor pre-preenchido do depósito agora é clamped pro mínimo da brand</strong> (<code>fix/clamp-prefilled-deposit-min</code>). Antes, <code>?value=500</code> num site com mínimo <code>R$ 10</code> (1000 cents) abria o form com <code>R$ 5</code> e bloqueava o submit; agora <code>Math.max(initialAmount, minAmount)</code> garante que o form nunca abre abaixo do threshold.</li>
<li class=""><strong>"Dados Pessoais" virou read-only</strong> (<code>fix/readonly-personal-data</code>). Nome, sobrenome, data de nascimento e gênero vêm da validação de documento (CPF/RUT/CURP) no registro e o BFF rejeita updates com <code>VUD001</code> — manter os inputs editáveis dava falsa impressão de poder editar. Exceção: <code>BirthdateForm</code> ainda aparece quando <code>birth_date</code> é nulo ou <code>0000-00-00</code> (raro pra BR, possível pra outros países). Mesma lógica do legado <code>Document.vue</code>.</li>
<li class=""><strong>Tournament/Mission/MiniGames cards e wins-carousel ganham <code>key</code> props faltantes</strong> + miscelâneas de lint (<code>chore/biome-lint-warnings-cleanup</code>, 74 warnings biome saneados em ~35 arquivos sem mudança funcional).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="kyc--validação">KYC / Validação<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#kyc--valida%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para KYC / Validação" title="Link direto para KYC / Validação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Race condition no anti-tamper do <code>ValidationBlockerOverlay</code> corrigida</strong> (<code>fix/kyc-blocker-logout-anti-tamper-race</code>). Quando o user clicava "Sair" no blocker, o <code>MutationObserver</code> e o <code>setInterval</code> que protegem o overlay disparavam <code>window.location.reload()</code> no momento em que o React desmontava o componente em resposta ao logout — causava reload espúrio. Novo <code>isLoggingOutRef</code> desarma os dois antes de o overlay sumir e o cleanup do effect reseta <code>armed</code> pra evitar re-armar em strict-mode.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="favoritos--carousel--logo">Favoritos / Carousel / Logo<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#favoritos--carousel--logo" class="hash-link" aria-label="Link direto para Favoritos / Carousel / Logo" title="Link direto para Favoritos / Carousel / Logo" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sheet de Favoritos hidrata o store no reload</strong> (<code>fix/favorites-hydration-on-reload</code>). O gate de fetch era <code>isFavoritesSynced(userId)</code> — flag em <code>sessionStorage</code> que sobrevive ao F5 enquanto o store Zustand não. Resultado: depois de reload o sheet abria vazio porque a flag dizia "já sincronizado" mas o store estava frio. Novo gate é <code>_isFavoritesHydrated</code> (em-memory, morre com o store). <code>markFavoritesSynced</code> permanece exportado pra observabilidade. Cobertura nova em <code>useFavoritesSync.test.ts</code> (149 linhas).</li>
<li class=""><strong>Carousel ganha <code>arrowStyle="outside"</code></strong> (<code>fix/carousel-outside-arrows-payment-method</code>) — setas como flex siblings do track (não overlay), <code>self-stretch</code> na altura da linha, theme tokens (<code>bg-bg-secondary</code>, <code>border-texts/10</code>). Disabled nos edges mantém espaço (não pula). Nova prop <code>hideArrowsOnMobile</code> esconde abaixo de <code>sm</code>. Aplicado no <code>MethodSelector</code> (depósito/saque) e na <code>GamesFilterBar</code> — nas duas, as setas pequenas competiam por espaço com o conteúdo.</li>
<li class=""><strong>Modal de Oferta Relâmpago FTD desativado nas brands 7k-bet-br, cassino-bet-br e vera-bet-br</strong> (<code>hotfix/disable-ftd-offer-modal-vera-cassino-7k</code>). <code>ftdOffer.enabled = false</code> faz o <code>FtdOfferProvider</code> virar pass-through (não monta hook, modal central, widget flutuante nem story thumb). Config preservada pra reativação rápida quando marketing pedir.</li>
<li class=""><strong><code>cassino-bet-br</code> mostra logo compacto (símbolo apenas) no header mobile autenticado</strong> (<code>fix/cassino-logo-responsive</code>). Reaproveita o gate <code>showCompactMobileLogo</code> (<code>hydrated &amp;&amp; isAuthenticated</code>) que <code>vera-bet-br</code> já usa. Novo asset <code>logo-icon.png</code> (32×32) declarado em <code>theme/sizes.ts</code> via <code>logo.mobile.compactSrc</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>vera-bet-br</code></strong> — <code>ftdOffer.enabled = false</code> (hotfix), <code>showSidebarBettingRules: true</code>, novo <code>extra-legal-links.ts</code> com Denúncias/Privacidade/Ouvidoria.</li>
<li class=""><strong><code>cassino-bet-br</code></strong> — <code>theme/sizes.ts</code> ganha <code>logo.mobile.compactSrc</code> apontando pro novo <code>public/assets/brand/logo-icon.png</code>. SEO <code>casino.category:todos-os-jogos</code> adicionado em <code>page-descriptions.server.ts</code>.</li>
<li class=""><strong><code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>vera-bet-br</code>, <code>state77-com</code>, <code>pt-state77-com</code>, <code>ph-state77-com</code>, <code>fi-7k-bet</code>, <code>ng-7k-bet</code>, <code>pb-bet</code>, <code>rj-bet</code>, <code>betpontobet-bet-br</code>, <code>casateste-com</code>, <code>x2b-bet</code></strong> — flag <code>showSidebarBettingRules</code> adicionada em todas (default <code>false</code> exceto nas três acima) pra acompanhar o file-replacement do <code>features.ts</code> e evitar <code>undefined</code> silencioso. SEO override <code>casino.category:todos-os-jogos</code> propagado em todos os 13 overrides de <code>page-descriptions.server.ts</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/i18n</code></strong> — chave nova <code>gender</code> em <code>user.json</code> nos 4 locales ativos (<code>pt-br</code>, <code>pt</code>, <code>es</code>, <code>en</code>) — alimenta o display read-only do gênero no novo <code>InfosSection</code>.</li>
<li class=""><strong><code>@cactus-agents/api-client</code></strong> e <strong><code>@cactus-agents/platform-cache</code></strong> — 16 warnings pré-existentes do biome saneados (<code>packages/api-client/src/server.ts</code>, <code>packages/platform-cache/src/engine.ts</code>) + ajustes em <code>biome.json</code>. Sem mudança funcional.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ops--infra">Ops / Infra<a href="https://docs.c4ctus.com/changelog/2026/05/05/daily#ops--infra" class="hash-link" aria-label="Link direto para Ops / Infra" title="Link direto para Ops / Infra" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>platform-cache</code></strong> — <code>gameListPage</code> adicionado à configuração de cache pra suportar as listagens paginadas do hook híbrido novo (<code>useHybridGamesList</code> consome <code>/api/games/{category,provider}/:slug</code>).</li>
<li class=""><strong><code>sitemap</code> cache policy</strong> atualizada no front-ops pra acompanhar o split de sub-sitemaps.</li>
<li class=""><strong>Performance defaults Chile</strong> ajustados como configuração padrão da stage.</li>
</ul>]]></content:encoded>
            <category>analytics</category>
            <category>sitemap</category>
            <category>seo</category>
            <category>deposit</category>
            <category>sidebar</category>
            <category>kyc</category>
            <category>performance</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 04/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/04/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/04/daily</guid>
            <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Jornada FTD — Refatoração Completa e Novas Brands]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="jornada-ftd--refatoração-completa-e-novas-brands">Jornada FTD — Refatoração Completa e Novas Brands<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#jornada-ftd--refatora%C3%A7%C3%A3o-completa-e-novas-brands" class="hash-link" aria-label="Link direto para Jornada FTD — Refatoração Completa e Novas Brands" title="Link direto para Jornada FTD — Refatoração Completa e Novas Brands" translate="no">​</a></h2>
<ul>
<li class=""><strong>D0 e D1 desacoplados</strong> — antes, ativar o template <code>vera-legacy</code> do D0 (cashback) ligava implicitamente o modal de anúncio do D1 (check-in). Agora cada brand habilita as três etapas independentemente: <code>ftdCashback.enabled</code>, <code>ftdCheckin.enabled</code> e a flag nova <code>ftdCheckin.announcement.enabled</code>. Quatro combinações possíveis — só D0, só D1, ambos ou nenhum.</li>
<li class=""><strong>Copy e assets configuráveis por brand no template <code>vera-legacy</code></strong> — <code>FtdCashbackConfig</code> ganhou <code>copy.{firstBonusToastTitle,cashbackModalTitle,cashbackModalDescriptionHtml,cashbackModalCtaText}</code> + <code>templateAssets.{firstBonusToastImage,cashbackModalImage}</code>. <code>FtdCheckinConfig</code> ganhou <code>announcement.copy.{title,descriptionHtml,ctaText}</code> + <code>templateAssets.image</code>. Todos opcionais — fallback é o copy PT-BR + CDN da Vera (zero regressão).</li>
<li class=""><strong>16 tokens de tema novos para o template e o toast</strong> — <code>ftd-offer.template-{shell-bg,title-text,title-shadow,image-bg-from,image-bg-to,highlight-bg,highlight-border,highlight-text,cta-bg,cta-text}</code> e <code>ftd-offer.toast-{bg-from,bg-to,border,icon-bg-from,icon-bg-to,icon-border}</code>. <code>FtdOfferModalTemplate</code> e <code>FtdOfferInGameToast</code> agora leem essas cores via CSS custom properties inline (Tailwind JIT não consegue gerar classes a partir de tokens dinâmicos).</li>
<li class=""><strong><code>7k-bet-br</code> ativa a jornada completa</strong> — D0 cashback, D1 check-in (com modal de anúncio "Garanta sua diversão!") e oferta-relâmpago pré-FTD, todos via template unificado. Quatro assets brand-specific da CDN do 7k substituem os fallbacks da Vera (toast, modal cashback, anúncio D1 e modal de oferta).</li>
<li class=""><strong><code>cassino-bet-br</code> ativa a jornada completa</strong> — paleta + assets próprios, <code>brand_id: 2</code> no Dark Verifier/Freedom (paridade com <code>useFtdCashback.ts:52</code> do legado), kill-switches <code>feFtdD0Cassino</code> / <code>feFtdD1Cassino</code>, tabela <code>bonusTiers</code> de 35 níveis portada verbatim do legado (R$ 5 → R$ 3000+, cap em R$ 800), <code>autoDepositModal</code> abre o drawer de depósito automaticamente no login quando saldo ≤ R$ 0,10. Inclui lista de jogos elegíveis em <code>cashback/eligible-games.ts</code>.</li>
<li class=""><strong><code>cl-bet7k-com</code> ativa D0 + D1 com identidade chilena</strong> — <code>brandId: 4</code>, kill-switch <code>feFtdD07KCl</code> (compartilhado D0/D1), tabela <code>bonusTiers</code> de 11 níveis em CLP (CLP 850 → CLP 510 000, cap CLP 136 000), 6995 IDs elegíveis portados 1:1 do legado, copy em espanhol chileno ("¡Sigue jugando para ganar un cashback!"). Moeda renderiza sem decimais (<code>$50.000</code>) via <code>useFormatMoney()</code> + <code>CountryConfig.displayDecimalDigits</code>. D1 começa desabilitado no primeiro rollout.</li>
<li class=""><strong>STT 2 (saldo bônus) com kill-switch remoto</strong> — <code>saldoBonus.featureFlags?: { legacy?, configcat? }</code> permite desligar a STT 2 instantaneamente via ConfigCat ou FF legado sem deploy. O hook <code>useFtdCashbackFlow</code> computa <code>saldoBonusRemoteKillSwitchPass</code> (closed-by-default enquanto a API está em voo, mesma semântica do D0/D1). Cassino declara <code>feFtdSaldoBonus</code> + <code>feFtdSaldoBonusCassino</code>.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="vera--header-footer-e-visual">Vera — Header, Footer e Visual<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#vera--header-footer-e-visual" class="hash-link" aria-label="Link direto para Vera — Header, Footer e Visual" title="Link direto para Vera — Header, Footer e Visual" translate="no">​</a></h2>
<ul>
<li class=""><strong>Header reescrito</strong> — single-row, scroll-aware, cluster com skew. Novos componentes <code>CtaButtonVera</code> e <code>CtaButtonSecondary</code>, <code>HeaderUserAreaVera</code>, hook <code>useScrolledPast</code>. <code>GameCardVera</code> agora usa <code>CtaButton id="game-card-play" visualOnly</code> em vez de <code>&lt;span&gt;</code> hardcoded — herda variant <code>cta-vera</code> da brand e mantém consistência com Cadastrar/Depositar. Overlay de hover ganhou <code>pointer-events-none</code> para preservar o click do <code>&lt;Link&gt;</code> pai.</li>
<li class=""><strong>Footer full-width</strong> — nova flag <code>structure.footerFullWidth</code> (default <code>false</code>); quando ativa, o footer escapa do <code>contentContainer</code> e renderiza como sibling viewport-wide nos shells <code>header-top</code> e <code>split-shell</code>. Vera ativa a flag para alinhar com o header.</li>
<li class=""><strong>Tom de verde alinhado entre header e footer</strong> — <code>bg-primary</code> da Vera passou de <code>#0e1d21</code> para <code>#003628</code> (= <code>header.main-bg</code>), <code>button-bg</code> foi para <code>#026455</code> (= <code>header.balance-bg</code>) para manter contraste das pílulas mobile.</li>
<li class=""><strong>Gradient unificado em sidebar e maiores ganhos</strong> — <code>SidebarTitledSection</code> e <code>WinsListHeader</code> (Vera) trocaram <code>primary</code> (<code>#06e05b</code> neon) por <code>header-balance-bg</code> (<code>#026455</code> teal-green), eliminando dissonância visual com os cards do "Pagou Muito". Outras brands não consomem essas surfaces.</li>
<li class=""><strong>Footer sem grupo "amigos verabet"</strong> — campaign API e widget atualizados para remover o grupo descontinuado; widget <code>campaign-widget</code> continua ativo para os demais grupos.</li>
<li class=""><strong>Indique e Ganhe</strong> — porta da página <code>/games/indique-ganhe</code> do legado para o LP system: rota canônica <code>/lp/indique-ganhe</code> com redirect 301 da URL antiga (preserva backlinks externos do Telegram, Instagram e WhatsApp). Inclui banner CDN, hero com CTA "Acessar meu código" → <code>/user/refers</code>, seção "Como funciona" com 3 passos, grid de 6 jogos populares (família Fortune do PG Soft + Touro/Macaco Sortudo da Pragmatic) e CTA secundário no fim.</li>
<li class=""><strong>Link de download canônico do app</strong> — novo override <code>brand.appearance.links</code> aplicado em <code>DefaultLayout</code> antes do <code>BrandProvider</code> montar. Vera força <code>https://play.google.com/store/apps/details?id=br.bet.vera.twa.apk&amp;hl=pt_BR</code> para todos os consumers (<code>FooterBadges</code>, <code>SidebarBottomItems</code>, <code>useAppInstall</code>, <code>InstallAppTooltip</code>) — corrige URLs stale vindas do BFF. Brands sem override têm zero overhead.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="analytics--view_item_list-e-gtm">Analytics — view_item_list e GTM<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#analytics--view_item_list-e-gtm" class="hash-link" aria-label="Link direto para Analytics — view_item_list e GTM" title="Link direto para Analytics — view_item_list e GTM" translate="no">​</a></h2>
<ul>
<li class=""><strong>Tracking GA4 <code>view_item_list</code> no primeiro scroll-into-view</strong> — novo hook <code>useOnFirstScrollIntoView</code> instrumenta <code>GameSection</code>, <code>TournamentsSection</code>, <code>WinsCombinedWidget</code>, <code>HomeBannerCarousel</code>, <code>MainLeaguesWidget</code>, <code>RecommendedGamesBento</code> e <code>TopGamesSection</code>. <code>item_list_name</code> segue o título da row (Vera: "Jogos de Cassino Recomendados", fallback "Jogos Recomendados"). Items mapeados pelo <code>banner.action</code> + <code>banner.alt</code>.</li>
<li class=""><strong>Helpers de métricas centralizados</strong> — novo <code>app/utils/metrics/index.ts</code> + ampliação de <code>app/utils/metrics/gtm.ts</code> para suportar o evento.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cookie-consent--modo-cosmético">Cookie Consent — Modo Cosmético<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#cookie-consent--modo-cosm%C3%A9tico" class="hash-link" aria-label="Link direto para Cookie Consent — Modo Cosmético" title="Link direto para Cookie Consent — Modo Cosmético" translate="no">​</a></h2>
<ul>
<li class=""><strong>Nova flag <code>cookieConsentCosmeticOnly</code></strong> — quando <code>true</code>, o banner continua renderizando visualmente (cumprir compliance), mas NÃO gateia tracking: o <code>initGTM</code> injeta <code>acceptAll</code> como Google Consent Mode v2 default (zero janela cega entre carregamento do GTM e clique), e qualquer caminho de clique (Aceitar / Recusar / Salvar personalizado) converge para <code>acceptAll</code> no cookie <code>cookies_consent</code> e no signal de update. Os toggles do painel "Personalizar" continuam visíveis mas sem efeito real. Pré-requisito: <code>brand.features.cookieConsentPopup</code> (BFF) precisa estar <code>true</code>. Ativada em <code>7k-bet-br</code>, <code>fi-7k-bet</code>, <code>ng-7k-bet</code>, <code>cl-bet7k-com</code>, <code>cassino-bet-br</code> e <code>vera-bet-br</code> por decisão conjunta de marketing + compliance. Demais brands preservam o comportamento legado (consent real respeitado).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sports--gate-de-validação-real-time">Sports — Gate de Validação Real-Time<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#sports--gate-de-valida%C3%A7%C3%A3o-real-time" class="hash-link" aria-label="Link direto para Sports — Gate de Validação Real-Time" title="Link direto para Sports — Gate de Validação Real-Time" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo componente <code>ValidationContextGate</code></strong> — gate que bloqueia o iframe de esportes quando o usuário tem KYC pendente, lendo o contexto <code>sports</code> do <code>fetchAllValidations()</code> em real-time.</li>
<li class=""><strong>Bottom nav mobile não some mais quando o modal de validação é dispensado</strong> — <code>setFullscreenOpen(true)</code> foi movido para dentro de <code>startGameRequest</code> em <code>GameIframe.tsx</code>. Antes, ao clicar em "Jogar" e fechar o modal de KYC sem completar, o estado <code>gameFullscreenOpen</code> ficava preso em <code>true</code> e a bottom nav permanecia escondida até a próxima navegação.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="detail-page-de-jogos--stats-alinhados-com-a-home">Detail Page de Jogos — Stats Alinhados com a Home<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#detail-page-de-jogos--stats-alinhados-com-a-home" class="hash-link" aria-label="Link direto para Detail Page de Jogos — Stats Alinhados com a Home" title="Link direto para Detail Page de Jogos — Stats Alinhados com a Home" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>high-payers</code> vira fonte autoritativa para valores monetários</strong> — o widget "Pagou muito" (home) consome <code>/bff/games/game-high-payers-dl</code> (<code>totalPaid</code> por janela), enquanto o detail page consumia <code>/bff/games/statistics-dl</code> (<code>last24Hours.wins</code>, <code>last1Hour.wins</code>). As duas APIs têm pipelines de cálculo independentes no BI e divergiam sutilmente. Agora o <code>getStats(slug)</code> server-side, depois de buscar stats, consulta <code>getHighPayers()</code> (já cacheado por brand via <code>platform-cache</code>) e sobrescreve <code>last24Hours.wins</code> e <code>last1Hour.wins</code> quando o slug está no top-N. Jogos fora do top-N caem no fallback. Custo zero de chamada extra. Try/catch silencioso garante que o detail page nunca quebra por isso.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="proteção-de-conta--acordeões">Proteção de Conta — Acordeões<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#prote%C3%A7%C3%A3o-de-conta--acorde%C3%B5es" class="hash-link" aria-label="Link direto para Proteção de Conta — Acordeões" title="Link direto para Proteção de Conta — Acordeões" translate="no">​</a></h2>
<ul>
<li class=""><strong>Botões de "Alternativas à exclusão" agora abrem o acordeão alvo</strong> — port do <code>forceOpenAccordion</code> do legado Vera. Antes os 4 botões (Pausar 7d, Pausar 30d, Manter com limite aposta, Manter com limite depósito) só faziam <code>scrollIntoView</code>; o usuário chegava lá e tinha que clicar de novo. Agora um novo store <code>useProtectionStore</code> com <code>forceOpenAccordion(id)</code> é ouvido pelo <code>LimitAccordion</code>, que auto-abre + scrolla quando o id bate. Store reseta em 1s para permitir re-clicar no mesmo botão. Adicionado também o botão "Falar com suporte" quando <code>hasLiveSupport</code> (paridade com legado).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--mobile-bottom-nav">Cassino — Mobile Bottom Nav<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#cassino--mobile-bottom-nav" class="hash-link" aria-label="Link direto para Cassino — Mobile Bottom Nav" title="Link direto para Cassino — Mobile Bottom Nav" translate="no">​</a></h2>
<ul>
<li class=""><strong>Variant <code>mobile-nav-fab-center</code> restaurada no <code>cassino-bet-br</code></strong> — após experimento com <code>mobile-nav-illustrated-tabs</code>, o cassino volta para o FAB "Depositar" flutuante no centro. Ordem: Menu · Cassino · [FAB] · Cassino Ao Vivo · Esportes. Ícones herdam <code>INTENT_DEFAULTS</code> (Dices, MonitorPlay, Soccer), paths via <code>routeHref()</code> respeitam overrides da brand.</li>
<li class=""><strong>Refinamentos no primitive compartilhado</strong> — <code>FabButton</code> sai menos do topo da barra (<code>translate-y-1/4</code> ancorado em <code>-top-2</code>), label "Depositar" em <code>font-bold</code>, pulse suave (<code>animate-promo-pulse</code>) aplicado só no ícone interno. <code>NavTile</code> ganhou <code>gap-1.5</code> (de <code>gap-0.5</code>) para mais respiro entre ícone e label. Afeta todos os variants do mobile bottom nav.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="twa--digital-asset-links">TWA — Digital Asset Links<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#twa--digital-asset-links" class="hash-link" aria-label="Link direto para TWA — Digital Asset Links" title="Link direto para TWA — Digital Asset Links" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>assetlinks.json</code> restaurado para <code>7k-bet-br</code>, <code>cassino-bet-br</code> e <code>vera-bet-br</code></strong> — portado dos repos Vue legados para <code>overrides/&lt;brand&gt;/public/.well-known/</code>. Sem esses arquivos os TWAs Android perdem modo url-bar-hidden, Smart Lock (<code>common.get_login_creds</code>) e a permissão <code>common.use_as_origin</code> do Smartico. Placeholders dos 10 repos <code>base-*</code> legados (com <code>package_name</code> vazio) intencionalmente não foram portados — falham na validação DAL do Google e seriam piores que 404. Smoke-tested via <code>pnpm dev --&lt;brand&gt; --port 5185</code>; <code>state77</code> confirmou 404 como negative control.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>vera-bet-br</code> — campaign API</strong> — atualização da configuração do <code>strategy.ts</code> (com testes para Mini Games standalone) e ajuste do trigger de registro do mini-game. Widget <code>campaign-widget</code> permanece ativo após remoção do grupo descontinuado.</li>
<li class=""><strong><code>vera-bet-br</code> — promocode</strong> — refactor removendo o estado de feedback e mensagens inline da <code>PromocodePageContent</code> em preparação para integração via SDK.</li>
<li class=""><strong><code>vera-bet-br</code> — UI tweaks</strong> — ajustes no badge dos cards de torneio (<code>TournamentCardClassic</code>/<code>Stacked</code>), label dos top-games, hint de quick-access e visual do gift no campaign-widget.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/validations</code> — cobertura do gate de esportes</strong> — três casos novos em <code>evaluate.test.ts</code> validam <code>contexts.sports = { allowed: false, pendingModule: "kyc" }</code> quando a brand declara <code>sports.active=true, modules=["kyc"]</code> e o usuário não tem KYC aprovado. Inclui também: liberado pós-login com KYC aprovado e <code>undefined</code> quando a brand opta out via <code>sports.active=false</code>. Regressão necessária para o novo <code>ValidationContextGate</code> do base.</li>
<li class=""><strong>Limpeza de 16 warnings do Biome no core</strong> — sem mudança funcional. <code>noNonNullAssertion</code> (13 casos em testes do <code>accounts</code>) silenciado via override de <code>biome.json</code> para <code>**/__tests__/**</code> (<code>!</code> é uso legítimo em tests). <code>noExplicitAny</code> em <code>platform-cache/engine.ts</code> resolvido trocando <code>Map&lt;string, Promise&lt;CacheResult&lt;any&gt;&gt;&gt;</code> por <code>CacheResult&lt;unknown&gt;</code> (call-sites já fazem <code>as Promise&lt;CacheResult&lt;T&gt;&gt;</code>). Suppressions órfãos removidos em <code>api-client/server.ts</code>. Verificado: <code>pnpm lint</code>, <code>pnpm check</code>, <code>pnpm build</code> (20/20) e <code>pnpm test</code> (409+ casos) todos passando.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ops">Ops<a href="https://docs.c4ctus.com/changelog/2026/05/04/daily#ops" class="hash-link" aria-label="Link direto para Ops" title="Link direto para Ops" translate="no">​</a></h2>
<ul>
<li class=""><strong>Branches default padronizadas para <code>main-ana</code></strong> — múltiplos workflows de deploy do <code>front-ops</code> ajustados para consumir <code>main-ana</code> em vários ambientes (paridade da convenção de naming).</li>
</ul>]]></content:encoded>
            <category>ftd</category>
            <category>vera</category>
            <category>cassino</category>
            <category>analytics</category>
            <category>consent</category>
            <category>sports</category>
            <category>validation</category>
        </item>
        <item>
            <title><![CDATA[Changelog — 03/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/03/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/03/daily</guid>
            <pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[CASSINOMODE — frontcustom removido (breaking change)]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino_mode--front_custom-removido-breaking-change">CASSINO_MODE — <code>front_custom</code> removido (breaking change)<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#cassino_mode--front_custom-removido-breaking-change" class="hash-link" aria-label="Link direto para cassino_mode--front_custom-removido-breaking-change" title="Link direto para cassino_mode--front_custom-removido-breaking-change" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/games</code> 2.0.0 — <code>CASSINO_MODE=front_custom</code> deixou de existir.</strong> Antes desta data havia 3 modes (<code>legacy</code>, <code>api_new</code>, <code>front_custom</code>); agora são apenas 2 (<code>legacy</code>, <code>api_new</code>). O <code>front_custom</code> era usado só pelo sandbox interno <code>stage1</code> — stage1 migrou pra <code>legacy</code> e nenhuma brand de cliente consumia o mode. Em vez de manter código morto como surface area pública, o package removeu de vez.</li>
<li class=""><strong>Exports públicos removidos:</strong> <code>createCustomCategoriesGamesFromClient</code>, <code>createCustomCategoriesGamesService</code>, <code>CustomCategoriesGamesOptions</code>, <code>CustomCategoryDef</code>, <code>CustomCategoriesConfig</code>, <code>CustomCategorySource</code>, <code>filterExplicitCategory</code>, <code>resolveCategoryGames</code>, <code>customCategoryDefToRaw</code>. Tipo <code>LegacyHomeRowConfig.type</code> deixou de aceitar <code>"custom-category"</code>.</li>
<li class=""><strong>Sobrevive intacto:</strong> <code>HomeRow.type</code> runtime ainda inclui <code>"custom-category"</code> / <code>"custom-section"</code> — esses são emitidos pelo BFF page pipeline em <code>legacy</code> e <code>api_new</code> e continuam funcionando. Curadoria por overlay (<code>categories.personalize.ts</code>) também continua válida nos dois modes.</li>
<li class=""><strong>Refactor interno:</strong> <code>src/custom-categories.ts</code> virou <code>src/stats-ordering.ts</code>; <code>orderByStats</code>, <code>CustomCategoryOrderBy</code>, <code>DisplayPriority</code>, <code>StatDisplayKey</code> continuam exportados do root do package (API pública preservada).</li>
<li class=""><strong>Documentação atualizada em conjunto:</strong> <code>docs-internal/template/env-vars.md</code>, <code>docs-internal/template/games.md</code>, <code>docs-internal/forking/override-files.md</code>, <code>docs-external/customization/games.md</code> e <code>docs-external/deployment/environment.md</code> reescritos para descrever apenas os 2 modes restantes. <code>CASSINO_FRONT_CUSTOM_BASE</code> removido das tabelas de env vars; regra de "paridade obrigatória" pra <code>.new.ts</code>/<code>.custom.ts</code> foi droppada (não existem mais). Guia de personalização aponta agora pra <code>categories.personalize.ts</code> em vez de <code>categories.custom.ts</code>.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="busca--diacritic-insensitive">Busca — Diacritic-insensitive<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#busca--diacritic-insensitive" class="hash-link" aria-label="Link direto para Busca — Diacritic-insensitive" title="Link direto para Busca — Diacritic-insensitive" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>filterGames</code> agora ignora diacríticos simetricamente.</strong> Buscar <code>"aviao"</code> (sem acento) retorna <code>"aviãozinho"</code>; <code>"cabeca"</code> bate com <code>"cabeça"</code>; cedilha, til, agudo, grave, circunflexo, trema cobertos sem custo de manutenção por caractere. Cobre português, espanhol, francês, alemão e qualquer diacrítico do bloco combining Unicode.</li>
<li class=""><strong>Estratégia canônica:</strong> <code>normalize("NFD")</code> separa caractere base do diacrítico, regex <code>[\u0300-\u036f]</code> remove os combining marks. <code>normalize</code> é nativo C++ em V8 — overhead de ~5ms pra ~10k normalizações por query (2k jogos × 5 tags), imperceptível.</li>
<li class=""><strong>Helper <code>foldForSearch(s)</code> exportado</strong> pelo package <code>@cactus-agents/games</code> — consumido pelo base nas páginas de categoria/provedor/mini-games onde a busca é client-side local (fora do <code>filterGames</code>), garantindo paridade com a search global.</li>
<li class=""><strong>9 testes novos</strong> cobrindo: sem acento → com acento, com acento → sem acento, cedilha nos dois lados, til em nome + provider, case + diacrítico combinados, filter <code>tags</code> separado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="page-rows--multi-página-vindo-do-bff">Page Rows — Multi-página vindo do BFF<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#page-rows--multi-p%C3%A1gina-vindo-do-bff" class="hash-link" aria-label="Link direto para Page Rows — Multi-página vindo do BFF" title="Link direto para Page Rows — Multi-página vindo do BFF" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo método <code>getPageRows(page)</code> na <code>GamesService</code></strong> bate em 3 endpoints canônicos curados por brand via <code>ORIGIN_DOMAIN</code>: <code>GET /casino-games/page/home</code>, <code>/casino-games/page/cassino</code>, <code>/casino-games/page/cassino_live</code>. Substitui a necessidade de hardcoded row configs no base — backend vira fonte única de ordem, widgets e curadoria per-brand.</li>
<li class=""><strong><code>PageSlug = "home" | "cassino" | "cassino_live"</code></strong> exportado. <code>createGamesService</code> (api_new) implementa direto; <code>createLegacyGamesService</code> delega <code>"home"</code> pro <code>getHome()</code> legado e dá throw nos outros (<code>rowsConfig[page]</code> cobre os demais em legacy mode).</li>
<li class=""><strong>Non-breaking em wire-level:</strong> bases antigos bumpando o package continuam chamando os mesmos endpoints. <code>getHome()</code> segue intocado e marcado <code>@deprecated</code> com TODO de remoção pra quando dev → main/stage/prod for mergeado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="tracking-de-marketing--deposit-ganha-paridade-total">Tracking de Marketing — Deposit ganha paridade total<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#tracking-de-marketing--deposit-ganha-paridade-total" class="hash-link" aria-label="Link direto para Tracking de Marketing — Deposit ganha paridade total" title="Link direto para Tracking de Marketing — Deposit ganha paridade total" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>POST /wallet/add-credit</code></strong> agora forwarda <strong>todo o sinal de atribuição</strong> que o legado Vue mandava, não só o subset que o Register já enviava:<!-- -->
<ul>
<li class=""><code>src</code> propagado no deposit (antes era register-only);</li>
<li class=""><code>subid</code> (affiliate sub-id);</li>
<li class=""><code>utmExtras</code> — wildcard <code>utm_*</code> (utm_oferta, utm_term, utm_placement, …) emitido <strong>flat</strong> no body;</li>
<li class="">cada clicked-id como campo individual: <code>gclid</code>, <code>fbclid</code>, <code>ttclid</code>, <code>gbraid</code>, <code>wbraid</code>, <code>msclkid</code>, <code>mgclid</code>, <code>tclid</code>, <code>kwclid</code>, <code>obclid</code> (paridade 7k/vera-new);</li>
<li class="">par derivado <code>mediaSource</code> / <code>mediaClid</code> (paridade bullsbet/jogao/mcgames).</li>
</ul>
</li>
<li class=""><strong>Dois schemas em paralelo:</strong> os dois shapes (campos individuais E <code>media_source</code>+<code>media_clid</code>) são forwardados juntos porque diferentes BFF handlers de brand leem um ou outro. Garante atribuição independente do handler ativo.</li>
<li class=""><strong>Backward-compatible:</strong> todos os campos novos são opcionais — callers existentes mantêm o payload anterior intacto.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="vera-bet-br--paridade-visual-com-legado">Vera-bet-br — Paridade visual com legado<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#vera-bet-br--paridade-visual-com-legado" class="hash-link" aria-label="Link direto para Vera-bet-br — Paridade visual com legado" title="Link direto para Vera-bet-br — Paridade visual com legado" translate="no">​</a></h2>
<ul>
<li class=""><strong>Refatora pesada</strong> (<code>vera-layout-legacy-parity</code>) aproxima a vera-bet-br do legado preservando a dinamicidade e reuso por config. Todas as flags novas são opt-in, zero impacto nas outras 13 brands.</li>
<li class=""><strong>Infra nova reutilizável:</strong>
<ul>
<li class=""><code>SidebarLayoutConfig.scrollBehavior: "sticky" | "page"</code> — sidebar pode rolar junto com a página (vera) ou continuar sticky (default das demais);</li>
<li class=""><code>LayoutStructureConfig.contentContainer: "full" | "contained"</code> — linha sidebar+main+rightPanel centralizada em <code>max-w-content</code> (vera) ou full-width viewport (default);</li>
<li class=""><code>featuresConfig.homeRightPane: "wins-list" | null</code> — home ganha coluna direita em xl+ via <code>HomeWinsRightColumn</code> (não-sticky, dentro do flow);</li>
<li class=""><code>featuresConfig.homeRightPaneAboveSplit: number</code> — quantas rows sobem full-width no topo antes do split 2-col começar (banners fora do split);</li>
<li class=""><code>featuresConfig.showMobileSidebarToggle: boolean</code> — hamburger também no mobile no <code>HeaderDefault</code> (vera=true).</li>
</ul>
</li>
<li class=""><strong>Novo variant <code>sidebar-sectioned-titled</code></strong> — headers com gradient glow primário + pill "VER TODOS" + items com decoração diagonal + suporte a "titled" style no <code>SidebarBottomItems</code> (Outros Links).</li>
<li class=""><strong>Cleanup:</strong> variante <code>right-panel-winners</code> removida (única consumer era vera, agora usa <code>HomeWinsRightColumn</code>); <code>RightPanelVariantKey</code> vira <code>never</code> mas a slot infra fica intacta pra futuras brands. <code>WinsListHeader</code> + <code>WinListCard</code> extraídos pra <code>components/home/wins-shared.tsx</code>.</li>
<li class=""><strong>Propagação obrigatória:</strong> <code>homeRightPane: null</code> + <code>homeRightPaneAboveSplit: 0</code> adicionados em todas as 13 outras brands seguindo a regra de overrides file-replacement (sem deep-merge).</li>
<li class=""><strong>Outros ajustes vera:</strong> novo <code>vera</code> <code>SectionTitle</code> variant replicando os títulos de row do legado; logo compacto V-only no mobile pequeno (<code>&lt;640px</code>); <code>GameCard</code> variant <code>vera</code> específico; hamburger movido do header pro bottom-nav (drop V spike); top-games card com 2 linhas mínimas reservadas pro título evitar reflow.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino-bet-br--header--sidebar-repaginados">Cassino-bet-br — Header + sidebar repaginados<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#cassino-bet-br--header--sidebar-repaginados" class="hash-link" aria-label="Link direto para Cassino-bet-br — Header + sidebar repaginados" title="Link direto para Cassino-bet-br — Header + sidebar repaginados" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>sidebar-tabbed</code> — novo variant</strong> com tabs casino/sports no topo, auto-seleção route-aware, e square colapsável que alterna tabs no clique (ícones <code>mdi/cards</code> + <code>mdi/soccer</code> + dots indicator). <code>SidebarPillItem</code> + <code>SidebarBottomItems</code> ganham prop opt-in <code>hideChevron</code> (default mantido nos outros variants). Brand activa via <code>composition.ts</code> em <code>split-shell</code> + <code>sidebar-tabbed</code>, matchando a macro-arquitetura do 7k.</li>
<li class=""><strong><code>header-split-bar</code> + <code>header-secondary-flat</code> — dois headers genéricos novos.</strong> O split-bar tem logo+hamburger à esquerda da row centralizada, busca compacta 320px borderless à direita (h-9 igual aos botões, expande pra 380px no focus via <code>focus-within</code>), quick-access menu (+), 68px desktop height. O secondary-flat é uma submenu row transparente com hairline bottom border full-width, pills com fill sutil em rest e <code>bg-primary/20</code> no active (sem ring/glow), <code>isSecondaryNavLinkActive</code> com longest-prefix-wins dedup.</li>
<li class=""><strong>Wiring cassino-bet-br:</strong> topbar notification desligada; widget <code>search-field</code> removido das home rows (header agora owna a busca); botões Entrar (ghost cyan) e Cadastrar (filled blue) com hierarquia clara; novo <code>header-secondary-nav.ts</code> (5 casino + 6 sports) + <code>quick-access-menu.ts</code> (6 intents, paridade 7k); theme <code>sidebar.button-bg</code> migra de gray <code>#3f4142</code> pra navy <code>#18436e</code>.</li>
<li class=""><strong>Mobile-nav illustrated tabs:</strong> novo variant <code>mobile-nav-illustrated-tabs</code> com tabs ilustradas, ativado na cassino-bet-br.</li>
<li class=""><strong>Sidebar-buttons CTA Mini Games Diários</strong> — colored variant ganha emoji icon slot e nova strategy específica.</li>
<li class=""><strong>Pix — bancos individuais:</strong> <code>disable Pix grouping</code> na cassino-bet-br pra mostrar cards individuais por banco em vez de agrupar.</li>
<li class=""><strong>Login modal:</strong> texto legal específico cassino-bet-br adicionado.</li>
<li class=""><strong>Top Ganhos:</strong> seção migrada do legado com card style brand-configurable.</li>
<li class=""><strong>Fix <code>/buscar</code>:</strong> <code>InternalNavBar</code> movido pra fora do <code>max-w-content</code> wrapper em <code>/buscar</code> e <code>/buscar/all</code> — antes ficava clipped (bottom border full-width recortada). Fix é brand-agnostic, beneficia todas as brands.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="vera-ftd--paridade-total-com-legado">Vera FTD — Paridade total com legado<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#vera-ftd--paridade-total-com-legado" class="hash-link" aria-label="Link direto para Vera FTD — Paridade total com legado" title="Link direto para Vera FTD — Paridade total com legado" translate="no">​</a></h2>
<ul>
<li class=""><strong>Anúncio "Garanta sua diversão!" (variant <code>checkin</code>)</strong> — novo <code>FtdCheckinAnnouncementProvider</code> global, gated por <code>ftdCashback.modalVariant === "vera-legacy"</code> + <code>useFtdCheckinAccess().canAccess</code>. Once-per-session via sessionStorage; logout limpa flag; CTA "FAZER CHECK-IN" abre o panel diário existente.</li>
<li class=""><strong>Flash-offer com visual legacy vera (<code>flash-offer</code>)</strong> — <code>FtdOfferConfig.modalVariant?: "default" | "vera-legacy"</code> opt-in pra renderizar <code>FtdOfferModalContainer</code> + thumb/trigger paridade pixel-perfect. Ports 1:1 do legado: <code>FtdOfferFloatingTrigger</code> (de <code>FloatingTrigger.vue</code>) e <code>FtdOfferStoryThumbLegacy</code> (cronômetro CDN + pill verde sobreposta) usado em <code>widgetMode: "stories-first"</code>.</li>
<li class=""><strong>Config vera 1:1 com <code>Login.vue</code> legado:</strong> <code>ftdOffer</code> enabled + <code>vera-legacy</code> + <code>stories-first</code> + storyThumb CDN; <code>autoDepositModal: { minBalanceCents: 10, intervalMs: 0 }</code> replicando <code>walletReal &lt; 0.1</code> + sessionStorage <code>showDepositModalHome</code>.</li>
<li class=""><strong>Util <code>app/utils/ftd-offer.ts</code></strong> — port de <code>resolveOfferVariant</code>, <code>OFFER_EVENT_MAP</code> e <code>formatOfferCountdown</code> pra orchestrator unificado futuro.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="outras-features">Outras Features<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#outras-features" class="hash-link" aria-label="Link direto para Outras Features" title="Link direto para Outras Features" translate="no">​</a></h2>
<ul>
<li class=""><strong>Página <code>/promocode</code></strong> portada do legado vera/7k.</li>
<li class=""><strong>Mobile-nav auth-gate</strong> — itens user-specific (rewards, missions, etc.) abrem o login modal em vez de navegar quando o usuário está deslogado.</li>
<li class=""><strong>SEO hreflang</strong> ativado pra 7k-bet-br, cassino-bet-br e vera-bet-br + brands adicionais (compliance SEO geral).</li>
<li class=""><strong>Favicon Google-compliant</strong> — sizes em múltiplos de 48 pra renderizar corretamente nos resultados de busca do Google.</li>
<li class=""><strong>Diamond Stories</strong> — suporte ao variant <code>diamond-neon</code> no widget de stories da home.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="fixes">Fixes<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#fixes" class="hash-link" aria-label="Link direto para Fixes" title="Link direto para Fixes" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>VerticalMarquee</code></strong> — container clamped pro <code>setHeight</code> pra loop infinito ficar seamless (antes "pulava" ao reciclar).</li>
<li class=""><strong>Sidebar titled-section</strong> — gradient escopado só pro header (antes vazava); spacing tweaks (<code>mb-4</code>, <code>w-full</code> no gradient, <code>mt-2</code> no content).</li>
<li class=""><strong>Wins trophy</strong> — top offset reduzido pra impedir crop do troféu no card.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>vera-bet-br:</strong> repaginação completa (vide seção dedicada acima) — composition <code>sidebar-sectioned-titled</code> + <code>scrollBehavior: "page"</code> + <code>contentContainer: "contained"</code>, widths 230/70px, rightPanel removido, FTD legacy parity, GameCard variant próprio, logo compacto mobile, hamburger no bottom-nav, section-title variant próprio.</li>
<li class=""><strong>cassino-bet-br:</strong> <code>split-shell</code> + <code>sidebar-tabbed</code> + <code>header-split-bar</code> + <code>header-secondary-flat</code> + <code>mobile-nav-illustrated-tabs</code>, theme <code>sidebar.button-bg</code> migrado pra navy, sidebar-buttons unificado em gradient navy (<code>#2a74bd</code> → <code>#0a2140</code>) com pattern fade-to-near-black-same-hue, Pix banks individuais, Top Ganhos card brand-configurable, login legal text.</li>
<li class=""><strong>7k-bet-br:</strong> SEO hreflang ativado.</li>
<li class=""><strong>Demais 13 brands:</strong> <code>homeRightPane: null</code> + <code>homeRightPaneAboveSplit: 0</code> propagados (regra file-replacement de overrides — Critical Rules).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/03/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/games</code> 2.0.0</strong> — breaking: remove <code>CASSINO_MODE=front_custom</code>; non-breaking: <code>getPageRows(page)</code> + <code>foldForSearch</code> + busca diacritic-insensitive em <code>filterGames</code>. Documentação cleanup em paralelo no <code>front-cactus-docs</code> (commit <code>f675d0c</code>).</li>
<li class=""><strong><code>@cactus-agents/payments</code></strong> — <code>buildDepositPayload</code> agora forwarda <code>src</code>, <code>subid</code>, <code>utmExtras</code> wildcard, todos os clicked-ids individuais e o par derivado <code>mediaSource</code>/<code>mediaClid</code>. Backward-compatible: campos novos opcionais.</li>
</ul>]]></content:encoded>
            <category>cassino-mode</category>
            <category>busca</category>
            <category>vera</category>
            <category>cassino-bet-br</category>
            <category>layout</category>
            <category>tracking</category>
            <category>brand-overrides</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 02/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/02/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/02/daily</guid>
            <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Dia pesado revamp completo da gamificação 7k (cards de missão/torneio + modal), portabilidade massiva da personalização da vera-bet-br (paridade visual com legado), fluxo FTD completo da Vera (oferta, cashback, autoDeposit, check-in), polimentos visuais da cassino-bet-br e correções de Proteção de Conta.]]></description>
            <content:encoded><![CDATA[<p>Dia pesado: 30 PRs no <code>front-web-base</code> + 4 PRs no <code>front-cactus-core</code>. Destaques: revamp completo da gamificação 7k (cards de missão/torneio + modal), portabilidade massiva da personalização da <code>vera-bet-br</code> (paridade visual com legado), fluxo FTD completo da Vera (oferta, cashback, autoDeposit, check-in), polimentos visuais da <code>cassino-bet-br</code> e correções de Proteção de Conta.</p>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="gamificação--revamp-7k">Gamificação — Revamp 7k<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#gamifica%C3%A7%C3%A3o--revamp-7k" class="hash-link" aria-label="Link direto para Gamificação — Revamp 7k" title="Link direto para Gamificação — Revamp 7k" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cards de missão e torneio</strong> completamente revisados nas variantes <code>classic</code> e <code>stacked</code> para 7k-bet-br/fi-7k-bet/ng-7k-bet, com novos visuais, badges de status e progresso refinado.</li>
<li class=""><strong>Modal de detalhe da missão</strong> (<code>MissionDetailModal</code>) ganhou layout novo com seção de prêmios, regras de elegibilidade e CTA de inscrição/reivindicação.</li>
<li class=""><strong>Tela de torneio</strong> (<code>tournaments.$id.tsx</code>) reescrita: novo <code>TournamentHero</code>, <code>RegistrationCTA</code> com estados expandidos, <code>TournamentStatsCarousel</code> e <code>tournament-internals</code> consolidados.</li>
<li class=""><strong><code>LevelsSection</code></strong> ajustada e página <code>vip/levels.tsx</code> reorganizada para refletir a hierarquia de níveis VIP do 7k.</li>
<li class=""><strong>Smartico Initializer</strong> atualizado para integrar com o novo fluxo de claim e <code>pending_claim</code> exposto pelo SDK.</li>
<li class=""><strong>Configs de brand</strong> (<code>gamification.ts</code>, <code>widgets/mission-card.ts</code>, <code>widgets/tournament-card.ts</code>) propagadas nos três overrides 7k para refletir o novo visual.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides--vera-bet-br-paridade-visual-com-legado">Brand Overrides — Vera-bet-br (paridade visual com legado)<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#brand-overrides--vera-bet-br-paridade-visual-com-legado" class="hash-link" aria-label="Link direto para Brand Overrides — Vera-bet-br (paridade visual com legado)" title="Link direto para Brand Overrides — Vera-bet-br (paridade visual com legado)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Home Vera</strong> ganhou painel de "biggest wins", menu horizontal com imagens, menu de acesso rápido e bloco de conteúdo SEO — replicando o layout do legado Nuxt (PRs #517–#520).</li>
<li class=""><strong>Top games e mobile nav</strong> com tweaks de tiles em grid e ajustes nos thumbs do destaque (PRs #523–#524); banner do meio em mobile removido (#531).</li>
<li class=""><strong>Topbar notifications desativadas</strong> para a Vera via flag de brand (#521); CDN legado configurado para servir assets de imagem da brand (#525).</li>
<li class=""><strong>Fluxo FTD completo</strong> portado do 7k para a Vera: oferta de FTD, modais de cashback legados, check-in de bônus com mock + flags e <code>autoDeposit</code> integrado ao Smartico (PRs #530, #532, #534).</li>
<li class=""><strong>Pagamentos Vera</strong> — exibição dos bancos suportados na tela de saque/depósito ajustada à identidade visual da brand (#544).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides--cassino-bet-br">Brand Overrides — Cassino-bet-br<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#brand-overrides--cassino-bet-br" class="hash-link" aria-label="Link direto para Brand Overrides — Cassino-bet-br" title="Link direto para Brand Overrides — Cassino-bet-br" translate="no">​</a></h2>
<ul>
<li class=""><strong>Top games</strong> com watermark compacto — variante visual mais discreta que reaproveita melhor o espaço do thumb (#526).</li>
<li class=""><strong>Banner da home com side CTAs</strong> — slides aceitam botões laterais opcionais e tipografia ajustada para destaque do CTA principal (#529).</li>
<li class=""><strong>GameCardActions</strong> — ações inline no card (favoritar, jogar, info) com layout dedicado ao tema cassino (#539).</li>
<li class=""><strong>Section title neon</strong> — variante de título de seção com efeito neon, alinhada à identidade visual (#540).</li>
<li class=""><strong>Home leagues circle gradient</strong> — widget de ligas esportivas com fundo circular em gradient (#543).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides--7k-bet-br">Brand Overrides — 7k-bet-br<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#brand-overrides--7k-bet-br" class="hash-link" aria-label="Link direto para Brand Overrides — 7k-bet-br" title="Link direto para Brand Overrides — 7k-bet-br" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sidebar Spaceman</strong> — botão dedicado ao jogo Spaceman adicionado à sidebar do 7k (#533).</li>
<li class=""><strong>Botão de promoções no header</strong> controlado por feature flag, permitindo brands ligar/desligar individualmente (#522).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--jogos">Cassino / Jogos<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#cassino--jogos" class="hash-link" aria-label="Link direto para Cassino / Jogos" title="Link direto para Cassino / Jogos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Mini-games iframe</strong> — pontos de acesso novos (rotas/links) para o iframe de mini-games em stage (#546).</li>
<li class=""><strong><code>game-stats-resolve</code></strong> — fallback adicional na resolução de estatísticas para evitar valores zerados quando tags chegam em formato inesperado (PR #562 / main-fix).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="proteção-de-conta">Proteção de Conta<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#prote%C3%A7%C3%A3o-de-conta" class="hash-link" aria-label="Link direto para Proteção de Conta" title="Link direto para Proteção de Conta" translate="no">​</a></h2>
<ul>
<li class=""><strong>Cache de limites</strong> revalidado após alteração — ao salvar um novo limite a UI agora reflete o estado correto sem exigir reload manual (#541).</li>
<li class=""><strong>Mensagem de erro de proteção</strong> ajustada para exibir o motivo do BFF de forma legível em vez de fallback genérico (#551).</li>
<li class=""><strong>Auto-exclusão via chat de suporte</strong> — fluxo dedicado que encaminha o usuário ao atendimento humano em casos de timeout/self-exclusion (#536).</li>
<li class=""><strong>Salvar dados pessoais</strong> — feedback de sucesso/erro corrigido na tela de conta (#557).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="performance-e-layout">Performance e Layout<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#performance-e-layout" class="hash-link" aria-label="Link direto para Performance e Layout" title="Link direto para Performance e Layout" translate="no">​</a></h2>
<ul>
<li class=""><strong>Bundle size audit</strong> — auditoria de tamanho de bundle aplicada em rotas críticas, com remoção de imports redundantes e ajustes de code-splitting (#549).</li>
<li class=""><strong>Service worker cache 206 + RA reputation race</strong> — corrige resposta parcial (HTTP 206) sendo cacheada pelo SW e race condition na inicialização do RA reputation script (#554).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/games</code> — High Payers</strong> (#162): novo recurso de jogos "que mais pagam" com tipos, transforms e métodos no <code>service</code>/<code>legacy-service</code>/<code>custom-service</code>. 128 linhas de testes em <code>transform.test.ts</code>.</li>
<li class=""><strong><code>@cactus-agents/games</code> — Normalize tags</strong> (#165): normalização de tags de jogo + melhorias na resolução de métricas (<code>game.metric</code>), exposto novo helper no <code>index.ts</code>.</li>
<li class=""><strong><code>@cactus-agents/gamification</code></strong> (#164): novos utilitários <code>level-utils</code>, <code>mission-utils</code> e <code>tournament-utils</code>, além de <code>pending_claim</code> e expansão dos i18n keys de gamificação nos 4 idiomas (pt-br, pt, es, en) — base do revamp 7k no front.</li>
<li class=""><strong><code>@cactus-agents/platform-cache</code></strong> (#163): removido o structured log <code>cache_decision</code> do engine — reduz volume de logs em produção sem perder o single-flight test essencial.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="infra--ci">Infra / CI<a href="https://docs.c4ctus.com/changelog/2026/05/02/daily#infra--ci" class="hash-link" aria-label="Link direto para Infra / CI" title="Link direto para Infra / CI" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sync de assets para R2</strong> — script <code>scripts/sync-assets-r2.mjs</code> adicionado ao base (via main-7k-merge) para envio de assets via API S3-compatible da Cloudflare R2; complementado no <code>front-ops</code> com workflow de envio + assets <code>bluetec</code> no R2.</li>
<li class=""><strong><code>wrangler.toml</code></strong> atualizado para expor o binding do bucket R2 no Worker, e <code>workers/middleware.ts</code> ganhou rota para servir assets do R2 quando aplicável.</li>
<li class=""><strong><code>front-ops</code></strong> — branch de performance ativada em testes para <code>state77-com</code> antes do rollout completo.</li>
</ul>]]></content:encoded>
            <category>gamification</category>
            <category>vera-bet-br</category>
            <category>cassino-bet-br</category>
            <category>ftd</category>
            <category>layout</category>
            <category>brand-overrides</category>
            <category>infra</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 01/05/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/05/01/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/05/01/daily</guid>
            <pubDate>Fri, 01 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[FTD Onboarding — três fluxos novos consolidados (stage-ftd)]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ftd-onboarding--três-fluxos-novos-consolidados-stage-ftd">FTD Onboarding — três fluxos novos consolidados (<code>stage-ftd</code>)<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#ftd-onboarding--tr%C3%AAs-fluxos-novos-consolidados-stage-ftd" class="hash-link" aria-label="Link direto para ftd-onboarding--três-fluxos-novos-consolidados-stage-ftd" title="Link direto para ftd-onboarding--três-fluxos-novos-consolidados-stage-ftd" translate="no">​</a></h2>
<p>Maior entregável do dia. A branch <code>stage-ftd</code> aterrissou três fluxos completos de retenção/conversão D0, todos brand-configuráveis e cobertos por testes:</p>
<ul>
<li class=""><strong>FTD Offer ("Oferta Relâmpago")</strong> — modal + floating widget + story thumb com Quick Deposit embutido. Componentes em <code>app/components/ftd-offer/</code> (Provider, Modal, FloatingWidget, StoryThumb), storage isolado por marca em <code>ftd-offer-storage.ts</code> e analytics em <code>ftd-offer-analytics.ts</code>.</li>
<li class=""><strong>FTD Cashback</strong> — fluxo D0 com modal de oferta inicial (<code>FtdCashbackFirstModal</code>), modal de prêmio (<code>FtdCashbackPrizeModal</code>), Provider, dev panel, scaffolding de tiers (<code>ftd-cashback-tiers.ts</code>) e persistência local (<code>ftd-cashback-storage.ts</code>). Testes cobrem storage e cálculo de tiers.</li>
<li class=""><strong>FTD Check-in</strong> — daily check-in com mock fixture, logs diagnósticos, special offers, label "done today" e kill switch via feature flag remota da brand 7k (<code>feat/ftd-checkin-7k-feature-flags</code>). Componentes <code>Checkin</code>, <code>CheckinTrigger</code>, <code>CheckinStoreOffers</code>.</li>
<li class=""><strong>Loop de reabertura corrigido</strong> — <code>useFtdCashbackFlow</code> ganha guard pra não reabrir o first-modal logo depois do close (PR #485, <code>fix/ftd-cashback-first-modal-loop</code>).</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="busca-unificada-featsearch-unified-refactor">Busca Unificada (<code>feat/search-unified-refactor</code>)<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#busca-unificada-featsearch-unified-refactor" class="hash-link" aria-label="Link direto para busca-unificada-featsearch-unified-refactor" title="Link direto para busca-unificada-featsearch-unified-refactor" translate="no">​</a></h2>
<p>Refatoração grande da busca: header e página passam a compartilhar input + estado, com nova rota de resultados completos:</p>
<ul>
<li class=""><strong><code>HeaderSearchInput</code></strong> novo (substitui <code>HeaderSearchTrigger</code>) embeded direto no header, com hook <code>useHeaderHasEmbeddedSearch</code> pra evitar duplicação.</li>
<li class=""><strong>Estados <code>IdleState</code> e <code>ResultsState</code></strong> isolados em <code>app/components/search/</code>, alimentados por novo store Zustand (<code>app/store/search.ts</code>) com testes dedicados.</li>
<li class=""><strong><code>useRecentSearches</code></strong> novo hook com persistência client-side e suíte de testes.</li>
<li class=""><strong>Rota <code>/search/all</code></strong> nova pra full results, separada da busca interativa.</li>
<li class=""><strong>Idle rows turbinadas</strong> com <code>popular-terms</code> configurável e chips reaproveitáveis.</li>
<li class=""><strong>Sync de query param</strong> via <code>useSearchParamSync</code> mantém a URL coerente com o input.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--jogos">Cassino / Jogos<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#cassino--jogos" class="hash-link" aria-label="Link direto para Cassino / Jogos" title="Link direto para Cassino / Jogos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Refac de cents nas estatísticas</strong> (#461 + #473) — novo hook <code>useStatsFormatMoney</code>, feature flag <code>hideStatsCents</code> propagada pra todas as 13 brands, e refinamento posterior restringindo o opt-in apenas aos componentes menores (<code>GameStats</code>, <code>GameWinners</code>, <code>GameIframe</code>, <code>LazyWinnersSection</code>, <code>WinsCarousel</code>). 7k-bet-br ativa o flag.</li>
<li class=""><strong>Padronização de páginas de catálogo</strong> (#476) — <code>GamesPageHeader</code>, <code>GamesFilterBar</code> e <code>ProvidersGrid</code> passam a compartilhar header + container entre <code>category.$slug</code>, <code>providers.$slug</code> e <code>providers._index</code>.</li>
<li class=""><strong>Top Games legacy-grid (vera-bet-br)</strong> (#400) — nova variante <code>TopGameCardLegacyGrid</code> no widget de top-games, mais um bento <code>RecommendedGamesBento</code>, registry expandido e config dedicada em <code>overrides/vera-bet-br/app/config/widgets/top-games.ts</code>.</li>
<li class=""><strong>Linhas vazias colapsam</strong> (#474) — <code>DeferredHomeRow</code> agora detecta widgets que retornam <code>null</code> e remove o wrapper, eliminando phantom flex gap nas páginas <code>/</code>, <code>/games</code> e <code>/games/live</code>.</li>
<li class=""><strong>Favoritos e recents on-demand</strong> (#479) — fim das requests globais no <code>_layout.tsx</code>. <code>useFavorites</code> busca por contexto (home, sheets, search), novo hook <code>useFavoritesAuthSync</code>, novo <code>recents.server.ts</code> e <code>useRecentlyPlayedSeed</code> simplificado. Reduz overhead de cold-start em todas as páginas.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos">Pagamentos<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#pagamentos" class="hash-link" aria-label="Link direto para Pagamentos" title="Link direto para Pagamentos" translate="no">​</a></h2>
<ul>
<li class=""><strong>PIX random-key: <code>EVP</code> → <code>RANDOM</code></strong> (#472) — nome canônico corrigido em <code>PixKeySelector</code>, <code>PixSection</code>, tipo <code>account-features</code> e propagado pras 13 brands. Kanban <code>14a0b719</code>.</li>
<li class=""><strong>Bancos explícitos no 7k-bet-br</strong> (#483) — desativada a agregação "Pix" no <code>methods.ts</code> da 7k; cada banco aparece individualmente.</li>
<li class=""><strong>Deposit modal polish</strong> (#475) — banner do <code>FirstDepositBonusBanner</code> ganha cor consistente e CTA do <code>CtaButtonSignature</code> desabilitado quando o estado é inválido.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="auth-e-telefone">Auth e Telefone<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#auth-e-telefone" class="hash-link" aria-label="Link direto para Auth e Telefone" title="Link direto para Auth e Telefone" translate="no">​</a></h2>
<ul>
<li class=""><strong>Textos legais config-driven no LoginModal</strong> (#491) — novo componente <code>AuthLegalTexts</code> lê de <code>app/config/auth/legal-texts.ts</code> (override em <code>overrides/7k-bet-br/</code>), substituindo strings hardcoded.</li>
<li class=""><strong>Phone save desabilitado quando não há mudança</strong> (#503) — <code>PhoneSection</code> e <code>PhoneStep</code> comparam state inicial vs atual antes de habilitar o save.</li>
<li class=""><strong><code>lockDdiSelection</code> honrado em todos os fluxos de telefone</strong> (#506) — flag finalmente respeitada também no edit de account e no step de validação. Brand <code>fi-7k-bet</code> ativa.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ui--mobile--carousels">UI / Mobile / Carousels<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#ui--mobile--carousels" class="hash-link" aria-label="Link direto para UI / Mobile / Carousels" title="Link direto para UI / Mobile / Carousels" translate="no">​</a></h2>
<ul>
<li class=""><strong>Edge-to-edge mobile + lateral fade</strong> (#489 + #509) — <code>Carousel</code> e <code>Marquee</code> ganham scroll edge-to-edge no mobile e a borda de fade lateral vira opt-in. A relação entre os dois flags foi consolidada: edge-to-edge <strong>só aplica quando o fade está desabilitado</strong>, evitando combinações estranhas.</li>
<li class=""><strong>Lateral fade desligado nos 7k variants</strong> (#512) — <code>cl-bet7k-com</code>, <code>fi-7k-bet</code> e <code>ng-7k-bet</code> desativam o fade no mobile.</li>
<li class=""><strong>Modal mobile sem botão de fechar</strong> (#486) — <code>Modal</code> esconde o close-button no mobile (gesture-driven), preservando-o no desktop.</li>
<li class=""><strong>Sidebar dos 7k: "Aviator" → "Aviator Crash"</strong> (#490) — label corrigido no override da 7k-bet-br.</li>
<li class=""><strong>Minigames sidebar/strategy</strong> (#513) — <code>StrategyOverlay</code> e <code>useStrategyAuthTrigger</code> ganham flag <code>hasFtd</code> propagada pelo store; novo helper <code>ftd-encrypted-id.ts</code> substitui parte de <code>campaign-widget-url.ts</code>.</li>
<li class=""><strong>Refac do <code>CampaignWidget</code></strong> (#515) — iframe handling agora roda via strategy nomeada (<code>app/utils/strategy/iframe.ts</code>); <code>app/utils/campaign-widget-url.ts</code> deletado, lógica e testes migrados pro namespace de strategy. Override da <code>cassino-bet-br</code> e <code>vera-bet-br</code> ganham <code>app/config/strategy/strategy.ts</code>. Limpeza líquida de ~120 linhas.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="seo-e-redirects-legados">SEO e Redirects Legados<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#seo-e-redirects-legados" class="hash-link" aria-label="Link direto para SEO e Redirects Legados" title="Link direto para SEO e Redirects Legados" translate="no">​</a></h2>
<ul>
<li class=""><strong>Separador de título "|" → "–"</strong> (#484) — padronização em FAQ, page descriptions, games-seo, sports-seo e game-details. Aplicado no base e em todos os overrides (7k, betpontobet, cassino, vera).</li>
<li class=""><strong>Canonical/og<!-- -->:url<!-- --> no sports respeita rota localizada</strong> (#449) — <code>sports/$.tsx</code> e <code>sports/_index.tsx</code> agora usam <code>routeHref()</code> em vez de path hardcoded; tipo <code>sports-seo.ts</code> ajustado e configs de cada brand limpas.</li>
<li class=""><strong>301s pra rotas Nuxt legadas</strong> (#446) — <code>app/config/routes/legacy-redirects.ts</code> ganha entradas adicionais; <code>legacy-redirect.ts</code> cobre o catchall de sports.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="gamificação--recompensas">Gamificação / Recompensas<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#gamifica%C3%A7%C3%A3o--recompensas" class="hash-link" aria-label="Link direto para Gamificação / Recompensas" title="Link direto para Gamificação / Recompensas" translate="no">​</a></h2>
<ul>
<li class=""><strong>Data real de expiração nos rewards expirados</strong> (#477) — <code>RewardCard</code> deixa de mostrar "expirado" sem data; usa o timestamp original.</li>
<li class=""><strong>Bottom notification de download do app no 7k</strong> (#471) — <code>BottomNotification</code> ganha suporte a navegação externa pra LP; <code>entry.client.tsx</code> integra; override da 7k-bet-br aponta o link.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>Alinhamento visual fi/ng/cl-bet7k com 7k-bet-br</strong> (#492) — entrega massiva: <code>cl-bet7k-com</code>, <code>fi-7k-bet</code> e <code>ng-7k-bet</code> ganham FAQ próprio (<code>faq.server.ts</code>), composition do layout, menu, theme sizes, header secondary nav, sidebar, footer-stacked heart row, e widgets (<code>campaign-widget</code>, <code>featured-game</code>, <code>footer-socials</code>, <code>game-card</code>, <code>post-card</code>, <code>sidebar-buttons</code>). Inclui assets novos (<code>destaque-aviator.svg</code>, <code>exclusivo.svg</code>, thumbnails).</li>
<li class=""><strong>Stories alinhadas + busca removida da home (fi/ng-7k-bet)</strong> (#502) — fi e ng espelham a config de stories da cl, atualizam <code>new-badge.png</code> e removem a row de busca de <code>home-rows.{custom,legacy,new}.ts</code>.</li>
<li class=""><strong>Banner home responsivo no vera-bet-br</strong> (#500) — <code>HomeBannerCarousel</code> ganha tipo <code>home-banner</code> ampliado com sizes responsivos por breakpoint; portado direto do legado.</li>
<li class=""><strong>Painel de providers no vera-bet-br</strong> (#504) — novo <code>ProviderCardBare</code> + variant "panel" no <code>ProvidersCarousel</code>, com config <code>providers-section.ts</code> no base e override no vera reproduzindo o visual legado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/05/01/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/i18n</code></strong> — nova chave <code>casino:search.search_providers</code> adicionada nos 4 locales (pt-br, pt, es, en). Suporta o input de busca de providers introduzido no refac de busca unificada do base. Changeset <code>add-search-providers-key</code>.</li>
</ul>]]></content:encoded>
            <category>ftd-onboarding</category>
            <category>busca-unificada</category>
            <category>payments</category>
            <category>ui-mobile</category>
            <category>brand-overrides</category>
            <category>seo</category>
            <category>gamification</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 30/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/30/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/30/daily</guid>
            <pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Dia muito pesado: 27 PRs no base + 8 PRs no core. O core foi quase todo dedicado a expandir o @cactus-agents/i18n para alimentar a leva grande de novas variants do base (mobile bottom nav, footer-stacked, quick-access menu, side sheets, tournament detail, FTD offer/cashback). No base, o destaque foi a reformulação completa do MobileBottomNav com 4 variantes tipadas, novo primitive CtaButton, footer-stacked do 7k-bet-br e revamp da página de detalhe de torneios.]]></description>
            <content:encoded><![CDATA[<p>Dia muito pesado: 27 PRs no base + 8 PRs no core. O core foi quase todo dedicado a expandir o <code>@cactus-agents/i18n</code> para alimentar a leva grande de novas variants do base (mobile bottom nav, footer-stacked, quick-access menu, side sheets, tournament detail, FTD offer/cashback). No base, o destaque foi a reformulação completa do <code>MobileBottomNav</code> com 4 variantes tipadas, novo primitive <code>CtaButton</code>, footer-stacked do 7k-bet-br e revamp da página de detalhe de torneios.</p>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="i18n--internacionalização">i18n / Internacionalização<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#i18n--internacionaliza%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para i18n / Internacionalização" title="Link direto para i18n / Internacionalização" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/i18n</code> recebeu 4 PRs de chaves novas no mesmo dia</strong> (#157, #158, #159, #160), todas em pt-br (autoritativo) + pt + es + en. Cada PR cobre um surface novo do base: <code>mobile_nav.*</code> para variantes de bottom nav, <code>tournaments.*</code> para a tela de detalhe, quick-access menu + side sheets, e footer-stacked.</li>
<li class=""><strong><code>mobile_nav</code> expandido</strong> (PR #157, core): de <code>home/casino/sports/deposit/menu</code> para 13 intents — <code>tournaments</code>, <code>missions</code>, <code>promotions</code>, <code>rewards</code>, <code>referral</code>, <code>wallet</code>, <code>profile</code>, <code>store</code>, <code>slots</code>, <code>withdraw</code>, <code>casino_live</code>, <code>sports_live</code>. Suporta os 4 variants novos do <code>MobileBottomNav</code> (flat, trail-cta, fab-center, illustrated) sem brand precisar declarar namespace próprio.</li>
<li class=""><strong><code>tournaments.*</code> na gamification</strong> (PR #158, core): 5 chaves para a revamp da tournament detail — <code>information</code> (título do stats carousel), <code>show_more</code>/<code>show_less</code> (collapse/expand das descrições e tabs), <code>no_players</code>/<code>no_prizes</code> (empty states de ranking e prêmios).</li>
<li class=""><strong>Quick-access menu + side sheets</strong> (PR #159, core): chaves nos namespaces <code>layout</code>, <code>casino</code> e <code>gamification</code> para o novo widget <code>+</code> no header e os painéis laterais de Favoritos, Recentes e Notificações usados em 7k-bet-br e cl-bet7k-com.</li>
<li class=""><strong>Footer-stacked</strong> (PR #160, core): 14 chaves por locale para o novo variant — <code>col_doubts</code>, <code>col_vip_games</code>, <code>vip_*</code>, <code>payment_label</code>, <code>download_app_label</code>, <code>back_to_top_cta</code>, <code>central_atendimento</code>. Antes os textos viviam na pasta <code>overrides/7k-bet-br/app/locales</code> e bloqueavam o uso compartilhado entre brands.</li>
<li class=""><strong><code>ftd_offer</code> e <code>ftd_cashback</code> no namespace <code>payments</code></strong> (PRs #152 e #155, core): chaves para o modal de oferta FTD e o fluxo de cashback FTD (D0) — <code>first_modal.*</code> e <code>prize_modal.*</code>. PT-BR neutra e brand-agnostic; PT, ES, EN traduzidos.</li>
<li class=""><strong><code>favorites.empty_*</code> e <code>recently_played.empty_*</code></strong> (PR #153, core): chaves para o <code>EmptyRowPlaceholder</code> quando o usuário tem favoritos/recentes vazios — labels de scroll, aria-labels do botão e variantes com sugestões ao lado.</li>
<li class=""><strong><code>casino:show_filters</code>/<code>hide_filters</code></strong> (PR #438, base via bump i18n ^0.78.0): chaves para o toggle de filtros recolhíveis nas páginas de games.</li>
<li class=""><strong>Normalização "Casino" → "Cassino" para BRA</strong> (PR #432, base): hook <code>useCasinoNomenclature</code> aplica regex whole-word só quando <code>countryCode === "BRA"</code> — corrige o fato do BFF retornar <code>nomenclature_for_casino_games = "Casino"</code> (com um 's') que vencia o i18n correto via fallback <code>||</code>. Workaround temporário enquanto o <code>@cactus-agents/brand.transformFeatures()</code> está congelado; outros países (pt-PT, es-ES, en-GB) passam direto porque "Casino" é gramaticalmente correto neles.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="gamificação">Gamificação<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#gamifica%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Gamificação" title="Link direto para Gamificação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Tournament detail revamp completo</strong> (PR #441): a rota <code>vip/tournaments.$id.tsx</code> passou de 304 linhas monolíticas para uma composição de 11 componentes novos em <code>app/components/gamification/tournament-detail/</code> — <code>TournamentHero</code>, <code>RegistrationCTA</code>, <code>TournamentDescription</code> (com <code>show_more</code>/<code>show_less</code>), <code>TournamentDetailTabs</code>, <code>TournamentStatsCarousel</code> + <code>TournamentStatCard</code>, <code>TournamentPrizesList</code> + <code>PrizeRow</code>, <code>TournamentRankingList</code>, e helpers utilitários.</li>
<li class=""><strong>Sizing dos cards de missão e torneio é configurável por brand</strong> via <code>app/types/mission-card.ts</code> e <code>app/types/tournament-card.ts</code> novos. Brands 7k-bet-br, cl-bet7k-com declararam o <code>widgets/mission-card.ts</code> + <code>widgets/tournament-card.ts</code> próprios.</li>
<li class=""><strong><code>MissionCardClassic</code>/<code>Stacked</code></strong> e <strong><code>TournamentCardClassic</code>/<code>Stacked</code></strong> agora usam o novo primitive <code>CtaButton</code> em vez de implementar botão próprio — convergência visual entre cards e signature header.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ui-e-componentes">UI e Componentes<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#ui-e-componentes" class="hash-link" aria-label="Link direto para UI e Componentes" title="Link direto para UI e Componentes" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo primitive <code>CtaButton</code></strong> (PR #431, <code>app/components/ui/cta-button/</code>): substitui implementações ad-hoc de botão CTA em <code>LoginModal</code>, <code>RegisterModal</code>, <code>GameCardClassic</code>, <code>GameIframe</code>, <code>DepositForm</code>, <code>HeaderUserArea</code> e nos cards de missão/torneio. Vem em 2 variants — <code>flat</code> (botão sólido tradicional) e <code>signature</code> (estilo do botão "Depositar" do header 7k com brilho/gradient). Configurável por brand via <code>app/config/widgets/cta-button.ts</code>. Cobertura completa de tests (<code>CtaButton.test.tsx</code>, <code>CtaButton.config.test.tsx</code>).</li>
<li class=""><strong><code>MobileBottomNav</code> reescrito do zero como sistema de variants tipado</strong> (PR #430): o componente legado (<code>app/components/layout/MobileBottomNav.tsx</code>, 204 linhas) foi removido. Substituído por dispatcher + 4 variants em <code>app/layouts/variants/mobile-bottom-nav/</code> — <code>MobileBottomNavFlat</code> (5 tiles iguais), <code>MobileBottomNavTrailCta</code> (4 tiles + CTA destacado à direita), <code>MobileBottomNavFabCenter</code> (FAB no centro estilo Material), <code>MobileBottomNavIllustrated</code> (tiles com ilustração). Catálogo de intents typed (<code>intents.ts</code>), resolver com fallback (<code>resolve.ts</code>), specials (<code>specials.ts</code>), handlers compartilhados, plus shared shells (<code>BottomNavShell</code>, <code>CtaPill</code>, <code>FabButton</code>, <code>NavTile</code>, <code>RotatingLabel</code>). Cobertura: <code>resolve.test.ts</code> (102 linhas) + <code>variants.test.tsx</code> (213 linhas).</li>
<li class=""><strong>CTA de depósito alinhado entre header / mobile-nav / game fullscreen</strong> (PR #439): o <code>CtaPill</code> do mobile-bottom-nav e o overlay do <code>GameIframe</code> agora reaproveitam o variant <code>signature</code> do <code>CtaButton</code>, evitando 3 implementações divergentes do mesmo botão "Depositar".</li>
<li class=""><strong>Filtros do cassino recolhíveis</strong> (PR #438): aplicado em <code>/games</code>, <code>/games/live</code>, <code>/games/category/:slug</code> e <code>/games/providers/:slug</code>. Novo ícone de filtro à esquerda da busca expande/recolhe o bloco de categorias e providers in-place. Mobile e desktop começam com filtros escondidos.</li>
<li class=""><strong>Breadcrumb do detalhe de jogo</strong> (PR #434): adicionada etapa "Início" como primeiro link e listagem do provider antes do nome do jogo (<code>Início &gt; Provider &gt; Jogo</code>). <code>app/components/layout/InternalNavBar.tsx</code> e <code>app/routes/games/$provider.$game.tsx</code>.</li>
<li class=""><strong><code>StoriesCircles</code> ganha overlay de badge "NEW"</strong> (PR #453): novo campo <code>badge</code> em <code>StoriesConfig</code> controla <code>enabled</code>, asset e índices 1-based dos circles que recebem o ribbon. Asset padrão <code>/assets/stories/new-badge.png</code> é trocado pelo override <code>overrides/&lt;brand&gt;/public/assets/stories/new-badge.png</code>. Os 14 brand overrides ganharam o bloco <code>badge</code> preparado (<code>enabled: false</code>); ativado para 7k-bet-br (pt-br) e cl-bet7k-com (es) com items <code>[1, 2]</code>.</li>
<li class=""><strong><code>FavoriteButton</code> e <code>LastGameFloatingWidget</code></strong> (PR #442): correção de SVG fill handling no botão de favoritar e ajustes de layout no widget flutuante de "última jogada".</li>
<li class=""><strong>Quick-access menu + side sheets</strong> (PR #445): novo widget <code>+</code> no header de 7k-bet-br e cl-bet7k-com abre painéis laterais para Favoritos, Recentes e Notificações. Adicionado <code>widgets/quick-access-menu.ts</code> no override do cl-bet7k-com; rotas e flags propagadas em todos os 14 brand overrides.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino">Cassino<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#cassino" class="hash-link" aria-label="Link direto para Cassino" title="Link direto para Cassino" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sidebar não roteia mais "Mines" e "Aviator" pro provider errado</strong> (PR #437): ambos estavam apontando para o provider Spribe, mas a curadoria desses jogos no catálogo Cactus é via Banana Games. Correção em <code>app/config/layout/sidebar.ts</code> e nos 11 brand overrides afetados.</li>
<li class=""><strong>Item permanente "Mini Games Diários" na sidebar do 7k-bet-br</strong> (PR #440): adicionado ao <code>sidebar-buttons</code> config do 7k. Doc operacional nova em <code>docs/features/strategy/overview.md</code> cobrindo a feature.</li>
<li class=""><strong>Rows pessoais (Favoritos / Jogados Recentemente) escondidas quando vazias</strong> (PR #436): novo flag <code>hidePersonalRowsWhenEmpty</code> em <code>features.ts</code>. Quando ativo, <code>FavoritesRow</code> e <code>RecentlyPlayedRow</code> somem da home se o usuário não tem dado em vez de mostrar empty state. Default <code>false</code> em todos os 14 overrides; ativado só no 7k-bet-br. Hook novo <code>useRecentlyPlayedSlugs</code> em <code>app/hooks/</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos">Pagamentos<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#pagamentos" class="hash-link" aria-label="Link direto para Pagamentos" title="Link direto para Pagamentos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Allow-list de métodos de pagamento por brand + agrupamento de Pix providers</strong> (PR #435): <code>app/config/payments/methods.ts</code> e <code>app/types/payments-config.ts</code> novos. Brand declara quais métodos aceita; múltiplos providers de Pix são agrupados visualmente no <code>DepositForm</code>. Hook <code>usePayments</code> ganhou +83 linhas para resolver a lista permitida e o agrupamento.</li>
<li class=""><strong>Withdraw "Valor máximo" mostra cap da casa, não saldo do usuário</strong> (PR #450): a célula "Máximo" no <code>WithdrawForm</code> estava puxando <code>availableBalance</code> do user. Trocado para o teto que a casa permite por transação — fim das confusões em saques limitados pela política do operador.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="auth">Auth<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#auth" class="hash-link" aria-label="Link direto para Auth" title="Link direto para Auth" translate="no">​</a></h2>
<ul>
<li class=""><strong>Kill-switch front-end <code>disableSocialAuth</code></strong> (PR #457): nova flag em <code>app/config/features/auth.ts</code> (type em <code>app/types/auth-features.ts</code>). Quando ligada, o <code>RegisterModal</code> esconde os botões "Continuar com Google/Facebook" e o <code>SocialAccountsSection</code> da área de Segurança fica oculto. Propagada para os 14 brand overrides com default <code>false</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sports">Sports<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#sports" class="hash-link" aria-label="Link direto para Sports" title="Link direto para Sports" translate="no">​</a></h2>
<ul>
<li class=""><strong>Header secundário pode ser renderizado dentro do header sticky em páginas de esportes</strong> (PR #469): novo flag em <code>LayoutConfig</code> + suporte nos shells <code>HeaderTopShell</code> e <code>SplitShell</code>. Adicionado <code>secondaryHeader.stickyInsideHeader</code> ao type. Ativado no <code>overrides/7k-bet-br/app/config/layout/composition.ts</code>.</li>
<li class=""><strong><code>useSportsAvailableHeight</code> com <code>ResizeObserver</code></strong> (PR #470): substitui o cálculo via <code>useEffect</code> + <code>window.resize</code> por observação contínua do header — o iframe do sportsbook se reajusta dinamicamente quando o header secundário muda de altura (ex: quando colapsa em scroll).</li>
<li class=""><strong>SEO copy do sports-seo do 7k removido</strong> (PR #466): conteúdo grande de copy SEO comentado em <code>overrides/7k-bet-br/app/config/seo/sports-seo.ts</code> para revisão futura. Rota de sports continua respondendo, só sem o bloco.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="layout--footer">Layout / Footer<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#layout--footer" class="hash-link" aria-label="Link direto para Layout / Footer" title="Link direto para Layout / Footer" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo variant <code>footer-stacked</code> para 7k-bet-br</strong> (PR #454): footer reorganizado em 6 partes empilhadas — <code>FooterStackedMain</code> (com 4 colunas: VIP games, dúvidas, downloads, central de atendimento), <code>FooterStackedHeartRow</code>, <code>FooterStackedSeals</code>, <code>FooterStackedSponsors</code>, <code>FooterStackedPaymentBar</code>, <code>FooterStackedBottomBar</code>, <code>FooterStackedLegal</code>. Inclui novo asset <code>public/assets/seals/google-play.png</code>. Config nova <code>app/config/widgets/footer-socials.ts</code> permite brand declarar quais redes sociais aparecem no footer.</li>
<li class=""><strong><code>topbarNotification</code> agora é configurável via <code>LayoutConfig</code></strong> (PR #455): adicionado em <code>overrides/7k-bet-br/app/config/layout/composition.ts</code> para deixar a barra de notificações controlada pelo composition em vez de hardcoded no shell.</li>
<li class=""><strong>Logo da sidebar do 7k-bet-br</strong> (PR #452): redimensionada para 52x24 e alinhada à esquerda.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>7k-bet-br</strong> absorveu o grosso do dia: <code>MobileBottomNav</code> variant configurada, sidebar-buttons com novo intent <code>store</code> (item "Loja" como 5º), footer-stacked ligado, topbarNotification no composition, sports header sticky com secondary nav inside, novo <code>widgets/strategy/strategy.ts</code> para o item permanente "Mini Games Diários", logo da sidebar shrunk, stories badge ativado em items <code>[1, 2]</code>, <code>hidePersonalRowsWhenEmpty: true</code>, <code>widgets/footer-socials.ts</code> declarado.</li>
<li class=""><strong>cl-bet7k-com (chile, es)</strong>: quick-access menu + side sheets ligados, <code>widgets/quick-access-menu.ts</code> adicionado, stories badge ativado em items <code>[1, 2]</code>, <code>widgets/cta-button.ts</code>, <code>widgets/mission-card.ts</code> e <code>widgets/tournament-card.ts</code> declarados.</li>
<li class=""><strong>Propagação file-replacement em todos os 14 overrides</strong> (regra dura do brandOverridesPlugin): <code>hidePersonalRowsWhenEmpty: false</code>, <code>notificationsSheet: { enabled: true }</code>, <code>disableSocialAuth: false</code>, <code>widgets/stories.ts</code> com <code>badge.enabled: false</code>, novos paths em <code>routes/paths.ts</code>, <code>widgets/cta-button.ts</code> opcionais. PR #436 documenta o resolve de conflict entre <code>hidePersonalRowsWhenEmpty</code> (PR atual) e <code>notificationsSheet</code> (stage) em todos os overrides.</li>
<li class=""><strong>Sidebar do cassino</strong> corrigida em 11 overrides (<code>7k-bet-br</code>, <code>betpontobet-bet-br</code>, <code>cassino-bet-br</code>, <code>ng-7k-bet</code>, <code>pb-bet</code>, <code>ph-state77-com</code>, <code>pt-state77-com</code>, <code>rj-bet</code>, <code>state77-com</code>, <code>vera-bet-br</code>, <code>x2b-bet</code>): "Mines" e "Aviator" agora apontam para Banana Games em vez de Spribe.</li>
<li class=""><strong><code>sidebar-buttons</code> do 7k-bet-br</strong> ganhou intent <code>store</code> apontando para a rota canônica <code>gamification.store</code> com ícone <code>ShoppingBag</code>. <code>SidebarButtonIntent</code> virou união backwards-compatível — outros brands não precisam fazer nada.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="segurança">Segurança<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#seguran%C3%A7a" class="hash-link" aria-label="Link direto para Segurança" title="Link direto para Segurança" translate="no">​</a></h2>
<ul>
<li class=""><strong>Bloqueio de leak cross-user de saldo via cache classification</strong> (PR #428): bump de <code>@cactus-agents/platform-cache</code> para <code>0.12.0</code> + 85 linhas novas de classificação no <code>workers/middleware.ts</code>. Endpoints sensíveis a usuário (saldo, KYC status, transações) agora são marcados explicitamente para nunca atravessar o cache compartilhado da Cloudflare Cache API; só o cache scopado por user-key passa. Resolve incident report do dia anterior onde um usuário viu saldo de outro depois de um cache hit equivocado.</li>
<li class=""><strong>Suporte ao chat Zendesk abre instantâneo</strong> (PR #465): removido o <code>setTimeout(..., 5000)</code> legado do <code>useLiveSupport</code> que segurava o widget por 5s "esperando o script carregar". O script já tem promise própria de ready — o delay extra era redundante e quebrava o UX em sessões de suporte urgentes.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="strategy--campaigns">Strategy / Campaigns<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#strategy--campaigns" class="hash-link" aria-label="Link direto para Strategy / Campaigns" title="Link direto para Strategy / Campaigns" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>strategy-campaign-api.client</code> refatorado</strong> (PR #448): cliente de Campaign API trocou de fluxo imperativo (<code>init</code> → <code>fetch</code> → <code>dispatch</code>) para uma promise única que devolve user state + campaign manifest. <code>app/store/strategy.ts</code> fica mais enxuto (-26 linhas), tipos simplificados em <code>app/types/strategy.ts</code>. <code>CampaignWidget.tsx</code> consome o novo shape direto. Tests atualizados (<code>strategy-campaign-api.client.test.ts</code>: 192 linhas; <code>strategy.test.ts</code>: 71 linhas).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<p>8 PRs no <code>front-cactus-core</code> no mesmo dia — uma das maiores levas dos últimos 30 dias.</p>
<ul>
<li class=""><strong><code>@cactus-agents/i18n</code></strong> ganhou 4 PRs consecutivos (#157 → 0.75.0, #158 → 0.78.x, #159 → 0.8x.0, #160 → 0.81.0). Todas as 4 keys-libraries seguem o pattern: pt-br autoritativo, pt + es + en como traduções equivalentes, brand-agnostic. As surfaces cobertas (mobile_nav expandido, tournaments detail, quick-access menu, footer-stacked) liberaram o base para parar de injetar traduções via <code>app/locales/overrides/</code> (cumprindo a regra dura do AGENTS.md: traduções novas vão no core, não no base).</li>
<li class=""><strong><code>@cactus-agents/payments</code> locales</strong> (PRs #152 e #155): namespaces <code>ftd_offer.*</code> (4 locales × 15 keys = 60 keys) e <code>ftd_cashback.{first_modal,prize_modal}.*</code> adicionados ao <code>packages/i18n/locales/{pt-br,pt,es,en}/payments.json</code>. Alimentam o modal de oferta FTD e o fluxo de cashback FTD (D0) que o base vai shipar nos próximos dias.</li>
<li class=""><strong><code>@cactus-agents/i18n</code> com chaves de empty state pra Favoritos / Recentes</strong> (PR #153): <code>casino.favorites.empty_scroll_label</code>, <code>empty_scroll_label2</code>, <code>empty_scroll_aria</code>, <code>empty_casino_aria</code> + <code>casino.recently_played.empty_scroll_aria</code>. Suporta o <code>EmptyRowPlaceholder</code> do base nos modos com sugestões ao lado e variant botão.</li>
<li class=""><strong><code>@cactus-agents/brand</code> com guards de OTP frequente e KYC integration</strong> (PR #156): <code>transform/features.ts</code> ganhou +50 linhas e bateria de tests (94 linhas em <code>brand.test.ts</code>). A função <code>transformFeatures</code> agora valida e gateia módulos de OTP frequente e integração KYC vindos do BFF antes de expô-los ao base — evita que feature flag mal-configurada do admin estoure UI no front.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="infra--ci--deploy">Infra / CI / Deploy<a href="https://docs.c4ctus.com/changelog/2026/04/30/daily#infra--ci--deploy" class="hash-link" aria-label="Link direto para Infra / CI / Deploy" title="Link direto para Infra / CI / Deploy" translate="no">​</a></h2>
<ul>
<li class=""><strong>Deploy <code>prod-7k-bet-br</code> agora sai da branch <code>main-7k</code></strong> (PR #458, base): correção em <code>.github/workflows/ci-deploy.yml</code> — antes apontava para <code>main</code>, o que casava com o staging unificado mas não com o pipeline de produção do 7k que vive numa branch separada. Sem isso, hotfixes do 7k vinham com bundle errado.</li>
</ul>]]></content:encoded>
            <category>i18n</category>
            <category>gamificacao</category>
            <category>mobile-bottom-nav</category>
            <category>footer-stacked</category>
            <category>sidebar-buttons</category>
            <category>brand-overrides</category>
            <category>ci-deploy</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 29/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/29/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/29/daily</guid>
            <pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Dia pesado: 25 PRs no front-web-base cobrindo um empurrão grande de performance (CSS, ícones, modal), refatoração do header secundário com gating de rota, novas features de cassino e pagamentos, e ajustes finos de SEO/mobile/auth. Plus 4 mudanças de CI/CD em front-ops pra suportar o novo modelo de cache por device/country/buildId.]]></description>
            <content:encoded><![CDATA[<p>Dia pesado: 25 PRs no <code>front-web-base</code> cobrindo um empurrão grande de performance (CSS, ícones, modal), refatoração do header secundário com gating de rota, novas features de cassino e pagamentos, e ajustes finos de SEO/mobile/auth. Plus 4 mudanças de CI/CD em <code>front-ops</code> pra suportar o novo modelo de cache por device/country/buildId.</p>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="performance--empurrão-grande">Performance — empurrão grande<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#performance--empurr%C3%A3o-grande" class="hash-link" aria-label="Link direto para Performance — empurrão grande" title="Link direto para Performance — empurrão grande" translate="no">​</a></h2>
<ul>
<li class=""><strong>Drop <code>important: true</code> do Tailwind</strong> (<code>tailwind.config.js</code>). Removido globalmente — precedência CSS volta ao normal (inline style vence class). Resultado: <strong>CSS −23,5KB raw (−14,4%)</strong>. Componentes que usavam o pattern condicional <code>bgStyle ? "" : "bg-..."</code> (omitir classe quando havia inline style) foram simplificados — agora basta sempre emitir a classe que o <code>style</code> inline sobrescreve quando presente. Atualizados: <code>GameCardStacked</code>, <code>GameStats</code>, <code>GameWinners</code>, <code>MainLeaguesSquare</code>, <code>SidebarButtonsGradient</code>, <code>SidebarButtonsGrid</code>.</li>
<li class=""><strong>Migração <code>react-modal-sheet</code> → <code>vaul</code></strong> (<code>app/components/base/Modal.tsx</code>). Modal chunk caiu de ~158KB pra ~65KB raw — <strong>−93KB (−59%)</strong>. Mesma API externa, drop-in pelos consumers. (PR #405 fez ajustes finos depois pra resolver bugs de input-focus em mobile e espaço fantasma no footer.)</li>
<li class=""><strong>Remoção do <code>@tailwindcss/typography</code></strong>. Plugin não justificava o custo — uso restrito a 4 lugares (<code>FaqSingleContent</code>, <code>WpPostContent</code>, <code>page.$slug</code>, <code>vip/levels</code>) substituído por classes utilitárias inline. <code>package.json</code> enxuga uma dep + 22 linhas de <code>pnpm-lock.yaml</code>.</li>
<li class=""><strong>Ícones direct-imports + lazy <code>MainLeagues</code></strong> (<code>app/widgets/home-leagues/</code>). Drop do registry intermediário — agora cada componente faz <code>import Icon from "~icons/&lt;set&gt;/&lt;name&gt;"</code> direto. <code>MainLeaguesSquare</code> virou lazy chunk separado. 25 arquivos tocados, <strong>−564 linhas vs +454</strong>. Atualiza configs de <code>home-leagues</code> em 7 brands (7k-bet-br, cassino-bet-br, fi-7k-bet, ng-7k-bet, pb-bet, vera-bet-br, state77-com, x2b-bet).</li>
<li class=""><strong>CI Lighthouse manual</strong> (<code>.github/workflows/lighthouse.yml</code>). Workflow caller dispatchável via <code>workflow_dispatch</code> pra rodar Lighthouse on-demand contra preview/prod sem precisar de schedule fixo. Útil pra validar PRs pesados de UI antes de mergear.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cache-ssr-e-worker--modelo-per-devicecountrybuildid">Cache, SSR e Worker — modelo per-device/country/buildId<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#cache-ssr-e-worker--modelo-per-devicecountrybuildid" class="hash-link" aria-label="Link direto para Cache, SSR e Worker — modelo per-device/country/buildId" title="Link direto para Cache, SSR e Worker — modelo per-device/country/buildId" translate="no">​</a></h2>
<p>PR #390 (umbrella <code>performance → stage</code>, 25 arquivos, +840/−172) reescreveu o middleware do Worker pra fazer cache mais inteligente:</p>
<ul>
<li class=""><strong><code>workers/middleware.ts</code></strong> ganhou +306 linhas implementando cache de SSR particionado por <code>(device, country, bot, brand, buildId)</code>. Antes a chave de cache era global por path — agora bot crawler vê uma versão, mobile-pt-BR-7k vê outra, desktop-es-state77 vê outra. Bot detection isolado em <code>app/utils/bot.server.ts</code> (+24 linhas) e device detection em <code>app/utils/device-detect.ts</code>.</li>
<li class=""><strong>Service Worker (<code>public/sw.js</code>)</strong> atualizado (+92 linhas) pra propagar a nova chave de cache no client e respeitar invalidações por <code>buildId</code>.</li>
<li class=""><strong>Nova rota <code>/api/clear-cache</code></strong> (<code>app/routes/api/clear-cache.ts</code>) pra purge manual da edge cache de uma rota específica.</li>
<li class=""><strong><code>app/root.tsx</code></strong> ajustado com viewport correto pra acessibilidade + headers SSR alinhados ao novo modelo.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="layout--header-secundário-refatorado">Layout — header secundário refatorado<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#layout--header-secund%C3%A1rio-refatorado" class="hash-link" aria-label="Link direto para Layout — header secundário refatorado" title="Link direto para Layout — header secundário refatorado" translate="no">​</a></h2>
<ul>
<li class=""><strong>Refresh do <code>HeaderSecondaryNavButtons</code></strong> (PR #425) com ícones e items ricos por seção. Adicionado <code>app/config/layout/header-secondary-nav.ts</code> (config base) + <code>app/utils/sports-icons.ts</code> (registry de ícones por modalidade esportiva). <code>useSidebarSections</code> ajustado pra reutilizar a mesma estrutura. Tests do <code>resolve</code> reescritos pra cobrir os novos shapes. Brands com override próprio: <code>state77-com</code>, <code>vera-bet-br</code>.</li>
<li class=""><strong>Route gating do <code>headerSecondary</code></strong> (PR #426). O slot agora só renderiza nas rotas <code>home</code>, <code>casino</code> e <code>sports</code> — antes aparecia em todas as páginas, poluindo views de gamification, user, payments, etc. Resolução em <code>app/layouts/variants/header-secondary/resolve.ts</code> (+6 linhas) com 31 linhas de testes novos.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ui-e-componentes">UI e Componentes<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#ui-e-componentes" class="hash-link" aria-label="Link direto para UI e Componentes" title="Link direto para UI e Componentes" translate="no">​</a></h2>
<ul>
<li class=""><strong>Mouse drag em tempo real + setas no hover</strong> no <code>BannerSlide</code> multi-scroll (PR #407). <code>app/components/ui/Carousel.tsx</code> ganhou +184 linhas: drag responde durante o gesto (não só no release), setas aparecem em hover do desktop, e snap continua respeitado. Brand <code>cl-bet7k-com</code> já liga a feature.</li>
<li class=""><strong>Fix carousel <code>group-hover</code> collision</strong> (PR #410). O <code>group</code> do carousel disparava hover em cards filhos que também usavam <code>group-hover</code> — escopado pra named group (<code>group/carousel</code>), evitando que mover o mouse pelo carrossel ative o overlay de cards.</li>
<li class=""><strong>Card de favoritos / recentemente jogados com call-to-action</strong> quando vazio (PR #420). <code>EmptyRowPlaceholder</code> ganhou variant clicável; <code>FavoritesRow</code> e <code>RecentlyPlayedRow</code> agora mostram um placeholder que leva o usuário pra adicionar jogos. Config em <code>app/config/widgets/favorites-row.ts</code>.</li>
<li class=""><strong>Variants configuráveis pra <code>TournamentCard</code> e <code>MissionCard</code></strong> (PR #406, +1059/−381). Cada card ganhou 2 variants (<code>classic</code> e <code>stacked</code>) seguindo o pattern de domain primitives (mesmo de <code>GameCard</code>/<code>ProviderCard</code>). Internals compartilhados em <code>tournament-internals.tsx</code>. Brand controla via <code>app/config/widgets/tournament-card.ts</code> + <code>app/config/widgets/mission-card.ts</code>. Brands 7k-bet-br e cassino-bet-br ganharam overrides próprios.</li>
<li class=""><strong>Setas de overflow no <code>MethodSelector</code></strong> de depósito/saque (PR #414). Quando há mais métodos do que cabe na faixa visível, aparecem setas pra navegar lateralmente — substitui o scroll horizontal puro.</li>
<li class=""><strong>Título do torneio movido pra baixo do banner</strong> (PR #423) em <code>vip/tournaments/$id</code> — antes overlapava o banner em mobile.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--jogos">Cassino / Jogos<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#cassino--jogos" class="hash-link" aria-label="Link direto para Cassino / Jogos" title="Link direto para Cassino / Jogos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Esconder search trigger do header em listings</strong> (PR #412). Em <code>/games/category/:slug</code> e <code>/games/providers/:slug</code> o ícone de search do header some, e em vez disso aparece um pill de search dentro da página (<code>SearchPill.tsx</code>). Mesmo comportamento em <code>/sports/*</code>. Centralizou a regra em <code>app/utils/routes.ts</code> (+24 linhas).</li>
<li class=""><strong>Roleta do Craque Neto</strong> (PR #411, brand 7k-bet-br). Sidebar buttons ganharam suporte mais robusto a custom items — variants <code>Colored</code>, <code>Gradient</code> e <code>Grid</code> agora aceitam shapes ricos com mais campos opcionais. Brand 7k-bet-br adicionou item custom apontando pra <code>/lp/craque-neto-roulette</code> e nova <code>strategy.ts</code> de 21 linhas.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos">Pagamentos<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#pagamentos" class="hash-link" aria-label="Link direto para Pagamentos" title="Link direto para Pagamentos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Pré-seleção de shortcut default no depósito</strong> (PR #415). Ao abrir o <code>DepositModal</code>, o shortcut marcado como default na config da brand já vem selecionado (e o input já vem preenchido com o valor). <code>app/types/deposit.ts</code> ganha o flag <code>default?: boolean</code>. Brand 7k-bet-br configurou em <code>overrides/7k-bet-br/app/config/payments/deposit.ts</code>.</li>
<li class=""><strong>Mensagem de cupom inválido limpa</strong> (PR #418). <code>app/routes/api/payments/coupon.ts</code> parou de propagar a mensagem raw do BFF (que vinha em inglês ou com detalhes técnicos) pro UI — agora cai num fallback i18n controlado pelo front.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="seo">SEO<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#seo" class="hash-link" aria-label="Link direto para SEO" title="Link direto para SEO" translate="no">​</a></h2>
<ul>
<li class=""><strong>Redirect 301 de URL com letra maiúscula → minúscula</strong> (PR #419). <code>workers/middleware.ts</code> (+8 linhas) detecta paths com upper-case (ex: <code>/Casino/Slots</code>) e responde 301 pra versão lowercase. Evita conteúdo duplicado pro Google e bate com canonical.</li>
<li class=""><strong><code>og:image</code> agora usa o ícone PWA da brand</strong> como fallback (PR #422), em vez do logo do BFF (que era visualmente fraco em previews de WhatsApp/Twitter). Mudança de 1 linha em <code>app/routes/_layout.tsx</code>.</li>
<li class=""><strong>Footer: URLs do YouTube normalizadas pra <code>www.</code></strong> (PR #417). Evita o 301 que o YouTube faz em links sem <code>www</code>, melhorando crawl e performance percebida no clique.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="auth-e-recovery">Auth e Recovery<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#auth-e-recovery" class="hash-link" aria-label="Link direto para Auth e Recovery" title="Link direto para Auth e Recovery" translate="no">​</a></h2>
<ul>
<li class=""><strong>Recovery options com botão de live support</strong> (PR #427, fix <code>cant-recover-password</code>). Quando o usuário não consegue receber o e-mail/SMS de recovery, o <code>RecoverOptionsStep</code> agora mostra um botão direto pro chat ao vivo — antes ficava num beco sem saída.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="mobile--fixes">Mobile — fixes<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#mobile--fixes" class="hash-link" aria-label="Link direto para Mobile — fixes" title="Link direto para Mobile — fixes" translate="no">​</a></h2>
<ul>
<li class=""><strong>Espaço fantasma no footer + revert temporário do vaul</strong> (PR #405). <code>Modal.tsx</code> ajustado pra não criar <code>padding-bottom</code> parasita no body em iOS quando aberto. <code>app/entry.server.tsx</code> e <code>app/root.tsx</code> (+76 linhas) com fixes de viewport. PR também isola bot detection em <code>app/utils/bot.server.ts</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>7k-bet-br: nova landing <code>/lp/download-app</code></strong> (PR #384). Página dedicada de download do APK Android com 3 backgrounds responsivos (<code>bg-banner-desktop.png</code>, <code>bg-banner-mobile.png</code>, <code>bg-experience.png</code>) e redirect configurado em <code>routes/lp-redirects.ts</code> + <code>routes/lps.ts</code>. 337 linhas no template da LP em <code>routes/lps/download-app.tsx</code>.</li>
<li class=""><strong>release-gobbo (PR #404, +6425/−58, brand mega-bundle)</strong>: integra estratégia de autenticação configurável + enhances do <code>campaign-widget</code> em várias brands ao mesmo tempo.<!-- -->
<ul>
<li class=""><strong>7k-bet-br</strong>: novo <code>config/strategy/strategy.ts</code> (402 linhas), novo <code>config/ftd-checkin/ftd-checkin.ts</code> (14 linhas), atualiza <code>gamification.ts</code> e <code>campaign-widget.ts</code>.</li>
<li class=""><strong>cassino-bet-br</strong>: novo <code>config/strategy/strategy.ts</code> (402 linhas), <code>gamification.ts</code> atualizado, <code>campaign-widget.ts</code> (+11 linhas), novo asset <code>mini-games-gift-icon.png</code>.</li>
<li class=""><strong>vera-bet-br</strong>: novo <code>config/strategy/strategy.ts</code> (218 linhas), novo <code>ftd-checkin/ftd-checkin.ts</code> (12 linhas), <code>campaign-widget.ts</code> (+8 linhas), troca asset (<code>payday.png</code> removido, <code>mini-games-gift-icon.png</code> adicionado).</li>
<li class=""><strong>fi-7k-bet, ng-7k-bet</strong>: ajustes pequenos em <code>campaign-widget.ts</code>.</li>
<li class="">Docs internos da feature: <code>docs/features/ftd-checkin/overview.md</code> (366 linhas) + <code>docs/features/strategy/overview.md</code> (797 linhas).</li>
</ul>
</li>
<li class=""><strong>7k-bet-br: deposit shortcut default</strong> marcado em <code>overrides/7k-bet-br/app/config/payments/deposit.ts</code> (PR #415, +22 linhas).</li>
<li class=""><strong>7k-bet-br: sidebar-buttons custom item Craque Neto</strong> + nova <code>strategy.ts</code> (PR #411, +40 linhas combinadas).</li>
<li class=""><strong>state77-com, vera-bet-br: header-secondary-nav config própria</strong> (PR #425) — ambas brands declaram <code>overrides/&lt;brand&gt;/app/config/layout/header-secondary-nav.ts</code>.</li>
<li class=""><strong>7k-bet-br, cassino-bet-br: tournament-card e mission-card variants</strong> declaradas (PR #406).</li>
<li class=""><strong>cl-bet7k-com: home-banner ativa o multi-scroll variant</strong> (PR #407).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="infra--ci-front-ops">Infra / CI (front-ops)<a href="https://docs.c4ctus.com/changelog/2026/04/29/daily#infra--ci-front-ops" class="hash-link" aria-label="Link direto para Infra / CI (front-ops)" title="Link direto para Infra / CI (front-ops)" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>feat(deploy): send files to r2</code></strong> — workflow de deploy ganhou step pra subir assets pro R2 da Cloudflare (+13 linhas em <code>deploy.yml</code>).</li>
<li class=""><strong><code>feat(repos): cl-bet7k-com environment</code></strong> — nova entrada em <code>repos.yml</code> pro deploy da brand <code>cl.bet7k.com</code>. Cache config legada em <code>environments/stage-cl-bet7k-com/cache.yml</code> removida (a config nova vai inline em <code>repos.yml</code>).</li>
<li class=""><strong><code>fix(deploy): resolve CF zone via explicit cf_zone field</code></strong> — o step de SSR cache purge usava <code>origin_domain</code> pra query <code>GET /zones?name=</code>, que é strict-match. Pra subdomain deploys (<code>cl.bet7k.com</code>, <code>fi.7k.bet</code>, <code>pt.state77.com</code>) o apex zone nunca resolvia e o purge era silenciosamente skipado. Agora cada environment declara <code>cf_zone</code> explícito (com fallback compound-TLD-aware: <code>bet.br</code>, <code>com.br</code>, <code>co.uk</code>, etc) e o workflow usa esse campo direto.</li>
<li class=""><strong><code>refactor(deploy): wrangler.toml patching com BUILD_ID + runtime vars</code></strong> — o patch agora injeta <code>BUILD_ID</code> derivado do deploy ref (garante cache key invalidada a cada deploy, alinhado com o novo modelo de cache por buildId do middleware) e consolida runtime vars num único bloco <code>[vars]</code>. Cache policy e KV binding são apendados condicionalmente conforme disponibilidade.</li>
</ul>]]></content:encoded>
            <category>performance</category>
            <category>ui</category>
            <category>cassino</category>
            <category>pagamentos</category>
            <category>cache</category>
            <category>brand-overrides</category>
            <category>infra</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 28/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/28/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/28/daily</guid>
            <pubDate>Tue, 28 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Sistema de Ícones — migração lucide-react → unplugin-icons]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sistema-de-ícones--migração-lucide-react--unplugin-icons">Sistema de Ícones — migração lucide-react → unplugin-icons<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#sistema-de-%C3%ADcones--migra%C3%A7%C3%A3o-lucide-react--unplugin-icons" class="hash-link" aria-label="Link direto para Sistema de Ícones — migração lucide-react → unplugin-icons" title="Link direto para Sistema de Ícones — migração lucide-react → unplugin-icons" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>lucide-react</code> removido por completo</strong> e substituído por <code>unplugin-icons</code> apoiado em três datasets Iconify (<code>@iconify-json/lucide</code>, <code>@iconify-json/simple-icons</code>, <code>@iconify-json/mdi</code>) mais uma coleção <code>custom</code> lida via <code>FileSystemIconLoader</code> de <code>app/icons/custom/</code>. Cada ícone agora resolve para um componente SVG inline em build-time — zero fetch em runtime, SSR-safe no Cloudflare Workers, e o chunk manual <code>vendor-lucide</code> foi descontinuado.</li>
<li class=""><strong>API de uso por dataset:</strong> <code>import Trophy from "~icons/lucide/trophy"</code> para ícones genéricos de UI, <code>~icons/simple-icons/&lt;name&gt;</code> para logos de marca (WhatsApp, Facebook, X, Instagram, TikTok), <code>~icons/mdi/&lt;name&gt;</code> para esportes e equivalentes a emojis, e <code>~icons/custom/&lt;name&gt;</code> para SVGs próprios em <code>app/icons/custom/</code> (auto-discovered). Naming kebab-case obrigatório (<code>AlarmClock</code> → <code>alarm-clock</code>, <code>XCircle</code> → <code>x-circle</code>).</li>
<li class=""><strong>Tipo <code>IconComponent</code> substitui <code>LucideIcon</code></strong> em todo o codebase via <code>app/types/icon.ts</code> (superset de <code>SVGProps&lt;SVGSVGElement&gt;</code> com alias legacy de <code>size</code>). <code>unplugin-icons</code> não suporta a prop <code>size</code> do Lucide — codemod <code>scripts/migrate-icon-size-prop.mjs</code> converteu 471 ocorrências de <code>size={X}</code> para <code>width={X} height={X}</code>.</li>
<li class=""><strong>Registry string-driven</strong> em <code>app/icons/registry.ts</code> para configs que referenciam ícones por string (ex: <code>icon: "mdi:soccer"</code>) consumidos via <code>&lt;Icon name="..." /&gt;</code> ou <code>&lt;SmartIcon icon={...} /&gt;</code> (smart dispatch que renderiza o ícone registrado ou cai no texto literal — preserva configs legados com emoji).</li>
<li class=""><strong>Exceções aceitáveis para SVG inline:</strong> spinners/loaders animados customizados, checkmarks de success animados, indicadores visuais brand-specific que não existem em nenhum dataset Iconify, e seals de regulação (ANJL, Anatel, Compulsafe) servidos via <code>&lt;img&gt;</code> apontando para <code>public/assets/seals/</code>.</li>
<li class=""><strong>Migração automatizada via codemods</strong> em <code>scripts/</code>: <code>migrate-lucide-to-unplugin-icons.mjs</code> (converte imports nomeados em default imports kebab-case, ordenado via <code>biome check --write</code>), <code>migrate-icon-size-prop.mjs</code> (size → width/height em JSX) e <code>migrate-emoji-icons.mjs</code> (emoji literais → IconName registrado). Fix posterior em <code>toKebabCase</code> para acrônimos consecutivos (<code>XCircle</code> → <code>x-circle</code>, antes virava <code>xcircle</code> quebrado).</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="arquitetura-de-layout--shell-architecture">Arquitetura de Layout — Shell Architecture<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#arquitetura-de-layout--shell-architecture" class="hash-link" aria-label="Link direto para Arquitetura de Layout — Shell Architecture" title="Link direto para Arquitetura de Layout — Shell Architecture" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>DefaultLayout</code> deixa de ser monolítico</strong> e vira um dispatcher que delega o macro-arrangement do viewport a um componente <code>Shell</code> resolvido via <code>layoutConfig.shell</code>. Dois shells nascem prontos: <code>header-top</code> (default — header full-width sticky no topo, comportamento atual de todas as brands) e <code>split-shell</code> (sidebar full-height à esquerda do viewport, topbar + header + main + footer empilhados na coluna direita — replica o <code>7k.bet.br</code> produção).</li>
<li class=""><strong>Quatro camadas independentes e ortogonais</strong> na composição do layout: <code>shell</code> (macro-arrangement), <code>structure</code> (colunas internas + flags on/off), <code>slots</code> (variant de cada peça: header, sidebar, footer, rightPanel, banner) e <code>componentVariants</code> (variants globais de primitivas compartilhadas). Trocar uma camada não invalida as outras — <code>cl-bet7k-com</code> adota <code>split-shell</code> mantendo <code>slots.sidebar = "sidebar-narrow"</code> e <code>structure.columns = "left-main"</code>.</li>
<li class=""><strong>Sidebars shell-agnósticas</strong> via CSS custom properties (<code>--sidebar-top</code>, <code>--sidebar-h</code>, <code>--sidebar-w-collapsed</code>, <code>--sidebar-w-expanded</code>) publicadas pelo shell ativo — qualquer variant funciona dentro de qualquer shell sem fork.</li>
<li class=""><strong>Mobile permanece universal:</strong> independente do shell, sidebar vira drawer overlay e header sticky no topo. Shell só altera comportamento em <code>lg+</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sidebar-accordion-terceira-variant">Sidebar Accordion (terceira variant)<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#sidebar-accordion-terceira-variant" class="hash-link" aria-label="Link direto para Sidebar Accordion (terceira variant)" title="Link direto para Sidebar Accordion (terceira variant)" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>sidebar-accordion</code></strong> inspirada no layout legado 7k entra como terceira opção (junto com <code>sidebar-narrow</code> default e <code>sidebar-wide</code>). Header <code>h-[72px]</code> com logo + toggle expand/collapse, widget <code>SidebarButtons</code> no topo, seções full-width como acordeões (chevron rotativo, body com <code>grid-template-rows: 0fr → 1fr</code> para altura <code>auto</code> animada) e bloco de bottom items abaixo, separados por <code>border-t</code> full-width.</li>
<li class=""><strong>Comportamento route-aware:</strong> um <code>useEffect([pathname])</code> abre <code>casino</code> e fecha <code>sports</code> ao navegar para rota de cassino, e o inverso para esportes. Home e rotas neutras preservam a escolha manual.</li>
<li class=""><strong><code>&lt;SidebarPillItem&gt;</code> compartilhado</strong> com três modos (<code>link</code> / <code>external</code> / <code>action</code>) e visual gradient pill alinhado ao widget <code>SidebarButtons</code> — renderiza accordion items, grupos aninhados (popular, top-sports) e bottom items (promotions, help center, suporte ao vivo, referral, install app, blog, FAQ).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="widgets-configuráveis-top-games-home-banner-game-card-provider-card">Widgets Configuráveis (top-games, home-banner, game-card, provider-card)<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#widgets-configur%C3%A1veis-top-games-home-banner-game-card-provider-card" class="hash-link" aria-label="Link direto para Widgets Configuráveis (top-games, home-banner, game-card, provider-card)" title="Link direto para Widgets Configuráveis (top-games, home-banner, game-card, provider-card)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Top-games migrado para <code>app/widgets/top-games/</code></strong> seguindo a convenção do <code>sidebar-buttons</code>. Tipos centralizados em <code>app/types/top-games.ts</code> (<code>TopGamesRankStyle</code>, <code>TopGamesDecoration</code> discriminado <code>none | asset</code>, <code>TopGamesConfig</code>, <code>TopGamesVariantProps</code>) e config única em <code>app/config/widgets/top-games.ts</code> overrideable por brand via file-replacement. Nova variant <code>corner-diagonal</code> replica o widget legado 7k mantendo brand-awareness via theme tokens.</li>
<li class=""><strong>Home-banner extraído para <code>app/widgets/home-banner/</code></strong> com config <code>HomeBannerConfig</code> brand-overrideable. Variant <code>single-fade</code> (default, reusa <code>~/components/ui/Slideshow</code> com 1 slide, fade, autoplay 6s, preserva o visual pré-refactor) e <code>multi-scroll</code> (reusa <code>~/components/ui/Carousel</code> com N cards de largura fixa e scroll-snap). Dimensões propagadas via CSS custom properties consumidas por classes Tailwind arbitrary literais (<code>w-[var(--hb-w-d)]</code>) — JIT escaneia, runtime substitui.</li>
<li class=""><strong><code>GameCard</code> virou config-driven dispatcher</strong> (em vez de monolito de 305 linhas) com duas variants: <code>classic</code> (default) e <code>stacked</code>. Componente permanece em <code>app/components/games/</code> (é primitive de domínio usado em ~9 surfaces — mover causaria churn de imports sem ganho). Tipos em <code>app/types/game-card.ts</code>, config em <code>app/config/widgets/game-card.ts</code>, variant key declarada via <code>componentVariants.gameCard</code> no <code>layoutConfig</code>.</li>
<li class=""><strong><code>ProviderCard</code> extraído</strong> do inline em <code>ProvidersCarousel</code> para dispatcher com variants <code>classic</code> (default) e <code>logo-only</code>. Mesma estrutura: tipos em <code>app/types/provider-card.ts</code>, config em <code>app/config/widgets/provider-card.ts</code>, variant key em <code>componentVariants.providerCard</code>.</li>
<li class=""><strong>Cards de detalhe de jogo (<code>GameStats</code>, <code>GameWinners</code>)</strong> ganham per-brand decoration + gradient config via opt-in fields — sem variant system nem reorg estrutural; defaults preservam o look atual de todas as brands.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="reorganização-de-configs">Reorganização de Configs<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#reorganiza%C3%A7%C3%A3o-de-configs" class="hash-link" aria-label="Link direto para Reorganização de Configs" title="Link direto para Reorganização de Configs" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>features.ts</code> dividido por domínio:</strong> novo <code>app/config/features/account.ts</code> agrupa as 5 flags account-scoped (<code>accountSetLimits</code>, <code>accountTimeoutLimits</code>, <code>pixEvpEnabled</code>, <code>redeemableRewards</code>, <code>userPreferences</code>); novo <code>app/config/features/auth.ts</code> agrupa as 5 flags do fluxo de auth (registro + password recovery). Padrão para evitar inflar <code>features.ts</code> quando um domínio cresce.</li>
<li class=""><strong>Notificações consolidadas em <code>app/config/widgets/</code>:</strong> lista ordenada de tipos ativos sai de <code>featuresConfig.topbarNotifications</code> / <code>.bottomNotifications</code> e passa a viver junto com a metadata e parâmetros em <code>widgets/topbar-notifications.ts</code> / <code>widgets/bottom-notifications.ts</code> — fim do double source-of-truth.</li>
<li class=""><strong>Home-leagues vira widget config-driven</strong> com variants <code>default</code> (round avatars, agora prefere <code>iconName</code> brand quando declarado, country flag como fallback gracioso) e <code>square</code> (cards 104×144 verticais replicando o layout legado 7k — região superior diagonal com gradient, label <code>title</code>/<code>subtitle</code> em duas linhas, brand-aware via theme tokens).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="performance--home-cold-start">Performance — Home Cold-Start<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#performance--home-cold-start" class="hash-link" aria-label="Link direto para Performance — Home Cold-Start" title="Link direto para Performance — Home Cold-Start" translate="no">​</a></h2>
<ul>
<li class=""><strong>Loader da home filtra de <code>getAllGames()</code></strong> em vez de fan-out de N chamadas paralelas <code>getDetail</code> para resolver favoritos. <code>allGames</code> já é cacheado como entry quente; 1 cache hit + <code>Map.get</code> por slug substitui até 30 round-trips ao BFF. Mesma migração aplicada em <code>/favorites</code>. <code>FAVORITE_GAMES_RESOLVE_LIMIT</code> na home reduzido de 30 → 15 para casar com o card count visível em <code>FavoritesRow</code>.</li>
<li class=""><strong>Sugestões para <code>FavoritesRow</code> e <code>RecentlyPlayed</code> agora anon-gated.</strong> As duas rows são login-gated no render, mas o loader as computava para todos quando <code>rowSuggestionsInEmptyStates</code> estava true — anon SSR pagava ~200-500ms cold e ~10-20KB de payload descartado. Gate explícito.</li>
<li class=""><strong><code>/api/games/by-slugs</code> filtra de <code>allGames</code></strong> em vez de fan-out de <code>getDetail</code> por slug — mesmo padrão da migração do loader.</li>
<li class=""><strong>Icon registry split em duas etapas</strong> (<code>keys-only</code> + <code>Icon</code> lazy) corta <code>SectionTitle</code> de 142KB para 5.6KB. O registry estaticamente importava 60+ componentes SVG; qualquer um que importasse <code>&lt;SmartIcon&gt;</code> (primitive usada em 14+ componentes da home/cassino) arrastava o registry inteiro para o chunk graph.</li>
<li class=""><strong><code>tailwind.config.js</code> corrigido</strong> para incluir <code>overrides/**/app/**</code> no content scan — classes Tailwind usadas só em arquivos de override deixam de ser purgadas em prod.</li>
<li class=""><strong>Bundle analyzer (<code>ANALYZE=1</code>) + chunk warnings mais apertados + <code>lighthouserc</code></strong> adicionados para gating de regressão de performance.</li>
<li class=""><strong><code>@cactus-agents/platform-cache</code> bumpado para 0.11.0</strong> com KV snapshots para warm tier cross-datacenter, single-flight coalescing e <code>cache_decision</code> logging estruturado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>cl-bet7k-com:</strong> ativação completa do tema legado 7k — <code>split-shell</code> com sidebar widths legacy, sidebar-accordion variant, header stacked com subheader route-aware, top-games corner-diagonal, home-leagues square com glyphs próprios (20 logos de liga + 3 sport glyphs importados do legado, mais SVG da Primera División chilena), provider-card <code>logo-only</code>, game-card <code>stacked</code>, decoration brandizada em <code>GameStats</code> e <code>GameWinners</code>, home-banner multi-scroll, content max-width alinhado ao 7k produção (1440px), search row da home desabilitada, topbar notifications desativadas, e migração de configs com emoji literais para ícones MDI vetoriais.</li>
<li class=""><strong>7k-bet-br:</strong> replica do layout/tema cl-bet7k-com aplicada à brand BR — mesmo conjunto de variants e configs. Adicionado <code>FeaturedGameWidget</code> exclusivo (Aviator 7K com preview animado em GIF, thumbnail desktop opcional, badge "EXCLUSIVO" SVG, ícone <code>custom:destaque-aviator</code>, slug <code>banana/aviator-7k</code>) inserido após a row de top-games em <code>home-rows.legacy.ts</code>. Search row da home desabilitada. CDN-CGI image proxy temporariamente desativado até stage worker ser configurado.</li>
<li class=""><strong>betpontobet-bet-br:</strong> mantém ícones emoji intencionalmente em row e catalog configs (não migrou para MDI vetorial — decisão de brand).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/gamification</code></strong> ganha campo opcional <code>smartico.exclusiveTournaments?: string[]</code> em <code>GamificationConfig</code>. IDs listados são tratados como invite-only — filtrados da lista pública e exibidos apenas quando a URL carrega <code>?exclusive</code>, <code>?premium</code>, <code>?telegram</code> ou <code>?whatsapp</code>. Originalmente uma feature 7k-only do legado Vue (env <code>SMARTICO_EXCLUSIVE_TOURNAMENTS</code>), promovida ao SDK para qualquer brand Smartico-backed poder optar. Backward compatible (campo opcional).</li>
<li class=""><strong><code>@cactus-agents/platform-cache 0.11.0</code></strong> + <strong><code>@cactus-agents/gamification</code></strong> bumpados nas dependências do base.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="docs--infra">Docs / Infra<a href="https://docs.c4ctus.com/changelog/2026/04/28/daily#docs--infra" class="hash-link" aria-label="Link direto para Docs / Infra" title="Link direto para Docs / Infra" translate="no">​</a></h2>
<ul>
<li class=""><strong>Nova doc <code>architecture/icon-system.md</code></strong> cobrindo a migração lucide-react → unplugin-icons, datasets disponíveis, naming kebab-case, registry string-driven, exceções aceitáveis para SVG inline e troubleshooting de codemods.</li>
<li class=""><strong>Nova doc <code>architecture/performance-psi.md</code></strong> com requisito duro de PSI (LCP, FCP, CLS, TBT) para mudanças visuais above-the-fold, checklist de CLS = 0, padrões de LCP image, valores dinâmicos via CSS custom properties + Tailwind arbitrary literal, e regra "zero carousel libs" (estender <code>Slideshow.tsx</code> ou <code>Carousel.tsx</code> em <code>app/components/ui/</code>).</li>
<li class=""><strong><code>architecture/layout-composition.md</code> expandido</strong> com a seção sobre as quatro camadas independentes (shell, structure, slots, componentVariants), descrições detalhadas dos shells <code>header-top</code> e <code>split-shell</code>, e papel de cada camada na composição.</li>
<li class=""><strong>Sidebar interna</strong> ganha entradas para <code>icon-system</code> e <code>performance-psi</code>.</li>
</ul>]]></content:encoded>
            <category>icon-system</category>
            <category>shell-architecture</category>
            <category>theme-7k</category>
            <category>widgets</category>
            <category>performance</category>
            <category>gamification</category>
            <category>brand-overrides</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 27/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/27/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/27/daily</guid>
            <pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Sidebar Buttons widget + reorg de app/config/]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sidebar-buttons-widget--reorg-de-appconfig">Sidebar Buttons widget + reorg de <code>app/config/</code><a href="https://docs.c4ctus.com/changelog/2026/04/27/daily#sidebar-buttons-widget--reorg-de-appconfig" class="hash-link" aria-label="Link direto para sidebar-buttons-widget--reorg-de-appconfig" title="Link direto para sidebar-buttons-widget--reorg-de-appconfig" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo widget <code>sidebar-buttons</code></strong> substitui o <code>VipHighlightButton</code> hardcoded por um sistema config-driven com <strong>catálogo typed (closed enum) de 8 intents</strong>: <code>casino</code>, <code>sports</code>, <code>tournaments</code>, <code>missions</code>, <code>referral</code>, <code>rewards</code>, <code>mini-games</code>, <code>promotions</code>. Cada intent tem rota canônica + i18n key + ícone default (Lucide) em <code>app/widgets/sidebar-buttons/intents.ts</code> — brand customiza só o visual (label, icon, image, color, gradient, sublabel, badge), nunca a rota. Para CTAs one-off fora do catálogo, brand usa <code>customItems</code> (label + href diretos); para UI dedicada, há o special <code>sponsor-cta</code>.</li>
<li class=""><strong>3 variants visuais</strong> disponíveis: <code>colored</code> (pilha vertical full-width com gradient diagonal — mostra bônus dinâmico do referral), <code>gradient</code> (pills compactas com gradient horizontal accent → muted, single-line, look 7k legado) e <code>grid</code> (3-col tile grid com decoration watermark, look Vera). <code>variant: null</code> desliga o widget na brand.</li>
<li class=""><strong>Hierarquia de override de cor em 3 tiers</strong>: <code>gradient: [from, to]</code> (tupla, override completo) &gt; <code>color: "#hex"</code> (só accent) &gt; theme tokens brand-aware (<code>bg-sidebar-button-bg</code>). Sem override per-item, cada brand vê o widget pintado automaticamente nas suas próprias cores via tokens do tema.</li>
<li class=""><strong>Wiring no <code>SidebarNarrow.tsx</code></strong> lê <code>sidebarButtonsConfig</code> e delega pro <code>&lt;SidebarButtons /&gt;</code>. O <code>VipHighlightButton</code> foi removido — não há callers remanescentes.</li>
<li class=""><strong>Reorg do <code>app/config/</code> em 14 subpastas semânticas</strong> (<code>theme/</code>, <code>layout/</code>, <code>widgets/</code>, <code>sections/</code>, <code>catalog/</code>, <code>routes/</code>, <code>analytics/</code>, <code>features/</code>, <code>scripts/</code>, <code>legal/</code>, <code>seo/</code>, <code>payments/</code>, <code>gamification/</code>, <code>sports/</code>). Imports passam a usar caminho completo sem o sufixo <code>.config</code>: <code>~/config/theme/colors</code>, <code>~/config/layout/composition</code>, <code>~/config/widgets/sidebar-buttons</code>, <code>~/config/routes/paths</code>. Mesmo reorg replicado nos 13 brand overrides — mantém o lookup do <code>brandOverridesPlugin</code> em sincronia file-replacement.</li>
<li class=""><strong>Brand resolver compartilhado</strong> (<code>vite-plugins/brand-resolver.mjs</code>) consolida três blocos <code>existsSync(overrides/...)</code> duplicados (Vite plugin, <code>app/router/routes.ts</code> em vite-node, <code>tailwind.config.js</code> em PostCSS) em uma única source of truth: <code>toBrandOverrideKey</code>, <code>findBrandOverride</code>, <code>resolveBrandFile</code>.</li>
<li class=""><strong>Asset dedup</strong>: <code>fortune-fruits.mp4</code> e <code>fortune-snake.mp4</code> movidos para <code>public/assets/thumbs/</code> no base; cópias idênticas removidas de 6 brand overrides (validado por md5).</li>
<li class=""><strong>Bump <code>@cactus-agents/i18n</code> para <code>0.71.0</code></strong> com as keys do namespace <code>layout:sidebar-buttons.*</code> nos 4 locales (pt-br, pt, es, en).</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="home--sugestões-em-rows-vaziasescassas">Home — sugestões em rows vazias/escassas<a href="https://docs.c4ctus.com/changelog/2026/04/27/daily#home--sugest%C3%B5es-em-rows-vaziasescassas" class="hash-link" aria-label="Link direto para Home — sugestões em rows vazias/escassas" title="Link direto para Home — sugestões em rows vazias/escassas" translate="no">​</a></h2>
<ul>
<li class=""><strong>Rows de Favoritos e Jogos recentes</strong> ganham fallback automático: quando o user logado tem menos de 7 itens, a row renderiza no mesmo carrossel um placeholder dashed + divisor "Sugestões" + até 10 jogos sugeridos a 50% de opacidade (sobe pra 100% no hover/focus). A row de "Jogos recentes" passa a aparecer mesmo vazia (antes sumia).</li>
<li class=""><strong>Fonte das sugestões</strong>: <code>getSuggestions()</code> novo no <code>GamesCacheService</code> — lê catálogo completo via <code>getAllGames()</code>, ordena por stats cache-only (zero BFF round-trip) e filtra métrica &gt; 0. Favoritos usa <code>players_month</code> (top 10), recentes usa <code>players_today</code> (top 10). Em cold start sem stats, completa com random seeded por dia UTC pra ordem estável dentro do mesmo dia.</li>
<li class=""><strong>Gated pela flag opt-in <code>rowSuggestionsInEmptyStates</code></strong>, ligada em <code>vera-bet-br</code>, <code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>betpontobet-bet-br</code>, <code>cl-bet7k-com</code>. Default <code>false</code> nas demais 9 brands para não regredir visual.</li>
<li class=""><strong>Componentes novos</strong>: <code>EmptyRowPlaceholder</code> (card dashed com dimensões idênticas ao <code>GameCard</code> — zero layout shift) e <code>SuggestionsDivider</code> (separador vertical "— → Sugestões → —").</li>
<li class=""><strong>FLIP safety mantida</strong> no <code>FavoritesRow</code>: o <code>cardElementsRef</code> só registra cards de favoritos reais; sugestões não participam do reflow.</li>
<li class=""><strong>8 testes unit</strong> novos cobrindo <code>getSuggestions</code> (top-N, exclude, cold-start seed, short-circuits, métricas diferentes).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/04/27/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Documentação do widget pattern em <code>architecture/layout-composition.md</code></strong> com nova seção "Widgets — UI opcional embebida em slots". Documenta o pattern <code>{ variant: VariantKey | null, items: Item[] }</code>, lista os 5 widgets ativos hoje (<code>sidebar-buttons</code>, <code>topbar-notifications</code>, <code>bottom-notifications</code>, <code>campaign-widget</code>, <code>home-leagues</code>) e detalha o catálogo de 8 intents do sidebar-buttons, as 3 variants e a hierarquia de override (gradient &gt; color &gt; theme tokens).</li>
<li class=""><strong><code>forking/override-files.md</code> reescrito</strong> para refletir a estrutura de 14 subpastas. Nova árvore, tabelas escopo-a-escopo e regra de <strong>file-replacement (não deep-merge)</strong> chamada explicitamente — brands não devem esperar merge parcial em overrides.</li>
<li class=""><strong><code>template/layout.md</code></strong> ganhou tabela de Widgets + exemplo de config do sidebar-buttons mostrando uso brand-side com <code>intent</code> + <code>gradient</code>.</li>
<li class=""><strong>Sweep de paths em 18 docs (externa + interna)</strong> apontando pra nova estrutura de subpastas: <code>app/config/theme.config.ts</code> → <code>app/config/theme/colors.ts</code>, <code>routes.paths.ts</code> → <code>routes/paths.ts</code>, <code>layout.config.ts</code> → <code>layout/composition.ts</code>, <code>features.config.ts</code> → <code>features/features.ts</code> e equivalentes nos demais escopos. Pure path sweep, sem mudança semântica.</li>
</ul>]]></content:encoded>
            <category>widgets</category>
            <category>sidebar-buttons</category>
            <category>config-reorg</category>
            <category>home</category>
            <category>suggestions</category>
            <category>brands</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 26/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/26/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/26/daily</guid>
            <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Bottom Notification Widget (refator pra padrão topbar)]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="bottom-notification-widget-refator-pra-padrão-topbar">Bottom Notification Widget (refator pra padrão topbar)<a href="https://docs.c4ctus.com/changelog/2026/04/26/daily#bottom-notification-widget-refator-pra-padr%C3%A3o-topbar" class="hash-link" aria-label="Link direto para Bottom Notification Widget (refator pra padrão topbar)" title="Link direto para Bottom Notification Widget (refator pra padrão topbar)" translate="no">​</a></h2>
<ul>
<li class=""><strong>Substituição do <code>InstallAppFloatingWidget</code> por <code>BottomNotification</code> config-driven</strong> (<code>app/components/layout/BottomNotification.tsx</code>). O widget flutuante deixa de ser específico só pra "instalar app" e vira uma fila de tipos configuráveis, espelhando a arquitetura do topbar (types, store, handlers, definitions queue). O componente legado (<code>InstallAppFloatingWidget.tsx</code>, ~200 linhas) foi removido.</li>
<li class=""><strong>Seis tipos de notificação suportados:</strong> <code>download_app</code>, <code>push_notify</code>, <code>rewards_available</code>, <code>referral</code>, <code>telegram</code> e <code>tournament</code>. Cada brand define quais ficam habilitados e em qual ordem de prioridade — o primeiro que passa no <code>shouldShow</code> renderiza, igual à lógica do topbar.</li>
<li class=""><strong>Separação dados × runtime:</strong> <code>app/config/bottom-notifications.config.ts</code> carrega só dados puros (lista de <code>enabled</code>, <code>rotationDelaySeconds</code>, <code>params</code> por tipo) e é overrideable por brand. A lógica runtime (ícones Lucide, <code>ctaHref</code>/<code>onCta</code>, predicados <code>shouldShow</code>, estratégia de storage) fica em <code>app/layouts/bottom-notification/handlers.ts</code> — não overrideable, espelha 1:1 o <code>buildTopbarDefinitions</code>.</li>
<li class=""><strong><code>rotationDelaySeconds</code> configurável:</strong> com <code>0</code> (default) o swap pro próximo widget elegível é instantâneo após dismiss, igual ao topbar. Valores maiores dão um "respiro" antes do próximo slide-up.</li>
<li class=""><strong>Revalidação reativa em auth/rewards:</strong> quando o usuário loga, desloga ou ganha um reward, a fila reavalia automaticamente (paridade com o topbar) — sem precisar navegar pra atualizar o estado do widget.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="theme-tokens--storage">Theme Tokens &amp; Storage<a href="https://docs.c4ctus.com/changelog/2026/04/26/daily#theme-tokens--storage" class="hash-link" aria-label="Link direto para Theme Tokens &amp; Storage" title="Link direto para Theme Tokens &amp; Storage" translate="no">​</a></h2>
<ul>
<li class=""><strong>Novo bloco de tokens <code>bottom-notification-*</code></strong> em <code>theme.config.ts</code> (bg, text, icon-bg, icon-text, cta-bg, cta-text, border). Registrado no <code>tailwind.config.js</code> e propagado pra todas as 13 brands com valores específicos, permitindo cor distinta entre topbar e bottom-notification em cada brand.</li>
<li class=""><strong>Cookie/event/data-attr keys prefixados <code>bottom_*</code></strong> (vs <code>topbar_*</code> da counterpart). O estado de dismiss do widget flutuante é independente do topbar — usuário fechar a notificação no rodapé não some com o topbar equivalente, e vice-versa.</li>
<li class=""><strong>Floating stack registra só depois do slide-in</strong> (<code>app/store/floatingStack.ts</code>). Antes, o espaço era reservado prematuramente, o que podia empurrar layout em rotas que ainda não tinham decidido renderizar o widget.</li>
<li class=""><strong>Pin de largura desktop em 400px</strong> (<code>app/components/layout/BottomNotification.tsx</code>): substitui <code>lg:min-w-[300px]</code> por <code>lg:w-[400px]</code> pro widget manter tamanho estável em desktop ao invés de crescer com o conteúdo do CTA.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="i18n-no-core-cactus-agentsi18n-0702">i18n no Core (<code>@cactus-agents/i18n</code> 0.70.2)<a href="https://docs.c4ctus.com/changelog/2026/04/26/daily#i18n-no-core-cactus-agentsi18n-0702" class="hash-link" aria-label="Link direto para i18n-no-core-cactus-agentsi18n-0702" title="Link direto para i18n-no-core-cactus-agentsi18n-0702" translate="no">​</a></h2>
<ul>
<li class=""><strong>Bloco <code>layout.bottom_notification</code> adicionado nos 4 locales</strong> (pt-br, pt, es, en) em <code>packages/i18n/locales/&lt;locale&gt;/layout.json</code>. Estrutura: <code>close</code> + 6 sub-blocos (<code>download_app</code>, <code>push_notify</code>, <code>rewards_available</code>, <code>referral</code>, <code>telegram</code>, <code>tournament</code>), cada um com <code>title</code>, <code>subtitle</code> e <code>cta</code>. Espelha o shape existente de <code>topbar</code> — fica trivial reaproveitar copy entre as duas superfícies.</li>
<li class=""><strong>Variável <code>{{appName}}</code> preservada</strong> no título de <code>download_app</code> (paridade com a chave legada <code>install_app.floating_title</code> que já usava o pattern). Brand não precisa hard-codar o nome do app na cópia.</li>
<li class=""><strong>Bump do consumidor:</strong> o base atualizou <code>@cactus-agents/i18n</code> pra <code>0.70.2</code> no commit que landa o widget — sem essa versão, o componente renderiza chaves cruas.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/26/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>Topbar das 7k alinhada (br/fi/ng):</strong> <code>bg</code> mudou de <code>#373b5b</code> pra <code>#282b48</code> (mais escuro), <code>icon-bg</code> virou <code>#a1cd3d</code> (verde brand) e <code>icon-text</code> foi pra <code>#16182a</code> em todas as três variantes 7k (<code>7k-bet-br</code>, <code>fi-7k-bet</code>, <code>ng-7k-bet</code>). Os 3 brands compartilham o mesmo block de tema do topbar agora.</li>
<li class=""><strong>Config de bottom-notification propagada nas 13 brands:</strong> <code>7k-bet-br</code>, <code>betpontobet-bet-br</code>, <code>casateste-com</code>, <code>cassino-bet-br</code>, <code>fi-7k-bet</code>, <code>ng-7k-bet</code>, <code>pb-bet</code>, <code>ph-state77-com</code>, <code>pt-state77-com</code>, <code>rj-bet</code>, <code>state77-com</code>, <code>vera-bet-br</code> e <code>x2b-bet</code>. Cada brand recebeu seu próprio <code>bottom-notifications.config.ts</code> (lista <code>enabled</code> espelhando a lista do topbar daquela brand), <code>features.config.ts</code> (8 linhas adicionais por brand) e <code>theme.config.ts</code> (11 linhas: tokens <code>bottom-notification-*</code> específicos da brand).</li>
</ul>]]></content:encoded>
            <category>layout</category>
            <category>bottom-notification</category>
            <category>topbar</category>
            <category>theme</category>
            <category>brand-overrides</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 25/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/25/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/25/daily</guid>
            <pubDate>Sat, 25 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[SEO e FAQ]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="seo-e-faq">SEO e FAQ<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#seo-e-faq" class="hash-link" aria-label="Link direto para SEO e FAQ" title="Link direto para SEO e FAQ" translate="no">​</a></h2>
<ul>
<li class=""><strong>Port do SEO legado</strong> (PR #352): infraestrutura completa de SEO com verificação, JSON-LD e helpers no base, descrições ricas por jogo (<code>games-seo.server.ts</code>) com templating de <code>{brand_name}</code>, conteúdo SEO + JSON-LD por liga em <code>sports-seo.config.ts</code>, e correções de gênero feminino nos artigos (A/Da/Na/Pela/À) para brands com nomes femininos como "vera". Bump de <code>@cactus-agents/i18n</code> para <code>0.69.0</code> para consumir as chaves do core ao invés de tabelas de locale inline.</li>
<li class=""><strong>Sistema estruturado de FAQ</strong> (mesmo PR #352): rotas, componentes e helpers para FAQs, com bloco <code>GameFaqPreview</code> na página de detalhe do jogo e redirect do FAQ legado. FAQs populadas para <code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>vera-bet-br</code> e <code>betpontobet-bet-br</code> (≈725-790 entradas cada em <code>faq.server.ts</code>).</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="header-e-notificações">Header e Notificações<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#header-e-notifica%C3%A7%C3%B5es" class="hash-link" aria-label="Link direto para Header e Notificações" title="Link direto para Header e Notificações" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sino de notificações dinâmico no header</strong> (PR #354): novo componente <code>HeaderNotificationBell</code> com badge de notificações pendentes, helper <code>notification-badge.ts</code> (com testes) e topbar <code>rewards_available</code>. Feature flags propagadas para todos os 13 brand overrides; <code>tailwind.config.js</code> recebeu tokens de cor para o badge.</li>
<li class=""><strong>Contraste do sino corrigido</strong> (PR #356): em brands onde <code>primary === header-bg</code>, o ícone do sino sumia. Ajuste em <code>HeaderNotificationBell.tsx</code> força token de cor compatível com o fundo do header.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="preferências-de-usuário">Preferências de Usuário<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#prefer%C3%AAncias-de-usu%C3%A1rio" class="hash-link" aria-label="Link direto para Preferências de Usuário" title="Link direto para Preferências de Usuário" translate="no">​</a></h2>
<ul>
<li class=""><strong>Página de preferências em <code>/user/config</code></strong> (PR #357): nova seção <code>PreferencesSection</code> gated por feature flag <code>userPreferences</code>, com componente reutilizável <code>RadioField</code>, endpoint interno <code>POST /api/user/update-preferences</code> e redirect pós-login para a landing preferida do usuário (<code>landing-preference.ts</code>). Bump de <code>@cactus-agents/accounts</code> para <code>0.18.0</code> e <code>@cactus-agents/i18n</code> para <code>0.70.1</code>.</li>
<li class=""><strong>Stack das opções no mobile</strong> (PR #358): <code>PreferencesSection</code> ajustada para empilhar opções verticalmente em telas pequenas ao invés de quebrar layout em flex-row.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--jogos">Cassino / Jogos<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#cassino--jogos" class="hash-link" aria-label="Link direto para Cassino / Jogos" title="Link direto para Cassino / Jogos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Recently-played semeado pela API no login</strong> (PR #355): novo endpoint <code>/api/user/last-casino-games</code> autenticado via accounts service, util <code>recently-played.client.ts</code> (118 linhas) com live updates, gate de visibilidade e play-trigger. Feature flag <code>recentlyPlayedSeed</code> propagada para todos os 13 brand overrides. Bump de <code>@cactus-agents/games</code> para <code>0.21.0</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos-e-moeda">Pagamentos e Moeda<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#pagamentos-e-moeda" class="hash-link" aria-label="Link direto para Pagamentos e Moeda" title="Link direto para Pagamentos e Moeda" translate="no">​</a></h2>
<ul>
<li class=""><strong>Suporte a moedas sem decimais (Nigeria)</strong> (PR #365): <code>CurrencyInput</code> agora aceita prop obrigatória <code>displayDecimalDigits</code> (vinda de <code>useCountry()</code>) e ajusta display + matemática via <code>centsScale = 10 ** (2 - displayDecimalDigits)</code>. Para NGN, digitar "1000" armazena <code>100000</code> kobo e exibe <code>₦ 1,000</code>; contrato do BFF (kobo/centavos) preservado. Free-form rejeita separadores decimais quando <code>displayDecimalDigits=0</code>.</li>
<li class=""><strong>Colapso do <code>formatMoney</code> duplicado</strong>: <code>app/utils/money.ts</code> virou adapter fino que delega ao <code>coreFormatMoney</code> de <code>@cactus-agents/accounts</code>. Os 6 consumers React (<code>GameCard</code>, <code>GameIframe</code>, <code>GameStats</code>, <code>GameWinners</code>, <code>WinsCarousel</code>, <code>WinsCombinedWidget</code>, <code>RightPanelWinners</code>) migraram para o hook <code>useFormatMoney()</code> que auto-resolve locale + currency + <code>displayDecimalDigits</code> da <code>AccountsConfig</code>, eliminando prop drilling. <code>AccountsBootstrap</code> propaga <code>displayDecimalDigits</code> da <code>CountryConfig</code>. <code>AmountShortcuts</code> mantém override explícito para 0 decimais (UX de shortcuts limpos cross-brand).</li>
<li class=""><strong><code>GamesCacheService</code> com <code>displayDecimalDigits</code></strong>: payouts pré-formatados do cache server-side (top-games widget) agora resolvem o número de decimais via <code>getCountryConfig(BRAND_COUNTRY)</code>. Sem isso, a home da Nigeria mostraria <code>₦1,000.00</code> no top-games e <code>₦1,000</code> em todo o resto.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="layout-e-mobile">Layout e Mobile<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#layout-e-mobile" class="hash-link" aria-label="Link direto para Layout e Mobile" title="Link direto para Layout e Mobile" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>viewport-fit=cover</code> no <code>&lt;meta viewport&gt;</code></strong> (PR #363): correção de gap entre o bottom-nav e a borda inferior no Chrome iOS, principalmente em iPhones com home indicator.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>vera-bet-br:</strong> nova landing SEO em <code>/lp/apostas-esportivas</code> (PR #361) — config em <code>lps.config.ts</code> + componente dedicado <code>apostas-esportivas.tsx</code> (≈540 linhas). Tipo <code>LpDefinition</code> recebeu campos novos para suportar conteúdo rich.</li>
<li class=""><strong>vera-bet-br:</strong> afiliado <strong>Clever Advertising</strong> ativado (PR #360) via flag <code>affiliateClever</code>. Nova rota <code>/clever</code> (resource route loader-only) que escreve cookie <code>lastclick</code> a partir do <code>utm_source</code> da URL para os scripts 3rd-party da Clever atribuírem conversões. Resposta inclui <code>X-Robots-Tag: noindex, nofollow</code> e <code>Cache-Control: no-store</code>. Brands sem contrato Clever retornam 404. Doc em <code>docs/features/clever/overview.md</code>.</li>
<li class=""><strong>vera-bet-br:</strong> novo asset PWA <code>pwa-512x512.png</code> adicionado.</li>
<li class=""><strong>Múltiplas brands:</strong> página de empty-state ganhou imagem <code>assets/images/empty.jpg</code> no base e em <code>7k-bet-br</code>, <code>cassino-bet-br</code>, <code>betpontobet-bet-br</code> e <code>vera-bet-br</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>@cactus-agents/games</code> — <code>getUserLastCasinoGames</code></strong> (PR #148): novo método em <code>GamesService</code>, <code>LegacyGamesService</code> e <code>CustomGamesService</code> para buscar últimos jogos do usuário no cassino via BFF. Tipos correspondentes adicionados em <code>types.ts</code>. Consumido pelo endpoint <code>/api/user/last-casino-games</code> do base para semear o recently-played.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/04/25/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class="">Nova doc externa <strong><code>customization/user-preferences</code></strong> e nova doc interna <strong><code>template/user-preferences</code></strong> explicando como configurar o landing-preference e a <code>PreferencesSection</code>.</li>
<li class=""><strong><code>country-config</code></strong> documentação atualizada com o campo <code>displayDecimalDigits</code> e regras de exibição monetária (zero-decimal para NGN/CLP, 2 decimais default).</li>
<li class="">Bump de Docusaurus para <code>3.10.0</code> e Biome para <code>2.4.13</code>.</li>
</ul>]]></content:encoded>
            <category>seo</category>
            <category>faq</category>
            <category>preferencias</category>
            <category>notificacoes</category>
            <category>nigeria</category>
            <category>clever</category>
            <category>vera</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 24/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/24/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/24/daily</guid>
            <pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Topbar de Notificações]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="topbar-de-notificações">Topbar de Notificações<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#topbar-de-notifica%C3%A7%C3%B5es" class="hash-link" aria-label="Link direto para Topbar de Notificações" title="Link direto para Topbar de Notificações" translate="no">​</a></h2>
<ul>
<li class=""><strong>Redesign completo da <code>TopbarNotification</code></strong> (PR #333): novo layout com botão de fechar (X) à esquerda, ícone em chip arredondado com tokens dedicados (<code>topbar.icon-bg</code> / <code>topbar.icon-text</code>), pilha de título + subtítulo no centro e CTA pill à direita, com altura de 72 px alinhada às barras modernas de instalação de app. Os ícones emoji foram substituídos por componentes do <code>lucide-react</code> (<code>Smartphone</code>, <code>Bell</code>, <code>Gift</code>, <code>Send</code>, <code>Trophy</code>, <code>ShieldAlert</code>) e o tipo <code>TopbarDefinition.icon</code> virou <code>LucideIcon</code>. Os modos <code>default</code> e <code>restricted</code> foram unificados num <code>TopbarShell</code> compartilhado, e a topbar deixou de ser sticky — agora rola junto com a página enquanto o header permanece fixo no topo do viewport. Tooltip de instalação iOS foi reancorado para o rodapé do viewport com <code>safe-area-inset-bottom</code> para que a seta inferior alinhe com a barra de endereços do Safari.</li>
<li class=""><strong>Centralização do conteúdo no desktop</strong> (PR #334): o <code>TopbarNotification</code> recebeu um container interno <code>max-w-[460px] mx-auto</code> envolvendo close/ícone/texto/CTA. O fundo continua preenchendo edge-to-edge, mas em viewports largos o X e o CTA não escorregam mais para os cantos deixando um espaço vazio gigante no meio. Em mobile (<code>&lt; 460px</code>) o comportamento é idêntico ao anterior — o container preenche naturalmente toda a largura.</li>
<li class=""><strong>Refresh de tokens de tema da topbar em todos os overrides</strong> (PR #347): sincronizou o override <code>7k-bet-br</code> (que estava sem <code>icon-bg</code> e <code>icon-text</code>), padronizou a paleta com fundo mais suave (lifted <code>bg-primary</code>), chip do ícone tingido com <code>primary</code> e CTA combinando com o botão primário da brand. Ajustes manuais aplicados nas variantes <code>7k</code>, <code>state77</code>, <code>vera-bet-br</code>, <code>betpontobet-bet-br</code> e <code>cassino-bet-br</code>. Altura interna da linha do <code>TopbarNotification</code> reduzida de 68 px para 62 px para compactar o footprint vertical.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="analytics-datalayer-e-marketing">Analytics, dataLayer e Marketing<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#analytics-datalayer-e-marketing" class="hash-link" aria-label="Link direto para Analytics, dataLayer e Marketing" title="Link direto para Analytics, dataLayer e Marketing" translate="no">​</a></h2>
<ul>
<li class=""><strong>Stack de dataLayer de primeira classe</strong> (PR #321): nova fundação de analytics centralizada em <code>app/utils/metrics/schema.ts</code> com mapeamento entre <code>MetricEvent</code> e chaves do <code>AnalyticsEventNameMap</code>, garantindo dispatch correto para o GTM. O <code>pushGTMEvent</code> passou a usar nomes vindos do schema e payload construído sob demanda, com filtro brand-level antes do push para impedir vazamento de PII no <code>dataLayer</code>. O evento <code>deposit_initiated</code> resolve o nome final em função do método de pagamento. Foi adicionada também a página <code>/debug/analytics</code> (apenas em dev) com ativação por URL e stringification segura de payloads, mais a doc interna <code>docs/features/data-layer/overview.md</code> cobrindo todo o contrato.</li>
<li class=""><strong>Mixpanel consolidado no hook <code>useMixpanel</code></strong> (PR #335) com paridade total ao legado 7k/cassino/vera: <code>mixpanel.init</code> configurado com <code>persistence: "localStorage"</code> e <code>track_pageview: false</code> (evita pageview duplicado, já emitido pelo GTM); <code>identify</code> aceita tanto um <code>id</code> escalar quanto um <code>AuthUser</code> completo, disparando <code>people.set</code> (<code>$email</code>, <code>$first_name</code>, <code>$last_name</code>, <code>$phone</code>, <code>user_id</code>) e <code>register</code> de super-properties. Foi adicionada uma fila de identify para casos onde o <code>mixpanel.identify</code> é chamado antes do dynamic import do <code>mixpanel-browser</code> resolver, evitando perdas em hidratação com sessão pré-existente. <code>useAnalytics</code> ganhou um <code>useEffect</code> que re-identifica o usuário quando a store popula tardiamente. Removidos os arquivos legados <code>app/utils/metrics/mixpanel.ts</code> e os campos <code>analyticsConfig.mixpanel*</code> correspondentes.</li>
<li class=""><strong>Portabilidade das integrações legadas Nuxt</strong> (PR #341): backfill de quatro features cobertas pelo legado, todas config-driven e desligadas por default:<!-- -->
<ul>
<li class=""><strong>ConfigCat</strong> (feature flags remotas): novos <code>app/types/configcat.ts</code>, <code>app/config/configcat.config.ts</code>, hook <code>useConfigcat</code> (lazy import do <code>configcat-js-ssr</code>, no-op quando desativado) e wrapper ergonômico <code>useConfigcatFlag(key, defaultValue)</code>. Distribuído desativado nos 13 overrides; SDK key entra quando a brand decidir ativar.</li>
<li class=""><strong>AppsFlyer S2S</strong> (mobile app-only): tipos + config dedicados, hook <code>useAppsFlyer</code> que captura <code>af_id</code>/<code>aaid</code>/<code>app_version</code> da URL para <code>sessionStorage</code>, monta o payload S2S completo e dispara via proxy server-side (<code>/api/tracking/appsflyer</code>) — a URL S2S da brand nunca chega ao client. O proxy foi reescrito para ler de <code>appsFlyerConfig</code> (antes lia de <code>brand.settings</code>) e o payload foi expandido com <code>first/last name</code>, <code>city</code>, <code>region</code> e device data. Ativado em paridade com o legado para <code>7k-bet-br</code>, <code>cassino-bet-br</code> e <code>vera-bet-br</code>; desativado nas demais 10 brands.</li>
<li class=""><strong>Reclame Aqui Reputation seal</strong>: hook <code>useRaReputation</code> que injeta o bundle <code>raichu-beta</code> uma vez no mount quando a brand tem <code>customSeal</code> do tipo <code>ra-reputation</code>, lendo o id direto do <code>licensesConfig</code>. O <code>FooterBadges</code> renderiza o widget vivo (<code>&lt;div id="ra-reputation" data-id=… data-model="2" /&gt;</code>) ou cai no badge estático legado quando não há seal de widget. Habilitado em <code>7k-bet-br</code>, <code>cassino-bet-br</code> e <code>vera-bet-br</code> com os ids do <code>dynamicLicenses.json</code> legado.</li>
<li class=""><strong>LP <code>/esportes</code> da 7k-bet-br</strong> portada para o novo override.</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cookies-e-consentimento">Cookies e Consentimento<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#cookies-e-consentimento" class="hash-link" aria-label="Link direto para Cookies e Consentimento" title="Link direto para Cookies e Consentimento" translate="no">​</a></h2>
<ul>
<li class=""><strong>Renomeação do cookie de consentimento de <code>cactusCookiesConsent</code> para <code>cookies_consent</code></strong> (PR #337) com migração one-shot: na primeira leitura, se o cookie novo não existir e o antigo sim, o valor é copiado e o cookie legado é deletado, evitando re-prompt para usuários retornantes. A entrada <code>cactus_jwt</code> da <code>COOKIE_TABLE</code> também foi renomeada para <code>jwt_token</code>, batendo com o nome real setado em <code>cookie.server.ts</code>. A mudança elimina vazamento de termos internos da plataforma (<code>cactus</code>) em superfícies inspecionáveis pelo cliente final.</li>
<li class=""><strong>Coordenação de widgets flutuantes em mobile</strong> (PR #337): o banner de consentimento agora publica <code>cookieConsentMobileTopPx</code> na <code>floatingStack</code> store via <code>ResizeObserver</code> + listener de resize, capturando ao mesmo tempo a âncora inferior e a altura renderizada. Os widgets <code>LastGameFloatingWidget</code>, <code>InstallApp</code> e <code>Campaign</code> se auto-escondem em mobile (<code>&lt; 640px</code>) enquanto o banner está visível, e o <code>BackToTop</code> permanece visível mas ancora seu <code>bottom</code> em <code>topPx + 8px</code> com <code>z-[65]</code> para ficar acima do banner (<code>z-[60]</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="cassino--jogos">Cassino / Jogos<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#cassino--jogos" class="hash-link" aria-label="Link direto para Cassino / Jogos" title="Link direto para Cassino / Jogos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Borda em radial-gradient no <code>TopGameCardShowcase</code></strong> (PR #330): o paleta gold/silver/bronze do pódio foi removida — todos os ranks agora usam <code>primary</code> da brand para identidade visual uniforme e brand-aware. A borda flat de 2 px virou um padrão wrapper+inner pintando um anel em radial-gradient (50% no centro → 100% nas bordas) via <code>currentColor</code> + <code>color-mix</code>, então qualquer cor primária de brand é capturada automaticamente. O badge do rank foi remoldado em "corner tab" rente (<code>rounded-tl-lg</code> / <code>rounded-br-2xl</code>) com <code>bg-primary</code> e <code>text-button-text</code> em todas as posições.</li>
<li class=""><strong>Padronização do <code>topGamesRankStyle: "showcase"</code> em todos os brands</strong> (PR #349): default e os 10 overrides agora alinhados na mesma variante de rank para a row de top games, substituindo o legado <code>outline-sideways</code>.</li>
<li class=""><strong>Flag <code>onlineCountSource</code> para fonte do contador "online"</strong> (PR #331): badge "online" do <code>GameCard</code> e card "Online agora" do <code>GameStats</code> passam a ler <code>featuresConfig.onlineCountSource</code> e escolher entre <code>last1Hour.players</code> (default) e <code>last24Hours.players</code>. O fallback oportunista entre janelas foi removido — o badge agora some quando a janela escolhida está em zero. Brand <code>pb-bet</code> configurado para a janela de 24 h; demais permanecem na de 1 h.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos">Pagamentos<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#pagamentos" class="hash-link" aria-label="Link direto para Pagamentos" title="Link direto para Pagamentos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Fluxo de salvar conta bancária do Chile reformulado</strong> (PR #327): a operação de criar/editar conta bancária agora passa pelo <code>useValidationFlow({ context: "updateData" })</code> (mesma mecânica de update de endereço), garantindo o token de sessão exigido pelo BFF — antes <code>ChlBanksSection</code>, <code>NgaBanksSection</code> e <code>ClabeSection</code> postavam direto para <code>/api/payments/bank-account</code> e falhavam silenciosamente. Os selects do <code>ChlBanksSection</code> / <code>WithdrawForm</code> agora anexam o código bruto do backend entre parênteses (ex: <code>Cuenta Corriente (CHECKING)</code>, <code>CuentaRUT (RUT)</code>) para ops e usuários desambiguarem duplicatas, com ordenação por adoção (<code>CuentaRUT</code> primeiro, ~82% da população). O proxy de bank-account agora encaminha tanto o payload bruto do BFF quanto a <code>message</code> já extraída, e o componente cai num genérico traduzido (<code>"Error al guardar cuenta bancaria. Inténtalo de nuevo."</code>) quando o extrator só consegue surfar um código de máquina. <code>PixSection</code> reordenou as key types por probabilidade de digitação manual no withdraw (CPF → PHONE → EVP → EMAIL) e usa filtro inclusivo, garantindo que <code>EVP</code> cai na posição correta quando a feature flag está ligada. Bumps acompanhantes: <code>@cactus-agents/api-client ^0.10.1 → ^0.11.0</code>, <code>@cactus-agents/i18n ^0.65.0 → ^0.66.0</code> e <code>@cactus-agents/payments ^0.12.2 → ^0.14.0</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="esportes">Esportes<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#esportes" class="hash-link" aria-label="Link direto para Esportes" title="Link direto para Esportes" translate="no">​</a></h2>
<ul>
<li class=""><strong>Iframe do sportsbook agora dimensiona pela área visível do viewport</strong> (PR #350): novo hook <code>useSportsAvailableHeight</code> mede o <code>top</code> do <code>&lt;main&gt;</code> em coordenadas de viewport (capturando topbar + sticky header naturalmente) e o <code>offsetHeight</code> do bottom nav (resolvendo <code>safe-area-bottom</code>, que <code>getPropertyValue("--mobile-nav-h")</code> não consegue), publicando o resultado como <code>--sports-h</code>. Listeners cobrem <code>scroll</code> (rAF-throttled) para o iframe crescer conforme a topbar desliza, <code>visualViewport</code> resize/scroll para a animação da URL bar do iOS, mais <code>ResizeObserver</code> no header e no nav. As variantes First, Altenar e Betby agora consomem o mesmo token, eliminando o <code>calc(100vh - 200px)</code> mágico do Betby/Altenar e o <code>min-h-[100dvh]</code> que colapsava o iframe a zero na branch vera/cassino.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="conta-e-comunicações">Conta e Comunicações<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#conta-e-comunica%C3%A7%C3%B5es" class="hash-link" aria-label="Link direto para Conta e Comunicações" title="Link direto para Conta e Comunicações" translate="no">​</a></h2>
<ul>
<li class=""><strong>Toggle de marketing envia datetime e não boolean</strong> (PR #344): o BFF <code>/bff/users/self-mkt</code> espera <code>{ mkt_accepted_at: "YYYY-MM-DD HH:MM:SS" }</code> quando o usuário aceita comunicações e <code>null</code> quando revoga — não um boolean. O componente continua emitindo <code>{ mkt_accepted: boolean }</code> (paridade com o tipo do core), e a rota <code>/api/user/update-marketing</code> faz a tradução antes de encaminhar ao BFF. Acompanha o bump do core (<code>@cactus-agents/accounts</code>) que ajustou o tipo <code>UpdateMarketingPayload</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>7k-bet-br:</strong> bootstrap completo do override (PR #338) — 79 arquivos novos cobrindo <code>analytics</code>, <code>casino-rows</code> (custom/legacy/new), <code>home-rows</code> (custom/legacy/new), <code>casino-live-rows</code>, <code>categories.custom</code>, <code>categories.personalize</code>, <code>features</code>, <code>game-thumbs</code>, <code>gamification</code>, <code>hotjar</code>, <code>image</code>, <code>licenses</code>, <code>lp-redirects</code>, <code>lps</code>, <code>menu</code>, <code>mixpanel</code>, <code>page-descriptions.server</code>, <code>referral-custom-v1</code>, <code>routes.paths</code>, <code>scripts</code>, <code>sidebar</code>, <code>sports</code>, <code>theme-layout</code>, <code>theme</code>, locale override <code>pt-br/auth.json</code>, ícones PWA (apple-touch-icon em todos os tamanhos, favicon, pwa 96×96), 9 selos de regulação (<code>autorizado</code>, <code>bis-awards-apostas-esportivas</code>, <code>bis-awards-cassino</code>, <code>compulsafe2</code>, <code>ebac</code>, <code>gambling-brasil</code>, <code>ibia</code>, <code>sigma-awards</code>) e thumbs MP4 dos jogos <code>fortune-fruits</code> / <code>fortune-snake</code> / <code>fortune-tiger</code>.</li>
<li class=""><strong>fi-7k-bet:</strong> correção do id do certificado CGA no <code>licensesConfig</code> e adição do SVG <code>cga.svg</code> em <code>public/assets/seals/</code> (PR #346).</li>
<li class=""><strong>state77-com:</strong> substituição do selo GCB pelo CGA com o link de certificado correto (PR #348).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/04/24/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class="">Pacote <code>@cactus-agents/accounts</code> recebeu <code>UpdateMarketingPayload</code> ajustado para <code>mkt_accepted_at: string | null</code> (datetime <code>YYYY-MM-DD HH:MM:SS</code> ao aceitar, <code>null</code> ao revogar) — antes era <code>mkt_accepted: boolean</code>, divergindo do contrato real do BFF <code>/bff/users/self-mkt</code> (Core PR #147).</li>
<li class="">Pacote <code>@cactus-agents/payments</code> chegou na <strong>v0.14.0</strong> com <code>kycId</code> + <code>passwordCheck</code> adicionados a <code>GenericBankAccountPayload</code> e <code>MexBankAccountPayload</code>, permitindo encaminhamento tipado dos artefatos de validação no fluxo de bank-account do Chile, Nigéria e México.</li>
<li class="">Pacote <code>@cactus-agents/api-client</code> chegou na <strong>v0.11.0</strong> e <code>@cactus-agents/i18n</code> na <strong>v0.66.0</strong>, ambos consumidos pelo bump acompanhante do PR #327.</li>
</ul>]]></content:encoded>
            <category>topbar</category>
            <category>analytics</category>
            <category>datalayer</category>
            <category>mixpanel</category>
            <category>cassino</category>
            <category>pagamentos</category>
            <category>brands</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 23/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/23/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/23/daily</guid>
            <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Consentimento de Cookies / LGPD]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="consentimento-de-cookies--lgpd">Consentimento de Cookies / LGPD<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#consentimento-de-cookies--lgpd" class="hash-link" aria-label="Link direto para Consentimento de Cookies / LGPD" title="Link direto para Consentimento de Cookies / LGPD" translate="no">​</a></h2>
<ul>
<li class=""><strong>Banner de consentimento de cookies com Google Consent Mode v2</strong> chega ao base. O <code>CookieConsentBanner</code> (fixado no canto inferior esquerdo, animado, <code>z-60</code>) e o painel <code>CookieConsentDetails</code> (abas Configurações/Sobre, toggles por categoria, tabela de cookies) injetam os defaults do GCM antes do GTM inicializar e gravam a preferência no cookie <code>cactusCookiesConsent</code>. Mapeamento de categorias: <code>performance → analytics_storage</code>, <code>functionality → functionality_storage</code>, demais → <code>ad_storage + ad_user_data + ad_personalization</code>. Bypass para bots via <code>useIsBot()</code>. Feature flag <code>brand.features.cookieConsentPopup</code> controla a exibição por marca, sem build-time config.</li>
<li class=""><strong>Paridade total com o legado Vue (base + 7k + cassino + vera)</strong>: adicionado o sétimo sinal do GCM v2 (<code>security_storage: granted</code>) para auditorias Lighthouse/CMP enxergarem o contrato completo; <code>COOKIE_TABLE</code> expandida de 6 para 19 entradas cobrindo Cloudflare, GA4, Clarity, Meta Pixel, Kwai, Amplitude, Smartico e cookies de funcionalidade (<code>current_lang</code>, <code>topbar_closed</code>, <code>install_app_popup_closed</code>); migração transparente do formato legado (array JSON de strings) para o novo objeto, sem reprompt de usuários existentes.</li>
<li class=""><strong>UX e visual</strong>: o banner se posiciona acima do <code>MobileBottomNav</code> em mobile (<code>h-14</code>), some quando o drawer está aberto, fica <code>print:hidden</code>, e tem split 50/50 entre [Personalizar | Recusar] e [Aceitar todos]. Cores 100% via tokens de tema (<code>bg-secondary</code>, <code>text-texts</code>, <code>bg-primary</code>), eliminando <code>bg-white/*</code> e <code>border-white/*</code> hardcoded. O botão "Salvar" dentro do painel de detalhes se torna "Aceitar todos" quando só a categoria necessária está marcada — replica o <code>saveButton()</code> do legado.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="auth-e-recuperação-de-senha">Auth e Recuperação de Senha<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#auth-e-recupera%C3%A7%C3%A3o-de-senha" class="hash-link" aria-label="Link direto para Auth e Recuperação de Senha" title="Link direto para Auth e Recuperação de Senha" translate="no">​</a></h2>
<ul>
<li class=""><strong>Fluxo de recuperação de senha com KYC adota o contrato do BFF</strong> como fonte única da verdade. O endpoint <code>/auth/passwords/reset/options</code> agora retorna <code>active_options</code> (flags por método) + <code>kyc</code> (elegibilidade), e o <code>RecoverOptionsStep</code> renderiza estritamente a partir disso — sem flag, sem opção. Quando a interseção com os dados de contato disponíveis fica vazia, a tela mostra mensagem "nenhuma opção disponível" com CTA para suporte ao vivo, evitando que o usuário fique encalhado. A flag local <code>recoveryPasswordMethods</code> foi removida do tipo, do <code>features.config.ts</code> do base e dos 11 overrides de marca.</li>
<li class=""><strong><code>RecoverKycStep</code> reescrito como máquina de estados</strong> (<code>useKycStore.{open, step, error}</code>): substitui o spinner genérico anterior que deixava o usuário preso em "aguardando verificação" durante cancelamentos, erros do BFF e polling pós-iframe. Estados <code>validated</code> avançam para nova senha; erros fatais (<code>failed</code>/<code>expired</code>/<code>internal_start_error</code>/<code>internal_status_error</code>) fecham o modal, mostram toast e chamam <code>resetToDocument()</code> para reiniciar a partir do CPF; modal fechado em <code>initial</code>/<code>started</code> renderiza tela "Verificação interrompida" com CTAs de retentar/trocar método; estados <code>finished</code>/<code>checking</code>/<code>manual_refresh</code> mostram a UI de polling com botão "Cancelar" que conta 20s antes de habilitar.</li>
<li class=""><strong>Token de recuperação separado do access token</strong>: <code>/kyc/status/recovery</code> agora retorna <code>access_token</code> OU <code>recovery_token</code>. A rota <code>/api/kyc/status</code> só seta o cookie de sessão para <code>access_token</code>; <code>recovery_token</code> flui pelo body do JSON e o store atualiza o flow token via a nova action <code>setFlowToken</code>. Origem do KYC varia entre <code>recovery_password</code> e <code>password_recovery_attempt_limit</code> conforme o ponto de entrada.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="analytics--tracking">Analytics / Tracking<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#analytics--tracking" class="hash-link" aria-label="Link direto para Analytics / Tracking" title="Link direto para Analytics / Tracking" translate="no">​</a></h2>
<ul>
<li class=""><strong>Mixpanel, Hotjar e Pendo integrados como infra genérica e config-driven</strong>. Cada SDK ganha um arquivo de tipo (<code>app/types/{mixpanel,hotjar,pendo}.ts</code>), config default desabilitada (<code>enabled: false</code>) e hook próprio: <code>useMixpanel</code> (wrapper sobre <code>mixpanel-browser</code> com import dinâmico — zero custo de bundle quando desabilitado, expõe <code>trackEvent</code> que auto-injeta <code>{ house: houseName }</code>, <code>identify</code>, <code>reset</code>); <code>useHotjar</code> (injeta o snippet uma vez e opcionalmente chama <code>hj('identify', null, { encryptedId, brand, hasFirstDeposit })</code> pós-auth quando <code>identifyUsers: true</code>); <code>usePendo</code> (injeta o agent loader e roda <code>pendo.initialize({ visitor, account })</code> quando o estado de auth estabiliza). O componente <code>AnalyticsLoaders</code> monta os três hooks e está pendurado no <code>app/root.tsx</code> ao lado do <code>TrackingCapture</code>.</li>
<li class=""><strong>Eventos de auth no Mixpanel</strong>: <code>trackLogin</code> agora também chama <code>mixpanel.identify(user.id)</code> + evento <code>Login Succeeded</code>; <code>trackRegistration</code> adiciona <code>identify</code> + <code>Sign Up Succeeded</code>. Novos eventos <code>trackLoginSubmitted</code> / <code>trackLoginFailed</code> são exclusivos do Mixpanel (sem equivalente GTM), disparados pelo <code>LoginModal.handleSubmit</code> e pelo bloco catch.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="sistema-de-indique-e-ganhe">Sistema de Indique e Ganhe<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#sistema-de-indique-e-ganhe" class="hash-link" aria-label="Link direto para Sistema de Indique e Ganhe" title="Link direto para Sistema de Indique e Ganhe" translate="no">​</a></h2>
<ul>
<li class=""><strong>Variantes da página de Indique e Ganhe consolidadas em três opções</strong>: <code>default</code> (cactus, API padrão), <code>custom-v1</code> (ex <code>custom-stats</code>, hero com prêmio + cards de indicações qualificadas/total ganho — usado por 7k/cassino/vera) e <code>v2</code> (ex <code>betpontobet</code>, com tiers de recompensa configuráveis). Os nomes ficaram brand-agnósticos para qualquer marca poder reusar. O config padrão da <code>v2</code> agora ship vazio — antes carregava valores específicos do betpontobet (<code>api.donaldbet.app</code>, slug <code>betbet</code>, tiers R$ 50/100/200/300, shareMessage persuasivo) que vazavam em todo build; a marca <code>betpontobet-bet-br</code> ganhou override próprio com esses valores.</li>
<li class=""><strong>Novo proxy server-side <code>/api/user/referral-indicator</code></strong> para a variante <code>custom-v1</code>: encaminha Bearer <code>encrypted_user_id ?? user.id</code> para a API legada externa (<code>amigos.verabet.app</code> / <code>connect-7k.gdm7khub.com</code> / <code>connect-v2.gdmhub.io</code>), garantindo que o token nunca chega ao cliente. Acompanha novo tipo <code>ReferralCustomStatsConfig</code> (com <code>apiUrl</code>, <code>maxReward</code>, <code>minBetAmount</code>, <code>providerName</code>, <code>eligibleGamesPath</code>) e augmentation do <code>AuthUser</code> para incluir <code>encrypted_user_id</code> opcional.</li>
<li class=""><strong>CTA dinâmico da sidebar respeita a variante</strong>: <code>useReferral</code> resolve <code>bonusAmount</code> do config da variante ativa (<code>v2 → maxReward</code>, <code>custom-v1 → maxReward</code> se setado, senão API; <code>default → API</code>). Corrige o badge "Indique e Ganhe R$ 0,01" da betpontobet quando o stage retornava valor de teste. <code>SidebarNarrow</code> consome <code>bonusFormatted</code> direto do hook.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="pagamentos">Pagamentos<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#pagamentos" class="hash-link" aria-label="Link direto para Pagamentos" title="Link direto para Pagamentos" translate="no">​</a></h2>
<ul>
<li class=""><strong>Saque para Finlândia (FIN) renderiza formulário próprio</strong>. O <code>WithdrawForm</code> passa a tratar as três variantes de transferência bancária explicitamente (NGA/FIN/CHL) em vez do fallback "NGA vs tudo cai em CHL", que estava renderizando campos chilenos (tipo de conta, dropdown de banco, aviso de RUT) para usuários finlandeses. Novo <code>FinBankTransferFields</code> mostra um único input de número de conta + aviso de propriedade do documento via a chave i18n <code>fin_document_ownership_warning</code>. Payload final para FIN agora envia apenas <code>bank_number</code> (sem <code>bankCode</code>, <code>account_type</code> ou <code>generic_bank_*</code>).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="e2e-tests--playwright">E2E Tests / Playwright<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#e2e-tests--playwright" class="hash-link" aria-label="Link direto para E2E Tests / Playwright" title="Link direto para E2E Tests / Playwright" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>data-testid</code> nos elementos críticos para Playwright</strong>: botões de login/registro no <code>HeaderDefault</code>, submit do <code>LoginModal</code>, wrapper do <code>GameCard</code> e <code>MobileBottomNav</code>. Necessário para os seletores do repositório <code>front-tests-e2e</code>. Junto, todos os hooks Husky passam a fazer <code>source</code> do nvm com <code>--no-use</code>, evitando exit code 3 sob bash 3.2 (o <code>/bin/sh</code> do macOS) onde o auto-switch do nvm explodia em <code>sh -e</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="brand-overrides">Brand Overrides<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#brand-overrides" class="hash-link" aria-label="Link direto para Brand Overrides" title="Link direto para Brand Overrides" translate="no">​</a></h2>
<ul>
<li class=""><strong>cassino-bet-br:</strong> bootstrap completo da marca portada do legado Nuxt. 71 arquivos novos (theme azul-marinho + ciano, features, licenses com <code>textLicense</code> + selos da autorização SPA + IBIA + Sport Radar + EBAC + Compulsafe + BeGambleAware + Gambling Therapy + Be Complience + patrocínio Bis Awards, gamification com Smartico <code>brandKey 0a13ef04</code> + CassinoVip/VipCoins, page descriptions, menu/sidebar/home-rows/casino-rows espelhados do legado, Cactus Sportsbook, variante de referral <code>custom-v1</code> apontando para <code>connect-v2.gdmhub.io</code>). Assets PWA, favicons, logos, touch-icons e selos próprios da marca incluídos. Configurações de Mixpanel/Hotjar/Pendo virão em PR de follow-up depois que a infra base estabilizar no stage.</li>
<li class=""><strong>fi-7k-bet:</strong> menu lateral do cassino realinhado ao <code>games-menu.json</code> legado — reordenadas as categorias (All Games, Slots, Live Casino, Providers), slug do Aviator trocado de <code>spribe/aviator</code> para <code>banana/aviator-crash</code>, Blackjack ao vivo trocado de <code>evolution/infinite-blackjack</code> para <code>pragmaticplay/live-one-blackjack</code>, adicionados Fortune Dragon (PG Soft) e Fortune Fruits (Banana), removidos Brazilian Roulette e Infinite Blackjack. No mesmo dia: licenças com texto oficial de compliance (NovaWave Technology N.V. + Stonefield Technologies como Billing Agent, GoC <code>OGL/2024/547/0446</code>) substituindo o placeholder em espanhol; flag <code>pixEvpEnabled</code> zerada (Pix está desabilitado para FIN no <code>country-config</code>).</li>
<li class=""><strong>vera-bet-br:</strong> Mixpanel ativado (<code>house: 'Vera'</code>) + Hotjar (<code>hjid 6503935</code>, sem <code>identify</code> para casar com o snippet do legado). O proxy de <code>referral-indicator</code> aponta para <code>https://amigos.verabet.app</code>.</li>
<li class=""><strong>betpontobet-bet-br:</strong> ganha override completo do <code>referral-v2.config.ts</code> agora que o default ficou vazio — preserva os tiers de recompensa, <code>shareMessage</code> e configuração da <code>api.donaldbet.app</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="core--sdk">Core / SDK<a href="https://docs.c4ctus.com/changelog/2026/04/23/daily#core--sdk" class="hash-link" aria-label="Link direto para Core / SDK" title="Link direto para Core / SDK" translate="no">​</a></h2>
<ul>
<li class="">Pacote <code>@cactus-agents/i18n</code> ganha o <strong>namespace <code>consent</code> completo</strong> nos 4 locales (pt-br, pt, es, en) — primeiro com 68 chaves cobrindo o banner de cookies, depois expandido para o catálogo completo de 19 cookies que o front consome. Tipo <code>I18nNamespace</code> registrado.</li>
<li class="">Pacote <code>@cactus-agents/i18n</code> recebe a chave <code>fin_document_ownership_warning</code> no namespace <code>user</code> (4 locales) para o aviso do formulário de saque finlandês. Acompanha changeset.</li>
<li class="">Pacote <code>@cactus-agents/accounts</code> corrige <code>TwoFactorPayload</code> para os campos esperados pelo BFF: <code>two_factor_type</code> e <code>two_factor_enabled</code> (mesma shape do <code>AuthUserInfo</code>), em vez de <code>type</code>/<code>enabled</code>. Os nomes errados faziam o <code>PATCH /bff/users/self-two-factor</code> virar no-op silencioso, e o estado de 2FA do usuário nunca mudava de fato.</li>
</ul>]]></content:encoded>
            <category>consent</category>
            <category>lgpd</category>
            <category>gcm-v2</category>
            <category>recovery</category>
            <category>kyc</category>
            <category>analytics</category>
            <category>referral</category>
            <category>brand-overrides</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 22/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/22/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/22/daily</guid>
            <pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Tracking de Marketing / UTMs]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="tracking-de-marketing--utms">Tracking de Marketing / UTMs<a href="https://docs.c4ctus.com/changelog/2026/04/22/daily#tracking-de-marketing--utms" class="hash-link" aria-label="Link direto para Tracking de Marketing / UTMs" title="Link direto para Tracking de Marketing / UTMs" translate="no">​</a></h2>
<ul>
<li class=""><strong>Captura de UTMs agora roda server-side no root loader</strong> (<code>app/root.tsx</code>). Antes, a captura era apenas client-side em <code>useEffect</code> — UTMs se perdiam quando um loader fazia <code>redirect(...)</code> antes do hydrate. Ex: landing <code>/?utm_source=X&amp;ref=AFIL1</code> que dispara redirect para <code>/register</code> agora preserva os parâmetros via <code>Set-Cookie</code> na própria response do redirect. Resolve o report <strong>"Rastreamento perde-se após o login/redirecionamento"</strong>.</li>
<li class=""><strong>Política de atribuição alinhada com legado: last-touch.</strong> Cada nova URL com UTM sobrescreve o cookie anterior, igual ao middleware global do Nuxt antigo. A versão anterior (first-touch) divergia silenciosamente da atribuição calibrada nos dashboards de BI.</li>
<li class=""><strong>Captura também re-executa em toda navegação SPA</strong> via <code>useLocation()</code> no <code>TrackingCapture</code> — antes corria só 1x no mount.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="payloads-para-o-bff">Payloads para o BFF<a href="https://docs.c4ctus.com/changelog/2026/04/22/daily#payloads-para-o-bff" class="hash-link" aria-label="Link direto para Payloads para o BFF" title="Link direto para Payloads para o BFF" translate="no">​</a></h2>
<ul>
<li class=""><strong>Deposit (<code>POST /wallet/add-credit</code>)</strong>: removido o campo <code>src</code> do body em <code>buildDepositPayload</code>. Nenhum dos quatro projetos legado (base, 7k-bet-br, cassino-bet-br, vera-bet-br) jamais enviou <code>src</code> no deposit — só no signup. Manter paridade evita surpresas em dashboards de BI.</li>
<li class=""><strong>Signup (<code>POST /bff/register-simplified</code> / <code>POST /auth/register</code>)</strong>: payload continua enviando <code>utm_source</code>, <code>utm_campaign</code>, <code>utm_content</code>, <code>utm_medium</code>, <code>src</code>, <code>ga_client_id</code>, <code>app_source</code>, <code>affiliation_code</code> — já era assim antes, agora com cobertura de testes end-to-end (FormData → action → body do POST).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="headers-http-automáticos">Headers HTTP automáticos<a href="https://docs.c4ctus.com/changelog/2026/04/22/daily#headers-http-autom%C3%A1ticos" class="hash-link" aria-label="Link direto para Headers HTTP automáticos" title="Link direto para Headers HTTP automáticos" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>X-ORIGIN-ACCESS</code></strong> agora é derivado do User-Agent no <code>api.server.ts</code> (mobile vs desktop). Antes, todo request server enviava <code>Desktop</code> fixo; agora bate com a segmentação de device do BI legado.</li>
<li class=""><strong><code>X-LOG-INFO</code></strong> já era enviado em todo request, mas a documentação do SDK descrevia o formato errado (<code>{tenant, version, locale, isAuth, originAccess}</code>) — corrigido para o formato real <code>1-{timestamp}-{buildId}</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="documentação">Documentação<a href="https://docs.c4ctus.com/changelog/2026/04/22/daily#documenta%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Documentação" title="Link direto para Documentação" translate="no">​</a></h2>
<ul>
<li class="">Nova doc <strong><code>docs-internal/architecture/marketing-tracking.md</code></strong> cobrindo captura em 3 camadas, política last-touch, parâmetros e aliases, sanitização, storage (<code>cookie_tracking</code> + <code>cookie_referrer</code>), payloads finais por endpoint, headers automáticos, feature flag <code>keepTrackingParamsInUrl</code> e checklist anti-padrões.</li>
<li class=""><strong><code>sdk/api-client.md</code></strong> atualizado: tabela de headers automáticos completa (X-ORIGIN-REFERRER family); tipo <code>ReferrerInfo</code> corrigido (era declarado com campos UTM fictícios); tipo <code>RequestDebugInfo</code> corrigido (estava descrevendo um shape inexistente).</li>
<li class=""><strong><code>sdk/auth.md</code></strong>: <code>RegisterSimplifiedPayload</code> agora documenta os campos de tracking (<code>utm_*</code>, <code>src</code>, <code>ga_client_id</code>).</li>
<li class=""><strong><code>sdk/payments.md</code></strong>: shape completo do <code>DepositPayload</code> documentado, incluindo a regra "NÃO enviar <code>src</code>" como callout reforçado por teste automatizado.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="testes">Testes<a href="https://docs.c4ctus.com/changelog/2026/04/22/daily#testes" class="hash-link" aria-label="Link direto para Testes" title="Link direto para Testes" translate="no">​</a></h2>
<ul>
<li class="">Suite nova <code>app/utils/__tests__/tracking.test.ts</code> (15 casos): parser de URL com todos os aliases, sanitização, merge last-touch, casos edge.</li>
<li class="">Suite nova <code>app/utils/__tests__/tracking.server.test.ts</code> (9 casos): captura server-side, <code>Set-Cookie</code> serialization, strip de URL pela feature flag.</li>
<li class="">Suite nova <code>app/modules/register/__tests__/flow.tracking.test.ts</code> (4 casos): valida o pipeline FormData → <code>parseRegisterFormData</code> → <code>buildRegisterPayload</code> ponta-a-ponta — garante que o que o browser submete chega ao body do POST que vai pro BFF.</li>
<li class=""><code>packages/payments/src/__tests__/transform.test.ts</code>: novo caso confirma que <code>src</code> não sai no deposit.</li>
<li class=""><code>packages/api-client/src/__tests__/client.test.ts</code>: novos casos cobrem <code>X-LOG-INFO</code> (formato + fallback <code>nobuild</code>) e <code>X-ORIGIN-ACCESS</code> (presença/ausência conforme enum).</li>
</ul>]]></content:encoded>
            <category>tracking</category>
            <category>marketing</category>
            <category>atribuicao</category>
            <category>signup</category>
            <category>deposit</category>
            <category>paridade-legado</category>
            <category>docs</category>
        </item>
        <item>
            <title><![CDATA[Changelog - 02/04/2026]]></title>
            <link>https://docs.c4ctus.com/changelog/2026/04/02/daily</link>
            <guid>https://docs.c4ctus.com/changelog/2026/04/02/daily</guid>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[UI e Componentes]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="ui-e-componentes">UI e Componentes<a href="https://docs.c4ctus.com/changelog/2026/04/02/daily#ui-e-componentes" class="hash-link" aria-label="Link direto para UI e Componentes" title="Link direto para UI e Componentes" translate="no">​</a></h2>
<ul>
<li class=""><strong>Adicionada a seção de Promoções com um novo ícone animado de presente</strong> na navegação.</li>
<li class=""><strong>Convertidos os atalhos de busca rápida e a lista de categorias em carrosséis horizontais</strong> com navegação por toque (swipe).</li>
<li class=""><strong>Implementado modo de tela cheia para o modal de Stories</strong> e adicionada navegação por swipe horizontal entre os stories.</li>
<li class=""><strong>Aprimorado o layout do rodapé</strong> com a adição de selos de conformidade.</li>
<li class=""><strong>Melhorias de responsividade e UX nas páginas de erro 404 e 500</strong>, incluindo localização aprimorada.</li>
<li class=""><strong>Corrigido o alinhamento de altura entre o seletor de DDI e o campo de telefone</strong> em dispositivos móveis.</li>
<li class=""><strong>Ajustado alinhamento do tooltip de instalação do app para iOS</strong>.</li>
<li class=""><strong>Adicionado o componente <code>PageDescription</code></strong> e centralizada a configuração de descrições de página para SEO.</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="registro-e-validação">Registro e Validação<a href="https://docs.c4ctus.com/changelog/2026/04/02/daily#registro-e-valida%C3%A7%C3%A3o" class="hash-link" aria-label="Link direto para Registro e Validação" title="Link direto para Registro e Validação" translate="no">​</a></h2>
<ul>
<li class=""><strong>Melhorado o fluxo de validação de e-mail e telefone</strong> com funcionalidade de envio automático de código.</li>
<li class=""><strong>Aprimorado o formulário de endereço com busca de CEP</strong> e preenchimento automático.</li>
<li class=""><strong>O nome do documento no formulário de registro agora é dinâmico por país</strong> (ex: CPF no Brasil, RUT no Chile).</li>
<li class=""><strong>Melhorado o fluxo de validação de telefone</strong> com verificação de ID KYC e override de fonte.</li>
<li class=""><strong>Corrigido o identificador de origem do aplicativo (<code>app source</code>)</strong> no modal de registro.</li>
<li class=""><strong>Ajustadas as feature flags do fluxo de registro</strong> para a marca betpontobet.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_LqMH" id="geral-e-seo">Geral e SEO<a href="https://docs.c4ctus.com/changelog/2026/04/02/daily#geral-e-seo" class="hash-link" aria-label="Link direto para Geral e SEO" title="Link direto para Geral e SEO" translate="no">​</a></h2>
<ul>
<li class=""><strong>O nome da marca agora é anexado aos títulos de todas as páginas</strong>, melhorando a identificação e o SEO.</li>
<li class=""><strong>Aprimoradas as capacidades de tracking</strong> com a integração de um novo provedor de analytics.</li>
<li class=""><strong>Adicionado um ícone de Troféu para a aba de Recompensas</strong> no menu da conta do usuário.</li>
</ul>]]></content:encoded>
        </item>
    </channel>
</rss>