/* global React */
/* ===========================================================
   VOLTLAB · AUTH SYSTEM
   ─ Login / Cadastro com validação
   ─ Browser Fingerprinting (substituto do HWID para web)
   ─ JWT simulado (assinar/verificar via HMAC-SHA256 via SubtleCrypto)
   ─ Controle de licença por plano, expiração e dispositivos
   ─ Painel Admin: usuários, licenças, planos
   ─ Guard de rota: toda a aplicação passa pela validação antes de abrir
   
   ARQUITETURA (frontend-only para teste):
   ─ "Banco de dados" no localStorage (chave vl_auth_db)
   ─ JWT Base64 com assinatura local (seguro para demo/staging)
   ─ Pronto para substituir por chamadas fetch() para a API real

   PARA PRODUÇÃO: substituir VL_AUTH.api.* por fetch para o backend
   Node/Express + Prisma descrito na especificação.
=========================================================== */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

/* ═══════════════════════════════════════════════════════════
   ⚠️  FASE DE TESTES — BYPASS DE PAGAMENTO
   ───────────────────────────────────────────────────────────
   Enquanto VL_BYPASS_PAGAMENTO === true, TODOS os módulos ficam
   liberados para qualquer usuário, independentemente do plano.
   A tela de "módulo bloqueado" e a página de upgrade NÃO são
   exibidas. O login/cadastro continua funcionando normalmente.

   👉 Para reativar a cobrança no futuro, basta trocar para false.
   ═══════════════════════════════════════════════════════════ */
const VL_BYPASS_PAGAMENTO = false;
window.VL_BYPASS_PAGAMENTO = VL_BYPASS_PAGAMENTO;

/* ──────────────────────────────────────────────────────────
   BROWSER FINGERPRINT
   Combina propriedades estáveis do browser/dispositivo
   para gerar um ID único sem depender de cookies.
────────────────────────────────────────────────────────── */
async function gerarFingerprint() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.textBaseline = 'alphabetic';
  ctx.font = '14px Arial';
  ctx.fillText('VoltLab🔌⚡', 2, 15);
  const canvasHash = canvas.toDataURL();

  const components = [
    navigator.userAgent,
    navigator.language,
    navigator.hardwareConcurrency,
    screen.width + 'x' + screen.height + 'x' + screen.colorDepth,
    new Date().getTimezoneOffset(),
    navigator.platform,
    canvasHash.slice(0, 80),
  ].join('||');

  // SHA-256 via SubtleCrypto (disponível em todos browsers modernos)
  const msgBuffer = new TextEncoder().encode(components);
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 32);
}

/* ──────────────────────────────────────────────────────────
   JWT SIMULADO (Base64url + HMAC-SHA256)
   Para produção: o backend emite o JWT real
────────────────────────────────────────────────────────── */
const JWT_SECRET = 'vl_jwt_secret_2025_trocar_em_producao';

function b64url(str) {
  return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function b64urlDecode(str) {
  return atob(str.replace(/-/g, '+').replace(/_/g, '/'));
}

async function signJWT(payload) {
  const header  = b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
  const body    = b64url(JSON.stringify(payload));
  const data    = header + '.' + body;
  const key     = await crypto.subtle.importKey(
    'raw', new TextEncoder().encode(JWT_SECRET),
    { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
  );
  const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));
  const sigB64 = b64url(String.fromCharCode(...new Uint8Array(sig)));
  return data + '.' + sigB64;
}

async function verifyJWT(token) {
  try {
    const parts = token.split('.');
    if (parts.length !== 3) return null;
    const [header, body, sig] = parts;
    const data = header + '.' + body;
    const key = await crypto.subtle.importKey(
      'raw', new TextEncoder().encode(JWT_SECRET),
      { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']
    );
    const sigBytes = Uint8Array.from(b64urlDecode(sig), c => c.charCodeAt(0));
    const valid = await crypto.subtle.verify('HMAC', key, sigBytes, new TextEncoder().encode(data));
    if (!valid) return null;
    const payload = JSON.parse(b64urlDecode(body));
    if (payload.exp && Date.now() > payload.exp) return null; // expirado
    return payload;
  } catch { return null; }
}

/* ──────────────────────────────────────────────────────────
   BANCO DE DADOS LOCAL (localStorage)
   Estrutura real do Prisma mapeada para JSON
────────────────────────────────────────────────────────── */
const DB_KEY = 'vl_auth_db';

/* ──────────────────────────────────────────────────────────
   PLANOS (definidos no código — fonte da verdade)
   ───────────────────────────────────────────────────────────
   Módulos FREE: Corrente, Queda de Tensão, Dimensionamento e
   Taxa de Ocupação (+ Dashboard e Calculadoras sempre liberados).
   Os demais ('*') exigem assinatura paga.
────────────────────────────────────────────────────────── */
const MODULOS_FREE = ['dashboard', 'calculators', 'current', 'drop', 'cables', 'eletroduto'];

const PLANS_SEED = [
  {
    id: 'plan_free',
    nome_plano: 'Free',
    preco: 0,
    ciclo: 'mês',
    duracao_dias: 365,
    limite_dispositivos: 1,
    modulos: MODULOS_FREE,
    ativo: true,
    descricao: 'Corrente, Queda de Tensão, Dimensionamento e Taxa de Ocupação.',
  },
  {
    id: 'plan_estudante',
    nome_plano: 'Estudante',
    preco: 19.9,
    ciclo: 'mês',
    duracao_dias: 30,
    limite_dispositivos: 1,
    modulos: ['*'],
    ativo: true,
    requer_comprovante: true,
    descricao: 'Acesso total. Exige comprovante de matrícula.',
  },
  {
    id: 'plan_pro',
    nome_plano: 'Pro',
    preco: 49.9,
    ciclo: 'mês',
    duracao_dias: 30,
    limite_dispositivos: 2,
    modulos: ['*'],
    ativo: true,
    destaque: true,
    descricao: 'Acesso total + projetos ilimitados. Suporte por e-mail.',
  },
  {
    id: 'plan_pro_anual',
    nome_plano: 'Pro Anual',
    preco: 499,
    ciclo: 'ano',
    duracao_dias: 365,
    limite_dispositivos: 2,
    modulos: ['*'],
    ativo: true,
    descricao: 'Tudo do Pro com ~2 meses grátis (≈ R$ 41,58/mês).',
  },
];

function loadDB() {
  let db = null;
  try {
    const raw = localStorage.getItem(DB_KEY);
    if (raw) db = JSON.parse(raw);
  } catch {}
  if (!db) {
    db = { users: [], plans: [], licenses: [], subscriptions: [], devices: [], payments: [] };
  }
  // Os planos são definidos no código: sempre sincroniza com PLANS_SEED
  // (assim mudanças de preço/módulos valem mesmo com um DB já salvo).
  db.plans = PLANS_SEED.map(p => ({ ...p }));
  return db;
}

function saveDB(db) {
  try { localStorage.setItem(DB_KEY, JSON.stringify(db)); } catch {}
}

function genId(prefix = '') {
  return prefix + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
}

async function hashSenha(senha) {
  const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(senha + '_vl_salt'));
  return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,'0')).join('');
}

/* ──────────────────────────────────────────────────────────
   LICENÇA — garantia de licença ativa (self-healing)
   Evita "Licença expirada" para usuários válidos quando:
     • o plano Free não existe mais no DB salvo;
     • um upgrade do admin desativou licenças antigas;
     • o usuário foi criado por um caminho que não gerou licença.
   Sempre retorna uma licença ativa (provisiona Free se preciso).
   Free é gratuito, então re-provisionar é legítimo.
────────────────────────────────────────────────────────── */
function ensureActiveLicense(db, userId) {
  // 1) Já existe licença ativa e não expirada?
  let lic = db.licenses.find(l =>
    l.user_id === userId && l.ativa && new Date(l.data_expiracao) > new Date()
  );
  if (lic) return lic;

  const planFree = db.plans.find(p => p.id === 'plan_free');
  const dur = planFree?.duracao_dias || 365;
  const exp = new Date(Date.now() + dur * 864e5).toISOString();

  // 2) Reaproveita a licença Free mais recente do usuário (reativa + estende)
  const freeAntiga = db.licenses
    .filter(l => l.user_id === userId && l.plan_id === 'plan_free')
    .sort((a, b) => new Date(b.data_expiracao) - new Date(a.data_expiracao))[0];
  if (freeAntiga) {
    freeAntiga.ativa = true;
    freeAntiga.data_expiracao = exp;
    freeAntiga.ultima_validacao = new Date().toISOString();
    lic = freeAntiga;
  } else {
    // 3) Cria uma licença Free nova
    lic = {
      id: genId('lic_'),
      user_id: userId,
      chave_licenca: 'FREE-' + genId().toUpperCase(),
      hardware_id: null,
      ativa: true,
      ultima_validacao: new Date().toISOString(),
      data_expiracao: exp,
      plan_id: 'plan_free',
    };
    db.licenses.push(lic);
  }

  // Garante uma subscription ACTIVE coerente
  if (!db.subscriptions.find(s => s.user_id === userId && s.status === 'ACTIVE' && new Date(s.data_expiracao) > new Date())) {
    db.subscriptions.push({
      id: genId('sub_'), user_id: userId, plan_id: 'plan_free',
      status: 'ACTIVE', data_inicio: new Date().toISOString(), data_expiracao: exp,
    });
  }
  return lic;
}

