Redesign completo da TopbarNotification (PR #333): novo layout com botão de fechar (X) à esquerda, ícone em chip arredondado com tokens dedicados (topbar.icon-bg / topbar.icon-text), 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 lucide-react (Smartphone, Bell, Gift, Send, Trophy, ShieldAlert) e o tipo TopbarDefinition.icon virou LucideIcon. Os modos default e restricted foram unificados num TopbarShell 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 safe-area-inset-bottom para que a seta inferior alinhe com a barra de endereços do Safari.
Centralização do conteúdo no desktop (PR #334): o TopbarNotification recebeu um container interno max-w-[460px] mx-auto 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 (< 460px) o comportamento é idêntico ao anterior — o container preenche naturalmente toda a largura.
Refresh de tokens de tema da topbar em todos os overrides (PR #347): sincronizou o override 7k-bet-br (que estava sem icon-bg e icon-text), padronizou a paleta com fundo mais suave (lifted bg-primary), chip do ícone tingido com primary e CTA combinando com o botão primário da brand. Ajustes manuais aplicados nas variantes 7k, state77, vera-bet-br, betpontobet-bet-br e cassino-bet-br. Altura interna da linha do TopbarNotification reduzida de 68 px para 62 px para compactar o footprint vertical.
Stack de dataLayer de primeira classe (PR #321): nova fundação de analytics centralizada em app/utils/metrics/schema.ts com mapeamento entre MetricEvent e chaves do AnalyticsEventNameMap, garantindo dispatch correto para o GTM. O pushGTMEvent 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 dataLayer. O evento deposit_initiated resolve o nome final em função do método de pagamento. Foi adicionada também a página /debug/analytics (apenas em dev) com ativação por URL e stringification segura de payloads, mais a doc interna docs/features/data-layer/overview.md cobrindo todo o contrato.
Mixpanel consolidado no hook useMixpanel (PR #335) com paridade total ao legado 7k/cassino/vera: mixpanel.init configurado com persistence: "localStorage" e track_pageview: false (evita pageview duplicado, já emitido pelo GTM); identify aceita tanto um id escalar quanto um AuthUser completo, disparando people.set ($email, $first_name, $last_name, $phone, user_id) e register de super-properties. Foi adicionada uma fila de identify para casos onde o mixpanel.identify é chamado antes do dynamic import do mixpanel-browser resolver, evitando perdas em hidratação com sessão pré-existente. useAnalytics ganhou um useEffect que re-identifica o usuário quando a store popula tardiamente. Removidos os arquivos legados app/utils/metrics/mixpanel.ts e os campos analyticsConfig.mixpanel* correspondentes.
Portabilidade das integrações legadas Nuxt (PR #341): backfill de quatro features cobertas pelo legado, todas config-driven e desligadas por default:
ConfigCat (feature flags remotas): novos app/types/configcat.ts, app/config/configcat.config.ts, hook useConfigcat (lazy import do configcat-js-ssr, no-op quando desativado) e wrapper ergonômico useConfigcatFlag(key, defaultValue). Distribuído desativado nos 13 overrides; SDK key entra quando a brand decidir ativar.
AppsFlyer S2S (mobile app-only): tipos + config dedicados, hook useAppsFlyer que captura af_id/aaid/app_version da URL para sessionStorage, monta o payload S2S completo e dispara via proxy server-side (/api/tracking/appsflyer) — a URL S2S da brand nunca chega ao client. O proxy foi reescrito para ler de appsFlyerConfig (antes lia de brand.settings) e o payload foi expandido com first/last name, city, region e device data. Ativado em paridade com o legado para 7k-bet-br, cassino-bet-br e vera-bet-br; desativado nas demais 10 brands.
Reclame Aqui Reputation seal: hook useRaReputation que injeta o bundle raichu-beta uma vez no mount quando a brand tem customSeal do tipo ra-reputation, lendo o id direto do licensesConfig. O FooterBadges renderiza o widget vivo (<div id="ra-reputation" data-id=… data-model="2" />) ou cai no badge estático legado quando não há seal de widget. Habilitado em 7k-bet-br, cassino-bet-br e vera-bet-br com os ids do dynamicLicenses.json legado.
LP /esportes da 7k-bet-br portada para o novo override.
Renomeação do cookie de consentimento de cactusCookiesConsent para cookies_consent (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 cactus_jwt da COOKIE_TABLE também foi renomeada para jwt_token, batendo com o nome real setado em cookie.server.ts. A mudança elimina vazamento de termos internos da plataforma (cactus) em superfícies inspecionáveis pelo cliente final.
Coordenação de widgets flutuantes em mobile (PR #337): o banner de consentimento agora publica cookieConsentMobileTopPx na floatingStack store via ResizeObserver + listener de resize, capturando ao mesmo tempo a âncora inferior e a altura renderizada. Os widgets LastGameFloatingWidget, InstallApp e Campaign se auto-escondem em mobile (< 640px) enquanto o banner está visível, e o BackToTop permanece visível mas ancora seu bottom em topPx + 8px com z-[65] para ficar acima do banner (z-[60]).
Borda em radial-gradient no TopGameCardShowcase (PR #330): o paleta gold/silver/bronze do pódio foi removida — todos os ranks agora usam primary 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 currentColor + color-mix, então qualquer cor primária de brand é capturada automaticamente. O badge do rank foi remoldado em "corner tab" rente (rounded-tl-lg / rounded-br-2xl) com bg-primary e text-button-text em todas as posições.
Padronização do topGamesRankStyle: "showcase" em todos os brands (PR #349): default e os 10 overrides agora alinhados na mesma variante de rank para a row de top games, substituindo o legado outline-sideways.
Flag onlineCountSource para fonte do contador "online" (PR #331): badge "online" do GameCard e card "Online agora" do GameStats passam a ler featuresConfig.onlineCountSource e escolher entre last1Hour.players (default) e last24Hours.players. O fallback oportunista entre janelas foi removido — o badge agora some quando a janela escolhida está em zero. Brand pb-bet configurado para a janela de 24 h; demais permanecem na de 1 h.
Fluxo de salvar conta bancária do Chile reformulado (PR #327): a operação de criar/editar conta bancária agora passa pelo useValidationFlow({ context: "updateData" }) (mesma mecânica de update de endereço), garantindo o token de sessão exigido pelo BFF — antes ChlBanksSection, NgaBanksSection e ClabeSection postavam direto para /api/payments/bank-account e falhavam silenciosamente. Os selects do ChlBanksSection / WithdrawForm agora anexam o código bruto do backend entre parênteses (ex: Cuenta Corriente (CHECKING), CuentaRUT (RUT)) para ops e usuários desambiguarem duplicatas, com ordenação por adoção (CuentaRUT primeiro, ~82% da população). O proxy de bank-account agora encaminha tanto o payload bruto do BFF quanto a message já extraída, e o componente cai num genérico traduzido ("Error al guardar cuenta bancaria. Inténtalo de nuevo.") quando o extrator só consegue surfar um código de máquina. PixSection reordenou as key types por probabilidade de digitação manual no withdraw (CPF → PHONE → EVP → EMAIL) e usa filtro inclusivo, garantindo que EVP cai na posição correta quando a feature flag está ligada. Bumps acompanhantes: @cactus-agents/api-client ^0.10.1 → ^0.11.0, @cactus-agents/i18n ^0.65.0 → ^0.66.0 e @cactus-agents/payments ^0.12.2 → ^0.14.0.
Iframe do sportsbook agora dimensiona pela área visível do viewport (PR #350): novo hook useSportsAvailableHeight mede o top do <main> em coordenadas de viewport (capturando topbar + sticky header naturalmente) e o offsetHeight do bottom nav (resolvendo safe-area-bottom, que getPropertyValue("--mobile-nav-h") não consegue), publicando o resultado como --sports-h. Listeners cobrem scroll (rAF-throttled) para o iframe crescer conforme a topbar desliza, visualViewport resize/scroll para a animação da URL bar do iOS, mais ResizeObserver no header e no nav. As variantes First, Altenar e Betby agora consomem o mesmo token, eliminando o calc(100vh - 200px) mágico do Betby/Altenar e o min-h-[100dvh] que colapsava o iframe a zero na branch vera/cassino.
Toggle de marketing envia datetime e não boolean (PR #344): o BFF /bff/users/self-mkt espera { mkt_accepted_at: "YYYY-MM-DD HH:MM:SS" } quando o usuário aceita comunicações e null quando revoga — não um boolean. O componente continua emitindo { mkt_accepted: boolean } (paridade com o tipo do core), e a rota /api/user/update-marketing faz a tradução antes de encaminhar ao BFF. Acompanha o bump do core (@cactus-agents/accounts) que ajustou o tipo UpdateMarketingPayload.
Pacote @cactus-agents/accounts recebeu UpdateMarketingPayload ajustado para mkt_accepted_at: string | null (datetime YYYY-MM-DD HH:MM:SS ao aceitar, null ao revogar) — antes era mkt_accepted: boolean, divergindo do contrato real do BFF /bff/users/self-mkt (Core PR #147).
Pacote @cactus-agents/payments chegou na v0.14.0 com kycId + passwordCheck adicionados a GenericBankAccountPayload e MexBankAccountPayload, permitindo encaminhamento tipado dos artefatos de validação no fluxo de bank-account do Chile, Nigéria e México.
Pacote @cactus-agents/api-client chegou na v0.11.0 e @cactus-agents/i18n na v0.66.0, ambos consumidos pelo bump acompanhante do PR #327.