 ---
 
  # TAREFA: Implementar modelo freemium/PLG (plano gratuito self-service + Upgrade PRO)

  Você vai transformar o caminho de aquisição de assinatura deste SaaS multi-tenant
  em um modelo freemium/PLG, igual ao que já fiz num sistema irmão. O objetivo:
  qualquer visitante cria uma conta gratuita sozinho, confirma o e-mail, e o ambiente
  do tenant é provisionado automaticamente — sem dev no meio. Plano gratuito limitado
  + botão "Upgrade PRO" no topo.

  IMPORTANTE: este sistema é PARECIDO mas NÃO idêntico ao de referência. NÃO assuma
  nomes de tabelas/funções/rotas. Antes de QUALQUER código, faça a fase de descoberta
  e me apresente o mapa + as decisões pra eu confirmar. Trabalhe em fases, commitando
  por assunto, e validando cada migration no banco local em transação com ROLLBACK
  antes de seguir. Rode o build a cada bloco de frontend.

  ## FASE 0 — DESCOBERTA (não codar ainda; me devolva um mapa com file:line)
  Mapeie e me explique como funciona hoje:
  1. Landing page / vitrine de planos e como o signup é acionado (query params? rota?).
  2. Fluxo de signup: componente, se usa supabase.auth.signUp direto ou um wrapper,
     o que cria (auth user, profile, tenant, subscription). Existe trigger
     handle_new_user em auth.users? Onde o profile nasce e com qual role default?
  3. Modelo de planos SaaS: tabelas (plans, plan_prices, plan_features, plan_limits,
     subscriptions, subscription_intents...), e o catálogo de features atual (LEIA
     DO BANCO, não de seeds antigos — o catálogo costuma divergir do seed inicial).
  4. Feature gating: como uma feature é checada (composable hasFeature? guard de
     rota com meta.feature? filtro de menu?).
  5. Enforcement de limites por plano: existe? (na maioria das vezes plan_limits
     está semeado mas NINGUÉM lê — confirme).
  6. Provisionamento de tenant: como um tenant nasce hoje (função provision_*?),
     é manual (dev) ou automático? É multi-tenant por RLS ou schema-per-tenant?
     Se schema-per-tenant: existe clone_tenant_schema/tenant_schema_name? O clone
     copia triggers do template?
  7. Fluxo de auth: onde o profile é carregado no login (carregarPerfil?), onde o
     guard decide pra onde mandar o usuário (roleHomePath), e o que acontece com um
     usuário logado SEM tenant.
  8. Infra de e-mail: como e-mails transacionais são enviados (Resend? SMTP? edge
     function?). Existe tabela de templates + algum render de {{var}}? O e-mail do
     GoTrue (confirmação) funciona? Existe pg_net?
  9. Infra de billing/pagamento (AsaaS/Stripe?): existe checkout de assinatura
     RECORRENTE em nível de plano, ou só cobrança avulsa? Onde está o webhook?

  ## FASE 0.5 — DECISÕES (me apresente como perguntas; estes são os defaults que
  ## funcionaram bem, com o porquê):
  - Provisionamento: AUTO, mas só DEPOIS de confirmar o e-mail (anti-spam: cada
    signup pode clonar dezenas de tabelas).
  - Funil: manter os dois caminhos (free self-service + pago via intent/comercial).
  - Upgrade PRO: checkout self-service (reusar infra de pagamento existente) — mas
    isso é FASE 3, deferida; no início o botão abre o canal comercial.
  - Trial: o "free para sempre" substitui o trial.
  - No limite: BLOQUEIA a inserção no banco (trigger) + toast amigável com CTA.
  - Slug do sindicato: a pessoa escolhe (sugestão automática a partir do nome,
    sanitizado), com checagem de disponibilidade ao vivo, e é IMUTÁVEL (se for
    schema-per-tenant, o slug É o nome do schema → trocar órfã tudo; trave em 3
    camadas: sem UI, guard no banco rejeitando UPDATE, validação na criação).

  ## FASE 1 — Fundação do plano gratuito
  1. Migration: criar plano `gratuito` (preço 0) + plan_features (tudo ON menos o
     módulo premium, ex: ordem_de_servico) + plan_limits (ex: 50 associados).
     REGRA DE OURO: referencie features POR KEY via subquery, NUNCA por uuid
     hardcoded (uuids de features geradas em runtime divergem entre ambientes).
     Deixe o plano OCULTO na vitrine nesta fase (self-service ainda não existe).
  2. Enforcement de limite GENÉRICO: uma função trigger que resolve o tenant pelo
     contexto (no schema-per-tenant: pelo nome do schema = TG_TABLE_SCHEMA; no
     RLS: pelo tenant_id), lê o plano ativo + plan_limits EM RUNTIME (pra mudar o
     número no painel valer sem deploy), conta linhas vivas e dá RAISE com um código
     parseável tipo 'PLAN_LIMIT_REACHED|<feature>|<limite>'. Trigger BEFORE INSERT
     na tabela limitada. Se schema-per-tenant: coloque no template E faça backfill
     nos schemas já existentes. Teste: 50 passam, 51º bloqueia; tenant pago intacto.
  3. Frontend: helper que traduz o erro PLAN_LIMIT_REACHED em toast amigável com
     CTA de upgrade, usado em TODOS os pontos de insert da tabela limitada. Botão
     "Upgrade PRO" no topbar quando o plano do tenant for 'gratuito'.

  ## FASE 2 — Self-service com confirmação de e-mail
  1. LIGUE a confirmação de e-mail (enable_confirmations=true no config.toml E no
     dashboard do hosted).
  2. ⚠️ PEGADINHA CRÍTICA #1: com confirmação ligada, o signup NÃO tem sessão. Então
     TUDO que dependia de auth.uid()/JWT no signup QUEBRA em silêncio:
     - inserir subscription_intents (RLS exige jwt email = email da linha) → erro.
     - registrar aceite legal (LGPD) → não grava.
     SOLUÇÃO: NÃO faça esses efeitos no signup. Grave a escolha (plan_key, interval,
     nome/slug do sindicato, ids das versões legais aceitas) no raw_user_meta_data
     do signUp, e processe TUDO no 1º login pós-confirmação, via RPCs idempotentes:
     - auto_provision_free_tenant() (lê metadata, cria tenant, provisiona, vira
       master, cria subscription gratuita ativa) — chamada em carregarPerfil quando
       o usuário não tem tenant. Gratuito não gera intenção.
     - processar_pos_signup() (aceite legal + cria a intenção SÓ pro caminho pago).
  3. ⚠️ PEGADINHA CRÍTICA #2 (segurança): após o signUp, se NÃO veio sessão
     (confirmação pendente), ENCERRE qualquer sessão local (signOut scope:'local')
     e mostre uma tela "confirme seu e-mail". Senão, uma sessão anterior (ex: dev
     testando) vaza e o push pra /login joga o usuário pro painel da sessão antiga.
     A pessoa só pode logar APÓS clicar no link do e-mail.
  4. ⚠️ PEGADINHA CRÍTICA #3 (blindagem): um usuário logado SEM tenant nunca pode
     cair num painel quebrado. No guard, redirecione todo logado-sem-tenant (não-dev)
     pra uma tela /onboarding que resolve os estados: provisionando, slug colidiu
     (deixa escolher outro slug e finalizar — faça o auto_provision aceitar um
     p_slug_override), conta paga aguardando ativação, sem acesso, erro (retry).
  5. Signup coleta nome do sindicato + slug (sugestão + sanitização + disponibilidade
     ao vivo via RPC slug_disponivel que retorna {ok, motivo}) + "seu nome".
     Torne o plano gratuito visível na vitrine agora.
  6. E-mail de boas-vindas: edge function (Resend) que renderiza o template, disparada
     no provisionamento. Best-effort (não bloqueia o login). Destinatário derivado
     do JWT, não do body.

  ## SAAS / EXTRAS (faça os que fizerem sentido)
  - Página /saas/usuarios: 1 linha por tenant com o DONO (master) — nome, slug,
    e-mail principal — via uma RPC dev-only que cruza tenants+profiles+subscriptions
    (SECURITY DEFINER). Realce em verde + selo "Novo" pra cliente criado nas últimas
    24h (rowClass baseado em created_at). Reaproveite essa RPC pra mostrar o e-mail
    principal também nas listagens de assinaturas e tenants.
  - Notificação aos devs quando nasce/muda uma assinatura (incl. trial): trigger em
    subscriptions chamando a função notify_all_devs com deeplink. ⚠️ PEGADINHA #4:
    se o sino de notificações é um singleton com flag "initialized", garanta que ele
    RE-BUSCA ao trocar de usuário (logout+login), senão fica stale e ainda vaza
    notificações entre usuários. A notificação só aparece pós-provisionamento e no
    sino do DEV (não do novo usuário).
  - "Esqueci meu e-mail": tela onde a pessoa informa o IDENTIFICADOR do sindicato
    (slug, que ela escolheu e foi avisada ser definitivo) → o servidor acha o e-mail
    do dono → mostra só uma DICA MASCARADA (jo****@gm****.com) → envia magic link
    (signInWithOtp, que usa o mesmo pipeline de e-mail do GoTrue, sem depender de
    Resend) → a pessoa clica e entra. O e-mail real NUNCA volta pro cliente.
  - root_redirect: coluna em config + RPC pública + guard, pra escolher pra onde o
    visitante não logado vai na raiz "/" (landing ou login).
  - Lista de bloqueio (blacklist) de e-mails e slugs, gerida em Configurações:
    tabela blacklist (kind email|slug). E-mail bloqueia o cadastro DE VERDADE via
    trigger BEFORE INSERT em auth.users (não só no front); suporte a domínio inteiro
    com entrada '@dominio.com'. Slug integra no slug_disponivel (motivo 'bloqueado').

  ## MÉTODO DE TRABALHO
  - Tudo numa branch nova. Commits pequenos por assunto, mensagem clara.
  - Cada migration: aplique no banco local e TESTE em transação com ROLLBACK (crie
    auth.users fake + impersone via set_config('request.jwt.claims',...)) antes de
    seguir. RPCs idempotentes.
  - Rode o build do frontend a cada bloco pra pegar erro cedo.
  - NUMERE as migrations com cuidado pra não colidir versão (quebra o db push).
  - Me mostre o mapa da Fase 0 e as decisões da Fase 0.5 ANTES de codar.

  ## DEPLOY (no fim)
  Migrations no hosted (db push) → dashboard Auth "Confirm email" ON + Site/Redirect
  URLs corretas → deploy das edge functions + secret do provedor de e-mail → rebuild
  do frontend → smoke test do fluxo: /lp → grátis → confirma e-mail → entra
  provisionado → limite bloqueia → sino do dev → esqueci-email.

  ---
  Esse prompt é "diretor": ele força a IA a mapear o teu outro sistema primeiro (porque as tabelas/nomes vão diferir) e
  te apresentar decisões antes de codar — do jeito que fizemos aqui. As 4 pegadinhas marcadas com ⚠️ são as que mais
  custaram tempo; com elas escritas, a IA evita de cara.

  Quer que eu gere também uma versão curta (1 parágrafo) pra um primeiro disparo, ou uma variante específica caso o
  outro sistema seja RLS puro (sem schema-per-tenant)? Aí eu ajusto os trechos de provisionamento/enforcement.