/* ──────────────────────────────────────────────────────────
   ARMAZENAMENTO DO TOKEN
   "Manter sessão" liga/desliga a persistência:
     • remember = true  → localStorage  (sobrevive a fechar o navegador)
     • remember = false → sessionStorage (some ao fechar a aba)
   getToken() lê de ambos; clearToken() limpa os dois.
────────────────────────────────────────────────────────── */
const TOKEN_KEY = 'vl_token';

function setToken(token, remember) {
  try {
    if (remember) {
      localStorage.setItem(TOKEN_KEY, token);
      sessionStorage.removeItem(TOKEN_KEY);
    } else {
      sessionStorage.setItem(TOKEN_KEY, token);
      localStorage.removeItem(TOKEN_KEY);
    }
  } catch {}
}
function getToken() {
  try { return localStorage.getItem(TOKEN_KEY) || sessionStorage.getItem(TOKEN_KEY); }
  catch { return null; }
}
function clearToken() {
  try { localStorage.removeItem(TOKEN_KEY); sessionStorage.removeItem(TOKEN_KEY); } catch {}
}

/* ──────────────────────────────────────────────────────────
   API CAMADA (simula backend — trocar por fetch em produção)
────────────────────────────────────────────────────────── */
/* ──────────────────────────────────────────────────────────
   MEMÓRIA TEMPORÁRIA PARA VALIDAÇÃO DE E-MAIL
────────────────────────────────────────────────────────── */
const pendingRegistrations = {};
const API_URL = 'api.php'; // Aponta para o arquivo que criamos na hospedagem

/* ──────────────────────────────────────────────────────────
   API CAMADA (Conectado ao Banco de Dados Real)
────────────────────────────────────────────────────────── */
const VL_AUTH = {
  // ── SOLICITAR CÓDIGO DE REGISTRO ────────────────────────
  async requestRegistrationCode({ nome, email, senha }) {
    try {
      const resp = await fetch(API_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'request_code', email })
      });
      const data = await resp.json();
      
      if (!data.ok) return { ok: false, error: data.error };

      pendingRegistrations[email] = { nome, email, senha, code: data.code, expires: Date.now() + 10 * 60000 };
      return { ok: true, codeForTesting: data.code };
    } catch (e) {
      return { ok: false, error: 'Erro ao conectar com o servidor.' };
    }
  },

  // ── VERIFICAR CÓDIGO E REGISTRAR ────────────────────────
  async verifyAndRegister({ email, code }) {
    const pending = pendingRegistrations[email];
    if (!pending) return { ok: false, error: 'Solicitação não encontrada ou expirada.' };
    if (Date.now() > pending.expires) return { ok: false, error: 'Código expirado. Tente novamente.' };
    if (pending.code !== code) return { ok: false, error: 'Código de verificação incorreto.' };

    const res = await this.register({ nome: pending.nome, email: pending.email, senha: pending.senha });
    delete pendingRegistrations[email];
    return res;
  },

  // ── REGISTRO OFICIAL NO MYSQL ───────────────────────────
  async register({ nome, email, senha }) {
    try {
      const resp = await fetch(API_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'register', nome, email, senha })
      });
      const data = await resp.json();
      
      if (!data.ok) return { ok: false, error: data.error };
      
      // Sincroniza com a interface para manter licenças funcionando
      const db = loadDB();
      if (!db.users.find(u => u.id === data.user.id)) {
          db.users.push({...data.user, senha_hash: 'backend_hash'});
          ensureActiveLicense(db, data.user.id);
          saveDB(db);
      }

      return { ok: true, user: data.user };
    } catch (e) {
      return { ok: false, error: 'Erro ao registrar usuário no banco de dados.' };
    }
  },

// ── LOGIN OFICIAL NO MYSQL ──────────────────────────────
  async login({ email, senha, fingerprint, remember = false }) {
    try {
      const resp = await fetch(API_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'login', email, senha })
      });
      const data = await resp.json();
      
      if (!data.ok) return { ok: false, error: data.error };

      const user = data.user;
      const role = user.role.toUpperCase();

      // Monta o plano dinamicamente com base na resposta do banco
      const plan = {
        id: `plan_${role.toLowerCase()}`,
        nome_plano: role === 'PRO' ? 'Pro' : role === 'GREEN' ? 'Green' : role === 'ADMIN' ? 'Admin' : 'Free',
        preco: role === 'FREE' ? 0 : null,
        modulos: ['FREE', 'USER'].includes(role) ? MODULOS_FREE : ['*'], // Admin, Pro e Green têm tudo ['*']
        data_expiracao: user.data_expiracao
      };

      const ttl = remember ? 30 * 864e5 : 8 * 3600 * 1000;
      
      // Coloca as informações vitais dentro do Token JWT
      const payload = { 
        sub: user.id, nome: user.nome, email: user.email, role: user.role, 
        plan, fingerprint, remember, iat: Date.now(), exp: Date.now() + ttl 
      };
      const token = await signJWT(payload);

      return { ok: true, token, remember, user, plan };
    } catch (e) {
      return { ok: false, error: 'Erro ao validar login no servidor.' };
    }
  },

  // ── VALIDAR TOKEN ────────────────────────────────────────
  async validateToken(token, fingerprint) {
    const payload = await verifyJWT(token); 
    if (!payload) return { ok: false, error: 'Sessão inválida ou expirada.' };

    if (!VL_BYPASS_PAGAMENTO && payload.fingerprint && fingerprint && payload.fingerprint !== fingerprint) {
      return { ok: false, error: 'Dispositivo não reconhecido. Faça login novamente.' };
    }

    // O Frontend confia totalmente no JWT e no plano que veio do banco na hora do login
    return { 
        ok: true, 
        payload: payload, 
        plan: payload.plan 
    };
  },

  // ── MÓDULOS LIBERADOS ────────────────────────────────────
  modulosLiberados(plan) {
    if (VL_BYPASS_PAGAMENTO) return null;         // Fase de testes: tudo liberado
    if (!plan) return MODULOS_FREE;
    if (plan.modulos?.includes('*')) return null; // Null significa "acesso total"
    return plan.modulos || MODULOS_FREE;
  },

  // ── ADMIN: listar usuários ───────────────────────────────
  adminListUsers() {
    const db = loadDB();
    return db.users.map(u => {
      const subs = db.subscriptions.filter(s => s.user_id === u.id);
      const lics = db.licenses.filter(l => l.user_id === u.id);
      const devs = db.devices.filter(d => d.user_id === u.id);
      const activeSub = subs.find(s => s.status === 'ACTIVE' && new Date(s.data_expiracao) > new Date());
      const plan = activeSub ? db.plans.find(p => p.id === activeSub.plan_id) : null;
      return { ...u, senha_hash: '***', subs, lics, devs, plan: plan?.nome_plano || 'Sem plano' };
    });
  },

  // ── ADMIN: upgradar plano ────────────────────────────────
  adminUpgradePlan(userId, planId) {
    const db = loadDB();
    const plan = db.plans.find(p => p.id === planId);
    if (!plan) return { ok: false, error: 'Plano não encontrado.' };

    // Cancelar subs anteriores
    db.subscriptions.filter(s => s.user_id === userId).forEach(s => s.status = 'CANCELED');
    db.licenses.filter(l => l.user_id === userId).forEach(l => { l.ativa = false; });

    const exp = new Date(Date.now() + plan.duracao_dias * 864e5).toISOString();
    const lic = {
      id: genId('lic_'), user_id: userId,
      chave_licenca: plan.nome_plano.toUpperCase() + '-' + genId().toUpperCase().slice(0,12),
      hardware_id: null, ativa: true,
      ultima_validacao: new Date().toISOString(),
      data_expiracao: exp, plan_id: planId,
    };
    db.licenses.push(lic);
    db.subscriptions.push({
      id: genId('sub_'), user_id: userId, plan_id: planId,
      status: 'ACTIVE', data_inicio: new Date().toISOString(), data_expiracao: exp,
    });
    saveDB(db);
    return { ok: true };
  },

  // ── ADMIN: remover dispositivo ───────────────────────────
  adminRemoveDevice(deviceId) {
    const db = loadDB();
    db.devices = db.devices.filter(d => d.id !== deviceId);
    saveDB(db);
    return { ok: true };
  },

  // ── USUÁRIO: remover próprio dispositivo ─────────────────
  userRemoveDevice(userId, deviceId) {
    const db = loadDB();
    db.devices = db.devices.filter(d => !(d.id === deviceId && d.user_id === userId));
    saveDB(db);
  },

  // ── LOGOUT ───────────────────────────────────────────────
  logout() {
    clearToken();
  },

  // ── TOKEN (helpers expostos para o app.jsx) ──────────────
  setToken,   // setToken(token, remember)
  getToken,   // lê de localStorage OU sessionStorage
  clearToken,
};

window.VL_AUTH = VL_AUTH;
// Exposto para a tela de login do app.jsx (escopos Babel separados não compartilham funções)
window.VL_fingerprint = gerarFingerprint;

/* ──────────────────────────────────────────────────────────
   ÍCONES
────────────────────────────────────────────────────────── */
const AuthIcon = ({ n, s = 18 }) => {
  const p = { width:s, height:s, viewBox:'0 0 24 24', fill:'none',
    stroke:'currentColor', strokeWidth:1.5, strokeLinecap:'round', strokeLinejoin:'round' };
  const d = {
    user:    <><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-7 8-7s8 3 8 7"/></>,
    lock:    <><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/></>,
    mail:    <><rect x="3" y="5" width="18" height="14" rx="2"/><polyline points="3 7 12 13 21 7"/></>,
    eye:     <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></>,
    eyeoff:  <><path d="M17.94 17.94A10 10 0 0112 19c-6.5 0-10-7-10-7a18 18 0 015.06-5.94M9.9 4.24A9 9 0 0112 4c6.5 0 10 7 10 7a18 18 0 01-1.72 2.54M1 1l22 22"/></>,
    shield:  <><path d="M12 2l8 4v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6z"/><polyline points="9 12 11 14 15 10"/></>,
    bolt:    <><path d="M13 2L4 14h7l-2 8 9-12h-7z"/></>,
    crown:   <><path d="M2 18h20l-2-8-6 4-2-8-2 8-6-4z"/></>,
    star:    <><polygon points="12 2 15.1 8.3 22 9.3 17 14.1 18.2 21 12 17.8 5.8 21 7 14.1 2 9.3 8.9 8.3 12 2"/></>,
    check:   <><polyline points="20 6 9 17 4 12"/></>,
    x:       <><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></>,
    refresh: <><path d="M21 12a9 9 0 11-3-6.7L21 8"/><path d="M21 3v5h-5"/></>,
    trash:   <><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></>,
    device:  <><rect x="5" y="2" width="14" height="20" rx="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></>,
    admin:   <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 00.3 1.8l.1.1a2 2 0 11-2.8 2.8l-.1-.1a1.7 1.7 0 00-1.8-.3 1.7 1.7 0 00-1 1.5V21a2 2 0 11-4 0v-.1a1.7 1.7 0 00-1.1-1.5 1.7 1.7 0 00-1.8.3l-.1.1a2 2 0 11-2.8-2.8l.1-.1a1.7 1.7 0 00.3-1.8 1.7 1.7 0 00-1.5-1H3a2 2 0 110-4h.1a1.7 1.7 0 001.5-1.1 1.7 1.7 0 00-.3-1.8l-.1-.1a2 2 0 112.8-2.8l.1.1a1.7 1.7 0 001.8.3H9a1.7 1.7 0 001-1.5V3a2 2 0 114 0v.1a1.7 1.7 0 001 1.5 1.7 1.7 0 001.8-.3l.1-.1a2 2 0 112.8 2.8l-.1.1a1.7 1.7 0 00-.3 1.8V9a1.7 1.7 0 001.5 1H21a2 2 0 110 4h-.1a1.7 1.7 0 00-1.5 1z"/></>,
    warn:    <><path d="M10.3 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></>,
    info:    <><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></>,
  };
  return <svg {...p}>{d[n]||null}</svg>;
};

/* ──────────────────────────────────────────────────────────
   FORMULÁRIO DE LOGIN / CADASTRO
────────────────────────────────────────────────────────── */
const AuthScreen = ({ onSuccess }) => {
  const [mode, setMode] = useState('login'); // login | register
  const [form, setForm] = useState({ nome:'', email:'', senha:'', confirmar:'' });
  const [showPwd, setShowPwd] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [fpReady, setFpReady] = useState(false);
  const fpRef = useRef('');

  useEffect(() => {
    gerarFingerprint().then(fp => { fpRef.current = fp; setFpReady(true); });
  }, []);

  const upd = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const handleSubmit = async () => {
    setError('');
    if (!fpReady) { setError('Aguardando identificação do dispositivo...'); return; }

    if (mode === 'register') {
      if (!form.nome.trim()) return setError('Informe seu nome.');
      if (!form.email.includes('@')) return setError('E-mail inválido.');
      if (form.senha.length < 6) return setError('Senha deve ter ao menos 6 caracteres.');
      if (form.senha !== form.confirmar) return setError('Senhas não conferem.');

      setLoading(true);
      const res = await VL_AUTH.register({ nome: form.nome, email: form.email, senha: form.senha });
      setLoading(false);
      if (!res.ok) return setError(res.error);
      setMode('login');
      setError('');
      setForm(f => ({ ...f, senha:'', confirmar:'' }));
      return;
    }

    // Login
    if (!form.email || !form.senha) return setError('Preencha e-mail e senha.');
    setLoading(true);
    const res = await VL_AUTH.login({ email: form.email, senha: form.senha, fingerprint: fpRef.current, remember: true });
    setLoading(false);
    if (!res.ok) return setError(res.error);

    VL_AUTH.setToken(res.token, true);
    onSuccess({ token: res.token, user: res.user, plan: res.plan, fingerprint: fpRef.current });
  };

  const planColors = { Free:'var(--fg-mute)', Pro:'var(--accent)', Enterprise:'var(--warn)' };

  return (
    <div style={{
      minHeight:'100vh', background:'var(--bg-0)',
      display:'flex', alignItems:'center', justifyContent:'center',
      fontFamily:'var(--ff-body)', padding:'24px',
    }}>
      {/* Background circuitry hint */}
      <div style={{ position:'fixed', inset:0, pointerEvents:'none', overflow:'hidden', zIndex:0 }}>
        <svg viewBox="0 0 1200 800" style={{ position:'absolute', width:'100%', height:'100%', opacity:0.04 }}>
          {[...Array(8)].map((_,i) => (
            <g key={i} stroke="var(--accent)" strokeWidth="1" fill="none">
              <line x1={i*160} y1="0" x2={i*160} y2="800"/>
              <line x1="0" y1={i*110} x2="1200" y2={i*110}/>
              <circle cx={i*160} cy={(i%4)*220} r="4" fill="var(--accent)"/>
            </g>
          ))}
        </svg>
      </div>

      <div style={{ position:'relative', zIndex:1, width:'100%', maxWidth:440 }}>
        {/* Logo */}
        <div style={{ textAlign:'center', marginBottom:32 }}>
          <div style={{
            display:'inline-flex', alignItems:'center', justifyContent:'center',
            width:52, height:52, borderRadius:14, background:'var(--accent)',
            marginBottom:16, boxShadow:'0 8px 32px var(--accent-glow)',
          }}>
            <AuthIcon n="bolt" s={26}/>
          </div>
          <div style={{ fontFamily:'var(--ff-display)', fontSize:24, fontWeight:700, letterSpacing:'0.04em' }}>
            VOLTLAB<span style={{ color:'var(--accent)' }}>.</span>
            <span style={{ color:'var(--fg-mute)', fontWeight:400 }}>eng</span>
          </div>
          <div style={{ fontSize:12, color:'var(--fg-mute)', marginTop:4, fontFamily:'var(--ff-mono)', letterSpacing:'0.08em' }}>
            PLATAFORMA DE ENGENHARIA ELÉTRICA
          </div>
        </div>

        {/* Card */}
        <div style={{
          background:'var(--bg-1)', border:'1px solid var(--border)',
          borderRadius:16, padding:32,
          boxShadow:'0 24px 80px oklch(0% 0 0 / 0.4)',
        }}>
          {/* Tabs */}
          <div style={{
            display:'flex', background:'var(--bg-2)', borderRadius:8,
            padding:3, marginBottom:24, gap:3,
          }}>
            {[['login','Entrar'],['register','Criar conta']].map(([id, lbl]) => (
              <button key={id} onClick={() => { setMode(id); setError(''); }}
                style={{
                  flex:1, padding:'8px', borderRadius:6, fontSize:13, fontWeight:600,
                  background: mode===id ? 'var(--bg-1)' : 'transparent',
                  color: mode===id ? 'var(--accent)' : 'var(--fg-mute)',
                  border: mode===id ? '1px solid var(--border-2)' : '1px solid transparent',
                  transition:'all .15s', cursor:'pointer',
                  boxShadow: mode===id ? '0 2px 8px var(--accent-glow)' : 'none',
                }}>
                {lbl}
              </button>
            ))}
          </div>

          {/* Fields */}
          <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
            {mode === 'register' && (
              <div>
                <label style={{ fontSize:12, color:'var(--fg-mute)', display:'block', marginBottom:5, fontFamily:'var(--ff-mono)', letterSpacing:'0.06em' }}>NOME COMPLETO</label>
                <div style={{ display:'flex', alignItems:'center', background:'var(--bg-2)', border:'1px solid var(--border-2)', borderRadius:8, padding:'0 12px', height:42, gap:10 }}>
                  <AuthIcon n="user" s={15}/>
                  <input type="text" value={form.nome} onChange={e=>upd('nome',e.target.value)}
                    placeholder="Seu nome" onKeyDown={e=>e.key==='Enter'&&handleSubmit()}
                    style={{ flex:1, background:'transparent', border:0, outline:0, fontSize:14, color:'var(--fg)', fontFamily:'var(--ff-body)' }}/>
                </div>
              </div>
            )}

            <div>
              <label style={{ fontSize:12, color:'var(--fg-mute)', display:'block', marginBottom:5, fontFamily:'var(--ff-mono)', letterSpacing:'0.06em' }}>E-MAIL</label>
              <div style={{ display:'flex', alignItems:'center', background:'var(--bg-2)', border:'1px solid var(--border-2)', borderRadius:8, padding:'0 12px', height:42, gap:10 }}>
                <AuthIcon n="mail" s={15}/>
                <input type="email" value={form.email} onChange={e=>upd('email',e.target.value)}
                  placeholder="engenheiro@empresa.com" onKeyDown={e=>e.key==='Enter'&&handleSubmit()}
                  style={{ flex:1, background:'transparent', border:0, outline:0, fontSize:14, color:'var(--fg)', fontFamily:'var(--ff-body)' }}/>
              </div>
            </div>

            <div>
              <label style={{ fontSize:12, color:'var(--fg-mute)', display:'block', marginBottom:5, fontFamily:'var(--ff-mono)', letterSpacing:'0.06em' }}>SENHA</label>
              <div style={{ display:'flex', alignItems:'center', background:'var(--bg-2)', border:'1px solid var(--border-2)', borderRadius:8, padding:'0 12px', height:42, gap:10 }}>
                <AuthIcon n="lock" s={15}/>
                <input type={showPwd?'text':'password'} value={form.senha} onChange={e=>upd('senha',e.target.value)}
                  placeholder="••••••••" onKeyDown={e=>e.key==='Enter'&&handleSubmit()}
                  style={{ flex:1, background:'transparent', border:0, outline:0, fontSize:14, color:'var(--fg)', fontFamily:'var(--ff-body)' }}/>
                <button onClick={()=>setShowPwd(v=>!v)} style={{ color:'var(--fg-mute)', padding:2 }}>
                  <AuthIcon n={showPwd?'eyeoff':'eye'} s={15}/>
                </button>
              </div>
            </div>

            {mode === 'register' && (
              <div>
                <label style={{ fontSize:12, color:'var(--fg-mute)', display:'block', marginBottom:5, fontFamily:'var(--ff-mono)', letterSpacing:'0.06em' }}>CONFIRMAR SENHA</label>
                <div style={{ display:'flex', alignItems:'center', background:'var(--bg-2)', border:'1px solid var(--border-2)', borderRadius:8, padding:'0 12px', height:42, gap:10 }}>
                  <AuthIcon n="lock" s={15}/>
                  <input type={showPwd?'text':'password'} value={form.confirmar} onChange={e=>upd('confirmar',e.target.value)}
                    placeholder="••••••••" onKeyDown={e=>e.key==='Enter'&&handleSubmit()}
                    style={{ flex:1, background:'transparent', border:0, outline:0, fontSize:14, color:'var(--fg)', fontFamily:'var(--ff-body)' }}/>
                </div>
              </div>
            )}
          </div>

          {/* Error */}
          {error && (
            <div style={{
              marginTop:14, display:'flex', alignItems:'center', gap:8,
              background:'oklch(70% 0.20 25 / 0.1)', border:'1px solid oklch(70% 0.20 25 / 0.4)',
              borderRadius:8, padding:'10px 14px', fontSize:13, color:'var(--danger)',
            }}>
              <AuthIcon n="warn" s={14}/> {error}
            </div>
          )}

          {/* Submit */}
          <button onClick={handleSubmit} disabled={loading}
            style={{
              width:'100%', marginTop:20, padding:'12px',
              background:'var(--accent)', color:'oklch(15% 0.02 240)',
              border:'none', borderRadius:10, fontFamily:'var(--ff-display)',
              fontSize:14, fontWeight:700, cursor: loading?'wait':'pointer',
              boxShadow:'0 4px 20px var(--accent-glow)', transition:'all .18s',
              opacity: loading ? 0.7 : 1, letterSpacing:'0.04em',
            }}>
            {loading ? 'Aguarde…' : mode==='login' ? 'ENTRAR NO VOLTLAB' : 'CRIAR CONTA'}
          </button>

          {/* Fingerprint indicator */}
          <div style={{ marginTop:16, display:'flex', alignItems:'center', justifyContent:'center', gap:6, fontSize:11, color:'var(--fg-faint)' }}>
            <AuthIcon n="device" s={12}/>
            <span style={{ fontFamily:'var(--ff-mono)' }}>
              {fpReady ? `Dispositivo: ${fpRef.current.slice(0,12)}…` : 'Identificando dispositivo…'}
            </span>
          </div>

          {/* Planos */}
          {mode === 'register' && (
            <div style={{ marginTop:20, padding:'14px', background:'var(--bg-2)', borderRadius:8, border:'1px solid var(--border)' }}>
              <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.08em', marginBottom:10 }}>PLANOS DISPONÍVEIS</div>
              <div style={{ display:'flex', gap:8 }}>
                {loadDB().plans.filter(p=>p.ativo).map(p => (
                  <div key={p.id} style={{
                    flex:1, background:'var(--bg-3)', borderRadius:6, padding:'10px 8px',
                    border:`1px solid ${p.id==='plan_pro'?'var(--accent)':'var(--border)'}`,
                    textAlign:'center',
                  }}>
                    <div style={{ fontSize:11, fontWeight:700, color: p.id==='plan_pro'?'var(--accent)':p.id==='plan_enterprise'?'var(--warn)':'var(--fg-mute)', marginBottom:3 }}>{p.nome_plano}</div>
                    <div style={{ fontSize:13, fontWeight:700, fontFamily:'var(--ff-mono)', color:'var(--fg)' }}>
                      {p.preco===0 ? 'Grátis' : `R$ ${p.preco}/mês`}
                    </div>
                    <div style={{ fontSize:10, color:'var(--fg-faint)', marginTop:3 }}>{p.limite_dispositivos} dispositivo(s)</div>
                  </div>
                ))}
              </div>
              <div style={{ fontSize:11, color:'var(--fg-faint)', marginTop:8, textAlign:'center' }}>
                Cadastro inicia no plano Free. Upgrade no painel do usuário.
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

/* ──────────────────────────────────────────────────────────
   PAINEL DO USUÁRIO (minha conta)
────────────────────────────────────────────────────────── */
const UserPanel = ({ session, onLogout, onClose }) => {
  const { user, plan } = session;
  const [devices, setDevices] = useState([]);
  const [license, setLicense] = useState(null);

  const loadData = useCallback(() => {
    const db = loadDB();
    setDevices(db.devices.filter(d => d.user_id === user.id));
    setLicense(db.licenses.find(l => l.user_id === user.id && l.ativa) || null);
  }, [user.id]);

  useEffect(() => { loadData(); }, [loadData]);

  const removeDevice = (devId) => {
    VL_AUTH.userRemoveDevice(user.id, devId);
    loadData();
  };

  const planColor = { plan_free:'var(--fg-mute)', plan_pro:'var(--accent)', plan_enterprise:'var(--warn)' }[plan?.id] || 'var(--fg-mute)';

  const diasRestantes = license ? Math.max(0, Math.ceil((new Date(license.data_expiracao)-Date.now())/864e5)) : 0;

  return (
    <div style={{
      position:'fixed', inset:0, background:'rgba(0,0,0,.7)', zIndex:9998,
      display:'flex', alignItems:'flex-end', justifyContent:'flex-end',
    }} onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{
        width:380, height:'100vh', background:'var(--bg-1)',
        borderLeft:'1px solid var(--border-2)', padding:24,
        display:'flex', flexDirection:'column', gap:20, overflowY:'auto',
        boxShadow:'-20px 0 60px rgba(0,0,0,.4)',
      }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
          <div style={{ fontFamily:'var(--ff-display)', fontSize:16, fontWeight:700 }}>Minha Conta</div>
          <button onClick={onClose} style={{ color:'var(--fg-mute)', padding:4 }}><AuthIcon n="x" s={16}/></button>
        </div>

        {/* Avatar + nome */}
        <div style={{ display:'flex', alignItems:'center', gap:14, padding:'14px', background:'var(--bg-2)', borderRadius:10, border:'1px solid var(--border)' }}>
          <div style={{ width:44, height:44, borderRadius:12, background:'var(--accent-soft)', border:'1px solid var(--accent)', display:'flex', alignItems:'center', justifyContent:'center', color:'var(--accent)' }}>
            <AuthIcon n="user" s={22}/>
          </div>
          <div>
            <div style={{ fontWeight:700, fontSize:14 }}>{user.nome}</div>
            <div style={{ fontSize:12, color:'var(--fg-mute)' }}>{user.email}</div>
            {user.role==='ADMIN' && (
              <span style={{ fontSize:10, background:'oklch(80% 0.16 75 / 0.15)', border:'1px solid var(--warn)', color:'var(--warn)', borderRadius:4, padding:'1px 6px', fontFamily:'var(--ff-mono)', marginTop:3, display:'inline-block' }}>ADMIN</span>
            )}
          </div>
        </div>

        {/* Plano */}
        <div style={{ background:'var(--bg-2)', borderRadius:10, padding:16, border:`1px solid ${planColor}` }}>
          <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.08em', marginBottom:8 }}>PLANO ATIVO</div>
          <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
            <div style={{ fontSize:18, fontWeight:700, color: planColor, fontFamily:'var(--ff-display)' }}>
              {plan?.nome_plano || 'Sem plano'}
            </div>
            <div style={{ fontSize:12, color:'var(--fg-mute)' }}>
              {plan?.preco === 0 ? 'Gratuito' : `R$ ${plan?.preco}/mês`}
            </div>
          </div>
          {license && (
            <>
              <div style={{ height:4, background:'var(--bg-3)', borderRadius:2, marginBottom:8 }}>
                <div style={{ height:'100%', borderRadius:2, background: diasRestantes<5?'var(--danger)':planColor, width:`${Math.min(100,(diasRestantes/(plan?.duracao_dias||30))*100)}%`, transition:'width .4s' }}/>
              </div>
              <div style={{ display:'flex', justifyContent:'space-between', fontSize:11, color:'var(--fg-mute)' }}>
                <span>Expira: {new Date(license.data_expiracao).toLocaleDateString('pt-BR')}</span>
                <span style={{ color: diasRestantes<5?'var(--danger)':'var(--fg-mute)', fontWeight: diasRestantes<5?700:400 }}>
                  {diasRestantes} dias restantes
                </span>
              </div>
              <div style={{ marginTop:8, fontSize:10, color:'var(--fg-faint)', fontFamily:'var(--ff-mono)', wordBreak:'break-all' }}>
                Chave: {license.chave_licenca}
              </div>
            </>
          )}
        </div>

        {/* Módulos liberados */}
        <div style={{ background:'var(--bg-2)', borderRadius:10, padding:16, border:'1px solid var(--border)' }}>
          <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.08em', marginBottom:10 }}>MÓDULOS LIBERADOS{VL_BYPASS_PAGAMENTO ? ' · FASE DE TESTES' : ''}</div>
          <div style={{ display:'flex', flexWrap:'wrap', gap:6 }}>
            {(VL_BYPASS_PAGAMENTO || plan?.modulos?.includes('*') ? ['Todos os módulos liberados'] : (plan?.modulos||[])).map(m => (
              <span key={m} style={{
                background:'var(--accent-soft)', border:'1px solid var(--accent)',
                color:'var(--accent)', borderRadius:5, padding:'2px 8px',
                fontSize:10, fontFamily:'var(--ff-mono)',
              }}>{m}</span>
            ))}
          </div>
        </div>

        {/* Dispositivos */}
        <div style={{ background:'var(--bg-2)', borderRadius:10, padding:16, border:'1px solid var(--border)' }}>
          <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.08em', marginBottom:10 }}>
            DISPOSITIVOS ({devices.length}/{plan?.limite_dispositivos||1})
          </div>
          <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
            {devices.length === 0 && <div style={{ fontSize:12, color:'var(--fg-faint)' }}>Nenhum dispositivo registrado.</div>}
            {devices.map(d => (
              <div key={d.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 10px', background:'var(--bg-3)', borderRadius:7, border:'1px solid var(--border)' }}>
                <AuthIcon n="device" s={14}/>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:11, fontFamily:'var(--ff-mono)', color:'var(--fg-dim)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                    {d.hardware_id}
                  </div>
                  <div style={{ fontSize:10, color:'var(--fg-faint)' }}>
                    Último acesso: {new Date(d.ultima_conexao).toLocaleDateString('pt-BR')}
                  </div>
                </div>
                <button onClick={()=>removeDevice(d.id)} style={{ color:'var(--danger)', padding:3, flexShrink:0 }} title="Remover dispositivo">
                  <AuthIcon n="trash" s={13}/>
                </button>
              </div>
            ))}
          </div>
        </div>

        {/* Logout */}
        <button onClick={() => { VL_AUTH.logout(); onLogout(); }}
          style={{
            width:'100%', padding:'10px', borderRadius:8,
            background:'transparent', border:'1px solid var(--danger)',
            color:'var(--danger)', fontFamily:'var(--ff-display)', fontSize:13, fontWeight:600,
            cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', gap:8,
            marginTop:'auto',
          }}>
          <AuthIcon n="x" s={14}/> SAIR DO VOLTLAB
        </button>
      </div>
    </div>
  );
};

/* ──────────────────────────────────────────────────────────
   PAINEL ADMIN
────────────────────────────────────────────────────────── */
const AdminPanel = ({ session, onClose }) => {
  const [tab, setTab] = useState('users'); // users | plans | devices
  const [users, setUsers] = useState([]);
  const [plans, setPlans] = useState([]);
  const [upgradeModal, setUpgradeModal] = useState(null); // { userId, nome }

  const reload = useCallback(() => {
    setUsers(VL_AUTH.adminListUsers());
    setPlans(loadDB().plans);
  }, []);
  useEffect(reload, [reload]);

  const doUpgrade = (planId) => {
    if (!upgradeModal) return;
    VL_AUTH.adminUpgradePlan(upgradeModal.userId, planId);
    setUpgradeModal(null);
    reload();
  };

  const delDevice = (devId) => {
    VL_AUTH.adminRemoveDevice(devId);
    reload();
  };

  const allDevices = loadDB().devices;

  return (
    <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.75)', zIndex:9997, display:'flex', alignItems:'center', justifyContent:'center' }}
      onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{
        width:'90vw', maxWidth:900, maxHeight:'88vh', background:'var(--bg-1)',
        border:'1px solid var(--border-2)', borderRadius:14, display:'flex', flexDirection:'column',
        overflow:'hidden', boxShadow:'0 30px 100px rgba(0,0,0,.5)',
      }}>
        {/* Header */}
        <div style={{ display:'flex', alignItems:'center', gap:12, padding:'16px 24px', borderBottom:'1px solid var(--border)', flexShrink:0 }}>
          <div style={{ color:'var(--warn)' }}><AuthIcon n="admin" s={18}/></div>
          <div style={{ fontFamily:'var(--ff-display)', fontSize:16, fontWeight:700 }}>Painel Admin · VoltLab</div>
          <div style={{ marginLeft:'auto', display:'flex', gap:0, background:'var(--bg-2)', borderRadius:7, padding:2 }}>
            {[['users','Usuários'],['plans','Planos'],['devices','Dispositivos']].map(([id,lbl]) => (
              <button key={id} onClick={()=>setTab(id)} style={{
                padding:'6px 14px', borderRadius:5, fontSize:12, fontWeight:600,
                background: tab===id?'var(--bg-1)':'transparent',
                color: tab===id?'var(--accent)':'var(--fg-mute)',
                border: tab===id?'1px solid var(--border-2)':'1px solid transparent',
                cursor:'pointer', transition:'all .15s',
              }}>{lbl}</button>
            ))}
          </div>
          <button onClick={onClose} style={{ color:'var(--fg-mute)', padding:4, marginLeft:8 }}><AuthIcon n="x" s={16}/></button>
        </div>

        {/* Body */}
        <div style={{ flex:1, overflow:'auto', padding:24 }}>

          {/* USUÁRIOS */}
          {tab === 'users' && (
            <div>
              <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.1em', marginBottom:14 }}>
                USUÁRIOS CADASTRADOS ({users.length})
              </div>
              {users.length === 0 && <div style={{ color:'var(--fg-faint)' }}>Nenhum usuário.</div>}
              <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                {users.map(u => {
                  const activeLic = u.lics?.find(l => l.ativa && new Date(l.data_expiracao)>new Date());
                  const diasR = activeLic ? Math.ceil((new Date(activeLic.data_expiracao)-Date.now())/864e5) : 0;
                  return (
                    <div key={u.id} style={{ background:'var(--bg-2)', border:'1px solid var(--border)', borderRadius:10, padding:16 }}>
                      <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:10 }}>
                        <div style={{ width:36, height:36, borderRadius:9, background:'var(--bg-3)', display:'flex', alignItems:'center', justifyContent:'center', color:'var(--fg-mute)' }}>
                          <AuthIcon n={u.role==='ADMIN'?'crown':'user'} s={16}/>
                        </div>
                        <div style={{ flex:1 }}>
                          <div style={{ fontWeight:700, fontSize:13 }}>{u.nome}
                            {u.role==='ADMIN' && <span style={{ marginLeft:8, fontSize:9, background:'oklch(80% 0.16 75 / 0.15)', border:'1px solid var(--warn)', color:'var(--warn)', borderRadius:4, padding:'1px 5px', fontFamily:'var(--ff-mono)' }}>ADMIN</span>}
                          </div>
                          <div style={{ fontSize:12, color:'var(--fg-mute)' }}>{u.email}</div>
                        </div>
                        <div style={{ textAlign:'right' }}>
                          <div style={{ fontSize:13, fontWeight:700, color: u.plan==='Pro'?'var(--accent)':u.plan==='Enterprise'?'var(--warn)':'var(--fg-mute)' }}>{u.plan}</div>
                          {activeLic && <div style={{ fontSize:11, color: diasR<5?'var(--danger)':'var(--fg-faint)' }}>{diasR}d restantes</div>}
                        </div>
                        <button onClick={()=>setUpgradeModal({userId:u.id, nome:u.nome})}
                          style={{ background:'var(--accent-soft)', border:'1px solid var(--accent)', color:'var(--accent)', borderRadius:7, padding:'6px 12px', fontSize:11, fontFamily:'var(--ff-mono)', cursor:'pointer' }}>
                          Upgrade
                        </button>
                      </div>
                      <div style={{ display:'flex', gap:8, flexWrap:'wrap' }}>
                        <span style={{ fontSize:10, fontFamily:'var(--ff-mono)', color:'var(--fg-faint)' }}>
                          ID: {u.id.slice(0,12)}
                        </span>
                        <span style={{ fontSize:10, fontFamily:'var(--ff-mono)', color:'var(--fg-faint)' }}>
                          Criado: {new Date(u.created_at).toLocaleDateString('pt-BR')}
                        </span>
                        <span style={{ fontSize:10, fontFamily:'var(--ff-mono)', color:'var(--fg-faint)' }}>
                          Dispositivos: {u.devs?.length || 0}
                        </span>
                        {activeLic && <span style={{ fontSize:10, fontFamily:'var(--ff-mono)', color:'var(--fg-faint)' }}>
                          Chave: {activeLic.chave_licenca.slice(0,18)}…
                        </span>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* PLANOS */}
          {tab === 'plans' && (
            <div>
              <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.1em', marginBottom:14 }}>
                PLANOS DISPONÍVEIS ({plans.length})
              </div>
              <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(240px,1fr))', gap:14 }}>
                {plans.map(p => {
                  const usersNoPlan = users.filter(u => u.plan === p.nome_plano).length;
                  const cor = p.id==='plan_pro'?'var(--accent)':p.id==='plan_enterprise'?'var(--warn)':'var(--fg-mute)';
                  return (
                    <div key={p.id} style={{ background:'var(--bg-2)', border:`2px solid ${cor}`, borderRadius:12, padding:20 }}>
                      <div style={{ fontFamily:'var(--ff-display)', fontSize:16, fontWeight:700, color:cor, marginBottom:4 }}>{p.nome_plano}</div>
                      <div style={{ fontSize:20, fontWeight:700, fontFamily:'var(--ff-mono)', marginBottom:8 }}>
                        {p.preco===0?'Grátis':`R$ ${p.preco}`}
                        {p.preco>0&&<span style={{ fontSize:12, color:'var(--fg-mute)'}}>/mês</span>}
                      </div>
                      <div style={{ fontSize:12, color:'var(--fg-dim)', marginBottom:12 }}>{p.descricao}</div>
                      <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
                        {[
                          ['Duração', `${p.duracao_dias} dias`],
                          ['Dispositivos', `${p.limite_dispositivos}`],
                          ['Módulos', p.modulos?.includes('*')?'Todos':`${p.modulos?.length||0}`],
                          ['Usuários ativos', `${usersNoPlan}`],
                        ].map(([k,v])=>(
                          <div key={k} style={{ display:'flex', justifyContent:'space-between', fontSize:12 }}>
                            <span style={{ color:'var(--fg-mute)' }}>{k}</span>
                            <span style={{ fontFamily:'var(--ff-mono)', fontWeight:700 }}>{v}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* DISPOSITIVOS */}
          {tab === 'devices' && (
            <div>
              <div style={{ fontSize:10, color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', letterSpacing:'0.1em', marginBottom:14 }}>
                DISPOSITIVOS REGISTRADOS ({allDevices.length})
              </div>
              {allDevices.length === 0 && <div style={{ color:'var(--fg-faint)' }}>Nenhum dispositivo.</div>}
              <table style={{ width:'100%', borderCollapse:'collapse', fontSize:12 }}>
                <thead>
                  <tr style={{ borderBottom:'2px solid var(--border-2)' }}>
                    {['USUÁRIO','FINGERPRINT','ÚLTIMA CONEXÃO',''].map(h=>(
                      
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {allDevices.map(d => {
                    const u = users.find(u=>u.id===d.user_id);
                    return (
                      <tr key={d.id} style={{ borderBottom:'1px solid var(--border)' }}>
                        <td style={{ padding:'8px 10px' }}>{u?.nome||'?'}<br/><span style={{ fontSize:10, color:'var(--fg-mute)' }}>{u?.email}</span></td>
                        <td style={{ padding:'8px 10px', fontFamily:'var(--ff-mono)', fontSize:11, color:'var(--fg-dim)' }}>{d.hardware_id}</td>
                        <td style={{ padding:'8px 10px', color:'var(--fg-dim)' }}>{d.sistema_operacional||'—'}</td>
                        <td style={{ padding:'8px 10px', color:'var(--fg-mute)', fontFamily:'var(--ff-mono)', fontSize:11 }}>
                          {new Date(d.ultima_conexao).toLocaleString('pt-BR')}
                        </td>
                        <td style={{ padding:'8px 10px' }}>
                          <button onClick={()=>delDevice(d.id)} style={{ color:'var(--danger)', padding:4 }} title="Remover">
                            <AuthIcon n="trash" s={13}/>
                          </button>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}

        </div>
      </div>

      {/* Modal de upgrade */}
      {upgradeModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.6)', zIndex:9999, display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={()=>setUpgradeModal(null)}>
          <div onClick={e=>e.stopPropagation()} style={{ background:'var(--bg-1)', border:'1px solid var(--border-2)', borderRadius:12, padding:28, width:380 }}>
            <div style={{ fontFamily:'var(--ff-display)', fontSize:15, fontWeight:700, marginBottom:6 }}>Alterar plano</div>
            <div style={{ fontSize:13, color:'var(--fg-mute)', marginBottom:20 }}>Usuário: <b>{upgradeModal.nome}</b></div>
            <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
              {plans.filter(p=>p.ativo).map(p => (
                <button key={p.id} onClick={()=>doUpgrade(p.id)} style={{
                  width:'100%', padding:'12px 16px', borderRadius:8, textAlign:'left',
                  background:'var(--bg-2)', border:'1px solid var(--border-2)',
                  cursor:'pointer', transition:'all .15s', fontFamily:'var(--ff-body)', color:'var(--fg)',
                }}>
                  <div style={{ fontWeight:700, fontSize:13 }}>{p.nome_plano}</div>
                  <div style={{ fontSize:12, color:'var(--fg-mute)' }}>{p.descricao}</div>
                </button>
              ))}
            </div>
            <button onClick={()=>setUpgradeModal(null)} style={{ marginTop:16, color:'var(--fg-mute)', fontSize:12, cursor:'pointer', width:'100%', textAlign:'center' }}>Cancelar</button>
          </div>
        </div>
      )}
    </div>
  );
};

/* ──────────────────────────────────────────────────────────
   AUTH GUARD — componente wrapper para proteger o App
────────────────────────────────────────────────────────── */
const AuthGuard = ({ children, onUnauth }) => {
  const [authState, setAuthState] = useState('checking');
  const [session, setSession] = useState(null);
  const [showUserPanel, setShowUserPanel] = useState(false);
  const [showAdmin, setShowAdmin] = useState(false);
  const [upgradeModulo, setUpgradeModulo] = useState(null); // null = fechado
  const fpRef = useRef('');

  // Escuta evento global vl:upgrade disparado por módulos bloqueados
  useEffect(() => {
    const handler = (e) => setUpgradeModulo(e.detail?.moduloId || null);
    window.addEventListener('vl:upgrade', handler);
    return () => window.removeEventListener('vl:upgrade', handler);
  }, []);

  useEffect(() => {
    const init = async () => {
      // ORDEM CRÍTICA: gera o fingerprint ANTES de validar o token,
      // senão validateToken recebe fingerprint vazio.
      const fp = await gerarFingerprint();
      fpRef.current = fp;
      const token = VL_AUTH.getToken();           // localStorage OU sessionStorage
      if (!token) { setAuthState('unauth'); return; }
      const res = await VL_AUTH.validateToken(token, fp);
      if (res.ok) {
        setSession({ token, user: { id: res.payload.sub, nome: res.payload.nome, email: res.payload.email, role: res.payload.role }, plan: res.plan, fingerprint: fp });
        setAuthState('auth');
      } else {
        VL_AUTH.logout();                          // limpa os dois storages
        setAuthState('unauth');
      }
    };
    init();
  }, []);

  // Quando não autenticado, avisa o app (que volta para a tela de login).
  useEffect(() => {
    if (authState === 'unauth' && typeof onUnauth === 'function') onUnauth();
  }, [authState, onUnauth]);

  // Expõe sessão globalmente para outros módulos verificarem módulos liberados
  useEffect(() => {
    window.VL_SESSION = session;
    window.VL_moduloLiberado = (id) => {
      if (VL_BYPASS_PAGAMENTO) return true;        // fase de testes: nada bloqueado
      if (!session) return false;
      const mods = VL_AUTH.modulosLiberados(session.plan);
      return mods === null || mods.includes(id) || mods.includes('*');
    };
    // Hook global: qualquer módulo pode chamar window.VL_goUpgrade('modulo-id')
    window.VL_goUpgrade = (moduloId) => {
      window._vl_upgrade_modulo = moduloId;
      window.dispatchEvent(new CustomEvent('vl:upgrade', { detail: { moduloId } }));
    };
  }, [session]);

  if (authState === 'checking') {
    return (
      <div style={{ height:'100vh', background:'var(--bg-0)', display:'flex', alignItems:'center', justifyContent:'center', flexDirection:'column', gap:16 }}>
        <div style={{ width:40, height:40, borderRadius:10, background:'var(--accent)', display:'flex', alignItems:'center', justifyContent:'center', animation:'pulse 1s ease infinite' }}>
          <AuthIcon n="bolt" s={20}/>
        </div>
        <div style={{ fontFamily:'var(--ff-mono)', fontSize:12, color:'var(--fg-mute)', letterSpacing:'0.1em' }}>VERIFICANDO SESSÃO…</div>
        <style>{`@keyframes pulse { 0%,100%{opacity:1}50%{opacity:0.4} }`}</style>
      </div>
    );
  }

  if (authState === 'unauth') {
    // Se o app cuida da tela de login (Landing), não renderizamos o AuthScreen:
    // o onUnauth (acima) já mandou o app voltar para a landing.
    if (typeof onUnauth === 'function') {
      return (
        <div style={{ height:'100vh', background:'var(--bg-0)' }} />
      );
    }
    return (
      <AuthScreen onSuccess={(sess) => { setSession(sess); setAuthState('auth'); }}/>
    );
  }

  return (
    <>
      {/* Injeta botões na topbar do app via portal-like approach */}
      <AuthTopbarInjector
        session={session}
        onOpenUser={() => setShowUserPanel(true)}
        onOpenAdmin={() => setShowAdmin(true)}
      />

      {React.cloneElement(children, {
        session,
        onLogout: () => { VL_AUTH.logout(); setSession(null); setAuthState('unauth'); }
      })}

      {showUserPanel && (
        <UserPanel
          session={session}
          onLogout={() => { VL_AUTH.logout(); setSession(null); setAuthState('unauth'); setShowUserPanel(false); }}
          onClose={() => setShowUserPanel(false)}
        />
      )}
      {upgradeModulo !== null && (
        <UpgradePage
          session={session}
          moduloTentado={upgradeModulo}
          onClose={() => setUpgradeModulo(null)}
        />
      )}
    </>
  );
};

/* ──────────────────────────────────────────────────────────
   TOPBAR INJECTOR — adiciona chips de sessão ao header
   Funciona via DOM insertion após o React renderizar
────────────────────────────────────────────────────────── */
const AuthTopbarInjector = ({ session, onOpenUser, onOpenAdmin }) => {
  const mountRef = useRef(null);

  useEffect(() => {
    // Injeta no topbar real do app ou cria ponto de montagem flutuante
    let target = document.querySelector('.topbar-actions') || document.querySelector('.app-header-right');
    if (!target) {
      if (!mountRef.current) {
        const el = document.createElement('div');
        el.id = 'vl-auth-bar';
        Object.assign(el.style, {
          position: 'fixed', top: '8px', right: '16px', zIndex: 9990,
          display: 'flex', alignItems: 'center', gap: '8px',
        });
        document.body.appendChild(el);
        mountRef.current = el;
      }
      target = mountRef.current;
    }

    const { user, plan } = session || {};
    const planColor = { plan_free:'#888', plan_pro:'var(--accent)', plan_enterprise:'var(--warn)' }[plan?.id] || '#888';

    const html = `
      <div id="vl-auth-chip" style="display:flex;align-items:center;gap:8px;
        background:var(--bg-2,#1e1e2e);border:1px solid var(--border-2,#3a3a5c);
        border-radius:999px;padding:4px 12px 4px 8px;cursor:pointer;
        font-family:JetBrains Mono,monospace;gap:10px;">
        <span style="width:7px;height:7px;border-radius:50%;background:${planColor};
          box-shadow:0 0 8px ${planColor};flex-shrink:0;display:inline-block"></span>
        <span style="font-size:11px;color:var(--fg-mute,#888);max-width:120px;
          overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${user?.nome?.split(' ')[0]||'User'}</span>
        <span style="font-size:10px;font-weight:700;color:${planColor}">${plan?.nome_plano||'Free'}</span>
      </div>
    `;
    target.innerHTML = html;

    const chip = document.getElementById('vl-auth-chip');
    if (chip) chip.addEventListener('click', onOpenUser);
    const adminBtn = document.getElementById('vl-admin-btn');
    if (adminBtn) adminBtn.addEventListener('click', onOpenAdmin);

    return () => {
      if (target) target.innerHTML = '';
    };
  }, [session, onOpenUser, onOpenAdmin]);

  return null;
};

/* ──────────────────────────────────────────────────────────
   PÁGINA DE PLANOS / UPGRADE
   Exibida quando o usuário clica em um módulo bloqueado.
   Personalize a seção /* ★ EDITE AQUI ★ / com seus preços,
   PIX, links do Stripe/MercadoPago, WhatsApp, etc.
────────────────────────────────────────────────────────── */
const UpgradePage = ({ session, moduloTentado, onClose }) => {
  const [selected, setSelected] = useState('plan_pro');
  const plans = loadDB().plans.filter(p => p.ativo);
  const currentPlanId = session?.plan?.id || 'plan_free';

  /* ★ EDITE AQUI ★ ─────────────────────────────────────────
     Links de checkout da Cakto — um por plano pago.
     Cole as URLs reais geradas no painel da Cakto.
  ─────────────────────────────────────────────────────────── */
  const CAKTO_LINKS = {
    plan_estudante: 'https://pay.cakto.com.br/SEU-LINK-ESTUDANTE',
    plan_pro:       'https://pay.cakto.com.br/SEU-LINK-PRO-MENSAL',
    plan_pro_anual: 'https://pay.cakto.com.br/SEU-LINK-PRO-ANUAL',
  };
  /* ─────────────────────────────────────────── FIM DO EDITE */

  const fmtPreco = (v) => Number(v).toFixed(2).replace('.', ',');

  const planColors = {
    plan_free: 'var(--fg-mute)',
    plan_estudante: 'var(--accent-2)',
    plan_pro: 'var(--accent)',
    plan_pro_anual: 'var(--warn)',
  };

  const planIcons = {
    plan_free: 'star',
    plan_estudante: 'user',
    plan_pro: 'bolt',
    plan_pro_anual: 'crown',
  };

  const selPlan = plans.find(p => p.id === selected);
  const caktoUrl = CAKTO_LINKS[selected];
  const caktoConfigurado = !!caktoUrl && !caktoUrl.includes('SEU-LINK');

  const assinarCakto = () => {
    if (!selPlan || selPlan.id === 'plan_free' || selPlan.id === currentPlanId) return;
    if (!caktoConfigurado) {
      if (window.vlToast) window.vlToast('Configure o link da Cakto deste plano em auth.jsx (CAKTO_LINKS).');
      return;
    }
    window.open(caktoUrl, '_blank', 'noopener');
  };

  // Módulo que tentou acessar
  const MOD_LABELS = {
    drop:'Queda de Tensão', cables:'Dimensionamento de Cabos', fp:'Banco de Capacitores',
    short:'Curto-Circuito', trafo:'Transformadores', spda:'SPDA', light:'Iluminação',
    diagrams:'Diagramas Unifilares', quadros:'Quadros & Partidas', cad:'Editor CAD',
    infra:'Infraestrutura',
  };
  const modLabel = MOD_LABELS[moduloTentado] || moduloTentado || 'este módulo';

  return (
    <div style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,.82)',
      zIndex: 9995, display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 24, backdropFilter: 'blur(6px)',
    }}>
      <div style={{
        width: '100%', maxWidth: 860, maxHeight: '92vh',
        background: 'var(--bg-1)', border: '1px solid var(--border-2)',
        borderRadius: 18, overflow: 'hidden',
        display: 'flex', flexDirection: 'column',
        boxShadow: '0 40px 120px rgba(0,0,0,.6)',
      }}>

        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 14,
          padding: '20px 28px', borderBottom: '1px solid var(--border)',
          background: 'var(--bg-2)', flexShrink: 0,
        }}>
          <div style={{
            width: 40, height: 40, borderRadius: 10,
            background: 'oklch(70% 0.20 25 / 0.15)',
            border: '1px solid oklch(70% 0.20 25 / 0.5)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--danger)',
          }}>
            <AuthIcon n="lock" s={18}/>
          </div>
          <div>
            <div style={{ fontFamily: 'var(--ff-display)', fontSize: 16, fontWeight: 700 }}>
              Módulo bloqueado — <span style={{ color: 'var(--accent)' }}>{modLabel}</span>
            </div>
            <div style={{ fontSize: 12, color: 'var(--fg-mute)', marginTop: 2 }}>
              Seu plano atual (<b style={{ color: planColors[currentPlanId] }}>{session?.plan?.nome_plano || 'Free'}</b>) não inclui este módulo. Faça upgrade para continuar.
            </div>
          </div>
          <button onClick={onClose} style={{ marginLeft: 'auto', color: 'var(--fg-mute)', padding: 6 }}>
            <AuthIcon n="x" s={18}/>
          </button>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: '28px' }}>

          {/* Cards de planos */}
          <div style={{ fontSize: 10, color: 'var(--fg-mute)', fontFamily: 'var(--ff-mono)', letterSpacing: '0.1em', marginBottom: 16 }}>
            ESCOLHA SEU PLANO
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 14, marginBottom: 28 }}>
            {plans.map(p => {
              const cor = planColors[p.id];
              const isSelected = selected === p.id;
              const isCurrent = currentPlanId === p.id;
              const isUpgrade = plans.indexOf(p) > plans.findIndex(pl => pl.id === currentPlanId);
              return (
                <div key={p.id}
                  onClick={() => !isCurrent && setSelected(p.id)}
                  style={{
                    borderRadius: 12, padding: 20, cursor: isCurrent ? 'default' : 'pointer',
                    border: `2px solid ${isSelected ? cor : isCurrent ? 'var(--border-2)' : 'var(--border)'}`,
                    background: isSelected ? `oklch(from ${cor} l c h / 0.06)` : 'var(--bg-2)',
                    transition: 'all .2s', position: 'relative',
                    opacity: isCurrent ? 0.6 : 1,
                    transform: isSelected && !isCurrent ? 'translateY(-2px)' : 'none',
                    boxShadow: isSelected && !isCurrent ? `0 8px 32px oklch(from ${cor} l c h / 0.2)` : 'none',
                  }}>

                  {/* Badge */}
                  {isCurrent && (
                    <div style={{ position: 'absolute', top: -11, left: '50%', transform: 'translateX(-50%)', background: 'var(--bg-3)', border: '1px solid var(--border-2)', borderRadius: 99, padding: '2px 10px', fontSize: 9, color: 'var(--fg-mute)', fontFamily: 'var(--ff-mono)', whiteSpace: 'nowrap' }}>
                      PLANO ATUAL
                    </div>
                  )}
                  {p.id === 'plan_pro' && !isCurrent && (
                    <div style={{ position: 'absolute', top: -11, left: '50%', transform: 'translateX(-50%)', background: 'var(--accent)', borderRadius: 99, padding: '2px 10px', fontSize: 9, color: 'oklch(15% 0.02 240)', fontFamily: 'var(--ff-mono)', fontWeight: 700, whiteSpace: 'nowrap' }}>
                      MAIS POPULAR
                    </div>
                  )}

                  {/* Ícone + nome */}
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
                    <div style={{ width: 32, height: 32, borderRadius: 8, background: `oklch(from ${cor} l c h / 0.12)`, border: `1px solid ${cor}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: cor }}>
                      <AuthIcon n={planIcons[p.id] || 'star'} s={15}/>
                    </div>
                    <div style={{ fontFamily: 'var(--ff-display)', fontSize: 15, fontWeight: 700, color: cor }}>{p.nome_plano}</div>
                  </div>

                  {/* Preço */}
                  <div style={{ marginBottom: 12 }}>
                    {p.preco === 0 ? (
                      <div style={{ fontFamily: 'var(--ff-mono)', fontSize: 22, fontWeight: 700 }}>Grátis</div>
                    ) : (
                      <div style={{ display: 'flex', alignItems: 'baseline', gap: 4 }}>
                        <span style={{ fontSize: 12, color: 'var(--fg-mute)' }}>R$</span>
                        <span style={{ fontFamily: 'var(--ff-mono)', fontSize: 26, fontWeight: 700, color: isSelected ? cor : 'var(--fg)' }}>{fmtPreco(p.preco)}</span>
                        <span style={{ fontSize: 12, color: 'var(--fg-mute)' }}>/{p.ciclo}</span>
                      </div>
                    )}
                    <div style={{ fontSize: 11, color: 'var(--fg-mute)', marginTop: 3 }}>{p.descricao}</div>
                  </div>

                  {/* Features */}
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                    {[
                      [`${p.limite_dispositivos} dispositivo${p.limite_dispositivos > 1 ? 's' : ''}`, true],
                      [p.modulos?.includes('*') ? 'Todos os módulos' : `${p.modulos?.length || 0} módulos`, true],
                      [`${p.duracao_dias} dias por ciclo`, true],
                      [p.id === 'plan_free' ? 'Sem suporte' : 'Suporte por e-mail', p.id !== 'plan_free'],
                    ].map(([txt, ok], i) => (
                      <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
                        <span style={{ color: ok ? 'var(--ok)' : 'var(--fg-faint)', flexShrink: 0 }}>
                          <AuthIcon n={ok ? 'check' : 'x'} s={12}/>
                        </span>
                        <span style={{ color: ok ? 'var(--fg-dim)' : 'var(--fg-faint)' }}>{txt}</span>
                      </div>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>

          {/* CTA de assinatura via Cakto */}
          {selPlan && selPlan.id !== 'plan_free' && selPlan.id !== currentPlanId && (
            <div>
              <div style={{ fontSize: 10, color: 'var(--fg-mute)', fontFamily: 'var(--ff-mono)', letterSpacing: '0.1em', marginBottom: 14 }}>
                ASSINAR — PLANO {selPlan.nome_plano.toUpperCase()}
              </div>

              {selPlan.requer_comprovante && (
                <div style={{
                  display: 'flex', alignItems: 'flex-start', gap: 10,
                  background: 'oklch(80% 0.16 75 / 0.08)', border: '1px solid oklch(80% 0.16 75 / 0.35)',
                  borderRadius: 10, padding: '12px 14px', marginBottom: 16, fontSize: 12.5, color: 'var(--fg-dim)', lineHeight: 1.6,
                }}>
                  <span style={{ color: 'var(--warn)', flexShrink: 0, marginTop: 1 }}><AuthIcon n="info" s={15}/></span>
                  <span>O plano Estudante exige <b>comprovante de matrícula</b> válido. Após a compra, envie o documento para liberarmos o acesso.</span>
                </div>
              )}

              <button onClick={assinarCakto} style={{
                width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12,
                padding: '16px 20px', borderRadius: 12, cursor: 'pointer',
                background: 'var(--accent)', color: 'oklch(15% 0.02 240)', border: 'none',
                fontFamily: 'var(--ff-display)', fontSize: 15, fontWeight: 700, letterSpacing: '0.03em',
                boxShadow: '0 8px 28px var(--accent-glow)', transition: 'all .18s',
              }}>
                <AuthIcon n="bolt" s={18}/>
                Assinar {selPlan.nome_plano} — R$ {fmtPreco(selPlan.preco)}/{selPlan.ciclo}
              </button>

              <div style={{ marginTop: 12, textAlign: 'center', fontSize: 11.5, color: 'var(--fg-mute)', lineHeight: 1.6 }}>
                Você será redirecionado para o checkout seguro da <b style={{ color: 'var(--fg-dim)' }}>Cakto</b>.<br/>
                Cartão, PIX e boleto · acesso liberado após a confirmação.
              </div>

              {!caktoConfigurado && (
                <div style={{ marginTop: 14, fontSize: 11, color: 'var(--fg-faint)', fontFamily: 'var(--ff-mono)', textAlign: 'center' }}>
                  ⚙︎ Dev: defina o link em <span style={{ color: 'var(--accent)' }}>CAKTO_LINKS.{selPlan.id}</span> (auth.jsx)
                </div>
              )}
            </div>
          )}

          {/* Rodapé */}
          <div style={{ marginTop: 28, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, fontSize: 11, color: 'var(--fg-faint)' }}>
            <AuthIcon n="shield" s={12}/>
            Pagamento seguro · Acesso ativado em até 24h após confirmação
          </div>
        </div>
      </div>
    </div>
  );
};

/* ──────────────────────────────────────────────────────────
   MÓDULO BLOQUEADO — card inline + dispara UpgradePage
   Usado dentro do renderView() do app.jsx
────────────────────────────────────────────────────────── */
const ModuloBloqueado = ({ moduloId, onUpgrade }) => {
  const MOD_LABELS = {
    drop:'Queda de Tensão', cables:'Dimensionamento de Cabos', fp:'Banco de Capacitores',
    short:'Curto-Circuito', trafo:'Transformadores', spda:'SPDA', light:'Iluminação',
    diagrams:'Diagramas Unifilares', quadros:'Quadros & Partidas', cad:'Editor CAD',
    infra:'Infraestrutura',
  };
  const label = MOD_LABELS[moduloId] || moduloId;

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      justifyContent: 'center', height: '60vh', gap: 20, padding: 32,
    }}>
      <div style={{
        width: 72, height: 72, borderRadius: 18,
        background: 'oklch(70% 0.20 25 / 0.1)',
        border: '2px solid oklch(70% 0.20 25 / 0.3)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--danger)',
      }}>
        <AuthIcon n="lock" s={32}/>
      </div>

      <div style={{ textAlign: 'center', maxWidth: 400 }}>
        <div style={{ fontFamily: 'var(--ff-display)', fontSize: 22, fontWeight: 700, marginBottom: 8 }}>
          {label}
        </div>
        <div style={{ fontSize: 14, color: 'var(--fg-mute)', lineHeight: 1.7 }}>
          Este módulo não está incluído no seu plano atual.<br/>
          Faça upgrade para ter acesso completo a todos os módulos do VoltLab.
        </div>
      </div>

      <button onClick={onUpgrade} style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: '14px 28px', borderRadius: 12,
        background: 'var(--accent)', color: 'oklch(15% 0.02 240)',
        border: 'none', cursor: 'pointer', fontFamily: 'var(--ff-display)',
        fontSize: 15, fontWeight: 700, letterSpacing: '0.04em',
        boxShadow: '0 4px 24px var(--accent-glow)', transition: 'all .18s',
      }}>
        <AuthIcon n="crown" s={18}/>
        VER PLANOS E PREÇOS
      </button>

      <div style={{ fontSize: 11, color: 'var(--fg-faint)', display: 'flex', alignItems: 'center', gap: 6 }}>
        <AuthIcon n="shield" s={12}/> Pagamento seguro · Ativação rápida
      </div>
    </div>
  );
};

// Exporta componentes para uso no app.jsx
window.VL_AuthGuard   = AuthGuard;
window.VL_AuthScreen  = AuthScreen;
window.VL_UserPanel   = UserPanel;
window.VL_AdminPanel  = AdminPanel;
window.VL_UpgradePage = UpgradePage;
window.VL_ModuloBloqueado = ModuloBloqueado;
