﻿/* Dashboard — powered by Firestore (espelho do app mobile)
   Lê DailyDoc de loja/{cnpj}/data/{YYYY-MM-DD}
   Suporta: Vendas · Compras · Estoque · Contas a Pagar · Metas · Comparativo */

// ─── Constantes ───────────────────────────────────────────────────────────────
const CATEGORY_COLORS = ['var(--brand-500)', 'var(--brand-400)', 'var(--brand-300)', 'var(--brand-200)', 'var(--ink-200)'];
const MONTH_NAMES_PT      = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
const MONTH_NAMES_FULL_PT = ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];
const safeN = v => (typeof v === 'number' && !isNaN(v) ? v : 0);
// Rejeita nomes de classe que são IDs numéricos vazados (ex: "111111.0" ou "Classe 111111.0000000000")
const isValidClassName = name => !!name && !/^\s*(?:Classe\s+)?\d+(?:\.\d+)?\s*$/i.test(name);

// ─── Gestão de usuários (Cloud Function) ──────────────────────────────────────
const CF_URL   = 'https://southamerica-east1-myfarmax.cloudfunctions.net/user';
const PAGE_LIST = [
  { idx: 0, name: 'Vendas',         icon: 'sf-sales' },
  { idx: 1, name: 'Compras',        icon: 'sf-purchases' },
  { idx: 2, name: 'Metas',          icon: 'sf-goals' },
  { idx: 3, name: 'Estoque',        icon: 'sf-stock' },
  { idx: 4, name: 'Comparativo',    icon: 'sf-comparative' },
  { idx: 5, name: 'Análises',       icon: 'sf-overview' },
  { idx: 6, name: 'Entregas',       icon: 'sf-deliveries' },
  { idx: 7, name: 'Contas a Pagar', icon: 'sf-bills' },
];
const cfGet = async (params = {}) => {
  const token = await SF.auth.currentUser?.getIdToken?.(/* forceRefresh */ true).catch(() => null);
  if (!token) throw new Error('token-indisponível');
  const url = new URL(CF_URL);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)));
  const res = await fetch(url.toString(), { headers: { Authorization: `Bearer ${token}` } });
  if (!res.ok) {
    const body = await res.text().catch(() => '');
    throw new Error(`CF ${res.status}${body ? ': ' + body.slice(0, 300) : ''}`);
  }
  return res.json();
};
const cfPut = async (body) => {
  const token = await SF.auth.currentUser?.getIdToken?.(true).catch(() => null);
  if (!token) throw new Error('token-indisponível');
  const res = await fetch(CF_URL, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const body2 = await res.text().catch(() => '');
    throw new Error(`CF ${res.status}${body2 ? ': ' + body2.slice(0, 300) : ''}`);
  }
  return res.json();
};
const cfDelete = async (body) => {
  const token = await SF.auth.currentUser?.getIdToken?.(true).catch(() => null);
  if (!token) throw new Error('token-indisponível');
  const res = await fetch(CF_URL, {
    method: 'DELETE',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const body2 = await res.text().catch(() => '');
    throw new Error(`CF ${res.status}${body2 ? ': ' + body2.slice(0, 300) : ''}`);
  }
  return res.json();
};

const cfPost = async (body) => {
  const token = await SF.auth.currentUser?.getIdToken?.(true).catch(() => null);
  if (!token) throw new Error('token-indisponível');
  const res = await fetch(CF_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const body2 = await res.text().catch(() => '');
    throw new Error(`CF ${res.status}${body2 ? ': ' + body2.slice(0, 300) : ''}`);
  }
  return res.json();
};

// ─── Rótulo do eixo X do gráfico ─────────────────────────────────────────────
const chartLabel = (date, period) => {
  const todayStr = SF.today();
  if (date === todayStr) return 'Hoje';
  const diff = SF.daysBetween(date, todayStr);
  if (period === '7d' || period === '30d' || period === 'hoje') return diff === 1 ? 'Ontem' : `${diff}d`;
  if (period === 'mtd') return date.slice(8);                      // "01"–"31"
  if (period === 'custom') {
    const [, m, d] = date.split('-');
    return `${d}/${m}`;                                            // DD/MM
  }
  return MONTH_NAMES_PT[Number(date.slice(5, 7)) - 1] ?? date.slice(5, 7);
};

// ─── Label expandido para tooltip ────────────────────────────────────────────
const expandLabel = (l) => {
  if (l === 'Hoje' || l === 'Ontem') return l;
  const md = l.match(/^(\d+)d$/);
  if (md) return md[1] === '1' ? '1 dia atrás' : `${md[1]} dias atrás`;
  if (l.match(/^\d{2}\/\d{2}$/)) return l; // DD/MM — retorna como está
  return l; // dia do mês, mês abreviado, etc.
};

// ─── Constrói dados do dashboard a partir de um array de DailyDocs ────────────
// Espelha a lógica de redução dos hooks do mobile (ranking-sale, purchases, etc.)
// storeFilter: number[] — array de storeIds selecionados; [] = sem filtro (todas)
const buildDashboardData = (docs, allowedStores, period, activeFilter = null, storeFilter = []) => {
  if (!docs || !docs.length) return null;

  const allowed = (allowedStores == null || typeof allowedStores === 'boolean')
    ? null : allowedStores;

  const hasFilter = storeFilter.length > 0;

  // Chaves de filiais externas (ex: "20_5") sempre passam pelo isAllowedStore,
  // pois foram configuradas explicitamente pelo admin em Parametros.filiais
  const filialKeys = new Set(Object.keys(SF.getFiliaisMap()));

  // Prefixos numéricos das filiais (ex: "3" de "3_1") — usados para evitar
  // duplicação quando o doc principal já tem a loja sob chave simples ("3")
  // e o mergeFilialInto adiciona a mesma loja sob "3_1"
  const filialPrefixes = new Set();
  for (const fk of filialKeys) {
    const u = fk.indexOf('_');
    if (u > 0) filialPrefixes.add(fk.slice(0, u));
  }

  // Acumuladores por loja
  const storeMap = new Map();

  // Acumulador de formas de pagamento
  const paymentMap = {};

  // Dados por dia para o gráfico
  const dayTotals = [];

  // Bills acumulam de todos os docs do período (deduplicadas por ID)
  const billsMap = {};
  // Stock é snapshot do doc mais recente
  let billsDoc  = null; // mantido apenas para compatibilidade; não usado para bills
  let stockDoc  = null;
  // Evolução do estoque: snapshot por dia (para o gráfico)
  const stockEvolutionMap = {};

  for (const { date, data } of docs) {
    const dayBySid            = new Map(); // sid → vendas filtradas
    const dayQtySid           = new Map(); // sid → unidades vendidas
    const dayPurchaseBySid    = new Map(); // sid → compras do dia (R$)
    const dayPurchaseUnitySid = new Map(); // sid → unidades compradas
    const dayCostBySid        = new Map(); // sid → CMV do dia (custo das vendas)
    const dayMfgMap           = {};        // mfgKey → { total, qty, name } do dia

    if (data?.bills) {
      billsDoc = data;
      for (const [id, b] of Object.entries(data.bills)) {
        const sid = b.collectorStoreId
          ? b.collectorStoreId * 10000 + Number(b.storeId)
          : Number(b.storeId);
        if (allowed && sid < 10001 && !SF.isAllowedStore(sid, allowed)) continue;
        if (hasFilter && !storeFilter.includes(sid)) continue;
        billsMap[id] = {
          storeId:          sid,
          storeName:        b.storeName        ?? '',
          manufacturerName: b.manufacturerName ?? '',
          billingValue:     safeN(b.billingValue),
          paidValue:        safeN(b.paidValue),
          status:           b.status           ?? '',
          dueDate:          b.dueDate          ?? null,
          issueDate:        b.issueDate        ?? null,
          emissionDate:     b.emissionDate     ?? null,
        };
      }
    }
    if (data?.stock) {
      stockDoc = data;
      let esSale = 0, esCost = 0, esUnity = 0;
      for (const [key, se] of Object.entries(data.stock)) {
        const sid = SF.parseStoreKey(key);
        if (filialPrefixes.size > 0 && !key.includes('_') && filialPrefixes.has(key)) continue;
        if (allowed && !filialKeys.has(key) && !SF.isAllowedStore(sid, allowed)) continue;
        if (storeFilter.length > 0 && !storeFilter.includes(sid)) continue;
        esSale  += safeN(se.totalSale);
        esCost  += safeN(se.totalCost);
        esUnity += safeN(se.totalUnity);
      }
      if (esSale > 0 || esUnity > 0) stockEvolutionMap[date] = { totalSale: esSale, totalCost: esCost, totalUnity: esUnity };
    }

    if (data?.stores) {
      for (const [key, s] of Object.entries(data.stores)) {
        const sid = SF.parseStoreKey(key);
        // Ignora chave simples cujo prefixo é coberto por uma filial externa
        // (ex: ignora "3" quando "3_1" já está no filiaisMap)
        if (filialPrefixes.size > 0 && !key.includes('_') && filialPrefixes.has(key)) continue;
        if (allowed && !filialKeys.has(key) && !SF.isAllowedStore(sid, allowed)) continue;

        // Aplica filtro por grupo ou classe (total e qty filtrados; custo permanece bruto)
        let sTotal = safeN(s.total);
        let sQty   = safeN(s.quantity);
        if (activeFilter?.selected?.size > 0) {
          sTotal = 0; sQty = 0;
          if (activeFilter.dim === 'grupos') {
            for (const g of Object.values(s.salesByGroup ?? {})) {
              if (activeFilter.selected.has(g.groupName)) {
                sTotal += safeN(g.total);
                sQty   += safeN(g.quantity);
              }
            }
          } else {
            for (const c of Object.values(s.salesByClass ?? {})) {
              if (activeFilter.selected.has(c.className)) {
                sTotal += safeN(c.total);
                sQty   += safeN(c.quantity);
              }
            }
          }
        }

        dayBySid.set(sid, (dayBySid.get(sid) ?? 0) + sTotal);
        dayQtySid.set(sid, (dayQtySid.get(sid) ?? 0) + sQty);
        const _dayPurClsTotal = Object.values(s.purchases?.classifications ?? {})
          .reduce((a, c) => a + safeN(c.total), 0);
        dayPurchaseBySid.set(sid, (dayPurchaseBySid.get(sid) ?? 0) +
          (_dayPurClsTotal > 0 ? _dayPurClsTotal : safeN(s.purchases?.total)));
        dayCostBySid.set(sid, (dayCostBySid.get(sid) ?? 0) + safeN(s.cost));

        let acc = storeMap.get(sid);
        if (!acc) {
          acc = {
            storeId: sid,
            storeName: s.storeName ?? '',
            nameFallback: false, // true se o nome veio de Parametros.nome (ver mergeFilialInto)
            total: 0, cost: 0, quantity: 0, salesCount: 0,
            itemCount: 0, pbm: 0,
            // metas (do doc mais recente que tiver meta)
            totalGoal: 0, avgTicketGoal: 0, clientGoal: 0, unityGoal: 0,
            goalAccum: 0, // meta proporcional acumulada dia a dia (lida cruzamento de mês)
            clientCount: 0, unityCount: 0,
            salesByGroup: {},
            salesByClass: {},
            purchases: { total: 0, classifications: {}, manufacturers: {}, groups: {} },
            sellers: {},
            // entrega
            delivery: { balconSales: 0, deliverySales: 0, balconCoupon: 0, deliveryCoupon: 0, deliveryCount: 0, deliveryTotalTime: 0, topDeliveryManName: null, topDeliveryManAvgTime: 0, topDeliveryManCount: 0 },
          };
          storeMap.set(sid, acc);
        }

        if (!acc.storeName && s.storeName) acc.storeName = s.storeName;
        if (s.nameFallback) acc.nameFallback = true;

        acc.total       += sTotal;
        acc.cost        += safeN(s.cost);
        acc.quantity    += sQty;
        acc.salesCount  += safeN(s.salesCount || s.clientCount);
        acc.itemCount   += safeN(s.itemCount);
        acc.pbm         += safeN(s.pbm);
        acc.clientCount += safeN(s.clientCount);
        acc.unityCount  += safeN(s.unityCount);

        // Metas: captura do doc mais recente com meta configurada
        if (safeN(s.totalGoal) > 0) {
          acc.totalGoal     = safeN(s.totalGoal);
          acc.avgTicketGoal = safeN(s.avgTicketGoal);
          acc.clientGoal    = safeN(s.clientGoal);
          acc.unityGoal     = safeN(s.unityGoal);
          // Acumula meta proporcional: cada dia contribui meta_mensal / dias_do_mes
          const [_y, _m] = date.split('-').map(Number);
          const _daysInMonth = new Date(_y, _m, 0).getDate();
          acc.goalAccum += safeN(s.totalGoal) / _daysInMonth;
        }

        // Sellers: snapshot do doc mais recente
        if (s.sellers) acc.sellers = s.sellers;

        // salesByGroup
        for (const g of Object.values(s.salesByGroup ?? {})) {
          if (!g.groupName) continue;
          const ex = acc.salesByGroup[g.groupName] ?? { total: 0, quantity: 0, totalCost: 0 };
          ex.total     += safeN(g.totalSale ?? g.total);
          ex.quantity  += safeN(g.unity     ?? g.quantity);
          ex.totalCost += safeN(g.totalCost);
          acc.salesByGroup[g.groupName] = ex;
        }

        // salesByClass
        for (const c of Object.values(s.salesByClass ?? {})) {
          if (!c.className) continue;
          const ex = acc.salesByClass[c.className] ?? { total: 0, quantity: 0, totalCost: 0 };
          ex.total     += safeN(c.totalSale ?? c.total);
          ex.quantity  += safeN(c.unity     ?? c.quantity);
          ex.totalCost += safeN(c.totalCost);
          acc.salesByClass[c.className] = ex;
        }

        // Compras (purchases)
        if (s.purchases) {
          const _clsTotal = Object.values(s.purchases.classifications ?? {})
            .reduce((a, c) => a + safeN(c.total), 0);
          acc.purchases.total += _clsTotal > 0 ? _clsTotal : safeN(s.purchases.total);
          for (const [k, cv] of Object.entries(s.purchases.classifications ?? {})) {
            const ex = acc.purchases.classifications[k]
              ?? { className: cv.className, total: 0, quantity: 0 };
            ex.total    += safeN(cv.total);
            ex.quantity += safeN(cv.quantity);
            acc.purchases.classifications[k] = ex;
            dayPurchaseUnitySid.set(sid, (dayPurchaseUnitySid.get(sid) ?? 0) + safeN(cv.quantity));
          }
          for (const [k, m] of Object.entries(s.purchases.manufacturers ?? {})) {
            const mfgName = m.distributorName || m.manufacturerName || '';
            const ex = acc.purchases.manufacturers[k] ?? {
              manufacturerName: mfgName,
              total: 0, quantity: 0,
            };
            ex.total    += safeN(m.total);
            ex.quantity += safeN(m.quantity);
            acc.purchases.manufacturers[k] = ex;
            // Acumula por dia para série temporal por fornecedor
            if (mfgName) {
              const dm = dayMfgMap[k] ?? { total: 0, qty: 0, unity: 0, name: mfgName };
              dm.total += safeN(m.total);
              dm.qty   += safeN(m.quantity);
              dm.unity += safeN(m.unity);
              dayMfgMap[k] = dm;
            }
          }
          for (const [k, g] of Object.entries(s.purchases.groups ?? {})) {
            if (!g.groupName) continue;
            const ex = acc.purchases.groups[k] ?? { groupName: g.groupName, total: 0, quantity: 0 };
            ex.total    += safeN(g.totalCost ?? g.total ?? 0);
            ex.quantity += safeN(g.unity ?? g.quantity ?? 0);
            acc.purchases.groups[k] = ex;
          }
        }

        // Entrega
        if (s.delivery) {
          const d = acc.delivery;
          d.balconSales    += safeN(s.delivery.balconSales);
          d.deliverySales  += safeN(s.delivery.deliverySales);
          d.balconCoupon   += safeN(s.delivery.balconCoupon);
          d.deliveryCoupon += safeN(s.delivery.deliveryCoupon);
          const cnt = safeN(s.delivery.deliveryCount);
          d.deliveryCount     += cnt;
          d.deliveryTotalTime += cnt * safeN(s.delivery.deliveryAvgTime);
          if (s.delivery.topDeliveryManName != null) {
            d.topDeliveryManName    = s.delivery.topDeliveryManName;
            d.topDeliveryManAvgTime = safeN(s.delivery.topDeliveryManAvgTime);
            d.topDeliveryManCount  += safeN(s.delivery.topDeliveryManCount);
          }
        }
      }
    }

    // Formas de pagamento
    if (data?.payments) {
      for (const [key, methods] of Object.entries(data.payments)) {
        const sid = SF.parseStoreKey(key);
        if (filialPrefixes.size > 0 && !key.includes('_') && filialPrefixes.has(key)) continue;
        if (allowed && !filialKeys.has(key) && !SF.isAllowedStore(sid, allowed)) continue;
        if (storeFilter.length > 0 && !storeFilter.includes(sid)) continue;
        for (const [method, v] of Object.entries(methods)) {
          const ex = paymentMap[method] ?? { salesCount: 0, total: 0 };
          ex.salesCount += safeN(v.salesCount);
          ex.total      += safeN(v.total);
          paymentMap[method] = ex;
        }
      }
    }

    dayTotals.push({ l: chartLabel(date, period), c: 0, date, bySid: new Map(dayBySid), qtySid: new Map(dayQtySid), purchaseBySid: new Map(dayPurchaseBySid), purchaseUnitySid: new Map(dayPurchaseUnitySid), costBySid: new Map(dayCostBySid), mfgMap: { ...dayMfgMap } });
  }

  // ── Lojas: aplica storeFilter + resolve totais diários ───────────────────────
  const allStores = [...storeMap.values()]; // lista completa para o seletor
  const stores = hasFilter
    ? allStores.filter(s => storeFilter.includes(s.storeId))
    : allStores;

  // Resolve v de cada dia respeitando storeFilter
  const resolvedDayTotals = dayTotals.map(d => ({
    l: d.l, c: d.c, date: d.date,
    v: hasFilter
      ? storeFilter.reduce((sum, id) => sum + (d.bySid.get(id) ?? 0), 0)
      : Array.from(d.bySid.values()).reduce((a, b) => a + b, 0),
    q: hasFilter
      ? storeFilter.reduce((sum, id) => sum + (d.qtySid?.get(id) ?? 0), 0)
      : Array.from(d.qtySid?.values() ?? []).reduce((a, b) => a + b, 0),
    p: hasFilter
      ? storeFilter.reduce((sum, id) => sum + (d.purchaseBySid?.get(id) ?? 0), 0)
      : Array.from(d.purchaseBySid?.values() ?? []).reduce((a, b) => a + b, 0),
    pu: hasFilter
      ? storeFilter.reduce((sum, id) => sum + (d.purchaseUnitySid?.get(id) ?? 0), 0)
      : Array.from(d.purchaseUnitySid?.values() ?? []).reduce((a, b) => a + b, 0),
    k: hasFilter
      ? storeFilter.reduce((sum, id) => sum + (d.costBySid?.get(id) ?? 0), 0)
      : Array.from(d.costBySid?.values() ?? []).reduce((a, b) => a + b, 0),
  }));

  // ── Estoque ──────────────────────────────────────────────────────────────────
  const stockByStore = {};
  if (stockDoc?.stock) {
    for (const [key, se] of Object.entries(stockDoc.stock)) {
      const sid = SF.parseStoreKey(key);
      if (allowed && !filialKeys.has(key) && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      stockByStore[sid] = {
        storeId:    sid,
        storeName:  se.storeName  ?? '',
        totalSale:  safeN(se.totalSale),
        totalCost:  safeN(se.totalCost),
        totalUnity: safeN(se.totalUnity),
        totalSku:   safeN(se.totalSku),
        classifications: Object.values(se.classifications ?? {}).map(c => ({
          className:  c.className,
          unity:      safeN(c.unity),
          totalCost:  safeN(c.totalCost),
          totalSale:  safeN(c.totalSale),
        })),
        groups: Object.values(se.groups ?? {}).map(g => ({
          groupName: g.groupName,
          unity:     safeN(g.unity),
          totalCost: safeN(g.totalCost),
          totalSale: safeN(g.totalSale),
        })),
      };
    }
  }

  // ── Contas a pagar — acumuladas de todos os docs do período ─────────────────
  const bills = Object.values(billsMap).sort((a, b) => b.billingValue - a.billingValue);

  // stores já foi computado acima com storeFilter aplicado
  if (!stores.length) return null;

  // ── Métricas agregadas ───────────────────────────────────────────────────────
  const total          = stores.reduce((s, st) => s + st.total, 0);
  const quantity       = stores.reduce((s, st) => s + st.quantity, 0);
  const totalCost      = stores.reduce((s, st) => s + st.cost, 0);
  const totalSalesCount = stores.reduce((s, st) => s + st.salesCount, 0);
  const avgTicket      = totalSalesCount > 0 ? total / totalSalesCount : (quantity > 0 ? total / quantity : 0);
  // Margem indisponível com filtro ativo: docs não têm custo por grupo/classe
  const hasActiveFilter = (activeFilter?.selected?.size ?? 0) > 0;
  const margin = hasActiveFilter ? null : (total > 0 ? 100 - (totalCost / total) * 100 : 0);
  const purchaseTotal  = stores.reduce((s, st) => s + st.purchases.total, 0);
  const pbmTotal       = stores.reduce((s, st) => s + st.pbm, 0);
  const billTotal      = bills.reduce((s, b) => s + b.billingValue, 0);
  const billPaid       = bills.reduce((s, b) => s + b.paidValue, 0);

  // ── Top lojas ────────────────────────────────────────────────────────────────
  // goalFactor: quanto da meta mensal corresponde ao período selecionado
  // Espelha o cálculo do mobile (hoje → meta/diasNoMes; 7d → meta/diasNoMes*7; etc.)
  const _now       = new Date();
  const _diasNoMes = new Date(_now.getFullYear(), _now.getMonth() + 1, 0).getDate();
  // Para 'custom': calcula o número de dias do intervalo a partir dos docs
  const _customDays = period === 'custom' && docs.length > 0
    ? SF.daysBetween(docs[0].date, docs[docs.length - 1].date) + 1
    : null;
  const _goalFactors = {
    'hoje':   1 / _diasNoMes,                           // meta diária proporcional
    '7d':     7 / _diasNoMes,                            // meta para 7 dias
    '30d':    1.0,                                       // ~1 mês → meta mensal completa
    'mtd':    1.0,                                       // meta do mês completa
    'ytd':    null,                                      // sem meta mensal aplicável
    'custom': _customDays !== null ? _customDays / _diasNoMes : null,
  };
  const _goalFactor = _goalFactors[period] ?? null;

  const _allTotal = stores.reduce((s, st) => s + st.total, 0); // para sharePct
  const topStores = [...stores]
    .sort((a, b) => b.total - a.total)
    .slice(0, 8)
    .map((s, i, all) => {
      const proratedGoal = _goalFactor !== null && s.totalGoal > 0
        ? s.totalGoal * _goalFactor : 0;
      const goalPct  = proratedGoal > 0 ? (s.total / proratedGoal) * 100 : null;
      const avgTicket = s.salesCount > 0 ? s.total / s.salesCount
                      : s.quantity  > 0 ? s.total / s.quantity : 0;
      const sharePct  = _allTotal > 0 ? (s.total / _allTotal) * 100 : 0;
      return {
        storeId: s.storeId,
        name: s.storeName || `Loja ${s.storeId}`,
        val:  s.total,
        pct:  all[0]?.total > 0 ? (s.total / all[0].total) * 100 : 0,
        goalPct,
        proratedGoal,
        goalMonthly: s.totalGoal,   // meta mensal bruta (para referência no tooltip)
        avgTicket,
        sharePct,
        change: 0,
      };
    });

  // ── Grupos e Classes (salesByGroup + salesByClass agregados) ─────────────────
  const groupMap = {};
  const classMap = {};
  stores.forEach(s => {
    for (const [name, g] of Object.entries(s.salesByGroup)) {
      const ex = groupMap[name] ?? { total: 0, quantity: 0, totalCost: 0 };
      ex.total     += safeN(g.totalSale ?? g.total);
      ex.quantity  += safeN(g.unity     ?? g.quantity);
      ex.totalCost += safeN(g.totalCost);
      groupMap[name] = ex;
    }
    for (const [name, c] of Object.entries(s.salesByClass)) {
      const ex = classMap[name] ?? { total: 0, quantity: 0, totalCost: 0 };
      ex.total     += safeN(c.totalSale ?? c.total);
      ex.quantity  += safeN(c.unity     ?? c.quantity);
      ex.totalCost += safeN(c.totalCost);
      classMap[name] = ex;
    }
  });

  const groupsSorted = Object.entries(groupMap)
    .map(([name, g]) => ({ name, total: g.total, quantity: g.quantity, totalCost: g.totalCost }))
    .sort((a, b) => b.total - a.total);
  const classesSorted = Object.entries(classMap)
    .map(([name, c]) => ({ name, total: c.total, quantity: c.quantity, totalCost: c.totalCost }))
    .sort((a, b) => b.total - a.total);

  // Top 4 grupos + Outros → para o donut
  const topCats  = groupsSorted.slice(0, 4).map((c, i) => ({ name: c.name, v: c.total, color: CATEGORY_COLORS[i] }));
  const otherVal = groupsSorted.slice(4).reduce((s, c) => s + c.total, 0);
  if (otherVal > 0) topCats.push({ name: 'Outros', v: otherVal, color: CATEGORY_COLORS[4] });
  const categoryTotal = topCats.reduce((s, c) => s + c.v, 0);

  // ── Extração de yearly (ytd + projeção do mês) ───────────────────────────────
  // Espelha src/hooks/analytics/index.ts::yearlyDocToMonthData
  const latestWithYearly = docs.slice().reverse().find(d => d.data?.yearly);

  // MTD total para projeção:
  // - 'mtd': usa SEMPRE a soma dos daily docs (mesma fonte dos KPIs, real-time).
  //   O campo `yearly` é um snapshot escrito pelo mobile — pode estar desatualizado.
  // - 'hoje': yearly é a única fonte disponível (docs só têm o dia atual).
  let mtdTotal = period === 'mtd' ? total : 0;
  if (period !== 'mtd' && latestWithYearly?.data?.yearly) {
    const currentMonth = new Date().getMonth() + 1;
    for (const [idStr, storeEntry] of Object.entries(latestWithYearly.data.yearly)) {
      const sid = SF.parseStoreKey(idStr);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      const md = storeEntry.months?.[currentMonth];
      if (md) mtdTotal += safeN(md.salesTotal);
    }
  }

  // Para ytd: constrói série mensal a partir do campo yearly
  let ytdDayTotals = null;
  if (period === 'ytd' && latestWithYearly?.data?.yearly) {
    const monthMap = {}; // monthNum (1-12) → { salesTotal, salesQty }
    for (const [idStr, storeEntry] of Object.entries(latestWithYearly.data.yearly)) {
      const sid = SF.parseStoreKey(idStr);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      for (const [mk, md] of Object.entries(storeEntry.months ?? {})) {
        const m = Number(mk);
        if (isNaN(m)) continue;
        const ex = monthMap[m] ?? { salesTotal: 0, salesQty: 0 };
        ex.salesTotal += safeN(md.salesTotal);
        ex.salesQty   += safeN(md.salesQty);
        monthMap[m] = ex;
      }
    }
    ytdDayTotals = Object.entries(monthMap)
      .sort((a, b) => Number(a[0]) - Number(b[0]))
      .map(([m, v]) => ({ l: MONTH_NAMES_PT[Number(m) - 1] ?? `M${m}`, v: v.salesTotal, c: 0 }));
  }

  // ── YTD override: totais do ano vindos do campo yearly ───────────────────────
  // Os docs buscados para ytd cobrem só ~7 dias; os KPIs reais vêm do snapshot yearly.
  let kpiTotal     = total;
  let kpiQty       = quantity;
  let kpiAvgTicket = avgTicket;

  if (period === 'ytd' && latestWithYearly?.data?.yearly) {
    let ytdT = 0, ytdQ = 0;
    for (const [idStr, storeEntry] of Object.entries(latestWithYearly.data.yearly)) {
      const sid = SF.parseStoreKey(idStr);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      for (const md of Object.values(storeEntry.months ?? {})) {
        ytdT += safeN(md.salesTotal);
        ytdQ += safeN(md.salesQty);
      }
    }
    if (ytdT > 0) {
      kpiTotal     = ytdT;
      kpiQty       = ytdQ;
      kpiAvgTicket = ytdQ > 0 ? ytdT / ytdQ : 0;
    }
  }

  // ── YTD override: topStores, grupos, pagamentos ───────────────────────────────
  // yearly só tem salesTotal/salesQty por loja por mês — sem detalhe de grupo/classe/PBM/pagamentos.
  // Reconstruímos topStores do yearly e zeramos os campos sem cobertura.
  let ytdTopStores = null;
  const isYtd = period === 'ytd';

  if (isYtd && latestWithYearly?.data?.yearly) {
    const storeYtdMap = {};
    for (const [idStr, storeEntry] of Object.entries(latestWithYearly.data.yearly)) {
      const sid = SF.parseStoreKey(idStr);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      let t = 0, q = 0;
      for (const md of Object.values(storeEntry.months ?? {})) {
        t += safeN(md.salesTotal);
        q += safeN(md.salesQty);
      }
      if (t > 0) {
        const name = storeMap.get(sid)?.storeName || `Loja ${sid}`;
        storeYtdMap[sid] = { storeId: sid, name, total: t, qty: q };
      }
    }
    const ytdStoreList  = Object.values(storeYtdMap).sort((a, b) => b.total - a.total);
    const ytdAllTotal   = ytdStoreList.reduce((s, st) => s + st.total, 0);
    ytdTopStores = ytdStoreList.slice(0, 8).map((s, i, all) => ({
      storeId: s.storeId,
      name:    s.name,
      val:     s.total,
      pct:     all[0]?.total > 0 ? (s.total / all[0].total) * 100 : 0,
      goalPct: null,
      proratedGoal: 0,
      goalMonthly:  0,
      avgTicket:    s.qty > 0 ? s.total / s.qty : 0,
      sharePct:     ytdAllTotal > 0 ? (s.total / ytdAllTotal) * 100 : 0,
      change: 0,
    }));
  }

  // ── KPIs de vendas ───────────────────────────────────────────────────────────
  const finalDayTotals = ytdDayTotals ?? resolvedDayTotals;
  const spark = finalDayTotals.map(d => d.v);
  const kpis = [
    { label: 'Vendas totais',  value: BRL(kpiTotal),              rawValue: kpiTotal,     fmt: 'brl', delta: 0, spark, primary: true },
    { label: 'Ticket médio',   value: BRL(kpiAvgTicket),          rawValue: kpiAvgTicket, fmt: 'brl', delta: 0, spark },
    { label: 'Nº de vendas',   value: NUM(totalSalesCount),       rawValue: totalSalesCount, fmt: 'num', delta: 0, spark },
    { label: 'Margem bruta',   value: margin !== null ? `${margin.toFixed(1)}%` : '—', rawValue: margin ?? 0, fmt: 'pct', delta: 0, spark, negative: margin !== null && margin < 0 },
  ];

  // ── Grupos de compras: lidos do snapshot de estoque (igual ao mobile: data.stock[id].groups) ───
  const purchaseGroupMap = {};
  for (const st of Object.values(stockByStore)) {
    for (const g of st.groups ?? []) {
      if (!g.groupName) continue;
      const ex = purchaseGroupMap[g.groupName] ?? { name: g.groupName, totalCost: 0, unity: 0 };
      ex.totalCost += g.totalCost ?? 0;
      ex.unity     += g.unity     ?? 0;
      purchaseGroupMap[g.groupName] = ex;
    }
  }
  const purchaseGroups = Object.values(purchaseGroupMap).sort((a, b) => b.totalCost - a.totalCost);

  const payments = Object.entries(paymentMap)
    .map(([name, v]) => ({ name, salesCount: v.salesCount, total: v.total }))
    .sort((a, b) => b.total - a.total);

  const purchaseDayData = resolvedDayTotals.map(d => ({ l: d.l, v: d.p ?? 0, c: 0, date: d.date }));

  // Série por fornecedor: { [mfgKey]: { name, total, data: [{l,v,c,date}] } }
  const allMfgKeys = new Set();
  for (const d of dayTotals) Object.keys(d.mfgMap ?? {}).forEach(k => allMfgKeys.add(k));
  const mfgPurchaseSeries = {};
  for (const k of allMfgKeys) {
    let name = '';
    const data = dayTotals.map(d => {
      const m = d.mfgMap?.[k];
      if (m?.name && !name) name = m.name;
      return { l: d.l, v: m?.total ?? 0, q: m?.qty ?? 0, u: m?.unity ?? 0, c: 0, date: d.date };
    });
    const total = data.reduce((s, d) => s + d.v, 0);
    const qty   = data.reduce((s, d) => s + d.q, 0);
    const unity = data.reduce((s, d) => s + d.u, 0);
    if (total > 0) mfgPurchaseSeries[k] = { name, total, qty, unity, data };
  }

  // Enriquece cada ponto com % do total de compras daquele dia
  for (const k of Object.keys(mfgPurchaseSeries)) {
    mfgPurchaseSeries[k].data = mfgPurchaseSeries[k].data.map((d, i) => {
      const dayTotal = purchaseDayData[i]?.v ?? 0;
      return { ...d, pct: dayTotal > 0 ? (d.v / dayTotal) * 100 : 0 };
    });
  }

  return {
    kpis, dayData: finalDayTotals, purchaseDayData, mfgPurchaseSeries,
    topStores:     ytdTopStores ?? topStores,
    categories:    topCats,
    categoryTotal,
    groupRanking:  groupsSorted,
    classRanking:  classesSorted,
    payments,
    totals: { total, quantity, avgTicket, margin, totalCost, totalSalesCount,
              purchaseTotal, pbmTotal, billTotal, billPaid, goalFactor: _goalFactor },
    stores,
    allStores,
    mtdTotal,
    stockByStore: Object.values(stockByStore),
    stockEvolution: (() => {
      const numDays = resolvedDayTotals.length;
      const gran    = numDays <= 7 ? 'day' : numDays <= 62 ? 'week' : 'month';

      const bucketKey = (dateStr) => {
        if (gran === 'day')   return dateStr;
        if (gran === 'month') return dateStr.slice(0, 7);
        // ISO week → segunda-feira da semana
        const [y, m, d] = dateStr.split('-').map(Number);
        const dt  = new Date(y, m - 1, d);
        const dow = dt.getDay(); // 0=Dom
        const off = dow === 0 ? -6 : 1 - dow;
        const mon = new Date(y, m - 1, d + off);
        return `${mon.getFullYear()}-${String(mon.getMonth()+1).padStart(2,'0')}-${String(mon.getDate()).padStart(2,'0')}`;
      };

      const MONTHS_PT = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
      const pointLabel = (key) => {
        if (gran === 'month') {
          const [y, mo] = key.split('-').map(Number);
          return `${MONTHS_PT[mo-1]}/${String(y).slice(2)}`;
        }
        const [, mo, d] = key.split('-').map(Number);
        return `${String(d).padStart(2,'0')}/${String(mo).padStart(2,'0')}`;
      };

      const buckets = new Map();
      for (const d of resolvedDayTotals) {
        const key = bucketKey(d.date);
        if (!buckets.has(key)) buckets.set(key, { key, soldValue: 0, soldQty: 0, purchasedValue: 0, purchasedQty: 0, stockSale: 0, stockCost: 0, stockUnity: 0 });
        const b = buckets.get(key);
        b.soldValue      += d.v;
        b.soldQty        += d.q  ?? 0;
        b.purchasedValue += d.p;
        b.purchasedQty   += d.pu ?? 0;
        const snap = stockEvolutionMap[d.date];
        if (snap && snap.totalSale > 0) { b.stockSale = snap.totalSale; b.stockCost = snap.totalCost; b.stockUnity = snap.totalUnity; }
      }

      const pts = [...buckets.values()]
        .map(b => ({ label: pointLabel(b.key), stockUnits: b.stockUnity, stockValue: b.stockSale, stockCost: b.stockCost, soldValue: b.soldValue, soldQty: b.soldQty, purchasedValue: b.purchasedValue, purchasedQty: b.purchasedQty }))
        .filter(pt => pt.stockUnits > 0 || pt.stockValue > 0 || pt.soldValue > 0 || pt.purchasedValue > 0);
      return { points: pts, gran };
    })(),
    bills,
    purchaseGroups,
  };
};

// ─── Hook MTD (projeção) — sempre calcula o mês atual independente do período ──
function useMtdTotal(user, selectedStoreIds) {
  const [mtdTotal, setMtdTotal] = React.useState(null);

  React.useEffect(() => {
    if (!user?.cnpj) return;
    let cancelled = false;
    const cnpj  = user.cnpj;
    const start = SF.startOfMonth();
    const end   = SF.today();

    (async () => {
      try {
        // Garante filiais carregadas antes de buscar (getStaticDocMerged depende delas)
        await SF.loadFiliaisMap(cnpj);
        if (cancelled) return;

        const docs = await SF.fetchDailyRangeMerged(cnpj, start, end);
        if (cancelled) return;

        let t = 0;
        for (const { data } of docs) {
          if (!data?.stores) continue;
          for (const [key, st] of Object.entries(data.stores)) {
            const sid = SF.parseStoreKey(key);
            if (!SF.isAllowedStore(sid, user.allowedStores)) continue;
            if (selectedStoreIds.length && !selectedStoreIds.includes(sid)) continue;
            t += st.total ?? 0;
          }
        }
        setMtdTotal(t);
      } catch {}
    })();

    return () => { cancelled = true; };
  }, [user?.cnpj, selectedStoreIds.join(',')]);

  return mtdTotal;
}

// ─── Hook principal de dados ──────────────────────────────────────────────────
// Espelha a lógica dos hooks do mobile (useRankingSales, useInventory, useBills…)
function useDashboardData(user, period, customStart, customEnd) {
  const [docs,    setDocs]    = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error,   setError]   = React.useState(false);
  const [syncAt,  setSyncAt]  = React.useState(null);

  React.useEffect(() => {
    if (!user?.cnpj) { setLoading(false); return; }
    // Período custom sem datas ainda: aguarda
    if (period === 'custom' && (!customStart || !customEnd)) { setLoading(false); setDocs([]); return; }

    const cnpj     = user.cnpj;
    const todayStr = SF.today();

    // Calcula intervalo de datas para o período selecionado
    let startDate, endDate;
    switch (period) {
      case 'hoje':   startDate = todayStr;             endDate = todayStr;   break;
      case '7d':     startDate = SF.subtractDays(6);   endDate = todayStr;   break;
      case '30d':    startDate = SF.subtractDays(29);  endDate = todayStr;   break;
      case 'mtd':    startDate = SF.startOfMonth();    endDate = todayStr;   break;
      case 'ytd':    startDate = SF.startOfYear();     endDate = todayStr;   break;
      case 'custom': startDate = customStart;          endDate = customEnd;  break;
      default:       startDate = todayStr;             endDate = todayStr;
    }

    // "ytd" com 365 docs seria caro: usamos apenas o doc de hoje
    const isYtd      = period === 'ytd';
    // Se o endDate é hoje, precisa de listener ao vivo; senão apenas batch
    const isEndToday = endDate === todayStr;

    let cancelled  = false;
    let unsubLive  = null;
    let pastDocs   = [];
    let todayData  = null;

    setLoading(true);
    setError(false);
    setDocs([]);

    const commit = () => {
      if (cancelled) return;
      setDocs([...pastDocs, { date: todayStr, data: todayData }]);
      setLoading(false);
      setSyncAt(new Date());
    };

    (async () => {
      try {
        // Garante que o mapa de filiais foi carregado antes de buscar qualquer dado
        await SF.loadFiliaisMap(cnpj);
        if (cancelled) return;

        if (isYtd) {
          // Busca todos os docs do ano (cache de sessão evita re-fetch)
          const pastEnd = SF.subtractDays(1);
          if (startDate <= pastEnd) {
            pastDocs = await SF.fetchDailyRangeMerged(cnpj, startDate, pastEnd);
            if (cancelled) return;
            setDocs([...pastDocs, { date: todayStr, data: null }]);
            setLoading(false);
          }
        } else if (isEndToday) {
            // ── Dias passados (excluindo hoje) + listener ao vivo ────────────
            const pastEnd = SF.subtractDays(1);
            if (startDate <= pastEnd) {
              pastDocs = await SF.fetchDailyRangeMerged(cnpj, startDate, pastEnd);
              if (cancelled) return;
              setDocs([...pastDocs, { date: todayStr, data: null }]);
              setLoading(false);
            }
          } else {
            // ── Período totalmente no passado: apenas batch, sem listener ────
            const allDocs = await SF.fetchDailyRangeMerged(cnpj, startDate, endDate);
            if (cancelled) return;
            setDocs(allDocs);
            setLoading(false);
            setSyncAt(new Date());
            return; // sem listener — encerra aqui
          }

        // ── Listener ao vivo para hoje ─────────────────────────────────────
        unsubLive = SF.listenDailyDocMerged(
          cnpj, todayStr,
          (data) => { if (!cancelled) { todayData = data; commit(); } },
          ()     => { if (!cancelled) { setError(true); setLoading(false); } }
        );
      } catch (err) {
        console.error('[dashboard] load error:', err);
        if (!cancelled) { setError(true); setLoading(false); }
      }
    })();

    return () => { cancelled = true; unsubLive?.(); };
  }, [user?.cnpj, period, customStart, customEnd]);

  return { docs, loading, error, syncAt };
}

// ─── Dashboard (componente raiz) ──────────────────────────────────────────────
function Dashboard({ navigate, user, onLogout }) {
  const validRoutes = new Set(ROUTES.map(r => r.key));
  const [activeRoute, setActiveRoute] = React.useState(() => {
    try {
      const saved = localStorage.getItem('sf-route');
      return saved && validRoutes.has(saved) ? saved : 'sales';
    } catch { return 'sales'; }
  });

  const navigateTo = React.useCallback((route) => {
    setActiveRoute(route);
    if (route === 'purchases' || route === 'comparative') setPeriod('mtd');
    if (route === 'stock') setPeriod('30d');
    try { localStorage.setItem('sf-route', route); } catch {}
  }, []);
  const [period, setPeriod] = React.useState(() => {
    try { const r = localStorage.getItem('sf-route'); if (r === 'stock') return '30d'; if (r === 'purchases' || r === 'comparative') return 'mtd'; return 'hoje'; } catch { return 'hoje'; }
  });
  const [showUserMgmt, setShowUserMgmt] = React.useState(false);
  const [showConfig,   setShowConfig]   = React.useState(false);
  const [metaCompras,     setMetaCompras]     = React.useState(0.70);
  const [metaComprasMode, setMetaComprasMode] = React.useState('pct'); // 'pct' | 'cmv'
  const [cmvGreen,  setCmvGreen]  = React.useState(55); // até aqui = compra eficiente
  const [cmvYellow, setCmvYellow] = React.useState(65); // até aqui = dentro do padrão
  const [cmvOrange, setCmvOrange] = React.useState(70); // até aqui = margem apertando; acima = crítico

  // Carrega config de compras do Firebase (Site primeiro; fallback para Parametros.site)
  React.useEffect(() => {
    if (!user?.cnpj) return;
    const configRef = SF.db.collection('loja').doc(user.cnpj).collection('config');
    Promise.all([
      configRef.doc('Site').get(),
      configRef.doc('Parametros').get(),
    ]).then(([siteSnap, paramSnap]) => {
      const s = siteSnap.exists   ? siteSnap.data()          : null;
      const p = paramSnap.exists  ? paramSnap.data()?.site   : null;
      const d = { ...p, ...s }; // Site tem prioridade sobre Parametros.site
      if (d?.metaCompras     != null) setMetaCompras(d.metaCompras);
      if (d?.metaComprasMode != null) setMetaComprasMode(d.metaComprasMode);
      if (d?.cmvGreen        != null) setCmvGreen(d.cmvGreen);
      if (d?.cmvYellow       != null) setCmvYellow(d.cmvYellow);
      if (d?.cmvOrange       != null) setCmvOrange(d.cmvOrange);
    }).catch(() => {});
  }, [user?.cnpj]);

  // ── Estado de filtros ──────────────────────────────────────────────────────
  const [filterDim,  setFilterDim]  = React.useState('grupos'); // 'grupos' | 'classes' | 'fornecedores' | 'parametros'
  const [filterSel,  setFilterSel]  = React.useState([]);       // nomes selecionados
  const [billsParams, setBillsParams] = React.useState({
    statusPago: true, statusCancelado: false, statusPendente: true,
    minBilling: '', maxBilling: '', minPaid: '', maxPaid: '',
    dateMode: 'vencimento',
  });

  const handleFilterDimChange = (dim) => { setFilterDim(dim); setFilterSel([]); };
  const handleFilterClear     = ()    => setFilterSel([]);

  // Ao trocar de rota: reset completo dos filtros para os defaults da aba de destino
  React.useEffect(() => {
    const defaultDim = (activeRoute === 'bills' || activeRoute === 'deliveries')
      ? 'fornecedores'
      : 'grupos';
    setFilterDim(defaultDim);
    setFilterSel([]);
  }, [activeRoute]);

  // ── Filiais selecionadas ([] = todas) ─────────────────────────────────────
  const [selectedStoreIds, setSelectedStoreIds] = React.useState([]);

  // ── Período personalizado ──────────────────────────────────────────────────
  const [customStart, setCustomStart] = React.useState(null);
  const [customEnd,   setCustomEnd]   = React.useState(null);

  const setCustomRange = React.useCallback((start, end) => {
    setCustomStart(start);
    setCustomEnd(end);
    setPeriod('custom');
  }, []);

  const { docs, loading, error, syncAt } = useDashboardData(user, period, customStart, customEnd);
  const mtdFromHook = useMtdTotal(user, selectedStoreIds);

  // Carrega lojas conhecidas de ontem para popular o seletor mesmo em períodos
  // onde algumas filiais ainda não têm dados (ex: "hoje" cedo, antes do coletor rodar).
  const [seedStores, setSeedStores] = React.useState([]);
  React.useEffect(() => {
    if (!user?.cnpj) return;
    const cnpj = user.cnpj;
    let cancelled = false;
    (async () => {
      await SF.loadFiliaisMap(cnpj);
      if (cancelled) return;
      const doc = await SF.getStaticDocMerged(cnpj, SF.subtractDays(1)).catch(() => null);
      if (cancelled || !doc?.stores) return;
      const allowed = (user?.allowedStores == null || typeof user?.allowedStores === 'boolean')
        ? null : user.allowedStores;
      const filialKeys = new Set(Object.keys(SF.getFiliaisMap()));
      const stores = Object.entries(doc.stores)
        .map(([key, s]) => ({ storeId: SF.parseStoreKey(key), storeName: s.storeName ?? '', _key: key }))
        .filter(({ storeId, _key }) => !allowed || filialKeys.has(_key) || SF.isAllowedStore(storeId, allowed))
        .map(({ storeId, storeName }) => ({ storeId, storeName }));
      if (!cancelled) setSeedStores(stores);
    })();
    return () => { cancelled = true; };
  }, [user?.cnpj]);

  const data = React.useMemo(() => {
const activeFilter = filterSel.length > 0
      ? { dim: filterDim, selected: new Set(filterSel) }
      : null;
    return buildDashboardData(docs, user?.allowedStores, period, activeFilter, selectedStoreIds);
  }, [docs, user?.allowedStores, period, filterDim, filterSel, selectedStoreIds]);

  // allStores enriquecido: lojas do período atual + lojas conhecidas de ontem
  const _allStoresMerged = React.useMemo(() => {
    const current = data?.allStores ?? [];
    const existingIds = new Set(current.map(s => s.storeId));
    const extra = seedStores
      .filter(s => !existingIds.has(s.storeId))
      .map(s => ({ ...s, total: 0, silent: true }));
    return [...current, ...extra];
  }, [data?.allStores, seedStores]);

  // Para o seletor de lojas: todas as filiais conhecidas (silenciosas aparecem por último)
  const allStoresForSelector = _allStoresMerged;

  // Para gestão de usuários: todas as filiais conhecidas, independente do período
  const allStoresForUserMgmt = _allStoresMerged;

  // Lista de fornecedores para o filterOptions (compras)
  const manufacturersForFilter = React.useMemo(() => {
    const mfgMap = {};
    for (const s of (data?.stores ?? [])) {
      for (const m of Object.values(s.purchases?.manufacturers ?? {})) {
        const key = m.manufacturerName || '—';
        const ex = mfgMap[key] ?? { name: key, total: 0 };
        ex.total += m.total ?? 0;
        mfgMap[key] = ex;
      }
    }
    return Object.values(mfgMap).sort((a, b) => b.total - a.total);
  }, [data?.stores]);

  // Lista de fornecedores para contas a pagar
  const billsMfgForFilter = React.useMemo(() => {
    if (activeRoute !== 'bills') return [];
    const map = {};
    for (const b of (data?.bills ?? [])) {
      const key = b.manufacturerName || '—';
      if (!map[key]) map[key] = { name: key, total: 0 };
      map[key].total += b.billingValue ?? 0;
    }
    return Object.values(map).sort((a, b) => b.total - a.total);
  }, [activeRoute, data?.bills]);

  // Opções de classe e grupo para o estoque
  const stockClassesForFilter = React.useMemo(() => {
    if (activeRoute !== 'stock') return [];
    const map = {};
    for (const st of (data?.stockByStore ?? [])) {
      for (const c of st.classifications ?? []) {
        if (!isValidClassName(c.className)) continue;
        if (!map[c.className]) map[c.className] = { name: c.className, total: 0 };
        map[c.className].total += c.totalSale;
      }
    }
    return Object.values(map);
  }, [activeRoute, data?.stockByStore]);

  const stockGroupsForFilter = React.useMemo(() => {
    if (activeRoute !== 'stock') return [];
    const map = {};
    for (const st of (data?.stockByStore ?? [])) {
      for (const g of st.groups ?? []) {
        if (!map[g.groupName]) map[g.groupName] = { name: g.groupName, total: 0 };
        map[g.groupName].total += g.totalSale;
      }
    }
    return Object.values(map);
  }, [activeRoute, data?.stockByStore]);

  // Label legível para o período custom (usado no DashHeader)
  const customLabel = React.useMemo(() => {
    if (period !== 'custom' || !customStart || !customEnd) return '';
    const fmt = (iso) => { const [, m, d] = iso.split('-'); return `${d}/${m}`; };
    return `${fmt(customStart)} – ${fmt(customEnd)}`;
  }, [period, customStart, customEnd]);

  return (
    <div style={{ display: 'flex', minHeight: '100vh', background: 'var(--ink-50)' }}>
      <DashSidebar active={activeRoute} setActive={navigateTo} navigate={navigate} user={user} />
      <main style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column' }}>
        {user?.impersonated && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, padding: '8px 16px', background: '#7C3AED', color: '#fff', fontSize: 12.5, fontWeight: 700, flexShrink: 0 }}>
            <span>Vendo como <strong>{user.corporateName || user.cnpj}</strong> · somente leitura</span>
            <button onClick={onLogout} style={{ fontSize: 11, fontWeight: 800, color: '#7C3AED', background: '#fff', border: 'none', borderRadius: 6, padding: '4px 10px', cursor: 'pointer', fontFamily: 'inherit' }}>
              Sair da visualização
            </button>
          </div>
        )}
        <DashTopbar
          period={period} setPeriod={setPeriod}
          customStart={customStart} customEnd={customEnd}
          setCustomRange={setCustomRange}
          user={user} onLogout={onLogout}
          loading={loading} syncAt={syncAt}
          onOpenUserMgmt={() => setShowUserMgmt(true)}
          onOpenConfig={() => setShowConfig(true)}
          allStores={allStoresForSelector}
          onSelectStores={setSelectedStoreIds}
          selectedStoreIds={selectedStoreIds}
          activeRoute={activeRoute}
          filterDim={filterDim}    onFilterDimChange={handleFilterDimChange}
          filterSel={filterSel}    onFilterSel={setFilterSel}
          onFilterClear={handleFilterClear}
          billsParams={billsParams} onBillsParamsChange={setBillsParams}
          filterOptions={{
            groups:        (activeRoute === 'bills' || activeRoute === 'deliveries') ? [] : (activeRoute === 'stock' ? [] : (data?.groupRanking ?? [])),
            classes:       (activeRoute === 'bills' || activeRoute === 'deliveries') ? [] : (activeRoute === 'stock' ? stockClassesForFilter : (data?.classRanking ?? [])),
            manufacturers: activeRoute === 'bills' ? billsMfgForFilter : manufacturersForFilter,
          }}
        />
        {showUserMgmt && (
          <UserManagementPanel
            currentUser={user}
            stores={allStoresForUserMgmt}
            onClose={() => setShowUserMgmt(false)}
          />
        )}
        {showConfig && (
          <SiteConfigPanel
            user={user}
            metaCompras={metaCompras}
            metaComprasMode={metaComprasMode}
            cmvGreen={cmvGreen}
            cmvYellow={cmvYellow}
            cmvOrange={cmvOrange}
            onSave={(v, mode, g, y, o) => {
              setMetaCompras(v); setMetaComprasMode(mode);
              setCmvGreen(g); setCmvYellow(y); setCmvOrange(o);
              setShowConfig(false);
            }}
            onClose={() => setShowConfig(false)}
          />
        )}
        <div style={{ padding: '24px 32px 48px', maxWidth: 1400, width: '100%', alignSelf: 'center' }}>
          <DashHeader
            activeRoute={activeRoute} period={period} customLabel={customLabel}
            loading={loading} error={error} syncAt={syncAt}
          />

          {/* ── Vendas ── */}
          {activeRoute === 'sales' && (
            <SalesView data={data} loading={loading} error={error} syncAt={syncAt} period={period}
              mtdTotal={period === 'mtd' ? data?.totals?.total : mtdFromHook} />
          )}
          {/* ── Compras ── */}
          {activeRoute === 'purchases' && (
            <PurchasesView data={data} loading={loading} syncAt={syncAt}
              filterDim={filterDim} filterSel={filterSel}
              period={period} customStart={customStart} customEnd={customEnd}
              setPeriod={setPeriod} setCustomRange={setCustomRange}
              user={user} metaCompras={metaCompras} metaComprasMode={metaComprasMode} cmvGreen={cmvGreen} cmvYellow={cmvYellow} cmvOrange={cmvOrange} />
          )}
          {/* ── Estoque ── */}
          {activeRoute === 'stock' && (
            <StockView data={data} loading={loading} syncAt={syncAt} filterDim={filterDim} filterSel={filterSel} selectedStoreIds={selectedStoreIds} />
          )}
          {/* ── Metas ── */}
          {activeRoute === 'goals' && (
            <GoalsView user={user} selectedStoreIds={selectedStoreIds} />
          )}
          {/* ── Contas a Pagar ── */}
          {activeRoute === 'bills' && (
            <BillsView data={data} loading={loading} syncAt={syncAt} filterDim={filterDim} filterSel={filterSel} billsParams={billsParams}
              period={period} customStart={customStart} customEnd={customEnd}
              user={user} selectedStoreIds={selectedStoreIds} />
          )}
          {/* ── Comparativo ── */}
          {activeRoute === 'comparative' && (
            <ComparativeView data={data} loading={loading} user={user} period={period} customStart={customStart} customEnd={customEnd} selectedStoreIds={selectedStoreIds} onSelectStores={setSelectedStoreIds} />
          )}
          {/* ── Análises ── */}
          {activeRoute === 'analytics' && (
            <AnalyticsView user={user} selectedStoreIds={selectedStoreIds} />
          )}
          {/* ── Entregas ── */}
          {activeRoute === 'deliveries' && (
            <DeliveriesView data={data} loading={loading} syncAt={syncAt} />
          )}
          {/* ── Módulos em construção ── */}
          {activeRoute === 'dre' && (
            <ComingSoon route={activeRoute} />
          )}
        </div>
      </main>
    </div>
  );
}

// ─── Seletor de filial (multi-select) ────────────────────────────────────────
function StoreSelector({ stores, selectedIds = [], onSelect }) {
  if (!stores || stores.length < 2) return null;

  const [open,    setOpen]    = React.useState(false);
  const [query,   setQuery]   = React.useState('');
  const [dropPos, setDropPos] = React.useState({ top: 0, left: 0 });
  const triggerRef = React.useRef(null);
  const inputRef   = React.useRef(null);

  const sorted = [...stores].sort((a, b) => {
    if (!!a.silent !== !!b.silent) return a.silent ? 1 : -1;
    return (a.storeName || `Loja ${a.storeId}`).localeCompare(b.storeName || `Loja ${b.storeId}`, 'pt-BR');
  });
  const selSet   = new Set(selectedIds);
  const hasFilter = selSet.size > 0;

  // Rótulo do botão trigger
  const triggerLabel = !hasFilter
    ? 'Todas as lojas'
    : selSet.size === 1
      ? (sorted.find(s => selSet.has(s.storeId))?.storeName || `Loja ${[...selSet][0]}`)
      : `${selSet.size} lojas`;

  const filtered = query.trim()
    ? sorted.filter(s =>
        (s.storeName || `Loja ${s.storeId}`)
          .toLowerCase()
          .includes(query.trim().toLowerCase())
      )
    : sorted;

  const handleOpen = () => {
    if (triggerRef.current) {
      const r = triggerRef.current.getBoundingClientRect();
      setDropPos({ top: r.bottom + 6, left: r.left });
    }
    setQuery('');
    setOpen(true);
    setTimeout(() => inputRef.current?.focus(), 30);
  };

  const handleClose = () => { setOpen(false); setQuery(''); };

  const toggleStore = (id) => {
    const next = new Set(selSet);
    next.has(id) ? next.delete(id) : next.add(id);
    onSelect([...next]);
  };

  const toggleAll = () => {
    // Se todas estão marcadas (ou todas visíveis na busca estão marcadas) → limpa
    // Senão → seleciona todas as visíveis
    const visibleIds = filtered.map(s => s.storeId);
    const allVisible = visibleIds.every(id => selSet.has(id));
    if (allVisible && selSet.size > 0) {
      onSelect([]);
    } else {
      onSelect([...new Set([...selSet, ...visibleIds])]);
    }
  };

  const allVisibleChecked = filtered.length > 0 && filtered.every(s => selSet.has(s.storeId));
  const someChecked       = filtered.some(s => selSet.has(s.storeId));

  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      {/* ── Botão trigger ── */}
      <button
        ref={triggerRef}
        onClick={open ? handleClose : handleOpen}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '7px 14px', fontSize: 13.5, fontWeight: 600, fontFamily: 'inherit',
          border: `1.5px solid ${hasFilter ? 'var(--brand-500)' : 'var(--ink-200)'}`,
          borderRadius: 10, cursor: 'pointer',
          background: hasFilter ? 'var(--brand-50)' : 'var(--white)',
          color:      hasFilter ? 'var(--brand-700)' : 'var(--ink-800)',
          boxShadow: 'var(--shadow-sm)', maxWidth: 300,
        }}
      >
        <Icon name="barcode" size={15} />
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 220 }}>
          {triggerLabel}
        </span>
        {hasFilter && (
          <span style={{
            minWidth: 20, height: 18, borderRadius: 10, padding: '0 6px',
            background: 'var(--brand-500)', color: '#fff',
            fontSize: 11, fontWeight: 700,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          }}>{selSet.size}</span>
        )}
        <Icon name="chevron-down" size={13} style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
      </button>

      {/* Botão "Limpar" ao lado quando há filtro */}
      {hasFilter && !open && (
        <button
          onClick={() => onSelect([])}
          style={{
            marginLeft: 6, padding: '7px 10px', fontSize: 12, fontWeight: 600,
            border: '1.5px solid var(--ink-200)', borderRadius: 10, cursor: 'pointer',
            background: 'var(--white)', color: 'var(--ink-500)', fontFamily: 'inherit',
          }}
          title="Limpar seleção de filiais"
        >×</button>
      )}

      {open && (
        <>
          {/* Backdrop */}
          <div style={{ position: 'fixed', inset: 0, zIndex: 29 }} onClick={handleClose} />

          {/* Dropdown */}
          <div style={{
            position: 'fixed', top: dropPos.top, left: dropPos.left,
            width: 300, background: 'var(--white)',
            border: '1px solid var(--ink-200)', borderRadius: 12,
            boxShadow: 'var(--shadow-md)', zIndex: 30, overflow: 'hidden',
          }}>
            {/* Campo de busca */}
            <div style={{ padding: '10px 12px', borderBottom: '1px solid var(--ink-100)', position: 'relative' }}>
              <div style={{ position: 'absolute', top: '50%', left: 22, transform: 'translateY(-50%)', color: 'var(--ink-400)', pointerEvents: 'none' }}>
                <Icon name="search" size={14} />
              </div>
              <input
                ref={inputRef}
                value={query}
                onChange={e => setQuery(e.target.value)}
                onKeyDown={e => {
                  if (e.key === 'Enter') {
                    if (filtered.length === 1) {
                      toggleStore(filtered[0].storeId);
                      handleClose();
                    } else if (filtered.length > 1) {
                      toggleAll();
                    }
                  } else if (e.key === 'Escape') {
                    handleClose();
                  }
                }}
                placeholder="Buscar filial…"
                style={{
                  width: '100%', padding: '6px 8px 6px 28px',
                  fontSize: 13, fontFamily: 'inherit',
                  background: 'var(--ink-50)', border: '1px solid var(--ink-200)',
                  borderRadius: 7, outline: 'none', color: 'var(--ink-900)',
                  boxSizing: 'border-box',
                }}
              />
            </div>

            {/* "Todas" — linha de selecionar/desmarcar tudo */}
            {!query && (
              <StoreSelectorRow
                label="Todas as lojas"
                sublabel={`${sorted.filter(s => !s.silent).length} filiais`}
                checked={!hasFilter}
                indeterminate={hasFilter && selSet.size < sorted.length}
                onClick={() => onSelect([])}
              />
            )}
            {query && (
              <StoreSelectorRow
                label={allVisibleChecked ? 'Desmarcar visíveis' : 'Marcar visíveis'}
                checked={allVisibleChecked}
                indeterminate={!allVisibleChecked && someChecked}
                onClick={toggleAll}
                isAction
              />
            )}

            {/* Lista de filiais */}
            <div style={{ maxHeight: 280, overflowY: 'auto' }}>
              {filtered.length === 0
                ? <div style={{ padding: '20px 16px', textAlign: 'center', fontSize: 13, color: 'var(--ink-400)' }}>
                    Nenhuma filial encontrada
                  </div>
                : filtered.map(s => (
                    <StoreSelectorRow
                      key={s.storeId}
                      label={s.storeName || `Loja ${s.storeId}`}
                      checked={selSet.has(s.storeId)}
                      onClick={() => toggleStore(s.storeId)}
                      silent={!!s.silent}
                      warn={!s.silent && !!s.nameFallback}
                      sublabel={s.silent ? 'não comunicou via Farmax' : (!s.silent && s.nameFallback) ? 'nome não veio do Farmax hoje' : undefined}
                    />
                  ))
              }
            </div>

            {/* Rodapé com contagem */}
            <div style={{ padding: '10px 14px', borderTop: '1px solid var(--ink-100)' }}>
              <span style={{ fontSize: 12, color: 'var(--ink-400)' }}>
                {hasFilter ? `${selSet.size} de ${sorted.length} selecionadas` : 'Todas as filiais'}
              </span>
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function StoreSelectorRow({ label, sublabel, checked, indeterminate, onClick, isAction, silent, warn }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (ref.current) ref.current.indeterminate = !!indeterminate;
  }, [indeterminate]);

  return (
    <button
      onClick={onClick}
      style={{
        display: 'flex', alignItems: 'center', gap: 10,
        width: '100%', padding: '9px 14px',
        background: 'transparent', border: 0,
        cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
        borderBottom: '1px solid var(--ink-50)',
        opacity: silent ? 0.65 : 1,
      }}
      onMouseEnter={e => { e.currentTarget.style.background = 'var(--ink-50)'; }}
      onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}
    >
      {/* Checkbox customizado */}
      <div style={{
        width: 16, height: 16, borderRadius: 4, flexShrink: 0,
        border: `2px solid ${checked || indeterminate ? 'var(--brand-500)' : 'var(--ink-300)'}`,
        background: checked ? 'var(--brand-500)' : indeterminate ? 'var(--brand-200)' : 'var(--white)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transition: 'background .1s, border-color .1s',
      }}>
        {checked && (
          <svg width="9" height="7" viewBox="0 0 9 7" fill="none">
            <polyline points="1,3.5 3.5,6 8,1" stroke="#fff" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        )}
        {indeterminate && !checked && (
          <div style={{ width: 8, height: 2, background: 'var(--brand-500)', borderRadius: 1 }}/>
        )}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          {silent && (
            <div style={{ width: 7, height: 7, borderRadius: '50%', background: '#FF3B30', flexShrink: 0 }} />
          )}
          {!silent && warn && (
            <div style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--warning)', flexShrink: 0 }} />
          )}
          <span style={{
            fontSize: isAction ? 12.5 : 13.5,
            fontWeight: checked ? 600 : 500,
            color: isAction ? 'var(--brand-600, var(--brand-500))' : silent ? 'var(--ink-500)' : checked ? 'var(--brand-700)' : 'var(--ink-800)',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            fontStyle: isAction ? 'italic' : 'normal',
          }}>{label}</span>
        </div>
        {sublabel && (
          <div style={{ fontSize: 10.5, color: silent ? '#FF3B30' : warn ? 'var(--warning)' : 'var(--ink-400)', fontWeight: 500, marginTop: 1 }}>{sublabel}</div>
        )}
      </div>
    </button>
  );
}

// ─── Sidebar ──────────────────────────────────────────────────────────────────
// Ícones: espelham os ícones do mobile (components/icons/index.tsx)
const ROUTES = [
  { key: 'sales',       label: 'Vendas',         icon: 'sf-sales',       badge: 'live', pageIdx: 0 },
  { key: 'purchases',   label: 'Compras',        icon: 'sf-purchases',                  pageIdx: 1 },
  { key: 'goals',       label: 'Metas',          icon: 'sf-goals',                      pageIdx: 2 },
  { key: 'stock',       label: 'Estoque',        icon: 'sf-stock',                      pageIdx: 3 },
  { key: 'comparative', label: 'Comparativo',    icon: 'sf-comparative',                pageIdx: 4 },
  { key: 'analytics',   label: 'Análises',       icon: 'sf-overview',                   pageIdx: 5 },
  { key: 'deliveries',  label: 'Entregas',       icon: 'sf-deliveries',                 pageIdx: 6 },
  { key: 'bills',       label: 'Contas a pagar', icon: 'sf-bills',                      pageIdx: 7 },
  { key: 'dre',         label: 'DRE',            icon: 'dre',                           pageIdx: null },
];

function useDarkMode() {
  const [dark, setDark] = React.useState(() => {
    try { return localStorage.getItem('sf-theme') === 'dark'; } catch { return false; }
  });

  React.useEffect(() => {
    document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
    try { localStorage.setItem('sf-theme', dark ? 'dark' : 'light'); } catch {}
  }, [dark]);

  return [dark, () => setDark(d => !d)];
}

function DashSidebar({ active, setActive, navigate, user }) {
  const [dark, toggleDark] = useDarkMode();

  const visibleRoutes = ROUTES.filter(r => {
    if (r.pageIdx === null) return !!user?.admin;
    if (!Array.isArray(user?.allowedPages)) return true;
    return user.allowedPages.includes(r.pageIdx);
  });

  return (
    <aside style={{
      width: 248, flexShrink: 0,
      background: 'var(--white)', borderRight: '1px solid var(--ink-200)',
      display: 'flex', flexDirection: 'column',
      position: 'sticky', top: 0, height: '100vh',
    }}>
      <div style={{ padding: '20px 20px 16px', borderBottom: '1px solid var(--ink-100)' }}>
        <Logo size={26} />
      </div>
      <div style={{ padding: '16px 12px', flex: 1, overflowY: 'auto' }}>
        <div style={{
          fontSize: 11, fontWeight: 700, color: 'var(--ink-400)',
          letterSpacing: '0.1em', textTransform: 'uppercase',
          padding: '8px 12px 6px',
        }}>Módulos</div>
        {visibleRoutes.map(r => (
          <NavItem key={r.key} route={r} active={active === r.key} onClick={() => setActive(r.key)} />
        ))}
      </div>
      <div style={{ padding: '10px 16px 14px', borderTop: '1px solid var(--ink-100)' }}>
        <button
          onClick={toggleDark}
          title={dark ? 'Mudar para modo claro' : 'Mudar para modo escuro'}
          style={{
            display: 'flex', alignItems: 'center', gap: 9,
            width: '100%', padding: '8px 10px',
            background: 'transparent', border: 0,
            borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit',
            color: 'var(--ink-500)', fontSize: 13, fontWeight: 500,
            transition: 'background .12s, color .12s',
          }}
          onMouseEnter={e => { e.currentTarget.style.background = 'var(--ink-100)'; e.currentTarget.style.color = 'var(--ink-700)'; }}
          onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--ink-500)'; }}
        >
          <Icon name={dark ? 'sun' : 'moon'} size={15} />
          {dark ? 'Modo claro' : 'Modo escuro'}
        </button>
      </div>
    </aside>
  );
}

function NavItem({ route, active, onClick }) {
  return (
    <button onClick={onClick} style={{
      display: 'flex', alignItems: 'center', gap: 12,
      width: '100%', padding: '10px 12px',
      background: active ? 'var(--brand-50)' : 'transparent',
      color:      active ? 'var(--brand-700)' : 'var(--ink-700)',
      border: 0, borderRadius: 8, cursor: 'pointer',
      fontSize: 14, fontWeight: active ? 600 : 500, fontFamily: 'inherit',
      transition: 'background .12s', position: 'relative', textAlign: 'left',
    }}
    onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--ink-50)'; }}
    onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}
    >
      <Icon name={route.icon} size={18} />
      <span style={{ flex: 1 }}>{route.label}</span>
      {route.badge === 'live' && (
        <span style={{
          fontSize: 9.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase',
          color: 'var(--positive)', background: 'var(--positive-bg)',
          padding: '2px 6px', borderRadius: 4,
          display: 'inline-flex', alignItems: 'center', gap: 4,
        }}>
          <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--positive)' }}/>
          AO VIVO
        </span>
      )}
    </button>
  );
}

// ─── Topbar ───────────────────────────────────────────────────────────────────
function DashTopbar({ period, setPeriod, customStart, customEnd, setCustomRange, user, onLogout, loading, syncAt, onOpenUserMgmt, onOpenConfig, allStores = [], onSelectStores, selectedStoreIds = [], activeRoute, filterDim, onFilterDimChange, filterSel = [], onFilterSel, onFilterClear, filterOptions = {}, billsParams, onBillsParamsChange }) {
  const [userMenuOpen, setUserMenuOpen] = React.useState(false);
  const [filterOpen,   setFilterOpen]   = React.useState(false);
  const [filterPos,    setFilterPos]    = React.useState({ top: 0, right: 0 });
  const [searchQuery, setSearchQuery]   = React.useState('');
  const filterBtnRef = React.useRef(null);

  const initials = (name) => {
    if (!name) return '?';
    const parts = name.trim().split(/\s+/);
    return ((parts[0]?.[0] ?? '') + (parts[1]?.[0] ?? '')).toUpperCase();
  };

  const displayName  = user?.name || user?.corporateName || user?.email || 'Usuário';
  const displayCorp  = user?.corporateName || '';
  const displayEmail = user?.email || '';

  return (
    <header style={{
      background: 'var(--white)', borderBottom: '1px solid var(--ink-200)',
      padding: '10px 32px', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      position: 'sticky', top: 0, zIndex: 5, gap: 12,
    }}>
      {/* Esquerda: busca + seletor de loja + filtros */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
        {/* Busca rápida */}
        <div style={{ position: 'relative', flexShrink: 0, width: 200 }}>
          <div style={{ position: 'absolute', top: '50%', left: 10, transform: 'translateY(-50%)', color: 'var(--ink-400)' }}>
            <Icon name="search" size={15} />
          </div>
          <input
            value={searchQuery}
            onChange={e => setSearchQuery(e.target.value)}
            onKeyDown={e => {
              if (e.key === 'Enter' && searchQuery.trim() && onSelectStores) {
                const q = searchQuery.trim().toLowerCase();
                const matches = allStores.filter(s =>
                  (s.storeName || `Loja ${s.storeId}`).toLowerCase().includes(q)
                );
                if (matches.length > 0) {
                  onSelectStores(matches.map(s => s.storeId));
                  setSearchQuery('');
                }
              } else if (e.key === 'Escape') {
                setSearchQuery('');
              }
            }}
            placeholder="Buscar filial…"
            style={{
              width: '100%', padding: '7px 10px 7px 32px',
              fontSize: 13, fontFamily: 'inherit',
              background: 'var(--ink-50)', border: '1px solid transparent',
              borderRadius: 8, color: 'var(--ink-900)', outline: 'none',
            }}
          />
        </div>

        {/* Seletor de loja */}
        <StoreSelector
          stores={allStores}
          selectedIds={selectedStoreIds}
          onSelect={onSelectStores}
        />

        {/* Chips dos filtros ativos */}
        {filterSel.length > 0 && (
          <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', maxWidth: 320, overflow: 'hidden' }}>
            {filterSel.map(name => (
              <span key={name} style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                fontSize: 11.5, fontWeight: 600,
                background: 'var(--brand-50)', color: 'var(--brand-700)',
                border: '1px solid var(--brand-200)',
                padding: '3px 8px', borderRadius: 20, whiteSpace: 'nowrap',
              }}>
                {name}
                <button onClick={() => onFilterSel(s => s.filter(x => x !== name))} style={{
                  background: 'none', border: 0, padding: 0, cursor: 'pointer',
                  color: 'var(--brand-500)', display: 'flex', alignItems: 'center', lineHeight: 1,
                }}>✕</button>
              </span>
            ))}
          </div>
        )}

        {/* Botão Filtros */}
        <div style={{ flexShrink: 0 }}>
          <button
            ref={filterBtnRef}
            className="btn btn-outline btn-sm"
            onClick={() => {
              if (!filterOpen && filterBtnRef.current) {
                const r = filterBtnRef.current.getBoundingClientRect();
                setFilterPos({ top: r.bottom + 8, right: window.innerWidth - r.right });
              }
              setFilterOpen(o => !o);
            }}
            style={filterSel.length > 0 ? {
              borderColor: 'var(--brand-500)', color: 'var(--brand-700)',
              background: 'var(--brand-50)',
            } : {}}
          >
            <Icon name="filter" size={14} />
            Filtros
            {filterSel.length > 0 && (
              <span style={{
                marginLeft: 4, background: 'var(--brand-500)', color: '#fff',
                fontSize: 10.5, fontWeight: 700, borderRadius: 10,
                padding: '1px 6px', lineHeight: '16px',
              }}>{filterSel.length}</span>
            )}
          </button>

          {filterOpen && (
            <FilterPanel
              dim={filterDim}       setDim={onFilterDimChange}
              selected={filterSel}  setSelected={onFilterSel}
              onClear={onFilterClear}
              onClose={() => setFilterOpen(false)}
              options={filterOptions}
              activeRoute={activeRoute}
              anchorTop={filterPos.top}
              anchorRight={filterPos.right}
              billsParams={billsParams}
              onBillsParamsChange={onBillsParamsChange}
            />
          )}
        </div>
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
        {/* Indicador de sincronização */}
        {loading
          ? <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--ink-500)' }}>
              <Spinner size={13} color="var(--ink-400)" /> Carregando…
            </div>
          : syncAt && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--ink-500)' }}>
              <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--positive)' }}/>
              Ao vivo
            </div>
          )
        }

        {activeRoute !== 'goals' && activeRoute !== 'analytics' && (
          <PeriodSelector
            period={period} setPeriod={setPeriod}
            customStart={customStart} customEnd={customEnd}
            setCustomRange={setCustomRange}
            activeRoute={activeRoute}
          />
        )}

        <div style={{ width: 1, height: 24, background: 'var(--ink-200)', margin: '0 4px' }}/>

        {/* Menu do usuário */}
        <div style={{ position: 'relative' }}>
          <button
            onClick={() => setUserMenuOpen(!userMenuOpen)}
            style={{
              display: 'flex', alignItems: 'center', gap: 8,
              padding: '6px 10px', background: 'none', border: '1px solid var(--ink-200)',
              borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit',
            }}
          >
            <div style={{
              width: 28, height: 28, borderRadius: '50%',
              background: 'var(--brand-500)', color: '#fff',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 11, fontWeight: 700,
            }}>
              {initials(displayName)}
            </div>
            <div style={{ textAlign: 'left', lineHeight: 1.3 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-900)', maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {displayName}
              </div>
              {displayCorp && (
                <div style={{ fontSize: 11, color: 'var(--ink-500)', maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {displayCorp}
                </div>
              )}
            </div>
            <Icon name="chevron-down" size={13} />
          </button>

          {userMenuOpen && (
            <>
              {/* Backdrop para fechar ao clicar fora */}
              <div style={{ position: 'fixed', inset: 0, zIndex: 9 }} onClick={() => setUserMenuOpen(false)} />

              <div style={{
                position: 'absolute', top: 'calc(100% + 6px)', right: 0,
                background: 'var(--white)', border: '1px solid var(--ink-200)',
                borderRadius: 10, boxShadow: 'var(--shadow-md)',
                width: 240, padding: 6, zIndex: 20,
              }}>
                <div style={{ padding: '8px 12px 12px', borderBottom: '1px solid var(--ink-100)', marginBottom: 4 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-900)' }}>{displayName}</div>
                  <div style={{ fontSize: 11.5, color: 'var(--ink-500)', marginTop: 2 }}>{displayEmail}</div>
                  {user?.cnpj && (
                    <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-400)', marginTop: 3 }}>
                      CNPJ: {user.cnpj}
                    </div>
                  )}
                </div>

                {/* Gestão de Usuários — apenas para admins */}
                {user?.admin && (
                  <button
                    onClick={() => { setUserMenuOpen(false); onOpenUserMgmt?.(); }}
                    style={{
                      display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                      padding: '9px 12px', background: 'none', border: 0,
                      borderRadius: 6, cursor: 'pointer', fontFamily: 'inherit',
                      fontSize: 13.5, color: 'var(--ink-800)', fontWeight: 500,
                    }}
                    onMouseEnter={e => e.currentTarget.style.background = 'var(--ink-50)'}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                  >
                    <Icon name="users" size={15} />
                    Gestão de Usuários
                  </button>
                )}
                {/* Configurações — apenas admins */}
                {user?.admin && (
                  <button
                    onClick={() => { setUserMenuOpen(false); onOpenConfig?.(); }}
                    style={{
                      display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                      padding: '9px 12px', background: 'none', border: 0,
                      borderRadius: 6, cursor: 'pointer', fontFamily: 'inherit',
                      fontSize: 13.5, color: 'var(--ink-800)', fontWeight: 500,
                    }}
                    onMouseEnter={e => e.currentTarget.style.background = 'var(--ink-50)'}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                  >
                    <Icon name="settings" size={15} />
                    Configurações
                  </button>
                )}

                <button
                  onClick={() => { setUserMenuOpen(false); onLogout?.(); }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                    padding: '9px 12px', background: 'none', border: 0,
                    borderRadius: 6, cursor: 'pointer', fontFamily: 'inherit',
                    fontSize: 13.5, color: 'var(--danger)', fontWeight: 500,
                  }}
                  onMouseEnter={e => e.currentTarget.style.background = 'var(--danger-bg)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >
                  <Icon name="logout" size={15} />
                  Sair
                </button>
              </div>
            </>
          )}
        </div>
      </div>
    </header>
  );
}

// ─── Painel de Configurações do Site ─────────────────────────────────────────
function SiteConfigPanel({ user, metaCompras, metaComprasMode, cmvGreen = 55, cmvYellow = 65, cmvOrange = 70, onSave, onClose }) {
  // page: null = menu raiz | 'compras' = settings de compras
  const [page,      setPage]      = React.useState(null);
  const [value,     setValue]     = React.useState(Math.round((metaCompras ?? 0.70) * 100));
  const [mode,      setMode]      = React.useState(metaComprasMode ?? 'pct');
  const [greenVal,  setGreenVal]  = React.useState(cmvGreen);
  const [yellowVal, setYellowVal] = React.useState(cmvYellow);
  const [orangeVal, setOrangeVal] = React.useState(cmvOrange);
  const [saving, setSaving] = React.useState(false);
  const [error,  setError]  = React.useState('');

  const useCmv = mode === 'cmv';

  const handleSave = async () => {
    const pct = parseFloat(value);
    const g   = parseFloat(greenVal);
    const y   = parseFloat(yellowVal);
    const o   = parseFloat(orangeVal);
    if (!useCmv && (isNaN(pct) || pct <= 0 || pct > 200)) { setError('Orçamento inválido (1–200%)'); return; }
    if (isNaN(g) || g <= 0 || g >= 100) { setError('Limiar verde inválido (1–99%)'); return; }
    if (isNaN(y) || y <= g)             { setError('Amarelo deve ser maior que o verde'); return; }
    if (isNaN(o) || o <= y)             { setError('Laranja deve ser maior que o amarelo'); return; }
    setSaving(true); setError('');
    const v = useCmv ? (metaCompras ?? 0.70) : pct / 100;
    const configRef = SF.db.collection('loja').doc(user.cnpj).collection('config');
    try {
      await Promise.all([
        configRef.doc('Site').set({ metaCompras: v, metaComprasMode: mode, cmvGreen: g, cmvYellow: y, cmvOrange: o }, { merge: true }),
        configRef.doc('Parametros').set({ site: { metaCompras: v, metaComprasMode: mode, cmvGreen: g, cmvYellow: y, cmvOrange: o } }, { merge: true }),
      ]);
      onSave(v, mode, g, y, o);
    } catch (e) { setError('Erro ao salvar. Tente novamente.'); setSaving(false); }
  };

  // Seções do menu raiz — escalável para futuras telas
  const MENU = [
    { key: 'compras', icon: 'sf-purchases', label: 'Compras', hint: 'Orçamento e faixas de eficiência de CMV' },
    // { key: 'vendas',  emoji: '📈', label: 'Vendas',   hint: 'Metas e alertas de desempenho' },
  ];

  const inSub = page !== null;

  return (
    <>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.35)', zIndex: 49 }} />
      <div style={{
        position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%,-50%)',
        width: 420, background: 'var(--white)', borderRadius: 18,
        boxShadow: 'var(--shadow-lg)', zIndex: 50,
        display: 'flex', flexDirection: 'column', maxHeight: '90vh',
      }}>

        {/* ── Header ── */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '18px 20px 16px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0 }}>
          {inSub ? (
            <button onClick={() => { setPage(null); setError(''); }} style={{
              width: 30, height: 30, borderRadius: '50%', border: 'none', cursor: 'pointer',
              background: 'var(--ink-100)', color: 'var(--ink-600)', fontSize: 18, lineHeight: 1,
              display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
            }}>‹</button>
          ) : (
            <div style={{ width: 32, height: 32, borderRadius: 9, background: '#EEF5FF', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name="settings" size={16} color="var(--brand-500)" />
            </div>
          )}
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>
              {page === 'compras' ? 'Compras' : 'Configurações'}
            </div>
            {page === 'compras' && (
              <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 1 }}>Orçamento e faixas de eficiência</div>
            )}
          </div>
          <button onClick={onClose} style={{
            width: 28, height: 28, borderRadius: '50%', border: 'none', cursor: 'pointer',
            background: 'var(--ink-100)', color: 'var(--ink-500)', fontSize: 14,
            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
          }}>✕</button>
        </div>

        {/* ── Conteúdo ── */}
        <div style={{ overflowY: 'auto', flex: 1 }}>

          {/* ── Menu raiz ── */}
          {!inSub && (
            <div style={{ padding: '14px 16px 20px' }}>
              <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: 8, paddingLeft: 2 }}>
                Parâmetros por tela
              </div>
              <div style={{ borderRadius: 14, overflow: 'hidden', border: '1px solid var(--ink-100)', background: 'var(--ink-50)' }}>
                {MENU.map((item, i) => (
                  <button key={item.key} onClick={() => setPage(item.key)} style={{
                    width: '100%', display: 'flex', alignItems: 'center', gap: 14,
                    padding: '14px 16px', background: 'none', border: 'none',
                    borderTop: i > 0 ? '1px solid var(--ink-100)' : 'none',
                    cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
                  }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgba(0,0,0,0.025)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'none'}
                  >
                    <div style={{ width: 38, height: 38, borderRadius: 11, background: '#EEF5FF', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                      <Icon name={item.icon} size={20} color="var(--brand-500)" />
                    </div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-900)' }}>{item.label}</div>
                      <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>{item.hint}</div>
                    </div>
                    <span style={{ color: 'var(--ink-300)', fontSize: 20, fontWeight: 300, lineHeight: 1 }}>›</span>
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* ── Página Compras ── */}
          {page === 'compras' && (
            <div style={{ padding: '14px 16px 20px', display: 'flex', flexDirection: 'column', gap: 24 }}>

              {/* Orçamento */}
              <div>
                <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: 8, paddingLeft: 2 }}>
                  Orçamento de compras
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-400)', marginBottom: 12, lineHeight: 1.5, paddingLeft: 2 }}>
                  Percentual ideal de compras sobre as vendas. Usado no card de Planejamento.
                </div>
                {[
                  { val: 'pct', label: 'Percentual das vendas', hint: 'Define o orçamento como uma % do faturamento do período.' },
                  { val: 'cmv', label: 'CMV por filial',        hint: 'O orçamento de cada filial é igual ao seu CMV no período.' },
                ].map(opt => (
                  <label key={opt.val} style={{
                    display: 'flex', alignItems: 'flex-start', gap: 12, cursor: 'pointer',
                    padding: '12px 14px', borderRadius: 12, marginBottom: 8,
                    background: mode === opt.val ? '#EEF5FF' : 'var(--ink-50)',
                    border: `1.5px solid ${mode === opt.val ? 'var(--brand-300)' : 'var(--ink-100)'}`,
                    transition: 'all .12s',
                  }}>
                    <input type="radio" name="metaMode" value={opt.val} checked={mode === opt.val}
                      onChange={() => { setMode(opt.val); setError(''); }}
                      style={{ marginTop: 3, accentColor: 'var(--brand-500)', flexShrink: 0 }}
                    />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-800)' }}>{opt.label}</div>
                      <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>{opt.hint}</div>
                      {opt.val === 'pct' && !useCmv && (
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10 }}>
                          <input type="number" min="1" max="200" step="1" value={value}
                            onChange={e => { setValue(e.target.value); setError(''); }}
                            style={{ width: 68, padding: '7px 10px', fontSize: 15, fontFamily: 'inherit', fontWeight: 700,
                              border: `1.5px solid ${error ? 'var(--danger)' : 'var(--brand-300)'}`,
                              borderRadius: 8, outline: 'none', color: 'var(--brand-600)',
                              background: 'var(--white)', textAlign: 'center' }}
                          />
                          <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-500)' }}>% das vendas</span>
                        </div>
                      )}
                    </div>
                  </label>
                ))}
              </div>

              {/* Faixas CMV */}
              <div>
                <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: 8, paddingLeft: 2 }}>
                  Faixas de eficiência de CMV
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-400)', marginBottom: 12, lineHeight: 1.5, paddingLeft: 2 }}>
                  Define o que cada cor significa. O vermelho é automático — tudo acima do laranja.
                </div>
                {[
                  { color: '#34C759', label: 'Compra eficiente', hint: 'CMV até este % = ótimo',          val: greenVal,  set: setGreenVal },
                  { color: '#F5A623', label: 'Dentro do padrão', hint: 'Entre verde e aqui = aceitável',  val: yellowVal, set: setYellowVal },
                  { color: '#FF6B00', label: 'Margem apertando', hint: 'Entre amarelo e aqui = atenção',  val: orangeVal, set: setOrangeVal },
                ].map(({ color, label, hint, val, set }) => (
                  <div key={color} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', borderRadius: 12, marginBottom: 8, background: 'var(--ink-50)', border: '1px solid var(--ink-100)' }}>
                    <div style={{ width: 10, height: 10, borderRadius: '50%', background: color, flexShrink: 0 }} />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-800)' }}>{label}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--ink-400)', marginTop: 1 }}>{hint}</div>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <span style={{ fontSize: 11, color: 'var(--ink-400)' }}>até</span>
                      <input type="number" min="1" max="200" step="1" value={val}
                        onChange={e => { set(e.target.value); setError(''); }}
                        style={{ width: 52, padding: '6px 6px', fontSize: 13, fontFamily: 'inherit', fontWeight: 700,
                          border: '1.5px solid var(--ink-200)', borderRadius: 8, outline: 'none',
                          color, background: 'var(--white)', textAlign: 'center' }}
                      />
                      <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-400)' }}>%</span>
                    </div>
                  </div>
                ))}
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', borderRadius: 12, background: '#FEF2F2', border: '1px solid #FECACA' }}>
                  <div style={{ width: 10, height: 10, borderRadius: '50%', background: '#FF3B30', flexShrink: 0 }} />
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600, color: '#B91C1C' }}>Acima do recomendado</div>
                    <div style={{ fontSize: 10.5, color: '#EF4444', marginTop: 1 }}>Automático — acima de {orangeVal}% das vendas</div>
                  </div>
                  <span style={{ fontSize: 11, color: '#EF4444', fontWeight: 600 }}>automático</span>
                </div>
              </div>
            </div>
          )}
        </div>

        {/* ── Footer ── */}
        <div style={{ padding: '12px 20px 18px', borderTop: '1px solid var(--ink-100)', flexShrink: 0 }}>
          {error && <div style={{ fontSize: 11.5, color: 'var(--danger)', marginBottom: 10, background: 'var(--danger-bg)', padding: '8px 12px', borderRadius: 8 }}>{error}</div>}
          <div style={{ display: 'flex', gap: 10 }}>
            <button onClick={inSub ? () => { setPage(null); setError(''); } : onClose} style={{
              flex: 1, padding: '10px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
              background: 'var(--ink-50)', border: '1px solid var(--ink-200)',
              borderRadius: 8, cursor: 'pointer', color: 'var(--ink-600)',
            }}>{inSub ? '‹ Voltar' : 'Fechar'}</button>
            {inSub && (
              <button onClick={handleSave} disabled={saving} style={{
                flex: 2, padding: '10px', fontSize: 13, fontWeight: 700, fontFamily: 'inherit',
                background: saving ? 'var(--ink-100)' : 'var(--brand-500)',
                border: 'none', borderRadius: 8,
                cursor: saving ? 'default' : 'pointer',
                color: saving ? 'var(--ink-400)' : '#fff',
                transition: 'background .15s',
              }}>{saving ? 'Salvando…' : 'Salvar'}</button>
            )}
          </div>
        </div>
      </div>
    </>
  );
}

// ─── Painel de Gestão de Usuários ────────────────────────────────────────────
// Sub-componentes visuais (definidos fora para evitar remount em cada render)
function UMCheckBox({ checked, indeterminate }) {
  return (
    <div style={{
      width: 15, height: 15, borderRadius: 4, flexShrink: 0,
      border: `2px solid ${checked || indeterminate ? 'var(--brand-500)' : 'var(--ink-300)'}`,
      background: checked ? 'var(--brand-500)' : indeterminate ? '#dbeafe' : 'var(--white)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all .1s',
    }}>
      {checked && (
        <svg width="8" height="6" viewBox="0 0 8 6" fill="none">
          <polyline points="1,3 3,5 7,1" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      )}
      {indeterminate && !checked && (
        <div style={{ width: 7, height: 2, background: 'var(--brand-500)', borderRadius: 1 }} />
      )}
    </div>
  );
}

function UMMenuBtn({ label, sublabel, commStatus, checked, onClick, full }) {
  return (
    <button
      onClick={onClick}
      style={{
        display: 'flex', alignItems: 'center', gap: 8, padding: '7px 10px',
        background: checked ? 'var(--brand-50)' : 'var(--white)',
        border: `1.5px solid ${checked ? 'var(--brand-300)' : 'var(--ink-200)'}`,
        borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
        width: full ? '100%' : undefined, transition: 'all .1s',
      }}
    >
      <UMCheckBox checked={checked} />
      <span style={{ display: 'flex', flexDirection: 'column', minWidth: 0, gap: 1 }}>
        <span style={{
          fontSize: 13, fontWeight: checked ? 600 : 400,
          color: checked ? 'var(--ink-900)' : 'var(--ink-700)',
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>{label}</span>
        {sublabel && (
          <span
            onClick={e => e.stopPropagation()}
            style={{
              fontSize: 10.5, color: 'var(--ink-400)', fontWeight: 400,
              overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              letterSpacing: '0.02em', userSelect: 'text', cursor: 'text',
            }}
          >{sublabel}</span>
        )}
        {commStatus !== undefined && (
          <span style={{
            fontSize: 10, fontWeight: 600,
            color: commStatus === null ? 'var(--ink-400)' : commStatus ? '#16a34a' : '#dc2626',
          }}>
            {commStatus === null ? '…' : commStatus ? '● comunicou hoje' : '● sem comunicação'}
          </span>
        )}
      </span>
    </button>
  );
}

function UserManagementPanel({ currentUser, stores, onClose }) {
  const [users,     setUsers]     = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [loadError, setLoadError] = React.useState(null);
  const [retryKey,  setRetryKey]  = React.useState(0);   // incrementar força reload
  const [editingUid, setEditingUid] = React.useState(null);
  const [editPages,   setEditPages]   = React.useState([]);
  const [editAllStores, setEditAllStores] = React.useState(true);
  const [editStoreIds,  setEditStoreIds]  = React.useState([]);
  const [saving,    setSaving]    = React.useState(false);
  const [saveError, setSaveError] = React.useState(null);

  const [deletingUid,    setDeletingUid]    = React.useState(null);
  const [deleteLoading,  setDeleteLoading]  = React.useState(false);
  const [deleteError,    setDeleteError]    = React.useState(null);

  // ── Status de comunicação de hoje por loja ────────────────────────────────
  const [storeComm, setStoreComm] = React.useState(null); // null = carregando

  React.useEffect(() => {
    if (!currentUser?.cnpj) return;
    let cancelled = false;
    (async () => {
      await SF.loadFiliaisMap(currentUser.cnpj);
      const today = SF.today();
      const doc = await SF.getStaticDocMerged(currentUser.cnpj, today).catch(() => null);
      if (cancelled) return;
      const communicated = new Set();
      if (doc?.stores) {
        for (const key of Object.keys(doc.stores)) {
          communicated.add(SF.parseStoreKey(key));
        }
      }
      setStoreComm(communicated);
    })();
    return () => { cancelled = true; };
  }, [currentUser?.cnpj]);

  // ── Criar usuário ──────────────────────────────────────────────────────────
  const [showCreate,   setShowCreate]   = React.useState(false);
  const [createName,   setCreateName]   = React.useState('');
  const [createEmail,  setCreateEmail]  = React.useState('');
  const [createPass,   setCreatePass]   = React.useState('');
  const [creating,     setCreating]     = React.useState(false);
  const [createError,  setCreateError]  = React.useState(null);

  const openCreate  = () => { setShowCreate(true); setCreateName(''); setCreateEmail(''); setCreatePass(''); setCreateError(null); };
  const closeCreate = () => { setShowCreate(false); setCreateError(null); };

  const handleCreate = async () => {
    if (!createName.trim())    return setCreateError('Informe o nome.');
    if (!createEmail.trim())   return setCreateError('Informe o e-mail.');
    if (createPass.length < 6) return setCreateError('A senha deve ter ao menos 6 caracteres.');
    setCreating(true);
    setCreateError(null);
    let secondaryApp = null;
    try {
      // Usa app secundária para não deslogar o admin atual
      secondaryApp = firebase.initializeApp(SF.db.app.options, 'sf-create-user');
      const secondaryAuth = firebase.auth(secondaryApp);

      // 1. Cria no Firebase Auth via app secundária
      const cred = await secondaryAuth.createUserWithEmailAndPassword(createEmail.trim(), createPass);
      await cred.user.updateProfile({ displayName: createName.trim() });
      const uid = cred.user.uid;

      // 2. Desloga da app secundária imediatamente (sessão principal intacta)
      await secondaryAuth.signOut();

      // 3. Registra na Cloud Function (POST + PUT — igual ao antigo)
      const payload = {
        uid,
        cnpj:          currentUser.cnpj,
        corporateName: currentUser.corporateName,
        status:        'confirmed',
        admin:         false,
      };
      await cfPost(payload);
      await cfPut(payload);

      closeCreate();
      setRetryKey(k => k + 1);
    } catch (err) {
      setCreateError(err.message || 'Erro ao criar usuário.');
    } finally {
      setCreating(false);
      if (secondaryApp) secondaryApp.delete().catch(() => {});
    }
  };

  // Carrega usuários da empresa ao abrir (re-executa quando retryKey muda)
  React.useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setLoadError(null);
    (async () => {
      try {
        const res = await cfGet();
        if (cancelled) return;
        // Aceita múltiplos formatos: { data:[...] }, { users:[...] }, ou array direto
        const rawList = Array.isArray(res?.data)  ? res.data
                      : Array.isArray(res?.users) ? res.users
                      : Array.isArray(res)        ? res
                      : [];
        if (rawList.length === 0 && res && !Array.isArray(res)) {
          // resposta não-vazia mas formato inesperado → loga para diagnóstico
          console.warn('[UserMgmt] resposta da CF em formato desconhecido:', res);
        }
        const mapped = rawList.map(e => ({
          uid:           e.uid           || '',
          name:          e.fullName || e.name || 'Usuário',
          email:         e.email         || '',
          cnpj:          e.cnpj          || '',
          admin:         !!e.admin,
          allowedPages:  Array.isArray(e.allowedPages)
                           ? e.allowedPages
                           : Array.from({ length: 8 }, (_, i) => i),
          allowedStores: e.allowedStores ?? true,
        }))
        .filter(u => u.uid && u.cnpj === currentUser.cnpj && u.uid !== currentUser.uid);
        setUsers(mapped);
      } catch (err) {
        console.error('[UserMgmt] erro ao carregar usuários:', err);
        if (cancelled) return;
        setLoadError(err?.message || 'Não foi possível carregar os usuários.');
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [retryKey]);

  const initials = (name) => {
    const parts = (name || '').trim().split(/\s+/);
    return ((parts[0]?.[0] ?? '') + (parts[1]?.[0] ?? '')).toUpperCase() || '?';
  };

  const startEdit = (u) => {
    if (editingUid === u.uid) { cancelEdit(); return; }
    setSaveError(null);
    setEditingUid(u.uid);
    setEditPages(Array.isArray(u.allowedPages) ? [...u.allowedPages] : Array.from({ length: 8 }, (_, i) => i));
    const allStores = !Array.isArray(u.allowedStores) || u.allowedStores === true;
    setEditAllStores(allStores);
    setEditStoreIds(Array.isArray(u.allowedStores) ? [...u.allowedStores] : []);
  };

  const cancelEdit = () => {
    setEditingUid(null);
    setEditPages([]);
    setEditStoreIds([]);
    setSaveError(null);
  };

  const togglePage = (idx) => {
    setEditPages(prev => prev.includes(idx)
      ? prev.filter(p => p !== idx)
      : [...prev, idx].sort((a, b) => a - b)
    );
  };

  const toggleStoreId = (id) => {
    setEditStoreIds(prev => prev.includes(id)
      ? prev.filter(s => s !== id)
      : [...prev, id].sort((a, b) => a - b)
    );
  };

  const saveEdit = async (uid) => {
    setSaving(true);
    setSaveError(null);
    try {
      const allowedStores = editAllStores ? true : [...editStoreIds].sort((a, b) => a - b);
      await cfPut({ uid, allowedPages: [...editPages].sort((a, b) => a - b) });
      await cfPut({ uid, allowedStores });
      setUsers(prev => prev.map(u => u.uid === uid
        ? { ...u, allowedPages: editPages, allowedStores }
        : u
      ));
      setEditingUid(null);
    } catch {
      setSaveError('Erro ao salvar. Verifique sua conexão e tente novamente.');
    } finally {
      setSaving(false);
    }
  };

  const handleDelete = async () => {
    setDeleteLoading(true);
    setDeleteError(null);
    try {
      await cfDelete({ uid: deletingUid });
      setUsers(prev => prev.filter(u => u.uid !== deletingUid));
      setDeletingUid(null);
    } catch {
      setDeleteError('Erro ao excluir. Verifique sua conexão e tente novamente.');
    } finally {
      setDeleteLoading(false);
    }
  };

  // Lojas disponíveis (filtra IDs inválidos, remove duplicatas)
  const storeList = React.useMemo(() => {
    const filiaisMap = SF.getFiliaisMap();
    const storeIdToCnpj = {};
    for (const [key, cnpj] of Object.entries(filiaisMap)) {
      const sid = SF.parseStoreKey(key);
      if (sid > 0) storeIdToCnpj[sid] = cnpj;
    }
    const seen = new Set();
    return (stores || [])
      .filter(s => s.storeId > 0 && !seen.has(s.storeId) && seen.add(s.storeId))
      .sort((a, b) => a.storeId - b.storeId)
      .map(s => ({ ...s, cnpj: storeIdToCnpj[s.storeId] ?? null }));
  }, [stores]);

  return (
    <>
      {/* ── Modal de confirmação de exclusão ── */}
      {deletingUid && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 70, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.45)', backdropFilter: 'blur(2px)' }}>
          <div style={{ background: 'var(--white)', borderRadius: 16, padding: 28, width: 360, boxShadow: 'var(--shadow-lg)' }}>
            <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 10 }}>Excluir usuário</div>
            <p style={{ fontSize: 13.5, color: 'var(--ink-600)', marginBottom: 20 }}>
              Você confirma a exclusão deste usuário? Esta ação não pode ser desfeita.
            </p>
            {deleteError && (
              <div style={{ fontSize: 12.5, color: 'var(--danger)', background: 'var(--danger-bg)', padding: '8px 12px', borderRadius: 8, marginBottom: 14 }}>
                {deleteError}
              </div>
            )}
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
              <button
                onClick={() => { setDeletingUid(null); setDeleteError(null); }}
                disabled={deleteLoading}
                style={{ padding: '8px 16px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: 'var(--ink-100)', color: 'var(--ink-700)', border: 'none', borderRadius: 8, cursor: 'pointer' }}
              >Cancelar</button>
              <button
                onClick={handleDelete}
                disabled={deleteLoading}
                style={{ padding: '8px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: 'var(--danger)', color: '#fff', border: 'none', borderRadius: 8, cursor: deleteLoading ? 'default' : 'pointer', opacity: deleteLoading ? 0.7 : 1, display: 'flex', alignItems: 'center', gap: 6 }}
              >
                {deleteLoading && <Spinner size={13} color="#fff" />}
                {deleteLoading ? 'Excluindo…' : 'Excluir'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Overlay backdrop */}
      <div
        onClick={onClose}
        style={{ position: 'fixed', inset: 0, zIndex: 60, background: 'rgba(0,0,0,0.32)', backdropFilter: 'blur(2px)' }}
      />

      {/* Slide-over panel */}
      <div style={{
        position: 'fixed', top: 0, right: 0, bottom: 0, width: 640,
        background: 'var(--white)', zIndex: 61,
        display: 'flex', flexDirection: 'column',
        boxShadow: '-4px 0 32px rgba(0,0,0,0.14)',
      }}>
        {/* ── Header ── */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '16px 24px', borderBottom: '1px solid var(--ink-200)', flexShrink: 0,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <Icon name="users" size={18} color="var(--brand-500)" />
            <span style={{ fontSize: 16, fontWeight: 700, color: 'var(--ink-900)' }}>
              Gestão de Usuários
            </span>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <button
              onClick={openCreate}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '7px 14px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                background: 'var(--brand-500)', color: '#fff',
                border: 'none', borderRadius: 8, cursor: 'pointer',
              }}
            >
              <Icon name="plus" size={14} color="#fff" />
              Novo usuário
            </button>
          <button
            onClick={onClose}
            style={{
              width: 32, height: 32, borderRadius: 8, border: '1px solid var(--ink-200)',
              background: 'none', cursor: 'pointer', display: 'flex',
              alignItems: 'center', justifyContent: 'center',
              color: 'var(--ink-600)', fontSize: 18, lineHeight: 1,
            }}
            title="Fechar"
          >×</button>
          </div>
        </div>

        {/* ── Modal criar usuário ── */}
        {showCreate && (
          <div style={{ position: 'absolute', inset: 0, zIndex: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(2px)' }}>
            <div style={{ background: 'var(--white)', borderRadius: 16, padding: 28, width: 380, boxShadow: 'var(--shadow-lg)' }}>
              <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 20 }}>Novo usuário</div>

              {[
                { label: 'Nome',  value: createName,  set: setCreateName,  type: 'text',     placeholder: 'Nome completo' },
                { label: 'Email', value: createEmail, set: setCreateEmail, type: 'email',    placeholder: 'email@exemplo.com' },
                { label: 'Senha', value: createPass,  set: setCreatePass,  type: 'password', placeholder: 'Mínimo 6 caracteres' },
              ].map(({ label, value, set, type, placeholder }) => (
                <div key={label} style={{ marginBottom: 14 }}>
                  <label style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-600)', display: 'block', marginBottom: 5 }}>{label}</label>
                  <input
                    type={type}
                    value={value}
                    onChange={e => set(e.target.value)}
                    placeholder={placeholder}
                    className="input"
                    style={{ fontSize: 14 }}
                    onKeyDown={e => e.key === 'Enter' && handleCreate()}
                  />
                </div>
              ))}

              {createError && (
                <div style={{ fontSize: 13, color: 'var(--danger)', background: 'var(--danger-bg)', padding: '8px 12px', borderRadius: 8, marginBottom: 14 }}>
                  {createError}
                </div>
              )}

              <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 4 }}>
                <button onClick={closeCreate} disabled={creating} style={{ padding: '8px 16px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: 'var(--ink-100)', color: 'var(--ink-700)', border: 'none', borderRadius: 8, cursor: 'pointer' }}>
                  Cancelar
                </button>
                <button onClick={handleCreate} disabled={creating} style={{ padding: '8px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: 'var(--brand-500)', color: '#fff', border: 'none', borderRadius: 8, cursor: creating ? 'not-allowed' : 'pointer', opacity: creating ? 0.7 : 1 }}>
                  {creating ? 'Criando…' : 'Criar usuário'}
                </button>
              </div>
            </div>
          </div>
        )}

        {/* ── Content ── */}
        <div style={{ flex: 1, overflowY: 'auto' }}>
          {loading && (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10, padding: 56, color: 'var(--ink-500)', fontSize: 14 }}>
              <Spinner size={18} color="var(--brand-500)" /> Carregando usuários…
            </div>
          )}

          {!loading && loadError && (
            <div style={{ padding: 40, textAlign: 'center', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14 }}>
              <div style={{ width: 40, height: 40, borderRadius: '50%', background: 'var(--danger-bg)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--danger)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
                </svg>
              </div>
              <div style={{ color: 'var(--ink-800)', fontSize: 14, fontWeight: 600 }}>
                Erro ao carregar usuários
              </div>
              <div style={{
                color: 'var(--danger)', fontSize: 12, fontWeight: 500,
                background: 'var(--danger-bg)', padding: '8px 14px', borderRadius: 8,
                maxWidth: 380, wordBreak: 'break-word', fontFamily: 'monospace',
              }}>{loadError}</div>
              <button
                onClick={() => setRetryKey(k => k + 1)}
                style={{
                  padding: '8px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                  background: 'var(--brand-500)', color: '#fff', border: 0,
                  borderRadius: 8, cursor: 'pointer',
                }}
              >Tentar novamente</button>
            </div>
          )}

          {!loading && !loadError && (
            <>
              <div style={{ padding: '10px 24px 8px', borderBottom: '1px solid var(--ink-100)' }}>
                <span style={{ fontSize: 12, color: 'var(--ink-500)', fontWeight: 500 }}>
                  {users.length} {users.length === 1 ? 'usuário' : 'usuários'}
                </span>
              </div>

              {users.length === 0 && (
                <div style={{ padding: 40, textAlign: 'center', color: 'var(--ink-400)', fontSize: 14 }}>
                  Nenhum usuário encontrado.
                </div>
              )}

              {users.map(u => {
                const isEditing  = editingUid === u.uid;
                const pageNames  = (u.allowedPages ?? []).map(i => PAGE_LIST[i]?.name).filter(Boolean);
                const allPages   = pageNames.length >= PAGE_LIST.length;
                const storesAll  = !Array.isArray(u.allowedStores) || u.allowedStores === true;

                return (
                  <div key={u.uid} style={{ borderBottom: '1px solid var(--ink-100)' }}>
                    {/* ── User row ── */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 24px' }}>
                      {/* Avatar */}
                      <div style={{
                        width: 40, height: 40, borderRadius: '50%', flexShrink: 0,
                        background: u.admin ? 'var(--brand-500)' : 'var(--ink-200)',
                        color: u.admin ? '#fff' : 'var(--ink-700)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 13, fontWeight: 700,
                      }}>
                        {initials(u.name)}
                      </div>

                      {/* Info */}
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                          <span style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 260 }}>
                            {u.name}
                          </span>
                          {u.admin && (
                            <span style={{
                              fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em',
                              color: 'var(--brand-700)', background: 'var(--brand-50)',
                              padding: '2px 7px', borderRadius: 4, border: '1px solid var(--brand-200)',
                            }}>Admin</span>
                          )}
                        </div>
                        <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                          {u.email}
                        </div>
                        <div style={{ fontSize: 11.5, color: 'var(--ink-400)', marginTop: 3 }}>
                          {allPages ? 'Todos os módulos' : pageNames.length === 0 ? 'Nenhum módulo' : pageNames.join(' · ')}
                          {' · '}
                          {storesAll ? 'Todas as filiais' : `${(u.allowedStores ?? []).length} filiai${(u.allowedStores ?? []).length === 1 ? '' : 's'}`}
                        </div>
                      </div>

                      {/* Botão Editar */}
                      <button
                        onClick={() => startEdit(u)}
                        style={{
                          padding: '6px 14px', fontSize: 12.5, fontWeight: 600,
                          border: `1.5px solid ${isEditing ? 'var(--brand-500)' : 'var(--ink-200)'}`,
                          borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit',
                          background: isEditing ? 'var(--brand-50)' : 'var(--white)',
                          color: isEditing ? 'var(--brand-700)' : 'var(--ink-700)',
                          flexShrink: 0,
                        }}
                      >
                        {isEditing ? 'Fechar' : 'Editar'}
                      </button>

                      {/* Botão Excluir */}
                      {!u.admin && (
                        <button
                          onClick={() => { setDeletingUid(u.uid); setDeleteError(null); }}
                          title="Excluir usuário"
                          style={{
                            width: 32, height: 32, borderRadius: 8, flexShrink: 0,
                            border: '1.5px solid var(--ink-200)', background: 'var(--white)',
                            color: 'var(--danger)', cursor: 'pointer',
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                          }}
                          onMouseEnter={e => { e.currentTarget.style.background = 'var(--danger-bg)'; e.currentTarget.style.borderColor = 'var(--danger)'; }}
                          onMouseLeave={e => { e.currentTarget.style.background = 'var(--white)'; e.currentTarget.style.borderColor = 'var(--ink-200)'; }}
                        >×</button>
                      )}
                    </div>

                    {/* ── Formulário de edição (expandido) ── */}
                    {isEditing && (
                      <div style={{
                        padding: '4px 24px 20px',
                        borderTop: '1px solid var(--ink-100)',
                        background: 'var(--ink-50)',
                      }}>
                        {/* Módulos */}
                        <div style={{ paddingTop: 16, marginBottom: 16 }}>
                          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.09em', marginBottom: 10 }}>
                            Módulos
                          </div>
                          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
                            {PAGE_LIST.map(p => (
                              <UMMenuBtn
                                key={p.idx}
                                label={p.name}
                                checked={editPages.includes(p.idx)}
                                onClick={() => togglePage(p.idx)}
                              />
                            ))}
                          </div>
                        </div>

                        {/* Filiais (só mostra se há lojas disponíveis) */}
                        {storeList.length > 0 && (
                          <div style={{ marginBottom: 16 }}>
                            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.09em', marginBottom: 10 }}>
                              Filiais
                            </div>
                            <UMMenuBtn
                              label="Todas as filiais"
                              checked={editAllStores}
                              onClick={() => setEditAllStores(a => !a)}
                              full
                            />
                            {!editAllStores && (
                              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginTop: 6 }}>
                                {storeList.map(s => {
                                  const comm = storeComm === null ? null : storeComm.has(s.storeId);
                                  return (
                                    <UMMenuBtn
                                      key={s.storeId}
                                      label={s.storeName || `Loja ${s.storeId}`}
                                      sublabel={s.cnpj ? s.cnpj : `ID ${s.storeId}`}
                                      commStatus={comm}
                                      checked={editStoreIds.includes(s.storeId)}
                                      onClick={() => toggleStoreId(s.storeId)}
                                    />
                                  );
                                })}
                              </div>
                            )}
                          </div>
                        )}

                        {/* Erro ao salvar */}
                        {saveError && (
                          <div style={{ fontSize: 12.5, color: 'var(--danger)', marginBottom: 10, fontWeight: 500 }}>
                            {saveError}
                          </div>
                        )}

                        {/* Ações */}
                        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
                          <button
                            onClick={cancelEdit}
                            style={{
                              padding: '8px 16px', fontSize: 13, fontWeight: 500, fontFamily: 'inherit',
                              border: '1.5px solid var(--ink-200)', borderRadius: 8, cursor: 'pointer',
                              background: 'var(--white)', color: 'var(--ink-700)',
                            }}
                          >Cancelar</button>
                          <button
                            onClick={() => saveEdit(u.uid)}
                            disabled={saving}
                            style={{
                              padding: '8px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                              border: 0, borderRadius: 8, cursor: saving ? 'default' : 'pointer',
                              background: 'var(--brand-500)', color: '#fff',
                              opacity: saving ? 0.7 : 1,
                              display: 'flex', alignItems: 'center', gap: 6,
                            }}
                          >
                            {saving && <Spinner size={13} color="#fff" />}
                            {saving ? 'Salvando…' : 'Salvar'}
                          </button>
                        </div>
                      </div>
                    )}
                  </div>
                );
              })}
            </>
          )}
        </div>
      </div>
    </>
  );
}

function PeriodSelector({ period, setPeriod, customStart, customEnd, setCustomRange, activeRoute }) {
  const [pickerOpen, setPickerOpen] = React.useState(false);

  const isComp = activeRoute === 'comparative';
  const maxDays = isComp ? 30 : null;

  const options = [
    { v: 'hoje', l: 'Hoje' },
    { v: '7d',   l: '7d'   },
    { v: '30d',  l: '30d'  },
    { v: 'mtd',  l: MONTH_NAMES_FULL_PT[new Date().getMonth()] },
    ...(!isComp ? [{ v: 'ytd', l: 'Ano' }] : []),
  ];

  const fmtShort = (iso) => { if (!iso) return ''; const [, m, d] = iso.split('-'); return `${d}/${m}`; };
  const customActive  = period === 'custom';
  const customBtnLabel = customActive && customStart && customEnd
    ? `${fmtShort(customStart)} – ${fmtShort(customEnd)}`
    : 'Personalizado';

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      {/* Botões de período pré-definido */}
      <div style={{ display: 'flex', background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
        {options.map(o => (
          <button key={o.v} onClick={() => setPeriod(o.v)} style={{
            padding: '6px 12px', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
            border: 0, borderRadius: 6, cursor: 'pointer',
            background: period === o.v ? 'var(--white)' : 'transparent',
            color:      period === o.v ? 'var(--ink-900)' : 'var(--ink-600)',
            boxShadow:  period === o.v ? 'var(--shadow-sm)' : 'none',
            transition: 'all .12s',
          }}>{o.l}</button>
        ))}
      </div>

      {/* Botão período personalizado */}
      <div style={{ position: 'relative' }}>
        <button
          onClick={() => setPickerOpen(o => !o)}
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 5,
            padding: '6px 10px', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
            border: `1.5px solid ${customActive ? 'var(--brand-500)' : 'var(--ink-200)'}`,
            borderRadius: 8, cursor: 'pointer',
            background: customActive ? 'var(--brand-50)' : 'var(--white)',
            color:      customActive ? 'var(--brand-700)' : 'var(--ink-600)',
            boxShadow: 'var(--shadow-sm)',
            whiteSpace: 'nowrap',
          }}
        >
          {/* Ícone calendário */}
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
          </svg>
          {customBtnLabel}
        </button>

        {pickerOpen && (
          <CustomRangePicker
            start={customStart ?? SF.subtractDays(6)}
            end={customEnd   ?? SF.today()}
            onApply={(s, e) => { setCustomRange(s, e); setPickerOpen(false); }}
            onClose={() => setPickerOpen(false)}
            maxDays={maxDays}
            allowFuture={activeRoute === 'bills'}
          />
        )}
      </div>
    </div>
  );
}

// ─── Seletor de período personalizado ────────────────────────────────────────
function CustomRangePicker({ start, end, onApply, onClose, maxDays = null, allowFuture = false, posStyle = null }) {
  const [s, setS] = React.useState(start ?? '');
  const [e, setE] = React.useState(end   ?? '');
  const today = SF.today();
  // Ícone nativo do <input type="date"> segue o color-scheme: forçar 'light' sempre
  // deixa o ícone escuro invisível sobre o fundo escuro do tema dark.
  const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';

  const baseValid = s && e && s <= e && (allowFuture || e <= today);
  const diffDays  = baseValid ? SF.daysBetween(s, e) + 1 : 0;
  const overLimit = maxDays !== null && diffDays > maxDays;
  const valid     = baseValid && !overLimit;

  // Quando "De" muda, clampeia "Até" ao máximo permitido
  const handleStart = (v) => {
    setS(v);
    if (e && v > e) setE(v);
    if (maxDays !== null && e) {
      const maxEnd = SF.addDays ? SF.addDays(v, maxDays - 1) : (() => {
        const d = new Date(v); d.setDate(d.getDate() + maxDays - 1);
        return d.toISOString().slice(0, 10);
      })();
      if (e > maxEnd) setE(maxEnd);
    }
  };

  const maxEnd = (maxDays !== null && s) ? (() => {
    const d = new Date(s); d.setDate(d.getDate() + maxDays - 1);
    const cap = d.toISOString().slice(0, 10);
    return (!allowFuture && cap > today) ? today : cap;
  })() : (allowFuture ? undefined : today);

  return (
    <>
      {/* Backdrop */}
      <div style={{ position: 'fixed', inset: 0, zIndex: posStyle ? 249 : 29 }} onClick={onClose} />

      {/* Painel */}
      <div style={{
        ...(posStyle ?? { position: 'absolute', top: 'calc(100% + 6px)', right: 0 }),
        width: 272, background: 'var(--white)',
        border: '1px solid var(--ink-200)', borderRadius: 12,
        boxShadow: 'var(--shadow-md)', zIndex: posStyle ? 250 : 30, padding: '16px 16px 14px',
      }}>
        <div style={{ fontSize: 13.5, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 14 }}>
          Período personalizado
          {maxDays !== null && (
            <span style={{ marginLeft: 8, fontSize: 11, fontWeight: 600, color: 'var(--ink-400)' }}>
              (máx. {maxDays} dias)
            </span>
          )}
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 5 }}>De</div>
            <input
              type="date" value={s} max={allowFuture ? undefined : today}
              onChange={ev => handleStart(ev.target.value)}
              style={{ width: '100%', padding: '8px 10px', fontSize: 13.5, fontFamily: 'inherit', border: '1px solid var(--ink-200)', borderRadius: 8, outline: 'none', boxSizing: 'border-box', color: 'var(--ink-900)', background: 'var(--ink-50)', colorScheme: isDarkTheme ? 'dark' : 'light' }}
            />
          </div>
          <div>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 5 }}>Até</div>
            <input
              type="date" value={e} min={s || undefined} max={maxEnd}
              onChange={ev => setE(ev.target.value)}
              style={{ width: '100%', padding: '8px 10px', fontSize: 13.5, fontFamily: 'inherit', border: `1px solid ${overLimit ? 'var(--danger)' : 'var(--ink-200)'}`, borderRadius: 8, outline: 'none', boxSizing: 'border-box', color: overLimit ? 'var(--danger)' : 'var(--ink-900)', background: 'var(--ink-50)', colorScheme: isDarkTheme ? 'dark' : 'light' }}
            />
          </div>
        </div>

        {/* Resumo / aviso */}
        <div style={{ minHeight: 22, marginTop: 10, fontSize: 12, color: overLimit ? 'var(--danger)' : 'var(--ink-500)' }}>
          {overLimit
            ? `Máximo ${maxDays} dias — selecione até ${maxEnd}`
            : baseValid
            ? `${diffDays} ${diffDays === 1 ? 'dia' : 'dias'} selecionado${diffDays === 1 ? '' : 's'}`
            : ''}
        </div>

        <div style={{ display: 'flex', gap: 8, marginTop: 6 }}>
          <button onClick={onClose} style={{ flex: 1, padding: '8px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: 'var(--ink-50)', border: '1px solid var(--ink-200)', borderRadius: 8, cursor: 'pointer', color: 'var(--ink-600)' }}>
            Cancelar
          </button>
          <button
            onClick={() => valid && onApply(s, e)}
            disabled={!valid}
            style={{ flex: 1, padding: '8px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit', background: valid ? 'var(--brand-500)' : 'var(--ink-100)', border: 'none', borderRadius: 8, cursor: valid ? 'pointer' : 'not-allowed', color: valid ? '#fff' : 'var(--ink-400)', transition: 'background .12s' }}
          >
            Aplicar
          </button>
        </div>
      </div>
    </>
  );
}

// ─── Painel de Filtros ────────────────────────────────────────────────────────
function FilterPanel({ dim, setDim, selected, setSelected, onClear, onClose, options, activeRoute, anchorTop, anchorRight, billsParams, onBillsParamsChange }) {
  const isBills       = activeRoute === 'bills';
  const selSet        = new Set(selected);
  const hasGroups     = !isBills && (options?.groups?.length         ?? 0) > 0;
  const hasClasses    = !isBills && (options?.classes?.length        ?? 0) > 0;
  const hasMfg        = (activeRoute === 'purchases' || isBills) && (options?.manufacturers?.length ?? 0) > 0;
  const hasParams     = isBills;
  const [search, setSearch] = React.useState('');
  React.useEffect(() => setSearch(''), [dim]);
  React.useEffect(() => {
    if (dim === 'grupos' && !hasGroups && hasClasses) setDim('classes');
  }, [dim, hasGroups, hasClasses]);

  const alpha = (arr) => [...arr].sort((a, b) => a.name.localeCompare(b.name, 'pt-BR'));
  const applySearch = (arr) => !search.trim() ? arr : arr.filter(m => m.name.toLowerCase().includes(search.trim().toLowerCase()));
  const items = dim === 'grupos'       ? applySearch(alpha(options?.groups        ?? []))
              : dim === 'classes'      ? applySearch(alpha(options?.classes       ?? []))
              : dim === 'fornecedores' ? applySearch(alpha(options?.manufacturers ?? []))
              : [];

  const toggle = (name) =>
    setSelected(sel => sel.includes(name) ? sel.filter(s => s !== name) : [...sel, name]);

  const tabs = [
    hasGroups  && ['grupos',       'Grupos'],
    hasClasses && ['classes',      'Classes'],
    hasMfg     && ['fornecedores', 'Fornecedores'],
    hasParams  && ['parametros',   'Parâmetros'],
  ].filter(Boolean);

  const isActiveNum = (v) => { const n = parseFloat(v); return !isNaN(n) && n !== 0; };
  const billsParamsActive = billsParams && (
    !billsParams.statusPago || !billsParams.statusCancelado || !billsParams.statusPendente ||
    isActiveNum(billsParams.minBilling) || isActiveNum(billsParams.maxBilling) ||
    isActiveNum(billsParams.minPaid)    || isActiveNum(billsParams.maxPaid)     ||
    billsParams.dateMode !== 'vencimento'
  );

  const clearBillsParams = () => onBillsParamsChange?.({
    statusPago: true, statusCancelado: false, statusPendente: true,
    minBilling: '', maxBilling: '', minPaid: '', maxPaid: '',
    dateMode: 'vencimento',
  });

  const inputStyle = {
    flex: 1, minWidth: 0, padding: '6px 8px', borderRadius: 6,
    border: '1px solid var(--ink-200)', fontSize: 12,
    fontFamily: 'inherit', background: 'var(--white)', outline: 'none',
    color: 'var(--ink-900)', boxSizing: 'border-box', width: 0,
  };

  return (
    <>
      <div style={{ position: 'fixed', inset: 0, zIndex: 19 }} onClick={onClose} />

      <div style={{
        position: 'fixed', top: anchorTop ?? 60, right: anchorRight ?? 0,
        width: 300, background: 'var(--white)',
        border: '1px solid var(--ink-200)', borderRadius: 12,
        boxShadow: 'var(--shadow-md)', zIndex: 20,
      }}>
        {/* Cabeçalho */}
        <div style={{ padding: '14px 16px 12px', borderBottom: '1px solid var(--ink-100)' }}>
          <div style={{ fontSize: 13.5, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 10 }}>
            Filtrar por
          </div>
          {/* Abas */}
          {tabs.length > 0 && (
            <div style={{ display: 'flex', background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
              {tabs.map(([v, l]) => (
                <button key={v} onClick={() => { setDim(v); setSearch(''); }} style={{
                  flex: 1, padding: '5px 4px', fontSize: 11.5, fontWeight: 600, fontFamily: 'inherit',
                  border: 0, borderRadius: 6, cursor: 'pointer',
                  background: dim === v ? 'var(--white)' : 'transparent',
                  color:      dim === v ? 'var(--ink-900)' : 'var(--ink-500)',
                  boxShadow:  dim === v ? 'var(--shadow-sm)' : 'none',
                  transition: 'all .12s',
                }}>{l}</button>
              ))}
            </div>
          )}

          {/* Campo de busca — só para lista */}
          {dim !== 'parametros' && (
            <div style={{ position: 'relative', marginTop: 8 }}>
              <div style={{ position: 'absolute', left: 8, top: '50%', transform: 'translateY(-50%)', color: 'var(--ink-400)' }}>
                <Icon name="search" size={13} />
              </div>
              <input value={search} onChange={e => setSearch(e.target.value)}
                placeholder={`Buscar ${dim === 'fornecedores' ? 'fornecedor' : dim === 'classes' ? 'classe' : 'grupo'}...`}
                style={{ width: '100%', padding: '6px 8px 6px 28px', fontSize: 12.5, fontFamily: 'inherit', background: 'var(--ink-50)', border: '1px solid var(--ink-200)', borderRadius: 6, outline: 'none', color: 'var(--ink-900)', boxSizing: 'border-box' }}
              />
            </div>
          )}
        </div>

        {/* Aba Parâmetros (bills) */}
        {dim === 'parametros' && billsParams && onBillsParamsChange && (
          <div style={{ padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 16 }}>
            {/* Status */}
            <div>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Status</div>
                {(() => {
                  const count = [billsParams.statusPago, billsParams.statusCancelado, billsParams.statusPendente].filter(Boolean).length;
                  return count === 0
                    ? <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--danger)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>Sem contas para mostrar</span>
                    : <span style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)' }}>mostrando {count}</span>;
                })()}
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                {[
                  { key: 'statusPago',      label: 'Pago',      color: 'var(--positive)', bg: 'var(--positive-bg)' },
                  { key: 'statusCancelado', label: 'Cancelado', color: 'var(--danger)',   bg: 'var(--danger-bg)' },
                  { key: 'statusPendente',  label: 'Pendente',  color: 'var(--warning)',  bg: 'var(--warning-bg)' },
                ].map(({ key, label, color, bg }) => (
                  <button key={key}
                    onClick={() => onBillsParamsChange({ ...billsParams, [key]: !billsParams[key] })}
                    style={{
                      flex: 1, padding: '6px 4px', borderRadius: 20,
                      border: `1px solid ${billsParams[key] ? color + '55' : 'var(--ink-300)'}`,
                      background: billsParams[key] ? bg : 'var(--ink-100)',
                      color: billsParams[key] ? color : 'var(--ink-500)',
                      fontSize: 12, fontWeight: 600, cursor: 'pointer', transition: 'all .15s',
                      fontFamily: 'inherit',
                    }}
                  >{label}</button>
                ))}
              </div>
            </div>

            {/* Valor de pagamento */}
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Valor de pagamento</div>
              <div style={{ display: 'flex', gap: 6, alignItems: 'center', overflow: 'hidden' }}>
                <input type="number" placeholder="Mín" value={billsParams.minBilling}
                  onChange={e => onBillsParamsChange({ ...billsParams, minBilling: e.target.value })}
                  style={inputStyle} />
                <span style={{ color: 'var(--ink-300)', fontSize: 12, flexShrink: 0 }}>—</span>
                <input type="number" placeholder="Máx" value={billsParams.maxBilling}
                  onChange={e => onBillsParamsChange({ ...billsParams, maxBilling: e.target.value })}
                  style={inputStyle} />
              </div>
            </div>

            {/* Valor pago */}
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Valor pago</div>
              <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
                <input type="number" placeholder="Mín" value={billsParams.minPaid}
                  onChange={e => onBillsParamsChange({ ...billsParams, minPaid: e.target.value })}
                  style={inputStyle} />
                <span style={{ color: 'var(--ink-300)', fontSize: 12, flexShrink: 0 }}>—</span>
                <input type="number" placeholder="Máx" value={billsParams.maxPaid}
                  onChange={e => onBillsParamsChange({ ...billsParams, maxPaid: e.target.value })}
                  style={inputStyle} />
              </div>
            </div>

            {/* Filtrar período por */}
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Filtrar período por</div>
              <div style={{ display: 'flex', gap: 8 }}>
                {[
                  // Rótulo segue o ERP: DT_LANCAMENTO (key lancamento) é "Emissão" no
                  // sistema original. "Entrada" (DT_NOTA) foi removida: não bate com o
                  // filtro "Entrada" do Farmax — o ERP usa um campo que o coletor não exporta.
                  { key: 'vencimento', label: 'Vencimento' },
                  { key: 'lancamento', label: 'Emissão' },
                ].map(({ key, label }) => {
                  const active = (billsParams.dateMode ?? 'vencimento') === key;
                  return (
                    <button key={key}
                      onClick={() => onBillsParamsChange({ ...billsParams, dateMode: key })}
                      style={{
                        flex: 1, padding: '6px 4px', borderRadius: 20,
                        border: `1px solid ${active ? 'var(--brand-500)55' : 'var(--ink-300)'}`,
                        background: active ? 'var(--brand-50)' : 'var(--ink-100)',
                        color: active ? 'var(--brand-500)' : 'var(--ink-500)',
                        fontSize: 12, fontWeight: 600, cursor: 'pointer', transition: 'all .15s',
                        fontFamily: 'inherit',
                      }}
                    >{label}</button>
                  );
                })}
              </div>
            </div>

            {billsParamsActive && (
              <button onClick={clearBillsParams} style={{
                alignSelf: 'flex-start', background: 'none', border: 'none',
                color: 'var(--brand-500)', fontSize: 12, fontWeight: 600, cursor: 'pointer', padding: 0,
                fontFamily: 'inherit',
              }}>Limpar parâmetros</button>
            )}
          </div>
        )}

        {/* Lista com checkboxes (grupos / classes / fornecedores) */}
        {dim !== 'parametros' && (
          <div style={{ maxHeight: 320, overflowY: 'auto', overscrollBehavior: 'contain', borderRadius: '0 0 12px 12px' }}>
            {!items.length
              ? <div style={{ padding: '24px 16px', textAlign: 'center', fontSize: 13, color: 'var(--ink-400)' }}>
                  Sem dados disponíveis
                </div>
              : items.map(item => {
                  const checked = selSet.has(item.name);
                  return (
                    <button key={item.name} onClick={() => toggle(item.name)}
                      style={{
                        display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                        padding: '9px 16px',
                        background: checked ? 'var(--brand-50)' : 'transparent',
                        border: 0, cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
                        borderBottom: '1px solid var(--ink-50)', transition: 'background .1s',
                      }}
                      onMouseEnter={e => { if (!checked) e.currentTarget.style.background = 'var(--ink-50)'; }}
                      onMouseLeave={e => { if (!checked) e.currentTarget.style.background = 'transparent'; }}
                    >
                      <div style={{
                        width: 16, height: 16, borderRadius: 4, flexShrink: 0,
                        border: `2px solid ${checked ? 'var(--brand-500)' : 'var(--ink-300)'}`,
                        background: checked ? 'var(--brand-500)' : 'transparent',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        transition: 'all .12s',
                      }}>
                        {checked && (
                          <svg width="10" height="7" viewBox="0 0 10 7" fill="none">
                            <path d="M1 3.5L3.8 6.3L9 1" stroke="white" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
                          </svg>
                        )}
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{
                          fontSize: 13, fontWeight: checked ? 600 : 500,
                          color: checked ? 'var(--brand-800, var(--brand-700))' : 'var(--ink-800)',
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        }}>{item.name}</div>
                      </div>
                      <span className="mono" style={{ fontSize: 11.5, color: 'var(--ink-500)', flexShrink: 0 }}>
                        {BRL(item.total)}
                      </span>
                    </button>
                  );
                })
            }
          </div>
        )}

        {/* Rodapé */}
        {selected.length > 0 && dim !== 'parametros' && (
          <div style={{ padding: '10px 12px', borderTop: '1px solid var(--ink-100)' }}>
            <button onClick={() => { onClear(); onClose(); }} style={{
              width: '100%', padding: '8px', fontSize: 13, fontWeight: 600,
              background: 'var(--danger-bg)', color: 'var(--danger)',
              border: '1px solid rgba(255,59,48,0.25)', borderRadius: 8,
              cursor: 'pointer', fontFamily: 'inherit',
            }}>
              Limpar {selected.length} {selected.length === 1 ? 'filtro' : 'filtros'}
            </button>
          </div>
        )}
      </div>
    </>
  );
}

// ─── Header da seção ─────────────────────────────────────────────────────────
function DashHeader({ activeRoute, period, customLabel, loading, error }) {
  const routeLabels = {
    overview: 'Visão geral', sales: 'Vendas', purchases: 'Compras',
    goals: 'Metas', stock: 'Estoque', comparative: 'Comparativo',
    deliveries: 'Entregas', bills: 'Contas a pagar',
  };
  const periodLabels = {
    hoje: 'Hoje', '7d': 'Últimos 7 dias', '30d': 'Últimos 30 dias',
    mtd: 'Mês atual', ytd: 'Ano atual',
    custom: customLabel || 'Período personalizado',
  };
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 16 }}>
      <div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, color: 'var(--ink-500)', fontSize: 13, fontWeight: 500 }}>
          <span>Painel</span>
          <Icon name="chevron-right" size={12} />
          <span style={{ color: 'var(--ink-900)', fontWeight: 600 }}>{routeLabels[activeRoute] || activeRoute}</span>
        </div>
        <h1 style={{ fontSize: 28, fontWeight: 700, letterSpacing: '-0.02em', margin: '6px 0 4px' }}>
          {routeLabels[activeRoute]} — {periodLabels[period] || ''}
        </h1>
        <div style={{ fontSize: 13, color: 'var(--ink-500)', display: 'flex', alignItems: 'center', gap: 6 }}>
          {loading
            ? <><Spinner size={12} color="var(--ink-400)" /> Sincronizando…</>
            : error
            ? <><span style={{ color: 'var(--danger)' }}>⚠ Erro ao carregar dados</span></>
            : <><span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--positive)' }}/> Sincronização em tempo real</>
          }
        </div>
      </div>

    </div>
  );
}

// ─── KPI Row ──────────────────────────────────────────────────────────────────
function KpiRow({ kpis, loading, syncAt }) {
  const placeholders = [
    { label: 'Vendas totais',  value: '—', delta: 0, spark: [0], primary: true },
    { label: 'Ticket médio',   value: '—', delta: 0, spark: [0] },
    { label: 'Itens vendidos', value: '—', delta: 0, spark: [0] },
    { label: 'Margem bruta',   value: '—', delta: 0, spark: [0] },
  ];
  const items = (!loading && kpis?.length) ? kpis : placeholders;
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
      {items.map((k, i) => <KpiCard key={i} {...k} loading={loading} syncAt={syncAt} />)}
    </div>
  );
}

function KpiCard({ label, value, rawValue, fmt, delta, spark, primary, negative, loading, syncAt, noFlash }) {
  const [flash,     setFlash]     = React.useState(false);
  const [flashDiff, setFlashDiff] = React.useState(null);
  const prevVal  = React.useRef(null);
  const prevRaw  = React.useRef(null);
  const prevSync = React.useRef(null);

  React.useEffect(() => {
    if (noFlash) return;
    if (loading) {
      prevVal.current  = null;
      prevRaw.current  = null;
      prevSync.current = null;
      setFlash(false);
      setFlashDiff(null);
      return;
    }
    if (!syncAt) return;
    if (syncAt !== prevSync.current && value !== prevVal.current && prevVal.current !== null) {
      if (rawValue != null && prevRaw.current != null) {
        const diff = rawValue - prevRaw.current;
        const sign = diff >= 0 ? '+' : '';
        const diffStr = fmt === 'brl' ? sign + BRL(diff)
                      : fmt === 'pct' ? sign + diff.toFixed(1) + '%'
                      : sign + NUM(Math.round(diff));
        setFlashDiff(diffStr);
      }
      setFlash(true);
      const t = setTimeout(() => { setFlash(false); setFlashDiff(null); }, 2200);
      return () => clearTimeout(t);
    }
    prevVal.current  = value;
    prevRaw.current  = rawValue;
    prevSync.current = syncAt;
  }, [value, syncAt, loading, noFlash]);

  return (
    <div className="card" style={{
      padding: 20,
      background: primary
        ? 'linear-gradient(160deg, #1a4789 0%, #0c2340 100%)'
        : 'var(--white)',
      color:  primary ? '#ffffff' : 'var(--ink-900)',
      border: primary ? '1px solid #1e5099' : '1px solid var(--ink-200)',
      position: 'relative', overflow: 'hidden',
    }}>
      <div style={{
        fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase',
        color: primary ? 'rgba(255,255,255,0.7)' : 'var(--ink-500)',
      }}>{label}</div>

      {loading
        ? <div style={{ height: 36, marginTop: 8, borderRadius: 6, background: primary ? 'rgba(255,255,255,0.15)' : 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
        : <div className="mono" style={{
            fontSize: value && value.length > 14 ? 22 : 28, fontWeight: 700, marginTop: 8, letterSpacing: '-0.015em',
            color: flash ? (primary ? '#4ADE80' : '#16C784') : (primary ? '#ffffff' : 'var(--ink-900)'),
            transition: flash ? 'color 0.15s ease-in' : 'color 0.9s ease-out',
            whiteSpace: 'nowrap', overflow: 'hidden',
          }}>{value}</div>
      }

      <style>{`@keyframes sf-pulse { 0%,100%{opacity:1} 50%{opacity:.5} }`}</style>

      <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        {flash && flashDiff
          ? <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 12, fontWeight: 700, fontFeatureSettings: '"tnum"', color: primary ? '#4ADE80' : '#16C784' }}>
              <Icon name={flashDiff.startsWith('+') ? 'arrow-up' : 'arrow-down'} size={11} strokeWidth={2.5} />
              {flashDiff}
            </div>
          : delta !== 0
            ? primary
              ? <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 12, fontWeight: 600, color: 'rgba(255,255,255,0.9)', fontFeatureSettings: '"tnum"' }}>
                  <Icon name={delta >= 0 ? 'arrow-up' : 'arrow-down'} size={11} strokeWidth={2.5} />
                  {Math.abs(delta).toFixed(1)}% da meta
                </div>
              : <Delta value={delta} />
            : null
        }
        {!loading && spark?.length > 1 && (
          <Sparkline data={spark} color={primary ? 'rgba(255,255,255,0.7)' : (negative ? 'var(--danger)' : 'var(--brand-500)')} />
        )}
      </div>
    </div>
  );
}

function Sparkline({ data, color }) {
  const w = 80, h = 28;
  if (!data || data.length < 2) return null;
  const max = Math.max(...data), min = Math.min(...data);
  const step = (data.length - 1) > 0 ? w / (data.length - 1) : w;
  const range = max - min || 1;
  const pts = data.map((v, i) => [i * step, h - ((v - min) / range) * (h - 4) - 2]);
  const path = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(' ');
  return (
    <svg width={w} height={h} style={{ overflow: 'visible' }}>
      <path d={path} fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round" />
      <circle cx={pts.at(-1)[0]} cy={pts.at(-1)[1]} r="2.5" fill={color} />
    </svg>
  );
}

// ─── Gráfico de vendas ────────────────────────────────────────────────────────
function IndicadoresConsolidados({ totals }) {
  if (!totals) return null;
  const { total, totalCost, totalSalesCount, quantity, pbmTotal, avgTicket } = totals;
  if (!total) return null;

  const itemsPerSale = totalSalesCount > 0 ? quantity / totalSalesCount : 0;
  const avgItem      = quantity > 0 ? total / quantity : 0;
  const cmv          = total > 0 ? (totalCost / total) * 100 : 0;
  const markup       = totalCost > 0 ? ((total - totalCost) / totalCost) * 100 : 0;

  const metrics = [
    { label: 'Vendas',           value: BRL(total) },
    { label: 'Valor Custo',      value: BRL(totalCost) },
    { label: 'Nº Vendas',        value: NUM(totalSalesCount) },
    { label: 'Itens Vendidos',   value: NUM(quantity) },
    { label: 'Ticket Médio',     value: BRL(avgTicket) },
    { label: 'Itens / Venda',    value: itemsPerSale.toFixed(1) },
    { label: 'Valor Médio Item', value: BRL(avgItem) },
    { label: 'CMV %',            value: cmv.toFixed(2) + '%' },
    { label: 'Markup %',         value: markup.toFixed(2) + '%' },
    { label: 'Vendas PBM',       value: BRL(pbmTotal) },
  ];

  return (
    <div style={{ marginTop: 24, paddingTop: 20, borderTop: '1px solid var(--ink-100)' }}>
      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-500)', letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 12 }}>
        Indicadores
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', borderRadius: 8, overflow: 'hidden', border: '1px solid var(--ink-100)' }}>
        {metrics.map(({ label, value }, i) => {
          const row = Math.floor(i / 2);
          const isEvenRow = row % 2 === 0;
          const isLastRow = row === Math.floor((metrics.length - 1) / 2);
          return (
            <div key={label} style={{
              display: 'flex', justifyContent: 'space-between', alignItems: 'center',
              padding: '9px 14px',
              background: isEvenRow ? 'var(--ink-50)' : 'var(--white)',
              borderBottom: isLastRow ? 'none' : '1px solid var(--ink-100)',
              borderRight: i % 2 === 0 ? '1px solid var(--ink-100)' : 'none',
            }}>
              <span style={{ fontSize: 12.5, color: 'var(--ink-500)' }}>{label}</span>
              <span className="mono" style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-800)' }}>{value}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function SalesChart({ data, loading, totals }) {
  const dayData = (!loading && data?.length) ? data : [];
  return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', marginBottom: 24 }}>
        <div>
          <div style={{ fontSize: 16, fontWeight: 700 }}>Vendas por dia</div>
          <div style={{ fontSize: 13, color: 'var(--ink-500)', marginTop: 2 }}>Comparado ao período anterior</div>
        </div>
        <div style={{ display: 'flex', gap: 16, fontSize: 12.5, color: 'var(--ink-700)', fontWeight: 500 }}>
          <LegendDot color="#22C55E" label="Período atual" />
          <LegendDot color="var(--ink-300)" label="Período anterior" dashed />
        </div>
      </div>
      {loading
        ? <div style={{ height: 280, borderRadius: 8, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
        : dayData.length
        ? <BigChart data={dayData} lineColor="#22C55E" gradientId="bc-grad" tooltipLabel="Vendas" />
        : <EmptyState text="Sem dados de vendas no período." />
      }
      {!loading && <IndicadoresConsolidados totals={totals} />}
    </div>
  );
}

function LegendDot({ color, label, dashed }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <span style={{
        width: 14, height: 2, background: color,
        backgroundImage: dashed ? `repeating-linear-gradient(90deg,${color} 0 4px,transparent 4px 8px)` : 'none',
        borderRadius: 2,
      }}/>
      {label}
    </div>
  );
}

function BigChart({ data, lineColor, gradientId, tooltipLabel, hideVariation, series, comparativeMode }) {
  lineColor    = lineColor    ?? 'var(--brand-500)';
  gradientId   = gradientId   ?? 'cg2';
  tooltipLabel = tooltipLabel ?? 'Vendas';
  const isMulti = series && series.length > 0;
  const w = 760, h = 260, padL = 56, padR = 16, padT = 16, padB = 36;
  const innerW = w - padL - padR, innerH = h - padT - padB;
  const allVals = isMulti
    ? data.flatMap(d => [d.c]).concat(series.flatMap(s => s.data.map(d => d.v)))
    : data.flatMap(d => [d.v, d.c]);
  const vals   = allVals.filter(v => v > 0);
  const maxV   = Math.max(Math.ceil(Math.max(...(vals.length ? vals : [1])) / 5000) * 5000, 1000);
  const step   = data.length > 1 ? innerW / (data.length - 1) : innerW;
  const ptX    = (i) => data.length > 1 ? padL + i * step : padL + innerW / 2;
  const pt     = (val, i) => [ptX(i), padT + innerH - (val / maxV) * innerH];

  const buildPath = (arr) => arr.map((v, i) => {
    const [x, y] = pt(v, i);
    return (i === 0 ? 'M' : 'L') + x.toFixed(1) + ',' + y.toFixed(1);
  }).join(' ');

  const areaPath = !isMulti ? (buildPath(data.map(d => d.v)) + ` L${(padL + innerW).toFixed(1)},${padT + innerH} L${padL},${padT + innerH} Z`) : null;
  const linePath = !isMulti ? buildPath(data.map(d => d.v)) : null;
  const compPath = buildPath(data.map(d => d.c));

  // ── Estado ──────────────────────────────────────────────────────────────────
  const [hover,     setHover]     = React.useState(null);  // { i, x, y, d }
  const [dragStart, setDragStart] = React.useState(null);  // índice inicial
  const [dragCur,   setDragCur]   = React.useState(null);  // índice corrente (live)
  const [selection, setSelection] = React.useState(null);  // { from, to } finalizado
  const svgRef    = React.useRef(null);
  const isDragging = dragStart !== null;

  // ── Helpers ──────────────────────────────────────────────────────────────────
  // Usa getScreenCTM para converter coordenadas DOM → SVG corretamente,
  // independente de scroll, zoom ou transforms CSS no container.
  const getIdx = (e) => {
    const svg = svgRef.current;
    if (!svg || data.length === 0) return null;
    const pt   = svg.createSVGPoint();
    pt.x = e.clientX;
    pt.y = e.clientY;
    const svgX = pt.matrixTransform(svg.getScreenCTM().inverse()).x;
    const n    = data.length;
    if (n === 1) return 0;
    const _step = innerW / (n - 1);
    return Math.max(0, Math.min(n - 1, Math.round((svgX - padL) / _step)));
  };

  const svgToDom = (svgX) => {
    const svg = svgRef.current;
    if (!svg) return svgX;
    const pt = svg.createSVGPoint();
    pt.x = svgX; pt.y = 0;
    return pt.matrixTransform(svg.getScreenCTM()).x - svg.getBoundingClientRect().left;
  };

  // ── Handlers ────────────────────────────────────────────────────────────────
  const onMouseDown = (e) => {
    if (data.length < 2) return;
    e.preventDefault();
    const idx = getIdx(e);
    if (idx === null) return;
    setDragStart(idx);
    setDragCur(idx);
    setSelection(null);
    setHover(null);
  };

  const onMouseMove = (e) => {
    const idx = getIdx(e);
    if (idx === null) return;
    if (isDragging) {
      setDragCur(idx);
    } else if (!selection) {
      const [x, y] = pt(data[idx].v, idx);
      setHover({ i: idx, x, y, d: data[idx] });
    }
  };

  const onMouseUp = (e) => {
    if (!isDragging) return;
    const idx  = getIdx(e) ?? dragCur;
    const from = Math.min(dragStart, idx ?? dragStart);
    const to   = Math.max(dragStart, idx ?? dragStart);
    if (from === to) {
      const [x, y] = pt(data[from].v, from);
      setHover({ i: from, x, y, d: data[from] });
      setSelection(null);
    } else {
      setSelection({ from, to });
    }
    setDragStart(null);
    setDragCur(null);
  };

  const onMouseLeave = () => {
    if (isDragging) { setDragStart(null); setDragCur(null); }
    setHover(null);
  };

  // ── Seleção ativa (live ou finalizada) ───────────────────────────────────────
  const activeSel = isDragging && dragStart !== null && dragCur !== null
    ? { from: Math.min(dragStart, dragCur), to: Math.max(dragStart, dragCur) }
    : selection;

  // ── Estatísticas da seleção ──────────────────────────────────────────────────
  const selStats = activeSel && activeSel.to > activeSel.from ? (() => {
    const slice     = data.slice(activeSel.from, activeSel.to + 1);
    const total     = slice.reduce((s, d) => s + d.v, 0);
    const avg       = total / slice.length;
    const changePct = slice[0].v > 0 ? ((slice[slice.length - 1].v - slice[0].v) / slice[0].v) * 100 : null;
    const totalQty   = slice.reduce((s, d) => s + (d.q ?? 0), 0);
    const totalUnity = slice.reduce((s, d) => s + (d.u ?? 0), 0);
    const totalPurchasesSel = slice.reduce((s, d) =>
      s + (d.pct > 0 ? d.v / (d.pct / 100) : 0), 0);
    const accPct = totalPurchasesSel > 0 ? (total / totalPurchasesSel) * 100 : 0;
    // Modo comparativo: calcula total anterior e label do período
    const prevTotal = comparativeMode ? slice.reduce((s, d) => s + (d.c ?? 0), 0) : 0;
    const compVsPrev = comparativeMode && prevTotal > 0 ? ((total - prevTotal) / prevTotal) * 100 : null;
    const prevLabelFrom = slice[0].prevL ?? '';
    const prevLabelTo   = slice[slice.length - 1].prevL ?? '';
    const prevRangeLabel = prevLabelFrom === prevLabelTo
      ? prevLabelFrom
      : prevLabelFrom && prevLabelTo
        ? `${prevLabelFrom} a ${prevLabelTo}`
        : prevLabelFrom || prevLabelTo;
    const totalPurch  = slice.reduce((s, d) => s + (d.k  ?? 0), 0);
    const prevPurch   = slice.reduce((s, d) => s + (d.cp ?? 0), 0);
    const margin      = totalPurch > 0 ? ((total     - totalPurch) / totalPurch) * 100 : null;
    const prevMargin  = prevPurch  > 0 ? ((prevTotal - prevPurch)  / prevPurch)  * 100 : null;
    return { total, avg, changePct, up: changePct !== null && changePct >= 0, days: slice.length, totalQty, totalUnity, accPct, prevTotal, compVsPrev, prevRangeLabel, totalPurch, margin, prevPurch, prevMargin };
  })() : null;

  const yTicks    = [0, maxV * 0.25, maxV * 0.5, maxV * 0.75, maxV];
  const showLabel = (i, total) => {
    if (total <= 10) return true;
    if (total <= 31) return i % 3 === 0 || i === total - 1;
    return i % 7 === 0 || i === total - 1;
  };

  return (
    <div style={{ position: 'relative', userSelect: 'none' }}>
      <svg
        ref={svgRef}
        viewBox={`0 0 ${w} ${h}`}
        style={{ width: '100%', height: 280, display: 'block', cursor: isDragging ? 'col-resize' : 'crosshair' }}
        onMouseDown={onMouseDown}
        onMouseMove={onMouseMove}
        onMouseUp={onMouseUp}
        onMouseLeave={onMouseLeave}
      >
        <defs>
          <linearGradient id={gradientId} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={lineColor} stopOpacity="0.18" />
            <stop offset="100%" stopColor={lineColor} stopOpacity="0" />
          </linearGradient>
        </defs>

        {/* Grid Y */}
        {yTicks.map((v, i) => {
          const y = padT + innerH - (v / maxV) * innerH;
          const label = v === 0 ? '0' : v >= 1000000 ? `${(v/1000000).toFixed(1)}M` : `${(v/1000).toFixed(0)}k`;
          return (
            <g key={i}>
              <line x1={padL} x2={padL + innerW} y1={y} y2={y} stroke="var(--ink-100)" />
              <text x={padL - 8} y={y + 4} fontSize="11" fill="var(--ink-500)" textAnchor="end">{label}</text>
            </g>
          );
        })}

        {/* Rótulos eixo X */}
        {data.map((d, i) => {
          const label = showLabel(i, data.length) ? d.l : '';
          return label
            ? <text key={i} x={ptX(i)} y={h - 8} fontSize="10.5" fill="var(--ink-500)" textAnchor="middle">{label}</text>
            : null;
        })}

        {/* Linhas verticais sutis nos extremos da seleção */}
        {activeSel && [activeSel.from, activeSel.to].map((idx, k) => (
          <line key={k}
            x1={ptX(idx)} x2={ptX(idx)} y1={padT} y2={padT + innerH}
            stroke={lineColor} strokeOpacity="0.25" strokeWidth="1" strokeDasharray="3 3"
            style={{ pointerEvents: 'none' }}
          />
        ))}

        {/* Linhas do gráfico */}
        <path d={compPath} fill="none" stroke="var(--ink-500)" strokeWidth="2" strokeDasharray="5 3" strokeLinejoin="round" />
        {!isMulti && areaPath && <path d={areaPath} fill={`url(#${gradientId})`} />}
        {!isMulti && linePath && <path d={linePath} fill="none" stroke={lineColor} strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" />}
        {isMulti && series.map((s, si) => (
          <path key={si} d={buildPath(s.data.map(d => d.v))} fill="none" stroke={s.color} strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" />
        ))}

        {/* Pontos — extremos da seleção */}
        {isMulti
          ? series.map((s, si) => s.data.map((_, i) => {
              if (!activeSel || (i !== activeSel.from && i !== activeSel.to)) return null;
              const [x, y] = pt(s.data[i].v, i);
              return <circle key={`${si}-${i}`} cx={x} cy={y} r="4.5" fill={s.color} stroke="var(--white)" strokeWidth="2" style={{ pointerEvents: 'none' }} />;
            }))
          : data.map((d, i) => {
              const [x, y]   = pt(d.v, i);
              const endpoint = activeSel && (i === activeSel.from || i === activeSel.to);
              return (
                <circle key={i} cx={x} cy={y}
                  r={endpoint ? 4.5 : 3.5}
                  fill={endpoint ? lineColor : 'var(--white)'}
                  stroke={lineColor} strokeWidth="2"
                  style={{ pointerEvents: 'none' }}
                />
              );
            })
        }

        {/* Crosshair — só quando não há seleção */}
        {hover && !isDragging && !selection && (
          <>
            <line x1={hover.x} x2={hover.x} y1={padT} y2={padT + innerH}
              stroke={isMulti ? 'var(--ink-300)' : lineColor} strokeDasharray="3 3" strokeOpacity="0.5"
              style={{ pointerEvents: 'none' }}/>
            {isMulti
              ? series.map((s, si) => {
                  const [cx, cy] = pt(s.data[hover.i]?.v ?? 0, hover.i);
                  return <circle key={si} cx={cx} cy={cy} r="5" fill={s.color} stroke="var(--white)" strokeWidth="2" style={{ pointerEvents: 'none' }} />;
                })
              : <>
                  <circle cx={hover.x} cy={hover.y} r="5.5" fill={lineColor}    style={{ pointerEvents: 'none' }}/>
                  <circle cx={hover.x} cy={hover.y} r="3.5" fill="var(--white)" style={{ pointerEvents: 'none' }}/>
                </>
            }
          </>
        )}
      </svg>

      {/* ── Balão de hover (ponto único) ─────────────────────────────────────── */}
      {hover && !isDragging && !selection && (() => {
        const domX = svgToDom(hover.x);
        const maxL = svgToDom(padL + innerW) - 220;
        return (
          <div style={{
            position: 'absolute', top: 4,
            left: Math.min(Math.max(0, domX - 110), Math.max(0, maxL)),
            background: '#131C2E', color: '#ffffff',
            padding: '10px 14px', borderRadius: 8, fontSize: 12,
            pointerEvents: 'none', whiteSpace: 'nowrap', boxShadow: 'var(--shadow-md)',
          }}>
            <div style={{ fontWeight: 600, marginBottom: 2 }}>{expandLabel(hover.d.l)}</div>
            {hover.d.date && (
              <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', marginBottom: 8 }}>
                {(() => { const [y, mo, d] = hover.d.date.split('-'); return `${d}/${mo}/${y}`; })()}
              </div>
            )}
            {isMulti ? (<>
              {series.map((s, si) => {
                const pt = s.data[hover.i] ?? {};
                return (
                  <div key={si} style={{ marginTop: si > 0 ? 6 : 0, paddingTop: si > 0 ? 6 : 0, borderTop: si > 0 ? '1px solid rgba(255,255,255,0.08)' : 'none' }}>
                    <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20 }}>
                      <span style={{ color: s.color, fontWeight: 700, maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {s.name.length > 18 ? s.name.slice(0, 18) + '…' : s.name}
                      </span>
                      <span style={{ fontWeight: 600 }}>{BRL(pt.v ?? 0)}</span>
                    </div>
                    {pt.pct > 0 && (
                      <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 1 }}>
                        <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 11 }}>Participação nas compras</span>
                        <span style={{ color: '#F59E0B', fontSize: 11 }}>{pt.pct.toFixed(1)}%</span>
                      </div>
                    )}
                  </div>
                );
              })}
              {hover.d.pct > 0 && (
                <div style={{ marginTop: 6, paddingTop: 6, borderTop: '1px solid rgba(255,255,255,0.15)' }}
                     className="mono">
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 20 }}>
                    <span style={{ color: 'rgba(255,255,255,0.6)' }}>Total combinado</span>
                    <span style={{ fontWeight: 600 }}>{BRL(hover.d.v)}</span>
                  </div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 1 }}>
                    <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 11 }}>Participação combinada</span>
                    <span style={{ color: '#F59E0B', fontSize: 11, fontWeight: 700 }}>{hover.d.pct.toFixed(1)}%</span>
                  </div>
                </div>
              )}
            </>) : (
              <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20 }}>
                <span style={{ color: 'rgba(255,255,255,0.6)' }}>{tooltipLabel}</span>
                <span style={{ fontWeight: 600 }}>{BRL(hover.d.v)}</span>
              </div>
            )}
            {hover.d.q > 0 && !isMulti && (
              <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 2 }}>
                <span style={{ color: 'rgba(255,255,255,0.6)' }}>Qtd</span>
                <span>{NUM(hover.d.q)}</span>
              </div>
            )}
            {hover.d.u > 0 && (
              <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 2 }}>
                <span style={{ color: 'rgba(255,255,255,0.6)' }}>Unidades</span>
                <span>{NUM(hover.d.u)}</span>
              </div>
            )}
            {hover.d.pct > 0 && (
              <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 2 }}>
                <span style={{ color: 'rgba(255,255,255,0.6)' }}>Participação nas compras</span>
                <span style={{ color: '#F59E0B', fontWeight: 600 }}>{hover.d.pct.toFixed(1)}%</span>
              </div>
            )}
            {hover.d.c > 0 && (
              <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', gap: 20, marginTop: 2 }}>
                <span style={{ color: 'rgba(255,255,255,0.6)' }}>Anterior</span>
                <span>{BRL(hover.d.c)}</span>
              </div>
            )}
          </div>
        );
      })()}

      {/* ── Balão de intervalo (drag/seleção) ───────────────────────────────── */}
      {selStats && activeSel && (() => {
        // Centraliza sobre o intervalo selecionado, com clamping
        const midDom  = svgToDom(ptX(Math.round((activeSel.from + activeSel.to) / 2)));
        const tipW    = 220;
        const maxLeft = svgToDom(padL + innerW) - tipW;
        const left    = Math.min(Math.max(svgToDom(padL), midDom - tipW / 2), Math.max(svgToDom(padL), maxLeft));
        return (
          <div style={{
            position: 'absolute', top: 4, left,
            background: '#131C2E', color: '#ffffff',
            padding: '10px 14px', borderRadius: 8, fontSize: 12,
            pointerEvents: 'none', whiteSpace: 'nowrap', boxShadow: 'var(--shadow-md)',
            minWidth: tipW,
          }}>
            {/* Intervalo + contagem de dias */}
            <div style={{ fontWeight: 600, marginBottom: 8 }}>
              {data[activeSel.from].l}
              <span style={{ margin: '0 5px', color: 'rgba(255,255,255,0.4)' }}>→</span>
              {data[activeSel.to].l}
              <span style={{ marginLeft: 8, fontSize: 11, fontWeight: 400, color: 'rgba(255,255,255,0.45)' }}>
                {selStats.days} dias
              </span>
            </div>
            {/* Mensagem comparativa */}
            {comparativeMode && selStats.compVsPrev !== null && (
              <div style={{ marginBottom: 10, padding: '8px 10px', borderRadius: 6, background: selStats.compVsPrev >= 0 ? 'rgba(74,222,128,0.12)' : 'rgba(248,113,113,0.12)' }}>
                <div style={{ marginBottom: 4 }}>
                  <span style={{ fontSize: 13, fontWeight: 700, color: selStats.compVsPrev >= 0 ? '#4ADE80' : '#F87171' }}>
                    {selStats.compVsPrev >= 0 ? '↑' : '↓'} {Math.abs(selStats.compVsPrev).toFixed(1)}%
                  </span>
                  <span style={{ fontSize: 12, color: 'rgba(255,255,255,0.7)', marginLeft: 6 }}>
                    em {tooltipLabel.toLowerCase()} vs {selStats.prevRangeLabel}
                  </span>
                </div>
                <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', fontFeatureSettings: '"tnum"' }}>
                  Anterior: {BRL(selStats.prevTotal)} → Atual: {BRL(selStats.total)}
                </div>
              </div>
            )}
            <div style={{ borderTop: '1px solid rgba(255,255,255,0.1)', marginBottom: 8 }}/>
            <div className="mono" style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                <span style={{ color: 'rgba(255,255,255,0.55)' }}>{tooltipLabel} média/dia</span>
                <span style={{ fontWeight: 600 }}>{BRL(selStats.avg)}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                <span style={{ color: 'rgba(255,255,255,0.55)' }}>{tooltipLabel} total</span>
                <span style={{ fontWeight: 600 }}>{BRL(selStats.total)}</span>
              </div>
              {selStats.totalQty > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                  <span style={{ color: 'rgba(255,255,255,0.55)' }}>Qtd</span>
                  <span style={{ fontWeight: 600 }}>{NUM(selStats.totalQty)}</span>
                </div>
              )}
              {selStats.totalUnity > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                  <span style={{ color: 'rgba(255,255,255,0.55)' }}>Unidades</span>
                  <span style={{ fontWeight: 600 }}>{NUM(selStats.totalUnity)}</span>
                </div>
              )}
              {/* Participação por fornecedor (multi) */}
              {isMulti && series.map((s, si) => {
                const sTotal = activeSel
                  ? s.data.slice(activeSel.from, activeSel.to + 1).reduce((sum, d) => sum + (d.v ?? 0), 0)
                  : 0;
                const sPct = selStats.total > 0 ? (sTotal / (selStats.total / (selStats.accPct / 100 || 1)) * 100) : 0;
                const sRealPct = s.data.slice(activeSel?.from ?? 0, (activeSel?.to ?? 0) + 1)
                  .reduce((sum, d) => sum + (d.v ?? 0), 0);
                // % real: fornecedor / total compras do período
                const dayTotals = s.data.slice(activeSel?.from ?? 0, (activeSel?.to ?? 0) + 1);
                const totalComprasPeriodo = dayTotals.reduce((sum, d) => sum + (d.pct > 0 ? d.v / (d.pct / 100) : 0), 0);
                const accPctS = totalComprasPeriodo > 0 ? (sRealPct / totalComprasPeriodo) * 100 : 0;
                if (sRealPct === 0) return null;
                return (
                  <div key={si} style={{ marginTop: 4, paddingTop: 4, borderTop: si === 0 ? '1px solid rgba(255,255,255,0.1)' : 'none' }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                      <span style={{ color: s.color, fontWeight: 600, maxWidth: 130, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {s.name.length > 16 ? s.name.slice(0, 16) + '…' : s.name}
                      </span>
                      <span style={{ fontWeight: 600 }}>{BRL(sRealPct)}</span>
                    </div>
                    {accPctS > 0 && (
                      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                        <span style={{ color: 'rgba(255,255,255,0.45)', fontSize: 11 }}>Participação nas compras</span>
                        <span style={{ color: '#F59E0B', fontSize: 11 }}>{accPctS.toFixed(1)}%</span>
                      </div>
                    )}
                  </div>
                );
              })}
              {selStats.accPct > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24, marginTop: isMulti ? 6 : 0, paddingTop: isMulti ? 6 : 0, borderTop: isMulti ? '1px solid rgba(255,255,255,0.15)' : 'none' }}>
                  <span style={{ color: 'rgba(255,255,255,0.55)' }}>{isMulti ? 'Participação combinada' : 'Participação nas compras'}</span>
                  <span style={{ fontWeight: 700, color: '#F59E0B' }}>{selStats.accPct.toFixed(1)}%</span>
                </div>
              )}
              {!hideVariation && selStats.changePct !== null && (
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 24 }}>
                  <span style={{ color: 'rgba(255,255,255,0.55)' }}>Variação</span>
                  <span style={{ fontWeight: 700, color: selStats.up ? '#4ADE80' : '#F87171' }}>
                    {selStats.up ? '↑' : '↓'} {Math.abs(selStats.changePct).toFixed(1)}%
                  </span>
                </div>
              )}
            </div>
            {!isDragging && (
              <div style={{ marginTop: 8, paddingTop: 6, borderTop: '1px solid rgba(255,255,255,0.08)', fontSize: 10, color: 'rgba(255,255,255,0.3)' }}>
                clique no gráfico para fechar
              </div>
            )}
          </div>
        );
      })()}
    </div>
  );
}

// ─── Store Leaderboard ────────────────────────────────────────────────────────
// Mesmas cores de medalha do ClassGroupSection (= mobile ranking-item.tsx)
const SL_MEDALS = [
  { dot: '#F59E0B', bg: 'rgba(245,158,11,0.09)',  border: 'rgba(245,158,11,0.30)'  },
  { dot: '#94A3B8', bg: 'rgba(148,163,184,0.09)', border: 'rgba(148,163,184,0.30)' },
  { dot: '#CD7F32', bg: 'rgba(205,127,50,0.09)',  border: 'rgba(205,127,50,0.30)'  },
];

// Rótulo curto do período para exibir na linha de meta
const SL_PERIOD_LABEL = {
  'hoje': 'de hoje', '7d': 'dos últimos 7 dias', '30d': 'dos últimos 30 dias',
  'mtd': 'do mês', 'ytd': 'do ano', 'custom': 'do período',
};

function StoreLeaderboard({ stores, loading, period = 'hoje' }) {
  const [openIdx, setOpenIdx] = React.useState(null);

  if (loading) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Top lojas</div>
      {[...Array(5)].map((_, i) => (
        <div key={i} style={{ height: 78, borderRadius: 12, background: 'var(--ink-100)', marginBottom: 8, animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
      ))}
    </div>
  );
  if (!stores?.length) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 12 }}>Top lojas</div>
      <EmptyState text="Sem dados de lojas." />
    </div>
  );

  const periodLabel = SL_PERIOD_LABEL[period] ?? 'do período';

  return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Top lojas</div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {stores.map((s, i) => {
          const medal    = SL_MEDALS[i] ?? null;
          const hasGoal  = s.goalPct !== null && s.goalPct !== undefined;
          const barWidth = hasGoal ? Math.min(s.goalPct, 100) : s.pct;
          const isOpen   = openIdx === i;

          // Cor do pill / painel de meta baseada no atingimento
          const goalColor = s.goalPct >= 100 ? 'var(--positive)'
                          : s.goalPct >=  80 ? 'var(--warning)'
                          : 'var(--danger)';
          const goalBg    = s.goalPct >= 100 ? 'var(--positive-bg)'
                          : s.goalPct >=  80 ? 'var(--warning-bg)'
                          : 'var(--danger-bg)';
          const goalBorder = s.goalPct >= 100 ? 'rgba(26,155,110,0.35)'
                           : s.goalPct >=  80 ? 'rgba(179,100,10,0.35)'
                           : 'rgba(194,54,58,0.35)';

          return (
            <div key={i} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
              {/* Barra lateral colorida (medal ou transparente para os demais) */}
              <div style={{
                width: 4, borderRadius: 4, flexShrink: 0,
                background: medal?.dot ?? 'transparent',
              }}/>

              {/* Card — idêntico ao RankingSaleItem do mobile */}
              <div style={{
                flex: 1, borderRadius: 12, padding: '10px 16px',
                background: medal?.bg ?? 'var(--white)',
                border: `0.5px solid ${medal?.border ?? 'rgba(0,0,0,0.1)'}`,
              }}>
                {/* Linha superior: ticket médio (esq) + pill de meta clicável (dir) */}
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 5 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, color: '#6B7280' }}>
                    <span>Ticket médio</span>
                    <span className="mono" style={{ fontWeight: 500 }}>{BRL(s.avgTicket)}</span>
                  </div>
                  {hasGoal && (
                    <button
                      onClick={() => setOpenIdx(isOpen ? null : i)}
                      style={{
                        display: 'inline-flex', alignItems: 'center', gap: 4,
                        background: 'var(--brand-50)',
                        border: '1px solid var(--brand-200)',
                        borderRadius: 20, padding: '2px 8px 2px 10px',
                        fontSize: 11, fontWeight: 600,
                        color: 'var(--brand-700)',
                        cursor: 'pointer', fontFamily: 'inherit',
                      }}
                    >
                      {s.goalPct.toFixed(1)}% da meta
                      <svg
                        width="10" height="10" viewBox="0 0 24 24" fill="none"
                        stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"
                        style={{ transition: 'transform .2s', transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)' }}
                      >
                        <polyline points="6 9 12 15 18 9"/>
                      </svg>
                    </button>
                  )}
                </div>

                {/* Linha principal: badge circular + nome | valor azul + % das vendas verde */}
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
                    {/* Badge circular (26×26) — igual mobile */}
                    <div style={{
                      width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
                      background: medal ? medal.dot + '30' : '#F0F0F0',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 12, fontWeight: 700,
                      color: medal?.dot ?? '#999',
                    }}>{i + 1}</div>
                    <span style={{
                      fontSize: 14, fontWeight: 500, color: 'var(--ink-900)',
                      overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                    }}>{s.name}</span>
                  </div>

                  {/* Valor + % das vendas */}
                  <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 10 }}>
                    <div className="mono" style={{ fontSize: 14, fontWeight: 500, color: 'var(--brand-500)' }}>
                      {BRL(s.val)}
                    </div>
                    {s.sharePct > 0 && (
                      <div style={{ fontSize: 11, fontWeight: 700, color: '#1E8A4C' }}>
                        {s.sharePct.toFixed(1)}% das vendas
                      </div>
                    )}
                  </div>
                </div>

                {/* Barra de progresso (3px, azul — igual mobile) */}
                <div style={{ marginTop: 8, height: 3, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden' }}>
                  <div style={{
                    height: '100%', borderRadius: 99,
                    background: 'var(--brand-500)',
                    width: `${Math.round(barWidth)}%`,
                    transition: 'width .4s',
                  }}/>
                </div>

                {/* Painel de meta — expande ao clicar no pill */}
                {isOpen && hasGoal && (
                  <div style={{
                    marginTop: 10,
                    padding: '10px 12px',
                    background: 'var(--ink-50)',
                    border: '1px solid var(--ink-150, #E4E7EC)',
                    borderRadius: 8,
                    display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8,
                  }}>
                    {/* Meta do período */}
                    <div>
                      <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 3 }}>
                        Meta {periodLabel}
                      </div>
                      <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-700)' }}>
                        {BRL(s.proratedGoal)}
                      </div>
                    </div>
                    {/* Vendido */}
                    <div style={{ textAlign: 'center' }}>
                      <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 3 }}>
                        Vendido
                      </div>
                      <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--brand-500)' }}>
                        {BRL(s.val)}
                      </div>
                    </div>
                    {/* % da meta — só o valor recebe cor de alerta */}
                    <div style={{ textAlign: 'right' }}>
                      <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 3 }}>
                        % da meta
                      </div>
                      <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: goalColor }}>
                        {s.goalPct.toFixed(1)}%
                      </div>
                    </div>
                    {/* Meta mensal completa — referência */}
                    {s.goalMonthly > 0 && (
                      <div style={{ gridColumn: '1 / -1', marginTop: 2, paddingTop: 8, borderTop: '1px solid var(--ink-150, #E4E7EC)', fontSize: 11, color: 'var(--ink-500)' }}>
                        Meta mensal completa: <span className="mono" style={{ fontWeight: 700 }}>{BRL(s.goalMonthly)}</span>
                      </div>
                    )}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Category Breakdown (donut) ───────────────────────────────────────────────
function CategoryBreakdown({ cats, total, totals, loading }) {
  if (loading) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 20 }}>Vendas por categoria</div>
      <div style={{ height: 160, borderRadius: 8, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
    </div>
  );
  const categories = cats?.length ? cats : [];
  const totalValue = typeof total === 'number' && total > 0 ? total : categories.reduce((s, c) => s + c.v, 0);
  let offset = 0;
  const r = 56, c = 2 * Math.PI * r;

  const T = totals ?? {};
  const tot        = T.total      ?? 0;
  const totalCost  = T.totalCost  ?? 0;
  const quantity   = T.quantity   ?? 0;
  const salesCount = T.totalSalesCount ?? 0;
  const pbmTotal   = T.pbmTotal   ?? 0;

  const costPct      = tot > 0 ? totalCost / tot : 0;
  const marginVal    = tot - totalCost;
  const avgItemPrice = quantity > 0 ? tot / quantity : 0;
  const avgItemCost  = quantity > 0 ? totalCost / quantity : 0;
  const ticket       = salesCount > 0 ? tot / salesCount : 0;
  const itensVenda   = salesCount > 0 ? quantity / salesCount : 0;
  const pbmPct       = tot > 0 ? (pbmTotal / tot) * 100 : 0;
  const maxBar       = Math.max(ticket, avgItemPrice, avgItemCost, 1);

  const [pbmExp, setPbmExp] = React.useState(false);

  const SEP = <div style={{ height: 1, background: 'var(--ink-100)', margin: '18px 0' }} />;

  return (
    <div className="card" style={{ padding: 24 }}>

      {/* ── Vendas por categoria ── */}
      <div style={{ marginBottom: 20 }}>
        <div style={{ fontSize: 16, fontWeight: 700 }}>Vendas por categoria</div>
        <div style={{ fontSize: 13, color: 'var(--ink-500)', marginTop: 2 }}>Distribuição no período</div>
      </div>
      {!categories.length
        ? <EmptyState text="Sem dados de categorias." />
        : (
          <div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
            <div style={{ position: 'relative', flexShrink: 0 }}>
              <svg width="150" height="150" viewBox="0 0 150 150">
                <g transform="rotate(-90 75 75)">
                  {categories.map((cat, i) => {
                    const len = totalValue > 0 ? (cat.v / totalValue) * c : 0;
                    const dasharray = `${len} ${c - len}`;
                    const el = (
                      <circle key={i} cx="75" cy="75" r={r}
                        fill="none" stroke={cat.color} strokeWidth="22"
                        strokeDasharray={dasharray} strokeDashoffset={-offset}
                      />
                    );
                    offset += len;
                    return el;
                  })}
                </g>
                <text x="75" y="70" textAnchor="middle" fontSize="10" fill="var(--ink-500)" fontWeight="600" style={{ letterSpacing: '0.06em' }}>TOTAL</text>
                <text x="75" y="88" textAnchor="middle" fontSize="16" fill="var(--ink-900)" fontWeight="700">
                  {NUM(Math.round(totalValue))}
                </text>
              </svg>
            </div>
            <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 10 }}>
              {categories.map((cat, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13 }}>
                  <span style={{ width: 8, height: 8, borderRadius: 2, background: cat.color, flexShrink: 0 }}/>
                  <span style={{ flex: 1, color: 'var(--ink-700)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{cat.name}</span>
                  <span className="mono" style={{ fontWeight: 600, color: 'var(--ink-900)', fontSize: 12.5 }}>
                    {totalValue > 0 ? `${((cat.v / totalValue) * 100).toFixed(1)}%` : '—'}
                  </span>
                </div>
              ))}
            </div>
          </div>
        )
      }

      {tot > 0 && (<>

        {/* ── Composição do Faturamento ── */}
        {SEP}
        <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 3 }}>Composição do Faturamento</div>
        <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 10 }}>Como se divide cada R$ 100 vendidos</div>
        <div style={{ display: 'flex', height: 14, borderRadius: 7, overflow: 'hidden', marginBottom: 10 }}>
          <div style={{ flex: costPct,       background: '#F59E0B' }} />
          <div style={{ flex: 1 - costPct,   background: '#22C55E' }} />
        </div>
        <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', marginBottom: 10 }}>
          {[['#F59E0B','Custo', costPct, totalCost], ['#22C55E','Margem', 1-costPct, marginVal]].map(([cor, label, pct, val]) => (
            <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <div style={{ width: 8, height: 8, borderRadius: '50%', background: cor, flexShrink: 0 }} />
              <span style={{ fontSize: 11.5, color: 'var(--ink-600)' }}>{label} {(pct*100).toFixed(0)}% ({BRL(val)})</span>
            </div>
          ))}
        </div>

        {/* ── Volume de Itens ── */}
        {SEP}
        <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 12 }}>Volume de Itens</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
          {[
            ['Itens Vendidos',   NUM(quantity),   'var(--ink-900)'],
            ['Preço Médio/Item', BRL(avgItemPrice), 'var(--brand-600, var(--brand-700))'],
            ['Custo Médio/Item', BRL(avgItemCost),  '#D97706'],
            ['Itens / Venda',    itensVenda.toLocaleString('pt-BR',{minimumFractionDigits:1,maximumFractionDigits:1}), 'var(--ink-700)'],
          ].map(([label, value, cor]) => (
            <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 8, padding: '8px 12px', border: '1px solid var(--ink-100)' }}>
              <div style={{ fontSize: 10.5, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 3 }}>{label}</div>
              <div style={{ fontSize: 14, fontWeight: 800, color: cor }}>{value}</div>
            </div>
          ))}
        </div>

        {/* ── Valores Médios ── */}
        {SEP}
        <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 3 }}>Valores Médios</div>
        <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 12 }}>Ticket · Preço/item · Custo/item</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[
            ['Ticket médio',     ticket,       'var(--brand-500)'],
            ['Preço médio/item', avgItemPrice, '#3D8EE8'],
            ['Custo médio/item', avgItemCost,  '#F59E0B'],
          ].filter(([,v]) => v > 0).map(([label, value, color]) => (
            <div key={label}>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                <span style={{ fontSize: 11.5, color: 'var(--ink-500)', fontWeight: 600 }}>{label}</span>
                <span style={{ fontSize: 13, fontWeight: 700, color }}>{BRL(value)}</span>
              </div>
              <div style={{ height: 6, borderRadius: 3, background: 'var(--ink-100)', overflow: 'hidden' }}>
                <div style={{ height: '100%', borderRadius: 3, background: color, width: `${(value/maxBar)*100}%` }} />
              </div>
            </div>
          ))}
        </div>

        {/* ── Vendas PBM (só se > 0) ── */}
        {pbmTotal > 0 && (<>
          {SEP}
          <div
            style={{ cursor: 'pointer', userSelect: 'none' }}
            onClick={() => setPbmExp(e => !e)}
          >
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)' }}>Vendas PBM</div>
              <span style={{ fontSize: 12, color: 'var(--brand-400)', opacity: 0.7 }}>{pbmExp ? '▲' : 'ⓘ'}</span>
            </div>
            <div style={{ display: 'flex', gap: 0, marginBottom: 10 }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 10.5, color: 'var(--ink-400)', marginBottom: 2 }}>Valor PBM</div>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--brand-600, var(--brand-700))' }}>{BRL(pbmTotal)}</div>
              </div>
              <div style={{ width: 1, background: 'var(--ink-100)', margin: '0 16px' }} />
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 10.5, color: 'var(--ink-400)', marginBottom: 2 }}>% das vendas</div>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--brand-600, var(--brand-700))' }}>{pbmPct.toFixed(1)}%</div>
              </div>
            </div>
            <div style={{ height: 6, borderRadius: 3, background: 'var(--ink-100)', overflow: 'hidden' }}>
              <div style={{ height: '100%', borderRadius: 3, background: 'var(--brand-500)', width: `${pbmPct}%` }} />
            </div>
            {pbmExp && (
              <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--ink-100)', fontSize: 11.5, color: 'var(--ink-600)', lineHeight: 1.7 }}>
                PBM (Programa de Benefícios a Medicamentos) são vendas com desconto financiado por laboratórios ou convênios.
                <br />{pbmPct.toFixed(1)}% das vendas passaram por PBM ({BRL(pbmTotal)} de {BRL(tot)}).
              </div>
            )}
          </div>
        </>)}

      </>)}
    </div>
  );
}

// ─── Projeção do Mês ──────────────────────────────────────────────────────────
function ProjectionCard({ mtdTotal, stores }) {
  const [expanded, setExpanded] = React.useState(false);

  const now            = new Date();
  const diasDecorridos = now.getDate();
  const diasNoMes      = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
  const diasRestantes  = diasNoMes - diasDecorridos;
  const mesNome        = MONTH_NAMES_FULL_PT[now.getMonth()];

  const mtdBase = mtdTotal ?? 0;

  if (mtdBase === 0 || diasDecorridos === 0) return null;

  const projMes  = (mtdBase / diasDecorridos) * diasNoMes;
  const mediaDia = mtdBase / diasDecorridos;

  // Meta mensal: soma de totalGoal por loja
  const goalMes = (stores ?? []).reduce((s, st) => s + safeN(st.totalGoal), 0);
  const projPct = goalMes > 0 ? projMes / goalMes : null;

  // Thresholds: verde ≥ 100%, amarelo ≥ 80%, vermelho < 80% (espelho mobile)
  const projColor  = projPct !== null
    ? (projPct >= 1.0 ? '#22C55E' : projPct >= 0.8 ? '#F59E0B' : '#EF4444')
    : 'var(--brand-500)';
  const projBg     = projPct !== null
    ? (projPct >= 1.0 ? 'var(--positive-bg)' : projPct >= 0.8 ? 'var(--warning-bg)' : 'var(--danger-bg)')
    : 'var(--white)';
  const projBorder = projPct !== null
    ? (projPct >= 1.0 ? 'rgba(26,155,110,0.35)' : projPct >= 0.8 ? 'rgba(179,100,10,0.35)' : 'rgba(194,54,58,0.35)')
    : 'var(--ink-200)';

  return (
    <div className="card" style={{
      padding: '16px 20px', marginTop: 16,
      background: projBg, border: `1.5px solid ${projBorder}`,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>

        {/* Ícone */}
        <div style={{
          width: 40, height: 40, borderRadius: 10, flexShrink: 0,
          background: projColor + '22',
          display: 'flex', alignItems: 'center', justifyContent: 'center', color: projColor,
        }}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.3" strokeLinecap="round" strokeLinejoin="round">
            <path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
          </svg>
        </div>

        {/* Valor + subtítulo */}
        <div style={{ flex: 1, minWidth: 180 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: projColor, marginBottom: 3 }}>
            Projeção do mês — {mesNome}
          </div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }}>
            <span className="mono" style={{ fontSize: 26, fontWeight: 700, letterSpacing: '-0.01em', color: projColor }}>
              {BRL(projMes)}
            </span>
            {projPct !== null && (
              <span style={{ fontSize: 13.5, fontWeight: 600, color: projColor, opacity: 0.9 }}>
                {(projPct * 100).toFixed(1)}% da meta
              </span>
            )}
          </div>
          <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 4 }}>
            Dias 1–{diasDecorridos} de {mesNome}
            {' · '}
            {diasRestantes > 0 ? `${diasRestantes} dias restantes` : 'último dia do mês'}
            {' · '}
            média {BRL(mediaDia)}/dia
          </div>
        </div>

        {/* Barra de progresso vs meta */}
        {goalMes > 0 && (
          <div style={{ width: 168, flexShrink: 0 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 5 }}>
              <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                Meta {mesNome}
              </span>
              <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>
                {BRL(goalMes)}
              </span>
            </div>
            <div style={{ height: 8, background: 'var(--ink-200)', borderRadius: 999, overflow: 'hidden' }}>
              <div style={{
                height: '100%', borderRadius: 999, background: projColor,
                width: `${Math.min((projMes / goalMes) * 100, 100)}%`,
                transition: 'width .6s',
              }}/>
            </div>
            <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 4, textAlign: 'right' }}>
              Realizado: {BRL(mtdBase)}
            </div>
          </div>
        )}

        {/* Botão ⓘ */}
        <button
          onClick={() => setExpanded(e => !e)}
          style={{
            width: 26, height: 26, borderRadius: '50%', cursor: 'pointer',
            background: expanded ? projColor + '28' : 'rgba(0,0,0,0.06)',
            border: 'none', display: 'flex', alignItems: 'center',
            justifyContent: 'center', fontSize: 13.5,
            color: expanded ? projColor : 'var(--ink-500)',
            flexShrink: 0, transition: 'all .15s',
          }}
          title="Como é calculada a projeção"
        >ⓘ</button>
      </div>

      {/* Detalhe expansível */}
      {expanded && (
        <div style={{
          marginTop: 14, paddingTop: 14,
          borderTop: `1px solid ${projBorder}`,
          fontSize: 12.5, color: 'var(--ink-600)', lineHeight: 1.7,
        }}>
          <strong style={{ color: 'var(--ink-800)' }}>Como é calculado:</strong>
          <br/>
          Projeção = (Vendas acumuladas no mês ÷ Dias decorridos) × Dias no mês
          <br/>
          <span className="mono" style={{
            display: 'inline-block', marginTop: 6, padding: '6px 12px',
            background: 'var(--ink-100)', borderRadius: 6, fontSize: 12,
          }}>
            ({BRL(mtdBase)} ÷ {diasDecorridos}) × {diasNoMes} = {BRL(projMes)}
          </span>
          {projPct !== null && goalMes > 0 && (
            <div style={{ marginTop: 8, color: 'var(--ink-500)' }}>
              Projeção vs meta: <span style={{ fontWeight: 700, color: projColor }}>{(projPct * 100).toFixed(1)}%</span> de {BRL(goalMes)}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ─── Visão de Vendas ──────────────────────────────────────────────────────────
// ─── Composição do Faturamento ────────────────────────────────────────────────
function ComposicaoFaturamento({ totals }) {
  const { total = 0, totalCost = 0 } = totals ?? {};
  if (!total) return null;
  const costPct   = total > 0 ? totalCost / total : 0;
  const marginPct = 1 - costPct;
  const margin    = total - totalCost;

  return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 3 }}>
        Composição do Faturamento
      </div>
      <div style={{ fontSize: 12, color: 'var(--ink-400)', marginBottom: 14 }}>
        Como se divide cada R$ 100 vendidos
      </div>

      {/* Barra custo / margem */}
      <div style={{ display: 'flex', height: 18, borderRadius: 9, overflow: 'hidden', marginBottom: 12 }}>
        <div style={{ flex: costPct,   background: '#F59E0B', transition: 'flex .3s' }} />
        <div style={{ flex: marginPct, background: '#22C55E', transition: 'flex .3s' }} />
      </div>

      {/* Legenda */}
      <div style={{ display: 'flex', gap: 18, flexWrap: 'wrap', marginBottom: 16 }}>
        {[
          { cor: '#F59E0B', label: 'Custo',  pct: costPct,   val: totalCost },
          { cor: '#22C55E', label: 'Margem', pct: marginPct, val: margin    },
        ].map(({ cor, label, pct, val }) => (
          <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
            <div style={{ width: 10, height: 10, borderRadius: '50%', background: cor, flexShrink: 0 }} />
            <span style={{ fontSize: 12, color: 'var(--ink-600)' }}>
              {label} {(pct * 100).toFixed(0)}% ({BRL(val)})
            </span>
          </div>
        ))}
      </div>

      {/* Valores */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, paddingTop: 14, borderTop: '1px solid var(--ink-100)' }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 2 }}>Custo total</div>
          <div style={{ fontSize: 18, fontWeight: 700, color: '#D97706' }}>{BRL(totalCost)}</div>
        </div>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 2 }}>Margem bruta</div>
          <div style={{ fontSize: 18, fontWeight: 700, color: '#16A34A' }}>{BRL(margin)}</div>
        </div>
      </div>
    </div>
  );
}

// ─── Volume de Itens ──────────────────────────────────────────────────────────
function VolumeItens({ totals }) {
  const { total = 0, totalCost = 0, quantity = 0, totalSalesCount = 0 } = totals ?? {};
  const avgItemPrice = quantity > 0 ? total     / quantity : 0;
  const avgItemCost  = quantity > 0 ? totalCost / quantity : 0;
  const itensVenda   = totalSalesCount > 0 ? quantity / totalSalesCount : 0;

  const stats = [
    { label: 'Itens Vendidos',    value: NUM(quantity),                cor: 'var(--ink-900)' },
    { label: 'Preço Médio/Item',  value: BRL(avgItemPrice),            cor: 'var(--brand-600, var(--brand-700))' },
    { label: 'Custo Médio/Item',  value: BRL(avgItemCost),             cor: '#D97706' },
    { label: 'Itens / Venda',     value: itensVenda.toLocaleString('pt-BR', { minimumFractionDigits: 1, maximumFractionDigits: 1 }), cor: 'var(--ink-700)' },
  ];

  return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 16 }}>
        Volume de Itens
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        {stats.map(({ label, value, cor }) => (
          <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 10, padding: '10px 14px', border: '1px solid var(--ink-100)' }}>
            <div style={{ fontSize: 11, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 4 }}>{label}</div>
            <div style={{ fontSize: 16, fontWeight: 800, color: cor }}>{value}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Valores Médios ───────────────────────────────────────────────────────────
function ValoresMedios({ totals }) {
  const { total = 0, totalCost = 0, quantity = 0, totalSalesCount = 0 } = totals ?? {};
  const ticket       = totalSalesCount > 0 ? total     / totalSalesCount : 0;
  const avgItemPrice = quantity > 0          ? total     / quantity         : 0;
  const avgItemCost  = quantity > 0          ? totalCost / quantity         : 0;

  const BARS = [
    { label: 'Ticket médio',      value: ticket,       color: 'var(--brand-500)' },
    { label: 'Preço médio/item',  value: avgItemPrice, color: '#3D8EE8' },
    { label: 'Custo médio/item',  value: avgItemCost,  color: '#F59E0B' },
  ].filter(b => b.value > 0);

  const maxVal = Math.max(...BARS.map(b => b.value), 1);

  return (
    <div className="card" style={{ padding: '20px 24px', height: '100%', boxSizing: 'border-box' }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 3 }}>
        Valores Médios
      </div>
      <div style={{ fontSize: 12, color: 'var(--ink-400)', marginBottom: 18 }}>
        Ticket médio · Preço médio por item · Custo médio por item
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {BARS.map(({ label, value, color }) => (
          <div key={label}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 5 }}>
              <span style={{ fontSize: 12, color: 'var(--ink-500)', fontWeight: 600 }}>{label}</span>
              <span style={{ fontSize: 15, fontWeight: 800, color }}>{BRL(value)}</span>
            </div>
            <div style={{ height: 7, borderRadius: 4, background: 'var(--ink-100)', overflow: 'hidden' }}>
              <div style={{
                height: '100%', borderRadius: 4, background: color,
                width: `${(value / maxVal) * 100}%`, transition: 'width .4s',
              }} />
            </div>
          </div>
        ))}
      </div>

      {/* Legenda de pontos coloridos */}
      <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', marginTop: 16, paddingTop: 12, borderTop: '1px solid var(--ink-100)' }}>
        {BARS.map(({ label, color }) => (
          <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <div style={{ width: 8, height: 8, borderRadius: '50%', background: color, flexShrink: 0 }} />
            <span style={{ fontSize: 11, color: 'var(--ink-500)' }}>{label}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Vendas PBM ───────────────────────────────────────────────────────────────
function VendasPBM({ totals }) {
  const { total = 0, pbmTotal = 0 } = totals ?? {};
  if (!pbmTotal) return null;
  const pbmPct = total > 0 ? (pbmTotal / total) * 100 : 0;
  const [expanded, setExpanded] = React.useState(false);

  return (
    <div className="card" style={{ padding: '20px 24px', cursor: 'pointer', userSelect: 'none' }}
      onClick={() => setExpanded(e => !e)}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 2 }}>
        <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>Vendas PBM</div>
        <span style={{ fontSize: 13, color: 'var(--brand-400)', opacity: 0.7 }}>{expanded ? '▲' : 'ⓘ'}</span>
      </div>
      <div style={{ display: 'flex', gap: 0, marginBottom: 12, marginTop: 12 }}>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 3 }}>Valor PBM</div>
          <div style={{ fontSize: 18, fontWeight: 700, color: 'var(--brand-600, var(--brand-700))' }}>{BRL(pbmTotal)}</div>
        </div>
        <div style={{ width: 1, background: 'var(--ink-100)', margin: '0 20px' }} />
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginBottom: 3 }}>% das vendas</div>
          <div style={{ fontSize: 18, fontWeight: 700, color: 'var(--brand-600, var(--brand-700))' }}>{pbmPct.toFixed(1)}%</div>
        </div>
      </div>
      {/* Barra */}
      <div style={{ height: 7, borderRadius: 4, background: 'var(--ink-100)', overflow: 'hidden' }}>
        <div style={{ height: '100%', borderRadius: 4, background: 'var(--brand-500)', width: `${pbmPct}%`, transition: 'width .4s' }} />
      </div>
      {/* Explicação (expansível) */}
      {expanded && (
        <div style={{ marginTop: 14, paddingTop: 12, borderTop: '1px solid var(--ink-100)', fontSize: 12, color: 'var(--ink-600)', lineHeight: 1.7 }}>
          PBM (Programa de Benefícios a Medicamentos) representa vendas com desconto financiado por laboratórios ou convênios.
          <br /><br />
          {pbmPct.toFixed(1)}% das vendas passaram por PBM ({BRL(pbmTotal)} de {BRL(total)}).
          Acompanhe esse indicador para avaliar o volume de convênios e o impacto na margem real.
        </div>
      )}
    </div>
  );
}

// ─── Painel flutuante — Composição por Grupos/Classes ────────────────────────
const GRP_PALETTE = [
  '#3B82F6','#10B981','#F59E0B','#EF4444','#8B5CF6',
  '#EC4899','#06B6D4','#84CC16','#F97316','#6366F1',
  '#14B8A6','#F43F5E','#A855F7','#22D3EE','#EAB308',
  '#D946EF','#0EA5E9','#16A34A','#DC2626','#7C3AED',
];

function GroupsFloatingPanel({ groups, classes, grandTotal, anchorRef }) {
  const [visible,  setVisible]  = React.useState(false);
  const [expanded, setExpanded] = React.useState(false);
  const [tab,      setTab]      = React.useState('grupos');
  const [selected, setSelected] = React.useState(new Set());

  React.useEffect(() => {
    const el = anchorRef?.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      setVisible(e.isIntersecting || e.boundingClientRect.top < 0);
    }, { threshold: 0 });
    obs.observe(el);
    return () => obs.disconnect();
  }, [anchorRef]);

  // Reseta seleção ao trocar aba
  React.useEffect(() => { setSelected(new Set()); }, [tab]);

  const items = (tab === 'grupos' ? groups : classes) ?? [];

  const toggle = (name) => setSelected(prev => {
    const next = new Set(prev);
    next.has(name) ? next.delete(name) : next.add(name);
    return next;
  });

  // Donut: apenas grupos selecionados
  const selItems = items.filter(it => selected.has(it.name));
  const selTotal = selItems.reduce((s, it) => s + (it.total ?? 0), 0);
  const gTotal   = grandTotal || items.reduce((s, it) => s + (it.total ?? 0), 0);
  const selPct   = gTotal > 0 && selTotal > 0 ? (selTotal / gTotal * 100) : 0;

  // SVG donut
  const r = 52, circ = 2 * Math.PI * r;
  let offsetD = 0;
  const donutSegs = selItems.map((it, i) => {
    const colorIdx = items.findIndex(x => x.name === it.name) % GRP_PALETTE.length;
    const len = selTotal > 0 ? (it.total / selTotal) * circ : 0;
    const seg = { name: it.name, len, offset: offsetD, color: GRP_PALETTE[colorIdx] };
    offsetD += len;
    return seg;
  });

  if (!visible) return null;

  return (
    <div style={{
      position: 'fixed', bottom: 28, right: 20, zIndex: 200,
      display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8,
      pointerEvents: 'none',
    }}>
      {/* Painel expandido — aparece acima do botão */}
      {expanded && (
        <div style={{
          pointerEvents: 'auto',
          width: 340,
          maxHeight: 'calc(100vh - 140px)',
          display: 'flex', flexDirection: 'column',
          background: 'var(--white)',
          border: '1px solid var(--ink-200)',
          borderRadius: 16,
          boxShadow: 'var(--shadow-lg)',
          overflow: 'hidden',
        }}>
          {/* Header + tabs */}
          <div style={{ padding: '14px 16px 12px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
              <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-800)' }}>
                Composição de grupos ou classes
              </span>
              {selected.size > 0 && (
                <button onClick={() => setSelected(new Set())} style={{
                  fontSize: 11, color: 'var(--ink-400)', background: 'none', border: 'none',
                  cursor: 'pointer', padding: '2px 6px', borderRadius: 4,
                }}>Limpar</button>
              )}
            </div>
            <div style={{ display: 'flex', gap: 4, background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
              {['grupos', 'classes'].map(t => (
                <button key={t} onClick={() => setTab(t)} style={{
                  flex: 1, padding: '5px 0', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                  background: tab === t ? 'var(--white)' : 'transparent',
                  color: tab === t ? 'var(--ink-900)' : 'var(--ink-500)',
                  border: 'none', borderRadius: 6, cursor: 'pointer',
                  boxShadow: tab === t ? 'var(--shadow-sm)' : 'none',
                  textTransform: 'capitalize', transition: 'all .15s',
                }}>{t}</button>
              ))}
            </div>
          </div>

          {/* Donut */}
          <div style={{ padding: '16px 16px 12px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0, display: 'flex', alignItems: 'center', gap: 16 }}>
            <div style={{ position: 'relative', flexShrink: 0 }}>
              <svg width="128" height="128" viewBox="0 0 128 128">
                <g transform="rotate(-90 64 64)">
                  {selItems.length === 0
                    ? <circle cx="64" cy="64" r={r} fill="none" stroke="var(--ink-100)" strokeWidth="18" />
                    : donutSegs.map((seg, i) => (
                        <circle key={seg.name} cx="64" cy="64" r={r}
                          fill="none" stroke={seg.color} strokeWidth="18"
                          strokeDasharray={`${seg.len} ${circ - seg.len}`}
                          strokeDashoffset={-seg.offset}
                          style={{ transition: 'stroke-dasharray .4s, stroke-dashoffset .4s' }}
                        />
                      ))
                  }
                </g>
                {selItems.length === 0
                  ? <>
                      <text x="64" y="60" textAnchor="middle" fontSize="9.5" fill="var(--ink-400)" fontWeight="600">SELECIONE</text>
                      <text x="64" y="73" textAnchor="middle" fontSize="9.5" fill="var(--ink-400)" fontWeight="600">GRUPOS</text>
                    </>
                  : <>
                      <text x="64" y="58" textAnchor="middle" fontSize="9" fill="var(--ink-500)" fontWeight="600" letterSpacing="0.06em">SELEÇÃO</text>
                      <text x="64" y="74" textAnchor="middle" fontSize="13" fill="var(--ink-900)" fontWeight="700">
                        {selPct.toFixed(1)}%
                      </text>
                    </>
                }
              </svg>
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              {selItems.length === 0
                ? <div style={{ fontSize: 12, color: 'var(--ink-400)', lineHeight: 1.5 }}>
                    Clique nos grupos ao lado para montar a composição no gráfico.
                  </div>
                : <>
                    <div className="mono" style={{ fontSize: 17, fontWeight: 700, color: 'var(--ink-900)' }}>{BRL(selTotal)}</div>
                    <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 2 }}>{selPct.toFixed(1)}% do total</div>
                    <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
                      {donutSegs.map(seg => (
                        <div key={seg.name} style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5 }}>
                          <span style={{ width: 8, height: 8, borderRadius: 2, background: seg.color, flexShrink: 0 }}/>
                          <span style={{ color: 'var(--ink-700)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }} title={seg.name}>{seg.name}</span>
                        </div>
                      ))}
                    </div>
                  </>
              }
            </div>
          </div>

          {/* Lista de grupos */}
          <div style={{ flex: 1, overflowY: 'auto' }}>
            {items.length === 0
              ? <div style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-400)', fontSize: 13 }}>Sem dados</div>
              : items.map((it, i) => {
                  const isSel  = selected.has(it.name);
                  const color  = GRP_PALETTE[i % GRP_PALETTE.length];
                  const pct    = gTotal > 0 ? (it.total / gTotal * 100) : 0;
                  const barW   = items[0]?.total > 0 ? (it.total / items[0].total * 100) : 0;
                  return (
                    <button key={it.name} onClick={() => toggle(it.name)} style={{
                      display: 'flex', alignItems: 'center', gap: 10,
                      width: '100%', padding: '8px 14px',
                      background: isSel ? color + '14' : i % 2 === 0 ? 'transparent' : 'var(--ink-50)',
                      border: 'none', borderBottom: '1px solid var(--ink-100)',
                      cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
                      outline: isSel ? `2px solid ${color}33` : 'none',
                      transition: 'background .15s',
                    }}>
                      {/* Checkbox visual */}
                      <span style={{
                        width: 14, height: 14, borderRadius: 3, flexShrink: 0,
                        background: isSel ? color : 'var(--ink-100)',
                        border: `2px solid ${isSel ? color : 'var(--ink-300)'}`,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        transition: 'all .15s',
                      }}>
                        {isSel && <svg width="8" height="6" viewBox="0 0 8 6"><polyline points="1,3 3,5 7,1" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                      </span>

                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 3 }}>
                          <span style={{
                            fontSize: 12.5, fontWeight: isSel ? 600 : 400,
                            color: isSel ? 'var(--ink-900)' : 'var(--ink-700)',
                            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 140,
                          }} title={it.name}>{it.name}</span>
                          <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
                            <span className="mono" style={{ fontSize: 11, color: 'var(--ink-400)' }}>{pct.toFixed(1)}%</span>
                            <span className="mono" style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--ink-800)' }}>{BRL(it.total)}</span>
                          </div>
                        </div>
                        <div style={{ height: 3, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden' }}>
                          <div style={{ height: '100%', borderRadius: 99, background: isSel ? color : 'var(--ink-200)', width: `${barW}%`, transition: 'width .4s, background .2s' }} />
                        </div>
                      </div>
                    </button>
                  );
                })
            }
          </div>

          {/* Footer */}
          <div style={{ padding: '8px 14px', borderTop: '1px solid var(--ink-100)', background: 'var(--ink-50)', flexShrink: 0, display: 'flex', justifyContent: 'space-between', fontSize: 11.5, color: 'var(--ink-500)' }}>
            <span>{items.length} {tab} no período</span>
            <span style={{ fontWeight: 600, color: 'var(--ink-700)' }}>{selected.size} selecionados</span>
          </div>
        </div>
      )}

      {/* Botão toggle — horizontal, na parte inferior */}
      <button
        onClick={() => setExpanded(e => !e)}
        style={{
          pointerEvents: 'auto',
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '10px 16px',
          background: expanded ? 'var(--brand-500)' : 'var(--white)',
          color: expanded ? 'var(--white)' : 'var(--brand-500)',
          border: `1.5px solid ${expanded ? 'var(--brand-500)' : 'var(--ink-200)'}`,
          borderRadius: 40, cursor: 'pointer', fontFamily: 'inherit',
          fontSize: 13, fontWeight: 600,
          boxShadow: 'var(--shadow-md)',
          transition: 'all .2s',
        }}
      >
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <circle cx="12" cy="12" r="10"/><path d="M12 2a10 10 0 0 1 0 20M12 2C6.5 2 2 6.5 2 12M12 22C17.5 22 22 17.5 22 12"/>
        </svg>
        {expanded ? 'Fechar' : 'Mostrar Grupos/Classes'}
        {selected.size > 0 && !expanded && (
          <span style={{
            background: 'var(--brand-500)', color: 'var(--white)',
            borderRadius: 10, fontSize: 10, fontWeight: 700,
            padding: '1px 6px', marginLeft: 2,
          }}>{selected.size}</span>
        )}
      </button>
    </div>
  );
}

const PAYMENT_COLORS = ['#3B82F6','#22C55E','#14B8A6','#F59E0B','#8B5CF6','#EF4444','#06B6D4','#F97316'];

function PaymentsBreakdown({ payments, loading }) {
  if (loading) return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 16 }}>Formas de pagamento</div>
      {[...Array(4)].map((_, i) => (
        <div key={i} style={{ height: 36, borderRadius: 8, background: 'var(--ink-100)', marginBottom: 8, animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
      ))}
    </div>
  );

  const active = (payments ?? []).filter(p => p.total > 0);
  if (!active.length) return null;

  const grandTotal = active.reduce((s, p) => s + p.total, 0);

  return (
    <div className="card" style={{ padding: '20px 24px' }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 14 }}>
        Formas de pagamento
      </div>

      {/* Barra proporcional */}
      <div style={{ display: 'flex', height: 7, borderRadius: 4, overflow: 'hidden', marginBottom: 18, gap: 2 }}>
        {active.map((p, i) => (
          <div key={p.name} style={{
            flex: grandTotal > 0 ? p.total / grandTotal : 0,
            background: PAYMENT_COLORS[i % PAYMENT_COLORS.length],
            minWidth: p.total > 0 ? 3 : 0,
          }}/>
        ))}
      </div>

      {/* Lista */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
        {active.map((p, i) => {
          const pct = grandTotal > 0 ? (p.total / grandTotal) * 100 : 0;
          const color = PAYMENT_COLORS[i % PAYMENT_COLORS.length];
          return (
            <div key={p.name} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '7px 0', borderBottom: '1px solid var(--ink-50)' }}>
              <div style={{ width: 10, height: 10, borderRadius: '50%', background: color, flexShrink: 0 }}/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {p.name}
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--ink-400)', marginTop: 1 }}>
                  {NUM(p.salesCount)} {p.salesCount === 1 ? 'venda' : 'vendas'} · {pct.toFixed(1)}%
                </div>
              </div>
              <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-800)', flexShrink: 0 }}>
                {BRL(p.total)}
              </div>
            </div>
          );
        })}
        {/* Total */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0 2px' }}>
          <div style={{ width: 10, height: 10, flexShrink: 0 }}/>
          <div style={{ flex: 1, fontSize: 13.5, fontWeight: 700, color: 'var(--ink-700)' }}>Total</div>
          <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', flexShrink: 0 }}>
            {BRL(grandTotal)}
          </div>
        </div>
      </div>
    </div>
  );
}

function SalesView({ data, loading, error, syncAt, period, mtdTotal }) {
  if (error) return <ErrorState />;
  const sectionRef = React.useRef(null);
  const isYtd = period === 'ytd';
  return (
    <>
      <KpiRow kpis={data?.kpis} loading={loading} syncAt={syncAt} />
      {!loading && <ProjectionCard mtdTotal={mtdTotal} stores={data?.stores} />}
      <div ref={sectionRef} style={{ display: 'grid', gridTemplateColumns: '1.65fr 1fr', gap: 20, marginTop: 20 }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          <SalesChart data={data?.dayData} loading={loading} totals={data?.totals} />
          <ClassGroupSection groups={data?.groupRanking} classes={data?.classRanking} loading={loading} />
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          <StoreLeaderboard stores={data?.topStores} loading={loading} period={period} />
          <CategoryBreakdown cats={data?.categories} total={data?.categoryTotal} totals={data?.totals} loading={loading} />
          <PaymentsBreakdown payments={data?.payments} loading={loading} />
        </div>
      </div>
      {!loading && (
        <GroupsFloatingPanel
          groups={data?.groupRanking}
          classes={data?.classRanking}
          grandTotal={data?.totals?.total}
          anchorRef={sectionRef}
        />
      )}
    </>
  );
}

// ─── Classes / Grupos (espelho fiel do ClassGroupSection do mobile) ───────────
// Cores de medalha idênticas ao mobile (ranking-item.tsx)
const CG_MEDALS = [
  { dot: '#F59E0B', bg: 'rgba(245,158,11,0.09)',  border: 'rgba(245,158,11,0.30)'  },  // ouro
  { dot: '#94A3B8', bg: 'rgba(148,163,184,0.09)', border: 'rgba(148,163,184,0.30)' },  // prata
  { dot: '#CD7F32', bg: 'rgba(205,127,50,0.09)',  border: 'rgba(205,127,50,0.30)'  },  // bronze
];
const CG_PAGE       = 10;
const CG_BLUE       = '#3B82F6';  // cor da barra/valor para itens sem medalha (igual mobile)
const CG_PCT_GREEN  = '#1E8A4C';  // cor do "% do total" (igual mobile itemPct)

function ClassGroupSection({ groups, classes, loading }) {
  const hasGroups  = (groups?.length ?? 0) > 0;
  const hasClasses = (classes?.length ?? 0) > 0;

  const [tab,   setTab]   = React.useState(hasClasses ? 'classes' : 'grupos'); // mobile abre em Classes
  const [sort,  setSort]  = React.useState('total');
  const [limit, setLimit] = React.useState(CG_PAGE);

  React.useEffect(() => {
    if (!hasClasses && hasGroups) setTab('grupos');
    if (!hasGroups  && hasClasses) setTab('classes');
  }, [hasGroups, hasClasses]);

  React.useEffect(() => { setLimit(CG_PAGE); }, [tab, sort]);

  const rows = React.useMemo(() => {
    const src = tab === 'grupos' ? (groups ?? []) : (classes ?? []);
    return [...src].sort((a, b) => {
      if (sort === 'margin') {
        const ma = a.total > 0 ? (a.total - (a.totalCost ?? 0)) / a.total : 0;
        const mb = b.total > 0 ? (b.total - (b.totalCost ?? 0)) / b.total : 0;
        return mb - ma;
      }
      return b[sort] - a[sort];
    });
  }, [tab, sort, groups, classes]);

  const grandTotal    = rows.reduce((s, r) => s + r.total,    0);
  const grandQuantity = rows.reduce((s, r) => s + r.quantity, 0);
  const hasCost       = rows.some(r => (r.totalCost ?? 0) > 0);
  const visible       = rows.slice(0, limit);
  const hasMore       = rows.length > limit;

  if (loading) return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '20px 24px 14px', fontSize: 16, fontWeight: 700 }}>Classes / Grupos</div>
      <div style={{ padding: '0 16px 16px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {[...Array(5)].map((_, i) => (
          <div key={i} style={{ height: 72, borderRadius: 12, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
        ))}
      </div>
    </div>
  );

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>

      {/* ── Segment control (Classes / Grupos) — centralizado, igual mobile ── */}
      {hasGroups && hasClasses && (
        <div style={{ padding: '14px 16px 0', display: 'flex', justifyContent: 'center' }}>
          <div style={{ display: 'flex', background: '#E8EEF8', borderRadius: 10, padding: 3 }}>
            {[['classes','Classes'],['grupos','Grupos']].map(([v, l]) => (
              <button key={v} onClick={() => setTab(v)} style={{
                padding: '6px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                border: 0, borderRadius: 8, cursor: 'pointer',
                background: tab === v ? 'var(--white)' : 'transparent',
                color:      tab === v ? 'var(--brand-600, var(--brand-700))' : '#888',
                boxShadow:  tab === v ? '0 1px 4px rgba(0,0,0,0.08)' : 'none',
                transition: 'all .12s',
              }}>{l}</button>
            ))}
          </div>
        </div>
      )}

      {/* ── Cabeçalho do card: título + pills de ordenação ── */}
      <div style={{ padding: '12px 16px 10px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>
          {tab === 'classes' ? 'Vendas por Classe' : 'Vendas por Grupo'}
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {[['total', 'R$'], ['quantity', 'Qtd'], ...(hasCost ? [['margin', 'Margem']] : [])].map(([k, l]) => (
            <button key={k} onClick={() => setSort(k)} style={{
              padding: '3px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
              border: 0, borderRadius: 999, cursor: 'pointer',
              background: sort === k ? 'var(--brand-500)' : '#E8EEF8',
              color:      sort === k ? '#fff' : '#666',
              transition: 'all .12s',
            }}>{l}</button>
          ))}
        </div>
      </div>

      {/* ── Lista de itens ── */}
      {!rows.length
        ? <EmptyState text="Sem dados para este período." style={{ padding: '0 16px 20px' }} />
        : (
          <div style={{ padding: '0 12px 14px', display: 'flex', flexDirection: 'column', gap: 8 }}>
            {visible.map((row, i) => {
              const margin    = (row.totalCost ?? 0) > 0 && row.total > 0
                ? (row.total - row.totalCost) / row.total * 100 : null;
              const pct       = sort === 'total'   ? (grandTotal    > 0 ? row.total    / grandTotal    : 0)
                              : sort === 'quantity' ? (grandQuantity > 0 ? row.quantity / grandQuantity : 0)
                              : (grandTotal > 0 ? row.total / grandTotal : 0);
              const medal     = CG_MEDALS[i] ?? null;
              const barColor  = medal?.dot ?? CG_BLUE;
              const cardBg    = medal?.bg  ?? 'var(--white)';
              const cardBd    = medal?.border ?? 'rgba(0,0,0,0.1)';

              return (
                <div key={row.name} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
                  <div style={{ width: 4, borderRadius: 4, flexShrink: 0, background: barColor }}/>

                  <div style={{
                    flex: 1, borderRadius: 12, padding: '10px 16px',
                    background: cardBg, border: `0.5px solid ${cardBd}`,
                  }}>
                    {/* Sub-info superior */}
                    <div style={{ fontSize: 12, color: '#6B7280', marginBottom: 5 }}>
                      {sort === 'total'    ? `${NUM(row.quantity)} itens` :
                       sort === 'quantity' ? BRL(row.total) :
                       `${NUM(row.quantity)} itens · ${BRL(row.total)}`}
                    </div>

                    {/* Linha principal */}
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
                        <div style={{
                          width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
                          background: medal ? medal.dot + '30' : '#F0F0F0',
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontSize: 12, fontWeight: 700, color: medal?.dot ?? '#999',
                        }}>{i + 1}</div>
                        <span style={{
                          fontSize: 14, fontWeight: 500, color: 'var(--ink-900)',
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        }}>{row.name}</span>
                      </div>

                      <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 10 }}>
                        <div className="mono" style={{ fontSize: 14, fontWeight: 500, color: 'var(--brand-500)' }}>
                          {sort === 'quantity' ? NUM(row.quantity) : BRL(row.total)}
                        </div>
                        <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end', alignItems: 'center', marginTop: 2 }}>
                          <span style={{ fontSize: 11, fontWeight: 700, color: CG_PCT_GREEN }}>
                            {(pct * 100).toFixed(1)}% participação na rede
                          </span>
                          {margin !== null && (
                            <span style={{
                              fontSize: 11, fontWeight: 700,
                              color: margin >= 30 ? '#1D9E50' : margin >= 20 ? '#B36A00' : '#C2363A',
                            }}>
                              · {margin.toFixed(1)}% margem
                            </span>
                          )}
                        </div>
                        {hasCost && sort === 'total' && (row.totalCost ?? 0) > 0 && (
                          <div style={{ fontSize: 10, color: 'var(--ink-400)', marginTop: 1 }}>
                            CMV {BRL(row.totalCost)}
                          </div>
                        )}
                      </div>
                    </div>

                    {/* Barra de progresso */}
                    <div style={{ marginTop: 8, height: 3, background: '#E8EEF8', borderRadius: 99, overflow: 'hidden' }}>
                      <div style={{
                        height: '100%', width: `${Math.round(pct * 100)}%`,
                        background: CG_BLUE, borderRadius: 99,
                      }}/>
                    </div>
                  </div>
                </div>
              );
            })}

            {/* "Mostrar mais" — estilo azul bordado igual mobile */}
            {hasMore && (
              <button onClick={() => setLimit(l => l + CG_PAGE)} style={{
                marginTop: 2, padding: '8px', fontSize: 13, fontWeight: 600,
                background: '#F0F5FF', border: '1px solid #B5D4F4',
                borderRadius: 10, cursor: 'pointer',
                color: 'var(--brand-600, var(--brand-700))',
                fontFamily: 'inherit', width: '100%',
              }}>
                Mostrar mais ({rows.length - limit} restantes)
              </button>
            )}
          </div>
        )
      }
    </div>
  );
}

// ─── Compras: helpers de status/ratio (espelha mobile) ───────────────────────
const PUR_THRESHOLDS = { green: 55, yellow: 65, orange: 70 };
const purStatus = (ratio, { green = 55, yellow = 65, orange = 70 } = PUR_THRESHOLDS) => {
  if (!ratio || ratio <= 0) return { color: 'var(--ink-400)', label: 'Sem dados',           bg: 'var(--ink-100)', border: 'var(--ink-200)' };
  const pct = ratio * 100;
  if (pct <= green)  return { color: '#16A34A', label: 'Compra eficiente',     bg: '#E6F7EE', border: '#A3DDB8' };
  if (pct <= yellow) return { color: '#D97706', label: 'Dentro do padrão',     bg: '#FFF4E0', border: '#FBCF7C' };
  if (pct <= orange) return { color: '#EA6C00', label: 'Margem apertando',     bg: '#FFF0E6', border: '#FFBB94' };
  return                    { color: '#DC2626', label: 'Acima do recomendado', bg: '#FBE8E9', border: '#F0AAAC' };
};
const PUR_MEDALS = [
  { dot: '#FFD700', bg: '#FFFBEA', border: '#F0D060' },
  { dot: '#C0C0C0', bg: '#F8F8F8', border: '#C8C8C8' },
  { dot: '#CD7F32', bg: '#FDF5EE', border: '#E0B88A' },
];

// ─── KPI hero + planning card ─────────────────────────────────────────────────
function PurchasesChart({ data, loading, mfgSeries, externalMfgFilter }) {
  const [selectedMfg, setSelectedMfg] = React.useState(null);
  const [open, setOpen] = React.useState(false);

  // Lista de fornecedores ordenada por total
  const mfgList = React.useMemo(() => {
    if (!mfgSeries) return [];
    return Object.entries(mfgSeries)
      .map(([k, v]) => ({ key: k, name: v.name, total: v.total }))
      .sort((a, b) => b.total - a.total);
  }, [mfgSeries]);

  // Se o fornecedor selecionado sumiu dos dados, reseta
  React.useEffect(() => {
    if (selectedMfg && !mfgSeries?.[selectedMfg]) setSelectedMfg(null);
  }, [mfgSeries]);

  // Filtro externo (do topo) tem prioridade sobre o dropdown interno
  const hasExternalFilter = externalMfgFilter && externalMfgFilter.size > 0;
  const SERIES_COLORS = ['#3B82F6','#22C55E','#F59E0B','#EF4444','#8B5CF6','#14B8A6','#F97316','#EC4899'];

  // Séries múltiplas quando >1 fornecedor selecionado no filtro externo
  const multiSeries = React.useMemo(() => {
    if (!hasExternalFilter || !mfgSeries || externalMfgFilter.size <= 1) return null;
    return [...externalMfgFilter]
      .map((name, i) => {
        const entry = Object.values(mfgSeries).find(s => s.name === name);
        if (!entry) return null;
        return { name, color: SERIES_COLORS[i % SERIES_COLORS.length], data: entry.data };
      })
      .filter(Boolean);
  }, [hasExternalFilter, mfgSeries, externalMfgFilter]);
  const externalNames     = hasExternalFilter ? [...externalMfgFilter].join(', ') : null;

  const activeData  = !hasExternalFilter && selectedMfg && mfgSeries?.[selectedMfg]
    ? mfgSeries[selectedMfg].data
    : data;
  const activeName  = hasExternalFilter
    ? (externalMfgFilter.size === 1 ? externalNames : null)
    : selectedMfg ? (mfgSeries?.[selectedMfg]?.name ?? '') : null;
  const activeTotal = hasExternalFilter
    ? (data ?? []).reduce((s, d) => s + (d.v ?? 0), 0)
    : selectedMfg ? (mfgSeries?.[selectedMfg]?.total ?? 0) : null;
  const dayData     = (!loading && activeData?.length) ? activeData : [];

  if (loading) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Compras por dia</div>
      <div style={{ height: 280, borderRadius: 8, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
    </div>
  );

  if (!dayData.length || dayData.every(d => d.v === 0)) return null;

  return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', marginBottom: 20 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 16, fontWeight: 700 }}>
            {activeName ? activeName : 'Compras por dia'}
          </div>
          {activeTotal !== null && (
            <div style={{ fontSize: 13, color: 'var(--ink-500)', marginTop: 2, display: 'flex', gap: 12, flexWrap: 'wrap' }}>
              <span>{BRL(activeTotal)}</span>
              {mfgSeries?.[selectedMfg]?.qty > 0 && (
                <span>{NUM(mfgSeries[selectedMfg].qty)} itens</span>
              )}
              {mfgSeries?.[selectedMfg]?.unity > 0 && (
                <span>{NUM(mfgSeries[selectedMfg].unity)} unidades</span>
              )}
            </div>
          )}
        </div>

        {/* Seletor de fornecedor */}
        {!hasExternalFilter && mfgList.length > 0 && (
          <div style={{ position: 'relative', flexShrink: 0 }}>
            <button
              onClick={() => setOpen(o => !o)}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '6px 12px', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
                border: `1.5px solid ${selectedMfg ? 'var(--brand-500)' : 'var(--ink-200)'}`,
                borderRadius: 8, cursor: 'pointer',
                background: selectedMfg ? 'var(--brand-50)' : 'var(--white)',
                color: selectedMfg ? 'var(--brand-700)' : 'var(--ink-700)',
              }}
            >
              <Icon name="filter" size={13} />
              {selectedMfg ? 'Fornecedor' : 'Por fornecedor'}
              {selectedMfg && (
                <span
                  onClick={e => { e.stopPropagation(); setSelectedMfg(null); }}
                  style={{ marginLeft: 2, color: 'var(--brand-500)', fontWeight: 700, lineHeight: 1 }}
                >×</span>
              )}
            </button>

            {open && (
              <>
                <div style={{ position: 'fixed', inset: 0, zIndex: 29 }} onClick={() => setOpen(false)} />
                <div style={{
                  position: 'absolute', top: 'calc(100% + 6px)', right: 0,
                  width: 300, maxHeight: 320, overflowY: 'auto',
                  background: 'var(--white)', border: '1px solid var(--ink-200)',
                  borderRadius: 12, boxShadow: 'var(--shadow-md)', zIndex: 30,
                }}>
                  <button
                    onClick={() => { setSelectedMfg(null); setOpen(false); }}
                    style={{
                      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                      width: '100%', padding: '10px 14px',
                      background: !selectedMfg ? 'var(--brand-50)' : 'transparent',
                      color: !selectedMfg ? 'var(--brand-700)' : 'var(--ink-700)',
                      border: 0, borderBottom: '1px solid var(--ink-100)',
                      cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 600,
                    }}
                  >
                    Todos os fornecedores
                  </button>
                  {mfgList.map(m => (
                    <button
                      key={m.key}
                      onClick={() => { setSelectedMfg(m.key); setOpen(false); }}
                      style={{
                        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                        width: '100%', padding: '9px 14px',
                        background: selectedMfg === m.key ? 'var(--brand-50)' : 'transparent',
                        color: selectedMfg === m.key ? 'var(--brand-700)' : 'var(--ink-800)',
                        border: 0, borderBottom: '1px solid var(--ink-50)',
                        cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
                        fontSize: 13, fontWeight: selectedMfg === m.key ? 600 : 500,
                      }}
                      onMouseEnter={e => { if (selectedMfg !== m.key) e.currentTarget.style.background = 'var(--ink-50)'; }}
                      onMouseLeave={e => { if (selectedMfg !== m.key) e.currentTarget.style.background = 'transparent'; }}
                    >
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 200 }}>{m.name}</span>
                      <span className="mono" style={{ fontSize: 11.5, color: 'var(--ink-400)', flexShrink: 0, marginLeft: 8 }}>{BRL(m.total)}</span>
                    </button>
                  ))}
                </div>
              </>
            )}
          </div>
        )}
      </div>

      {multiSeries && (
        <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginBottom: 12 }}>
          {multiSeries.map(s => (
            <LegendDot key={s.name} color={s.color} label={s.name.length > 30 ? s.name.slice(0, 30) + '…' : s.name} />
          ))}
        </div>
      )}
      <BigChart data={dayData} lineColor="var(--brand-500)" gradientId="pc-grad" tooltipLabel={activeName ?? 'Compras'} hideVariation series={multiSeries} />
    </div>
  );
}

function PurchasesHeroCard({ totals, loading, cmvGreen = 55, cmvYellow = 65, cmvOrange = 70 }) {
  const [infoOpen, setInfoOpen] = React.useState(false);
  const purchaseTotal = totals?.purchaseTotal ?? 0;
  const salesTotal    = totals?.total ?? 0;
  const ratio         = salesTotal > 0 ? purchaseTotal / salesTotal : 0;
  const st            = purStatus(ratio, { green: cmvGreen, yellow: cmvYellow, orange: cmvOrange });

  // "para cada R$1 vendido, R$X foi comprado"
  const perReal = salesTotal > 0 ? purchaseTotal / salesTotal : 0;

  if (loading) return (
    <div className="card" style={{ padding: 24, background: 'linear-gradient(160deg,#1a4789,#0c2340)', border: '1px solid var(--brand-700)' }}>
      <div style={{ height: 80, borderRadius: 8, background: 'rgba(255,255,255,0.1)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
    </div>
  );

  return (
    <div className="card" style={{ padding: '20px 24px', background: 'linear-gradient(160deg,#1a4789 0%,#0c2340 100%)', border: '1px solid var(--brand-700)', color: '#ffffff' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 2 }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.65)' }}>Compras do Período</div>
        {ratio > 0 && (
          <button onClick={() => setInfoOpen(o => !o)} style={{
            width: 24, height: 24, borderRadius: '50%', border: 'none', cursor: 'pointer',
            background: infoOpen ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.12)',
            color: 'rgba(255,255,255,0.85)', fontSize: 13, display: 'flex',
            alignItems: 'center', justifyContent: 'center', flexShrink: 0,
            transition: 'background .15s',
          }} title="O que significa este %?">ⓘ</button>
        )}
      </div>
      <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', marginBottom: 10 }}>
        Compras / Vendas, não é o CMV
      </div>

      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16, flexWrap: 'wrap' }}>
        <div className="mono" style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.02em', lineHeight: 1 }}>{BRL(purchaseTotal)}</div>
        {ratio > 0 && (
          <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '4px 10px', borderRadius: 20, background: st.color + '33', border: `1px solid ${st.color}55`, marginBottom: 4 }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: st.color }} />
            <span style={{ fontSize: 12, fontWeight: 700, color: st.color }}>{(ratio * 100).toFixed(1)}% Compras/Vendas · {st.label}</span>
          </div>
        )}
      </div>

      {salesTotal > 0 && (
        <div style={{ marginTop: 14 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'rgba(255,255,255,0.55)', marginBottom: 6 }}>
            <span>Compras / Vendas</span>
            <span className="mono">{BRL(purchaseTotal)} / {BRL(salesTotal)}</span>
          </div>
          <div style={{ height: 5, background: 'rgba(255,255,255,0.15)', borderRadius: 99, overflow: 'hidden' }}>
            <div style={{ height: '100%', borderRadius: 99, background: st.color, width: `${Math.min(ratio * 100, 100)}%`, transition: 'width .6s' }} />
          </div>
        </div>
      )}

      {infoOpen && ratio > 0 && (
        <div style={{ marginTop: 14, paddingTop: 14, borderTop: '1px solid rgba(255,255,255,0.15)', fontSize: 13, color: 'rgba(255,255,255,0.8)', lineHeight: 1.6 }}>
          <div style={{ marginBottom: 8, color: 'rgba(255,255,255,0.55)', fontSize: 12 }}>
            Isso <strong>não é o CMV</strong> (custo das mercadorias vendidas). É a relação entre o quanto foi comprado dos fornecedores e o quanto foi vendido no período — mostra ritmo de reposição, não margem.
          </div>
          <span className="mono" style={{ fontWeight: 700, color: st.color }}>{(ratio * 100).toFixed(1)}%</span>
          {' '}significa que para os{' '}
          <span className="mono" style={{ fontWeight: 700 }}>{BRL(purchaseTotal)}</span>
          {' '}comprados, foram vendidos{' '}
          <span className="mono" style={{ fontWeight: 700 }}>{BRL(salesTotal)}</span>.
          <br/>
          A cada <span className="mono" style={{ fontWeight: 700 }}>R$ 1,00</span> vendido,{' '}
          <span className="mono" style={{ fontWeight: 700, color: st.color }}>R$ {perReal.toFixed(2)}</span> foi comprado.
        </div>
      )}
    </div>
  );
}

// ─── Planejamento de Compras ──────────────────────────────────────────────────
function PurchasesPlanningCard({ totals, loading, metaCompras = 0.70, metaComprasMode = 'pct', extraBudget = 0, extraTotal = 0 }) {
  const [infoOpen, setInfoOpen] = React.useState(false);
  const target  = metaCompras;
  const isCmv   = metaComprasMode === 'cmv';

  const purchaseTotal = totals?.purchaseTotal ?? 0;
  const salesTotal    = totals?.total         ?? 0;
  const totalCost     = totals?.totalCost      ?? 0;

  const planned     = isCmv ? totalCost : salesTotal * target;
  const achievement = planned > 0 ? purchaseTotal / planned : 0;

  const achColor = achievement <= 0.90 ? '#1D9E50'
                 : achievement <= 1.10 ? '#B36A00'
                 : achievement <= 1.25 ? '#C04B00'
                 : '#C2363A';

  if (loading) return <div className="card" style={{ padding: 24, height: 120, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />;

  const remaining = planned - purchaseTotal;

  return (
    <div className="card" style={{ padding: 20 }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 12, gap: 8 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--ink-500)' }}>
            Planejamento de Compras
          </div>
          {isCmv && (
            <button onClick={() => setInfoOpen(o => !o)} style={{
              width: 18, height: 18, borderRadius: '50%', border: 'none', cursor: 'pointer',
              background: infoOpen ? 'var(--ink-200)' : 'var(--ink-100)',
              color: 'var(--ink-500)', fontSize: 11, display: 'flex',
              alignItems: 'center', justifyContent: 'center', flexShrink: 0,
              transition: 'background .15s',
            }} title="Como o CMV foi calculado?">ⓘ</button>
          )}
        </div>
        <div style={{ fontSize: 11, color: 'var(--ink-400)' }}>
          {isCmv
            ? <>CMV: {BRL(totalCost)}{salesTotal > 0 && <span style={{ marginLeft: 6, fontWeight: 700, color: 'var(--ink-600)' }}>({((totalCost / salesTotal) * 100).toFixed(1)}% das vendas)</span>}</>
            : `base: ${BRL(salesTotal)} em vendas`}
        </div>
      </div>

      {isCmv && infoOpen && (
        <div style={{ fontSize: 11.5, color: 'var(--ink-600)', background: 'var(--ink-50)', border: '1px solid var(--ink-100)', borderRadius: 8, padding: '8px 10px', marginBottom: 12 }}>
          Vendeu <strong className="mono">{BRL(salesTotal)}</strong> no período · Custo do vendido <strong className="mono">{BRL(totalCost)}</strong> → CMV <strong className="mono">{((totalCost / (salesTotal || 1)) * 100).toFixed(1)}%</strong>
        </div>
      )}

      {/* Comprado vs Orçamento */}
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16 }}>
        <div>
          <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)', letterSpacing: '0.03em', marginBottom: 2 }}>Comprado</div>
          <div className="mono" style={{ fontSize: 26, fontWeight: 700, lineHeight: 1, color: achColor }}>
            {BRL(purchaseTotal)}
          </div>
        </div>
        <div style={{ color: 'var(--ink-300)', fontSize: 16, marginBottom: 4, flexShrink: 0 }}>→</div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)', letterSpacing: '0.03em', marginBottom: 2 }}>
            {isCmv ? 'Meta de reposição (= CMV do período)' : `Orçamento (${(target * 100).toFixed(0)}% das vendas)`}
          </div>
          <div className="mono" style={{ fontSize: 26, fontWeight: 700, lineHeight: 1, color: 'var(--ink-700)' }}>
            {BRL(planned)}
          </div>
        </div>
      </div>

      {/* Barra de progresso */}
      <div style={{ marginTop: 10, height: 6, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden' }}>
        <div style={{ height: '100%', borderRadius: 99, background: achColor, width: `${Math.min(achievement * 100, 100)}%`, transition: 'width .6s' }} />
      </div>

      {/* Rodapé: % de uso + saldo */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginTop: 6 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: achColor }}>
          {isCmv
            ? `Comprou ${(achievement * 100).toFixed(1)}% acima do orçamento.`
            : `${(achievement * 100).toFixed(1)}% do orçamento utilizado`}
        </div>
        {planned > 0 && (
          <div style={{ textAlign: 'right' }}>
            <div style={{ fontSize: 11, color: remaining >= 0 ? 'var(--ink-400)' : '#C2363A', fontWeight: 600 }}>
              {remaining >= 0
                ? (isCmv ? `${BRL(remaining)} até igualar o CMV` : `${BRL(remaining)} disponível`)
                : (isCmv ? `${BRL(Math.abs(remaining))} além do CMV | virou estoque` : `${BRL(Math.abs(remaining))} acima do orçamento`)}
            </div>
            {/* verba extra oculta temporariamente */}
          </div>
        )}
      </div>
      {!isCmv && (
        <div style={{ fontSize: 10, color: 'var(--ink-400)', marginTop: 4 }}>
          Comprado ÷ Orçamento — essa % não é a % de CMV
        </div>
      )}

    </div>
  );
}

// ─── Ranking de Compras por Filial ────────────────────────────────────────────
function PurchasesStoreRanking({ stores, loading, cmvGreen = 55, cmvYellow = 65, cmvOrange = 70 }) {
  const thresholds = { green: cmvGreen, yellow: cmvYellow, orange: cmvOrange };
  const [expandedRows, setExpandedRows] = React.useState(new Set());
  const toggleRow = (id) => setExpandedRows(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const rows = [...stores]
    .filter(s => s.purchases?.total > 0)
    .sort((a, b) => {
      const ra = a.total > 0 ? a.purchases.total / a.total : Infinity;
      const rb = b.total > 0 ? b.purchases.total / b.total : Infinity;
      return ra - rb;
    });
  const grandPur = rows.reduce((s, r) => s + r.purchases.total, 0);

  if (loading) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Compras por Filial</div>
      {[...Array(4)].map((_, i) => (
        <div key={i} style={{ height: 78, borderRadius: 12, background: 'var(--ink-100)', marginBottom: 8, animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
      ))}
    </div>
  );
  if (!rows.length) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 12 }}>Compras por Filial</div>
      <EmptyState text="Sem dados de compras no período." />
    </div>
  );

  return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Compras por Filial</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {rows.map((s, i) => {
          const medal      = CG_MEDALS[i] ?? null;
          const ratio      = s.total > 0 ? s.purchases.total / s.total : 0;
          const st         = purStatus(ratio, thresholds);
          const sharePct   = grandPur > 0 ? (s.purchases.total / grandPur * 100) : 0;
          const isExpanded = expandedRows.has(s.storeId);

          return (
            <div key={s.storeId} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
              <div style={{ width: 4, borderRadius: 4, flexShrink: 0, background: medal?.dot ?? 'transparent' }}/>

              <div
                onClick={() => toggleRow(s.storeId)}
                style={{
                  flex: 1, borderRadius: 12, padding: '10px 16px', cursor: 'pointer',
                  background: medal?.bg ?? 'var(--white)',
                  border: `0.5px solid ${medal?.border ?? 'rgba(0,0,0,0.1)'}`,
                  transition: 'filter .12s',
                }}
                onMouseEnter={e => e.currentTarget.style.filter = 'brightness(0.96)'}
                onMouseLeave={e => e.currentTarget.style.filter = ''}
              >
                {/* Badge + nome | valor comprado */}
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
                    <div style={{
                      width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
                      background: medal ? medal.dot + '30' : '#F0F0F0',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 12, fontWeight: 700, color: medal?.dot ?? '#999',
                    }}>{i + 1}</div>
                    <span style={{ fontSize: 14, fontWeight: 500, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {s.storeName || `Loja ${s.storeId}`}
                    </span>
                  </div>
                  <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 10 }}>
                    <div className="mono" style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-900)' }}>{BRL(s.purchases.total)}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 1 }}>
                      em compras{s.cost > 0 && s.total > 0 ? <> · CMV <span className="mono">{((s.cost / s.total) * 100).toFixed(1)}%</span></> : ''}
                    </div>
                  </div>
                </div>

                {/* Linha de métricas: cápsula participação + label de eficiência */}
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 8, marginBottom: 6 }}>
                  <span style={{
                    display: 'inline-flex', alignItems: 'center',
                    background: 'var(--ink-100)', border: '1px solid var(--ink-200)',
                    borderRadius: 20, padding: '2px 10px',
                    fontSize: 11, fontWeight: 700, color: 'var(--ink-700)',
                  }}>
                    {sharePct.toFixed(1)}% das compras da rede
                  </span>
                  {ratio > 0 && (
                    <span style={{
                      display: 'inline-flex', alignItems: 'center',
                      background: st.bg, border: `1px solid ${st.border}`,
                      borderRadius: 20, padding: '2px 10px',
                      fontSize: 11, fontWeight: 700, color: st.color,
                    }}>
                      {st.label}
                    </span>
                  )}
                </div>

                {/* Barra: comprado vs vendido (ratio), colorida por status */}
                <div style={{ height: 5, background: 'rgba(0,0,0,0.07)', borderRadius: 99, overflow: 'hidden' }}>
                  <div style={{ height: '100%', borderRadius: 99, background: st.color, width: `${Math.min(ratio * 100, 100)}%`, transition: 'width .5s' }} />
                </div>

                {isExpanded && s.total > 0 && (
                  <div style={{ marginTop: 8, paddingTop: 8, borderTop: '1px solid rgba(0,0,0,0.07)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                    <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)' }}>Compras / Vendas</span>
                    <span className="mono" style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-700)' }}>
                      {BRL(s.purchases.total)} / {BRL(s.total)}
                    </span>
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Compras por Classe ───────────────────────────────────────────────────────
// ─── Verba Extra de Compras ───────────────────────────────────────────────────
function PurchasesExtraBudgetCard({ stores, totals, loading, period, customStart, customEnd, user, periodStart, periodEnd, siteConfig, setSiteConfig }) {
  const [infoOpen, setInfoOpen] = React.useState(false);
  const [saving,   setSaving]   = React.useState(false);
  const [histOpen, setHistOpen] = React.useState(false);

  const cnpj       = user?.cnpj;
  const fmtDate    = (iso) => { if (!iso) return ''; const [, m, d] = iso.split('-'); return `${d}/${m}`; };
  const periodDisp = periodStart && periodEnd ? `${fmtDate(periodStart)}–${fmtDate(periodEnd)}` : '';
  const periodBase = periodStart && periodEnd ? `${periodStart}_${periodEnd}` : null;
  // Chave por filial: "2026-05-30_2026-06-05_1"
  const storeKey   = (storeId) => periodBase ? `${periodBase}_${storeId}` : null;

  const verbas = siteConfig?.verbas ?? {};

  const isStoreUsed  = (storeId) => !!(storeKey(storeId) && verbas[storeKey(storeId)]);
  const getStoreUse  = (storeId) => storeKey(storeId) ? verbas[storeKey(storeId)] : null;

  // Calcula cobertura de dias já utilizados para uma filial no período atual
  const getStoreCoverage = (storeId) => {
    if (!periodStart || !periodEnd) return null;
    const sk = storeKey(storeId);
    const totalDays = SF.daysBetween(periodStart, periodEnd) + 1;
    const overlaps = [];
    for (const [key, val] of Object.entries(verbas)) {
      if (key === sk) continue; // ignora o próprio período atual
      const parts = key.split('_');
      if (parts.length < 3 || String(parts[2]) !== String(storeId)) continue;
      const ks = parts[0], ke = parts[1];
      if (ks > periodEnd || ke < periodStart) continue; // sem sobreposição
      const intStart = ks < periodStart ? periodStart : ks;
      const intEnd   = ke > periodEnd   ? periodEnd   : ke;
      const intDays  = SF.daysBetween(intStart, intEnd) + 1;
      overlaps.push({ ks, ke, intStart, intEnd, intDays, val });
    }
    if (!overlaps.length) return null;
    const coveredDays = Math.min(totalDays, overlaps.reduce((s, o) => s + o.intDays, 0));
    const newDays = Math.max(0, totalDays - coveredDays);
    return { overlaps, coveredDays, newDays, totalDays };
  };

  const markStoreUsed = async (storeId, storeName, amount) => {
    const sk = storeKey(storeId);
    if (!cnpj || !sk || saving || isStoreUsed(storeId)) return;
    setSaving(true);
    const entry = { amount, storeName, usedAt: new Date().toISOString(), periodLabel: period === 'custom' ? periodDisp : period };
    try {
      await SF.db.collection('loja').doc(cnpj).collection('config').doc('Site')
        .set({ verbas: { [sk]: entry } }, { merge: true });
      setSiteConfig(prev => ({ ...prev, verbas: { ...(prev?.verbas ?? {}), [sk]: entry } }));
    } catch (e) {
      console.warn('[verba] erro ao salvar', e);
    } finally {
      setSaving(false);
    }
  };
  const salesTotal    = totals?.total ?? 0;
  const purchaseTotal = totals?.purchaseTotal ?? 0;
  const ratio         = salesTotal > 0 ? purchaseTotal / salesTotal : 0;

  // Calcula o goalFactor localmente para evitar dependência de dados cacheados
  const { goalFactor, goalLabel, crossMonth } = React.useMemo(() => {
    const now         = new Date();
    const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
    const currentDay  = now.getDate();

    if (period === 'hoje')   return { goalFactor: 1 / daysInMonth,           goalLabel: '1 dia',                   crossMonth: false };
    if (period === '7d')     return { goalFactor: 7 / daysInMonth,           goalLabel: '7 dias',                  crossMonth: currentDay < 7 };
    if (period === '30d')    return { goalFactor: 30 / daysInMonth,          goalLabel: '30 dias',                 crossMonth: currentDay < 30 };
    if (period === 'mtd')    return { goalFactor: currentDay / daysInMonth,  goalLabel: `${currentDay} dias (mês)`, crossMonth: false };
    if (period === 'custom' && customStart && customEnd) {
      const days = SF.daysBetween(customStart, customEnd) + 1;
      return { goalFactor: days / daysInMonth, goalLabel: `${days} dias`, crossMonth: new Date(customStart).getMonth() !== new Date(customEnd).getMonth() };
    }
    return { goalFactor: null, goalLabel: '', crossMonth: false };
  }, [period, customStart, customEnd]);

  const storeRows = React.useMemo(() => {
    if (goalFactor === null) return [];
    return stores
      .map(s => {
        // Usa goalAccum (acumulado dia a dia, respeita cruzamento de mês)
        // Fallback para totalGoal * goalFactor em dados sem goalAccum
        const goal     = safeN(s.goalAccum) > 0 ? safeN(s.goalAccum) : safeN(s.totalGoal) * goalFactor;
        const excess   = s.total - goal;
        const stRatio  = s.total > 0 ? safeN(s.purchases?.total) / s.total : ratio;
        const extra    = excess > 0 ? excess * (stRatio > 0 ? stRatio : ratio) : 0;
        return { id: s.storeId, name: s.storeName || `Loja ${s.storeId}`, goal, sales: s.total, excess, extra, exceeded: excess > 0 };
      })
      .filter(s => s.goal > 0)
      .sort((a, b) => b.excess - a.excess);
  }, [stores, goalFactor, ratio]);

  // Apenas filiais com meta configurada — evita inflação por filiais sem meta
  const networkGoal          = storeRows.reduce((s, r) => s + r.goal,              0);
  const salesWithGoal        = storeRows.reduce((s, r) => s + r.sales,             0);
  const networkExcess        = storeRows.reduce((s, r) => s + Math.max(0, r.excess), 0);
  const extraTotal           = storeRows.reduce((s, r) => s + r.extra,             0);
  const abovePct             = networkGoal > 0 ? (salesWithGoal / networkGoal - 1) * 100 : null;
  const usedExtra            = React.useMemo(() => {
    if (!periodBase) return 0;
    return Object.entries(verbas)
      .filter(([key]) => key.startsWith(`${periodBase}_`))
      .reduce((sum, [, val]) => sum + (val?.amount ?? 0), 0);
  }, [verbas, periodBase]);
  const availableExtra       = Math.max(0, extraTotal - usedExtra);

  if (loading) return (
    <div className="card" style={{ padding: 24, height: 120, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite', borderRadius: 'var(--radius)' }} />
  );

  if (goalFactor === null || networkGoal === 0) return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>Verba Extra de Compras</div>
      <div style={{ fontSize: 13, color: 'var(--ink-400)' }}>Metas não configuradas para este período.</div>
    </div>
  );

  const exceeded = storeRows.filter(r => r.exceeded);
  const missed   = storeRows.filter(r => !r.exceeded);

  // Vendas e meta apenas das filiais que superaram (para a explicação fechar matematicamente)
  const exceededSales = exceeded.reduce((s, r) => s + r.sales, 0);
  const exceededGoal  = exceeded.reduce((s, r) => s + r.goal,  0);

  return (
    <div className="card" style={{ padding: 20 }}>
      {/* Cabeçalho */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: crossMonth ? 8 : 16 }}>
        <div>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
            Verba Extra de Compras
          </div>
          {goalLabel && (
            <div style={{ fontSize: 10, color: 'var(--ink-400)', marginTop: 1 }}>
              Meta do período: {goalLabel} do mês corrente
            </div>
          )}
        </div>
        <button onClick={() => setInfoOpen(o => !o)} style={{
          width: 24, height: 24, borderRadius: '50%', border: 'none', cursor: 'pointer',
          background: infoOpen ? 'var(--brand-100)' : 'var(--ink-100)',
          color: infoOpen ? 'var(--brand-600)' : 'var(--ink-500)',
          fontSize: 13, display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0, transition: 'background .15s',
        }}>ⓘ</button>
      </div>

      {crossMonth && (
        <div style={{ marginBottom: 14, padding: '8px 12px', borderRadius: 8, background: 'var(--warning-bg)', border: '1px solid var(--warning)', fontSize: 11, color: 'var(--warning)' }}>
          O período selecionado cruza dois meses. A meta usa a proporção do mês corrente ({goalLabel}) — meses anteriores podem ter tido metas diferentes.
        </div>
      )}

      {infoOpen && (
        <div style={{ marginBottom: 16, padding: '12px 14px', borderRadius: 10, background: 'var(--brand-50)', border: '1px solid var(--brand-200)', fontSize: 12, color: 'var(--ink-700)', lineHeight: 1.7 }}>
          <strong style={{ color: 'var(--ink-900)', fontSize: 13 }}>Como é calculado:</strong>
          <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
            {[
              ['Vendas (filiais que superaram)', BRL(exceededSales),    null],
              ['Meta proporcional',              BRL(exceededGoal), null],
              ['Excedente',                      BRL(networkExcess),      networkExcess > 0 ? 'var(--positive)' : 'var(--danger)'],
              ['Compras / Venda',                `${(ratio * 100).toFixed(1)}% das vendas`, null],
              ['Verba extra',                    BRL(extraTotal),         'var(--brand-600)'],
            ].map(([label, value, color], i) => (
              <div key={i} style={{
                display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                padding: '4px 10px', borderRadius: 6,
                background: i === 4 ? 'var(--brand-100)' : i === 2 ? (networkExcess > 0 ? 'var(--positive-bg)' : 'var(--danger-bg)') : 'var(--white)',
                border: `1px solid ${i === 4 ? 'var(--brand-300)' : 'var(--ink-200)'}`,
                fontWeight: i === 4 ? 700 : 500,
              }}>
                <span style={{ color: 'var(--ink-600)' }}>{label}</span>
                <span className="mono" style={{ fontWeight: 700, color: color ?? 'var(--ink-900)' }}>{value}</span>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 8, fontSize: 11, color: 'var(--ink-500)' }}>
            <strong>Compras / Venda:</strong> no período você comprou{' '}
            <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-800)' }}>{BRL(ratio)}</span>{' '}
            para cada <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-800)' }}>R$ 1,00</span> vendido
            ({BRL(purchaseTotal)} comprado / {BRL(salesWithGoal)} vendido — todas as filiais com meta).
            A verba extra aplica essa mesma proporção sobre o excedente das filiais que superaram.
          </div>
        </div>
      )}

      {networkExcess <= 0 ? (
        <div style={{ padding: '14px 16px', borderRadius: 10, background: 'var(--danger-bg)', border: '1px solid var(--danger)', marginBottom: 4 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--danger)', marginBottom: 2 }}>Vendas abaixo da meta</div>
          <div style={{ fontSize: 13, color: 'var(--ink-700)' }}>
            Meta: <strong>{BRL(networkGoal)}</strong> · Realizado: <strong>{BRL(salesWithGoal)}</strong>
            {abovePct !== null && <span style={{ marginLeft: 8, fontWeight: 700, color: 'var(--danger)' }}>{abovePct.toFixed(1)}%</span>}
            <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>Nenhuma filial superou a meta no período.</div>
          </div>
        </div>
      ) : (
        <>
          {/* Resumo rede */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12, marginBottom: 16 }}>
            {[
              { label: 'Meta proporcional',  sub: 'filiais que superaram', value: BRL(exceededGoal),   color: 'var(--ink-700)' },
              { label: 'Vendas realizadas',  sub: 'filiais que superaram', value: BRL(exceededSales),  color: 'var(--positive)', tag: exceededGoal > 0 ? `+${((exceededSales / exceededGoal - 1) * 100).toFixed(1)}%` : null },
              { label: 'Excedente',          sub: 'vendas − meta',         value: BRL(networkExcess),  color: 'var(--positive)' },
            ].map(({ label, sub, value, color, tag }) => (
              <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 10, padding: '10px 12px', border: '1px solid var(--ink-200)' }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.4, marginBottom: 1 }}>{label}</div>
                <div style={{ fontSize: 9, color: 'var(--ink-400)', marginBottom: 4 }}>{sub}</div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <span className="mono" style={{ fontSize: 13, fontWeight: 700, color }}>{value}</span>
                  {tag && <span style={{ fontSize: 10, fontWeight: 700, color, background: color + '22', borderRadius: 99, padding: '1px 6px' }}>{tag}</span>}
                </div>
              </div>
            ))}
          </div>

          {/* Verba extra em destaque */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: '16px 20px', borderRadius: 12, background: 'linear-gradient(135deg, #0d3d7a 0%, #0B69C7 100%)', color: '#fff', marginBottom: 16 }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.65)', marginBottom: 4 }}>
                Verba disponível
              </div>
              <div className="mono" style={{ fontSize: 30, fontWeight: 800, letterSpacing: '-0.02em', lineHeight: 1 }}>
                {BRL(availableExtra)}
              </div>
              {usedExtra > 0 && (
                <div style={{ marginTop: 6, fontSize: 11, color: 'rgba(255,255,255,0.55)', fontWeight: 600 }}>
                  {BRL(usedExtra)} utilizado · {BRL(extraTotal)} período
                </div>
              )}
            </div>
            <div style={{ textAlign: 'right', flexShrink: 0 }}>
              <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.55)', marginBottom: 2 }}>Histórico</div>
              <div className="mono" style={{ fontSize: 18, fontWeight: 700, color: 'rgba(255,255,255,0.9)' }}>{(ratio * 100).toFixed(1)}%</div>
              <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.45)' }}>compras / vendas</div>
            </div>
          </div>

          {/* Por filial — superaram */}
          {exceeded.length > 0 && (
            <div style={{ marginBottom: 10 }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--positive)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>
                ✓ Superaram a meta
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {exceeded.map(s => {
                  const used     = isStoreUsed(s.id);
                  const useData  = getStoreUse(s.id);
                  const coverage = !used ? getStoreCoverage(s.id) : null;
                  const netExtra = coverage && coverage.newDays < coverage.totalDays
                    ? s.extra * (coverage.newDays / coverage.totalDays)
                    : s.extra;
                  return (
                    <div key={s.id} style={{ borderRadius: 10, background: 'var(--positive-bg)', border: `1px solid ${'var(--positive)'}44`, overflow: 'hidden' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px' }}>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.name}</div>
                          <div style={{ fontSize: 10, color: 'var(--ink-500)', marginTop: 1 }}>
                            <span className="mono">{BRL(s.sales)}</span> · meta <span className="mono">{BRL(s.goal)}</span>
                          </div>
                        </div>
                        <div style={{ textAlign: 'right', flexShrink: 0 }}>
                          <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--positive)' }}>+{BRL(s.excess)} acima</div>
                          <div className="mono" style={{ fontSize: 13, fontWeight: 800, color: 'var(--brand-600)' }}>
                            {BRL(netExtra)} extra
                            {coverage && coverage.newDays < coverage.totalDays && (
                              <span style={{ fontSize: 10, fontWeight: 500, color: 'var(--ink-500)', marginLeft: 4 }}>
                                ({coverage.newDays}d novos)
                              </span>
                            )}
                          </div>
                        </div>
                      </div>
                      {coverage && coverage.coveredDays > 0 && (
                        <div style={{ padding: '5px 12px', fontSize: 11, color: 'var(--warning)', background: 'var(--warning-bg)', borderTop: '1px solid var(--warning)' + '44' }}>
                          {coverage.overlaps.map((o, i) => (
                            <span key={i}>{i > 0 && ' · '}{fmtDate(o.ks)}–{fmtDate(o.ke)} já utilizado ({o.intDays} dia{o.intDays !== 1 ? 's' : ''})</span>
                          ))}
                          {' — '}verba disponível: <strong>{BRL(netExtra)}</strong> ({coverage.newDays} dia{coverage.newDays !== 1 ? 's' : ''} novo{coverage.newDays !== 1 ? 's' : ''})
                        </div>
                      )}
                      <button
                        onClick={() => markStoreUsed(s.id, s.name, netExtra)}
                        disabled={used || saving || siteConfig === null || (coverage && coverage.newDays === 0)}
                        style={{
                          width: '100%', padding: '8px 12px',
                          fontSize: 12, fontWeight: 700, fontFamily: 'inherit',
                          border: 'none', borderTop: `1px solid ${'var(--positive)'}44`,
                          background: used ? 'rgba(26,155,110,0.12)' : (coverage?.newDays === 0) ? 'var(--ink-100)' : 'rgba(26,155,110,0.06)',
                          color: used ? 'var(--positive)' : (coverage?.newDays === 0) ? 'var(--ink-400)' : 'var(--brand-600)',
                          cursor: (used || saving || coverage?.newDays === 0) ? 'default' : 'pointer',
                          textAlign: 'left', display: 'flex', alignItems: 'center', gap: 6,
                        }}
                      >
                        {used ? (
                          <>
                            <span>✓</span> Valor extra já utilizado
                            {useData?.usedAt && <span style={{ fontWeight: 500, opacity: 0.7, marginLeft: 2 }}>· {new Date(useData.usedAt).toLocaleDateString('pt-BR')}</span>}
                          </>
                        ) : coverage?.newDays === 0 ? (
                          <><span>✓</span> Todos os dias deste período já foram utilizados</>
                        ) : saving ? 'Salvando…' : (
                          <><span>✓</span> Já utilizei esta verba extra — {BRL(netExtra)}</>
                        )}
                      </button>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* Por filial — não superaram */}
          {missed.length > 0 && (
            <div>
              <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>
                Abaixo da meta
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                {missed.map(s => (
                  <div key={s.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '6px 12px', borderRadius: 8, background: 'var(--ink-50)', border: '1px solid var(--ink-200)' }}>
                    <span style={{ fontSize: 12, color: 'var(--ink-700)', fontWeight: 600 }}>{s.name}</span>
                    <span className="mono" style={{ fontSize: 11, color: 'var(--danger)', fontWeight: 700 }}>{BRL(s.excess)} abaixo</span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </>
      )}


      {/* ── Histórico de verbas utilizadas ── */}
      {Object.keys(verbas).length > 0 && (
        <div style={{ marginTop: 12 }}>
          <button onClick={() => setHistOpen(o => !o)} style={{
            background: 'none', border: 'none', cursor: 'pointer',
            fontSize: 11, fontWeight: 600, color: 'var(--ink-400)',
            fontFamily: 'inherit', padding: '4px 0', display: 'flex', alignItems: 'center', gap: 4,
          }}>
            {histOpen ? '▲' : '▼'} Histórico de verbas utilizadas ({Object.keys(verbas).length})
          </button>
          {histOpen && (
            <div style={{ marginTop: 6, display: 'flex', flexDirection: 'column', gap: 4 }}>
              {Object.entries(verbas)
                .sort(([, a], [, b]) => (b.usedAt ?? '').localeCompare(a.usedAt ?? ''))
                .map(([key, v]) => {
                  const parts = key.split('_');
                  const ks = parts[0], ke = parts[1];
                  return (
                    <div key={key} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '6px 10px', borderRadius: 8, background: 'var(--ink-50)', border: '1px solid var(--ink-200)' }}>
                      <div>
                        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>{v.storeName || `Filial`}</div>
                        <div style={{ fontSize: 10, color: 'var(--ink-400)' }}>{fmtDate(ks)}–{fmtDate(ke)} · {v.periodLabel}</div>
                      </div>
                      <div style={{ textAlign: 'right' }}>
                        <div className="mono" style={{ fontSize: 12, fontWeight: 700, color: 'var(--brand-600)' }}>{BRL(v.amount ?? 0)}</div>
                        {v.usedAt && <div style={{ fontSize: 10, color: 'var(--ink-400)' }}>{new Date(v.usedAt).toLocaleDateString('pt-BR')}</div>}
                      </div>
                    </div>
                  );
                })}
            </div>
          )}
        </div>
      )}

      <div style={{ marginTop: 12, fontSize: 10, color: 'var(--ink-400)' }}>
        Meta para o período selecionado · histórico: {(ratio * 100).toFixed(1)}% (compras / vendas)
      </div>
    </div>
  );
}

// ─── Multi-select filter dropdown (padrão StoreSelector) ─────────────────────
function PanelMultiFilter({ label, items, selected, onChange }) {
  const [open,  setOpen]  = React.useState(false);
  const [query, setQuery] = React.useState('');
  const ref = React.useRef(null);

  React.useEffect(() => {
    if (!open) setQuery('');
  }, [open]);

  const filtered = query.trim()
    ? items.filter(it => it.label.toLowerCase().includes(query.trim().toLowerCase()))
    : items;

  const toggle = (id) => {
    const next = new Set(selected);
    next.has(id) ? next.delete(id) : next.add(id);
    onChange(next);
  };

  const hasFilter = selected.size > 0;
  const btnLabel = !hasFilter
    ? `Todas as ${label}`
    : selected.size === 1
      ? (items.find(it => selected.has(it.id))?.label ?? `1 selecionada`)
      : `${selected.size} ${label}`;

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '6px 12px', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
        border: `1.5px solid ${hasFilter ? 'var(--brand-500)' : 'var(--ink-200)'}`,
        borderRadius: 8, cursor: 'pointer',
        background: hasFilter ? 'var(--brand-50, #EEF5FF)' : 'var(--white)',
        color: hasFilter ? 'var(--brand-700, #1a4789)' : 'var(--ink-700)',
        transition: 'all .12s',
      }}>
        {btnLabel}
        <span style={{ fontSize: 9, opacity: 0.55, marginLeft: 2 }}>▾</span>
      </button>

      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 299 }} />
          <div style={{
            position: 'absolute', top: 'calc(100% + 4px)', left: 0,
            width: 230, background: 'var(--white)',
            border: '1px solid var(--ink-200)', borderRadius: 10,
            boxShadow: '0 8px 24px rgba(0,0,0,0.14)', zIndex: 300, overflow: 'hidden',
          }}>
            {/* Search */}
            <div style={{ padding: '8px 10px', borderBottom: '1px solid var(--ink-100)' }}>
              <input
                autoFocus
                value={query}
                onChange={e => setQuery(e.target.value)}
                placeholder={`Buscar ${label.toLowerCase()}…`}
                style={{
                  width: '100%', padding: '5px 8px', fontSize: 12.5, boxSizing: 'border-box',
                  fontFamily: 'inherit', border: '1px solid var(--ink-200)',
                  borderRadius: 6, outline: 'none', background: 'var(--ink-50)',
                  color: 'var(--ink-900)',
                }}
              />
            </div>
            {/* Todas */}
            {!query && (
              <button onClick={() => onChange(new Set())} style={{
                display: 'flex', alignItems: 'center', gap: 9,
                width: '100%', padding: '8px 12px', background: 'transparent',
                border: 0, borderBottom: '1px solid var(--ink-100)',
                cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
              }}>
                <div style={{
                  width: 15, height: 15, borderRadius: 4, flexShrink: 0,
                  border: `2px solid ${!hasFilter ? 'var(--brand-500)' : 'var(--ink-300)'}`,
                  background: !hasFilter ? 'var(--brand-500)' : 'var(--white)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  {!hasFilter && <svg width="8" height="6" viewBox="0 0 9 7" fill="none"><polyline points="1,3.5 3.5,6 8,1" stroke="#fff" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                </div>
                <span style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-700)' }}>Todas as {label.toLowerCase()}</span>
              </button>
            )}
            {/* Items */}
            <div style={{ maxHeight: 220, overflowY: 'auto' }}>
              {filtered.map(it => {
                const checked = selected.has(it.id);
                return (
                  <button key={it.id} onClick={() => toggle(it.id)}
                    style={{
                      display: 'flex', alignItems: 'center', gap: 9,
                      width: '100%', padding: '7px 12px', background: 'transparent',
                      border: 0, borderBottom: '1px solid var(--ink-50)',
                      cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
                    }}
                    onMouseEnter={e => { e.currentTarget.style.background = 'var(--ink-50)'; }}
                    onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}
                  >
                    <div style={{
                      width: 15, height: 15, borderRadius: 4, flexShrink: 0,
                      border: `2px solid ${checked ? 'var(--brand-500)' : 'var(--ink-300)'}`,
                      background: checked ? 'var(--brand-500)' : 'var(--white)',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      transition: 'background .1s, border-color .1s',
                    }}>
                      {checked && <svg width="8" height="6" viewBox="0 0 9 7" fill="none"><polyline points="1,3.5 3.5,6 8,1" stroke="#fff" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                    </div>
                    <span style={{
                      fontSize: 12.5, fontWeight: checked ? 600 : 500,
                      color: checked ? 'var(--brand-700, #1a4789)' : 'var(--ink-800)',
                      overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1,
                    }} title={it.label}>{it.label}</span>
                  </button>
                );
              })}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

// ─── Painel flutuante — Planejamento de Compras ───────────────────────────────
function PurchasesBudgetFloatingPanel({ stores, classRanking = [], stockByStore = [], goalFactor, cmvGreen = 55, metaCompras = 0.70, metaComprasMode = 'pct', period = 'hoje', setPeriod, customStart, customEnd, setCustomRange, siteConfig = {}, setSiteConfig = () => {}, user }) {
  const isCmv = metaComprasMode === 'cmv';
  const rate  = metaCompras; // taxa configurada (ex: 0.64), igual ao card principal
  const [open,            setOpen]           = React.useState(false);
  const [tab,             setTab]            = React.useState('filiais');
  const [selectedStores,  setSelectedStores]  = React.useState(new Set()); // empty = todas
  const [selectedClasses, setSelectedClasses] = React.useState(new Set());
  const [panelPickerOpen, setPanelPickerOpen] = React.useState(false);
  const [panelPickerPos,  setPanelPickerPos]  = React.useState(null);
  const [includeExtra,    setIncludeExtra]    = React.useState(true);
  const [infoOpen,        setInfoOpen]        = React.useState(false);
  const [selectedRowId,   setSelectedRowId]   = React.useState(null);
  const [expandedUnits,     setExpandedUnits]     = React.useState(new Set());
  const [billsSectionOpen,  setBillsSectionOpen]  = React.useState(true);
  const [showBillsPanel,     setShowBillsPanel]     = React.useState(false);
  const [billsMonthOffset,   setBillsMonthOffset]   = React.useState(0);
  const [secondMonthVisible, setSecondMonthVisible] = React.useState(false);
  const [donutHover,        setDonutHover]        = React.useState(null); // { name, x, y }
  const [classesExpanded,   setClassesExpanded]   = React.useState(false);
  const [editingLabel,    setEditingLabel]    = React.useState(null); // null | 'verba' | 'extra'
  const [draftLabel,      setDraftLabel]      = React.useState('');
  const customBtnRef = React.useRef(null);

  const labelVerba = siteConfig?.labelVerba || 'Orçamento';
  const labelExtra = siteConfig?.labelExtra || 'Orçamento extra';

  const saveLabel = React.useCallback(async (key, value) => {
    const cnpj = user?.cnpj;
    const trimmed = value.trim().slice(0, 15);
    if (!trimmed) { setEditingLabel(null); return; }
    if (cnpj) {
      try {
        await SF.db.collection('loja').doc(cnpj).collection('config').doc('Site')
          .set({ [key]: trimmed }, { merge: true });
      } catch (e) { console.warn('[label] erro ao salvar', e); }
    }
    setSiteConfig(prev => ({ ...(prev ?? {}), [key]: trimmed }));
    setEditingLabel(null);
  }, [user?.cnpj, setSiteConfig]);

  const PencilBtn = ({ which, currentLabel }) => (
    <button
      onClick={e => { e.stopPropagation(); setDraftLabel(currentLabel); setEditingLabel(which); }}
      title="Renomear"
      style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '0 0 0 3px', display: 'inline-flex', alignItems: 'center', color: '#3B82F6', opacity: 0.75, lineHeight: 1, flexShrink: 0 }}
    >
      <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
        <path d="M8.5 1.5L10.5 3.5L4 10 1.5 10.5 2 8 8.5 1.5Z" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    </button>
  );

  const LabelInput = ({ field }) => (
    <input
      autoFocus
      value={draftLabel}
      maxLength={15}
      onChange={e => setDraftLabel(e.target.value)}
      onBlur={() => saveLabel(field, draftLabel)}
      onKeyDown={e => {
        if (e.key === 'Enter') saveLabel(field, draftLabel);
        if (e.key === 'Escape') setEditingLabel(null);
      }}
      style={{
        fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em',
        color: 'var(--ink-600)', border: 'none', borderBottom: '1.5px solid #3B82F6',
        background: 'transparent', outline: 'none', width: 88, padding: '0 2px',
        fontFamily: 'inherit',
      }}
    />
  );

  // Lojas base (sem filtro) → lista para o PanelMultiFilter
  const storeItems = stores.map(s => ({ id: s.storeId, label: s.storeName || `Loja ${s.storeId}` }));

  // storeRows: calcula para as lojas filtradas (ou todas)
  const storeRows = React.useMemo(() => {
    if (goalFactor == null) return [];
    const target = selectedStores.size > 0 ? stores.filter(s => selectedStores.has(s.storeId)) : stores;
    return target
      .map(s => {
        const goal      = safeN(s.goalAccum) > 0 ? safeN(s.goalAccum) : safeN(s.totalGoal) * goalFactor;
        const verba     = isCmv ? safeN(s.cost) : (goal > 0 ? Math.min(s.total, goal) : s.total) * rate;
        const extra     = isCmv ? 0 : (goal > 0 ? Math.max(0, s.total - goal) * rate : 0);
        const budget    = verba + extra;
        const realizado = safeN(s.purchases?.total);
        const saldo     = budget - realizado;
        const uso       = budget > 0 ? realizado / budget : 0;
        const soldQty    = safeN(s.quantity);
        const boughtQty  = Object.values(s.purchases?.classifications ?? {}).reduce((a, c) => a + safeN(c.quantity), 0);
        const stk        = stockByStore.find(st => st.storeId === s.storeId);
        const stockUnity = safeN(stk?.totalUnity);
        const stockCost  = safeN(stk?.totalCost);
        return { id: s.storeId, name: s.storeName || `Loja ${s.storeId}`, vendas: safeN(s.total), verba, extra, budget, realizado, saldo, uso, soldQty, boughtQty, stockUnity, stockCost };
      })
      .filter(r => r.verba > 0 || r.realizado > 0)
      .sort((a, b) => b.budget - a.budget);
  }, [stores, goalFactor, rate, isCmv, selectedStores, stockByStore]);

  // classRows: agrega usando lojas filtradas; depois aplica filtro de classe
  // Calcula as linhas por classe para um conjunto de lojas. Usado pela aba
  // "Por Classe" E pelo painel de detalhe da filial (rosquinha) — mesma função
  // pra mesma classe nunca mostrar valores diferentes em lugares diferentes.
  const buildClassRows = (target, useRanking) => {
    const salesByClass   = {};
    const cmvByClass     = {}; // custo das vendas por classe (para modo CMV)
    const soldQtyByClass = {};
    if (!useRanking) {
      for (const s of target) {
        for (const [name, c] of Object.entries(s.salesByClass ?? {})) {
          if (!isValidClassName(name)) continue;
          salesByClass[name]   = (salesByClass[name]   ?? 0) + safeN(c.totalSale ?? c.total);
          cmvByClass[name]     = (cmvByClass[name]     ?? 0) + safeN(c.totalCost);
          soldQtyByClass[name] = (soldQtyByClass[name] ?? 0) + safeN(c.unity ?? c.quantity);
        }
      }
    } else {
      for (const c of classRanking) {
        if (c.name) {
          salesByClass[c.name]   = (salesByClass[c.name]   ?? 0) + (c.total     ?? 0);
          cmvByClass[c.name]     = (cmvByClass[c.name]     ?? 0) + (c.totalCost ?? 0);
          soldQtyByClass[c.name] = (soldQtyByClass[c.name] ?? 0) + (c.quantity  ?? 0);
        }
      }
    }
    const costByClass      = {};
    const boughtQtyByClass = {};
    for (const s of target) {
      for (const c of Object.values(s.purchases?.classifications ?? {})) {
        if (!isValidClassName(c.className)) continue;
        costByClass[c.className]      = (costByClass[c.className]      ?? 0) + safeN(c.total);
        boughtQtyByClass[c.className] = (boughtQtyByClass[c.className] ?? 0) + safeN(c.quantity);
      }
    }
    const targetIds = new Set(target.map(s => s.storeId));
    const stockByClass = {};
    for (const st of stockByStore) {
      if (!targetIds.has(st.storeId)) continue;
      for (const c of st.classifications ?? []) {
        if (!isValidClassName(c.className)) continue;
        const ex = stockByClass[c.className] ?? { unity: 0, totalCost: 0, totalSale: 0 };
        ex.unity     += safeN(c.unity);
        ex.totalCost += safeN(c.totalCost);
        ex.totalSale += safeN(c.totalSale);
        stockByClass[c.className] = ex;
      }
    }
    // Distribui o orçamento extra de cada loja proporcionalmente às vendas por classe
    const extraByClass = {};
    if (goalFactor != null) {
      for (const s of target) {
        const goal = safeN(s.goalAccum) > 0 ? safeN(s.goalAccum) : safeN(s.totalGoal) * goalFactor;
        const storeExtra = goal > 0 ? Math.max(0, s.total - goal) * rate : 0;
        if (storeExtra > 0 && s.total > 0) {
          for (const [name, c] of Object.entries(s.salesByClass ?? {})) {
            if (!isValidClassName(name)) continue;
            const classSale = safeN(c.totalSale ?? c.total);
            extraByClass[name] = (extraByClass[name] ?? 0) + storeExtra * (classSale / s.total);
          }
        }
      }
    }

    // Razão CMV geral das lojas-alvo — fallback pra estimar CMV de classe sem estoque
    const targetSale = target.reduce((a, s) => a + safeN(s.total), 0);
    const targetCost = target.reduce((a, s) => a + safeN(s.cost),  0);
    const storeCmvRatio = targetSale > 0 ? targetCost / targetSale : 0;

    const allNames = new Set([...Object.keys(salesByClass), ...Object.keys(costByClass)]);
    const rows = [...allNames]
      .filter(isValidClassName)
      .map(name => {
        const sale       = salesByClass[name]     ?? 0;
        const realizado  = costByClass[name]      ?? 0;
        const soldQty    = soldQtyByClass[name]   ?? 0;
        const boughtQty  = boughtQtyByClass[name] ?? 0;
        const stk        = stockByClass[name];
        const stockUnity = safeN(stk?.unity);
        const stockCost  = safeN(stk?.totalCost);
        const extra      = isCmv ? 0 : (extraByClass[name] ?? 0);
        // CMV por classe não vem pronto do coletor (salesByClass não tem totalCost).
        // Quando zerado, estima: venda da classe × razão custo/venda do estoque da
        // própria classe (estrutura de margem real dela); sem estoque, CMV geral da loja.
        const stkRatio   = stk && stk.totalSale > 0 && stk.totalCost > 0 ? stk.totalCost / stk.totalSale : null;
        const cmvClasse  = (cmvByClass[name] ?? 0) > 0 ? cmvByClass[name] : sale * (stkRatio ?? storeCmvRatio);
        const verba      = isCmv ? cmvClasse : sale * rate;
        const budget     = verba + extra;
        const saldo      = budget - realizado;
        const uso        = budget > 0 ? realizado / budget : 0;
        return { name, sale, budget, extra, realizado, saldo, uso, soldQty, boughtQty, stockUnity, stockCost };
      });

    // Modo CMV com estimativas: normaliza pra soma das classes bater EXATO com o
    // CMV real das lojas (s.cost) — o mesmo número do orçamento por filial.
    if (isCmv) {
      const sumReal = rows.reduce((a, r) => a + (cmvByClass[r.name] ?? 0), 0);
      const sumEst  = rows.reduce((a, r) => a + r.budget, 0);
      if (sumReal === 0 && sumEst > 0 && targetCost > 0) {
        const f = targetCost / sumEst;
        for (const r of rows) {
          r.budget *= f;
          r.saldo   = r.budget - r.realizado;
          r.uso     = r.budget > 0 ? r.realizado / r.budget : 0;
        }
      }
    }

    return rows
      .filter(r => r.budget > 0 || r.realizado > 0)
      .sort((a, b) => b.budget - a.budget);
  };

  const classRows = React.useMemo(
    () => buildClassRows(
      selectedStores.size > 0 ? stores.filter(s => selectedStores.has(s.storeId)) : stores,
      selectedStores.size === 0
    ),
    [stores, classRanking, rate, isCmv, selectedStores, stockByStore, goalFactor]
  );

  // Itens para o filtro de classes (nomes disponíveis)
  const classItems = classRows.map(r => ({ id: r.name, label: r.name }));

  // Filtro de classes aplicado sobre classRows
  const filteredClassRows = selectedClasses.size > 0
    ? classRows.filter(r => selectedClasses.has(r.name))
    : classRows;

  const rows = tab === 'filiais' ? storeRows : filteredClassRows;

  // Verba e Verba extra sempre baseados em storeRows — o mesmo valor independente da aba ativa
  const totBudget     = storeRows.reduce((s, r) => s + r.budget,       0);
  const totReal       = storeRows.reduce((s, r) => s + r.realizado,    0);
  const totExtra      = storeRows.reduce((s, r) => s + (r.extra ?? 0), 0);
  const displayBudget = includeExtra ? totBudget : totBudget - totExtra;
  const displaySaldo  = displayBudget - totReal;

  // Loja selecionada para o painel de rosquinha
  const _selStore = selectedRowId ? stores.find(s => s.storeId === selectedRowId) : null;

  // Sincroniza painel rosquinha quando filtro de filial muda
  React.useEffect(() => {
    if (selectedStores.size === 1) {
      const [id] = selectedStores;
      setSelectedRowId(id);
    } else if (selectedStores.size === 0 && selectedRowId) {
      // filial selecionada pode não estar mais visível — mantém só se ainda existe
      const stillVisible = stores.some(s => s.storeId === selectedRowId);
      if (!stillVisible) setSelectedRowId(null);
    } else {
      setSelectedRowId(null);
    }
  }, [selectedStores]);

  // Recolhe a lista de classes ao trocar de loja
  React.useEffect(() => { setClassesExpanded(false); }, [selectedRowId]);
  // Mês atual: mesmo range MTD do BillsView (docs por data de vencimento)
  const billsStoreFilter = React.useMemo(() => {
    if (selectedRowId) return [selectedRowId];
    return [...selectedStores];
  }, [selectedRowId, selectedStores]);
  const billsRaw = useBillsLive(
    user?.cnpj, user?.allowedStores, billsStoreFilter,
    SF.startOfMonth(), SF.today()
  );

  // Segundo mês: range completo do mês escolhido (1º ao último dia)
  const { billsSecStart, billsSecEnd, secondMonthStr } = React.useMemo(() => {
    const today = SF.today();
    const [y, m] = today.slice(0, 7).split('-').map(Number);
    const tm  = m + 1 + billsMonthOffset;
    const sy  = y + Math.floor((tm - 1) / 12);
    const sm  = ((tm - 1) % 12) + 1;
    const str = `${sy}-${String(sm).padStart(2, '0')}`;
    const lastDay = new Date(sy, sm, 0).getDate();
    return { billsSecStart: `${str}-01`, billsSecEnd: `${str}-${String(lastDay).padStart(2, '0')}`, secondMonthStr: str };
  }, [billsMonthOffset]);
  const billsRawSecond = useBillsLive(
    user?.cnpj, user?.allowedStores, billsStoreFilter,
    billsSecStart, billsSecEnd
  );

  const billsMonthSummary = React.useMemo(() => {
    const curMonth = SF.today().slice(0, 7);
    const parseDue = (d) => {
      if (!d) return null;
      if (typeof d === 'string') return d.slice(0, 7);
      try {
        const dt = d.toDate ? d.toDate() : d.seconds ? new Date(d.seconds * 1000) : null;
        if (!dt || isNaN(dt.getTime())) return null;
        return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}`;
      } catch { return null; }
    };
    const agg = (rawList, monthKey) => {
      const e = { month: monthKey, total: 0, pago: 0, pendente: 0, count: 0 };
      if (rawList) {
        for (const b of rawList) {
          if (b.status !== 'A' && b.status !== 'P') continue;
          if (parseDue(b.dueDate) !== monthKey) continue;
          e.total    += b.billingValue;
          e.pago     += b.paidValue;
          e.pendente += Math.max(b.billingValue - b.paidValue, 0);
          e.count    += 1;
        }
      }
      return e;
    };
    return [agg(billsRaw, curMonth), agg(billsRawSecond, secondMonthStr)];
  }, [billsRaw, billsRawSecond, secondMonthStr]);

  const computeDonutSegs = (data, colors) => {
    const total = data.reduce((s, d) => s + d.value, 0);
    const main = data.slice(0, 8);
    const restTotal = data.slice(8).reduce((s, d) => s + d.value, 0);
    const items = restTotal > 0 ? [...main, { name: 'Outros', value: restTotal }] : main;
    const r = 48, circ = 2 * Math.PI * r;
    let off = 0;
    const segs = items.map(d => {
      const len = total > 0 ? (d.value / total) * circ : 0;
      const seg = { name: d.name, value: d.value, len, offset: off, color: d.name === 'Outros' ? '#CBD5E1' : (colors[d.name] ?? '#CBD5E1') };
      off += len;
      return seg;
    });
    return { segs, total, items, r, circ };
  };

  const donutPanelData = React.useMemo(() => {
    if (!_selStore) return null;
    const SW = 18, R = 48, SZ = (R + SW / 2 + 5) * 2;
    // Mesmíssimo cálculo da aba "Por Classe", restrito à loja selecionada —
    // garante que a classe mostre o mesmo orçamento/saldo aqui e na lista.
    const classData = buildClassRows([_selStore], false)
      .map(r => ({ name: r.name, budget: r.budget, purc: r.realizado, saldo: r.saldo, uso: r.uso }));
    const totalBudget = classData.reduce((s, d) => s + d.budget, 0);
    const totalPurc   = classData.reduce((s, d) => s + d.purc,   0);
    const totalSaldo  = totalBudget - totalPurc;
    const classColors = Object.fromEntries(classData.map((d, i) => [d.name, GRP_PALETTE[i % GRP_PALETTE.length]]));
    const donutC      = computeDonutSegs(classData.filter(d => d.purc > 0).map(d => ({ name: d.name, value: d.purc })), classColors);
    return { SW, R, SZ, classData, classColors, donutC, totalBudget, totalPurc, totalSaldo };
  }, [_selStore, rate, isCmv, stockByStore, goalFactor]); // buildClassRows é função pura no escopo

  return (
    <>
      {/* Botão fixo */}
      <div style={{ position: 'fixed', bottom: 28, right: 20, zIndex: 200 }}>
        <button onClick={() => setOpen(o => !o)} style={{
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '10px 18px',
          background: open ? '#0c2340' : '#1a4789',
          color: '#fff', border: 'none', borderRadius: 24,
          boxShadow: '0 4px 20px rgba(0,0,0,0.28)',
          cursor: 'pointer', fontFamily: 'inherit', fontWeight: 700, fontSize: 13,
          transition: 'background .15s',
        }}>
          <Icon name="sf-purchases" size={15} color="#fff" />
          Planejamento
        </button>
      </div>

      {/* Overlay */}
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 210, backdropFilter: 'blur(2px)' }} />

          <div style={{
            position: 'fixed', top: '4%', left: 0, right: 0, margin: '0 auto',
            width: 'min(880px, 92vw)', maxHeight: '90vh',
            background: 'var(--white)', borderRadius: 20,
            boxShadow: '0 24px 64px rgba(0,0,0,0.3)',
            zIndex: 211, display: 'flex', flexDirection: 'column', overflow: 'hidden',
          }}>

            {/* Header */}
            <div style={{ padding: '18px 24px 14px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <div style={{ fontSize: 17, fontWeight: 700, color: 'var(--ink-900)' }}>Planejamento de Compras</div>
                  <button onClick={() => setInfoOpen(o => !o)} title="Como é calculado?" style={{
                    width: 20, height: 20, borderRadius: '50%', border: `1.5px solid ${infoOpen ? 'var(--brand-500)' : 'var(--ink-300)'}`,
                    background: infoOpen ? 'var(--brand-50)' : 'var(--ink-50)', cursor: 'pointer',
                    fontSize: 11, fontWeight: 800, color: infoOpen ? 'var(--brand-600)' : 'var(--ink-400)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center', lineHeight: 1, flexShrink: 0,
                    transition: 'all .15s',
                  }}>i</button>
                </div>
                <button onClick={() => setOpen(false)} style={{
                  width: 30, height: 30, borderRadius: '50%', border: '1px solid var(--ink-200)',
                  background: 'var(--ink-50)', cursor: 'pointer', fontSize: 17, color: 'var(--ink-500)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', lineHeight: 1,
                }}>×</button>
              </div>

              {/* Tabs + Período */}
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
                <div style={{ display: 'flex', gap: 0, background: 'var(--ink-100)', borderRadius: 10, padding: 3, width: 'fit-content' }}>
                  {[['filiais', 'Por Filial'], ['classes', 'Por Classe']].map(([k, l]) => (
                    <button key={k} onClick={() => setTab(k)} style={{
                      padding: '6px 22px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                      border: 0, borderRadius: 8, cursor: 'pointer',
                      background: tab === k ? 'var(--white)' : 'transparent',
                      color:      tab === k ? 'var(--brand-600, var(--brand-500))' : 'var(--ink-500)',
                      boxShadow:  tab === k ? '0 1px 4px rgba(0,0,0,0.08)' : 'none',
                      transition: 'all .12s',
                    }}>{l}</button>
                  ))}
                </div>

                {setPeriod && (() => {
                  const fmtShort = iso => { const [,m,d] = iso.split('-'); return `${d}/${m}`; };
                  const customActive = period === 'custom';
                  const customLabel  = customActive && customStart && customEnd
                    ? `${fmtShort(customStart)} – ${fmtShort(customEnd)}`
                    : 'Personalizado';
                  return (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <div style={{ display: 'flex', background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
                        {[
                          { v: 'hoje', l: 'Hoje' },
                          { v: '7d',   l: '7d'   },
                          { v: '30d',  l: '30d'  },
                          { v: 'mtd',  l: MONTH_NAMES_FULL_PT[new Date().getMonth()] },
                          { v: 'ytd',  l: 'Ano'  },
                        ].map(o => (
                          <button key={o.v} onClick={() => setPeriod(o.v)} style={{
                            padding: '5px 11px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                            border: 0, borderRadius: 6, cursor: 'pointer',
                            background: period === o.v ? 'var(--white)' : 'transparent',
                            color:      period === o.v ? 'var(--ink-900)' : 'var(--ink-600)',
                            boxShadow:  period === o.v ? 'var(--shadow-sm)' : 'none',
                            transition: 'all .12s',
                          }}>{o.l}</button>
                        ))}
                      </div>
                      <button ref={customBtnRef} onClick={() => {
                        if (customBtnRef.current) {
                          const r = customBtnRef.current.getBoundingClientRect();
                          setPanelPickerPos({ position: 'fixed', top: r.bottom + 6, right: window.innerWidth - r.right });
                        }
                        setPanelPickerOpen(o => !o);
                      }} style={{
                        display: 'inline-flex', alignItems: 'center', gap: 5,
                        padding: '5px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                        border: `1.5px solid ${customActive ? 'var(--brand-500)' : 'var(--ink-200)'}`,
                        borderRadius: 8, cursor: 'pointer',
                        background: customActive ? 'var(--brand-50)' : 'var(--white)',
                        color:      customActive ? 'var(--brand-700)' : 'var(--ink-600)',
                        whiteSpace: 'nowrap',
                      }}>
                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                          <rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
                        </svg>
                        {customLabel}
                      </button>
                      {panelPickerOpen && panelPickerPos && (
                        <CustomRangePicker
                          start={customStart ?? SF.subtractDays(6)}
                          end={customEnd   ?? SF.today()}
                          posStyle={panelPickerPos}
                          onApply={(s, e) => { setCustomRange(s, e); setPanelPickerOpen(false); }}
                          onClose={() => setPanelPickerOpen(false)}
                        />
                      )}
                    </div>
                  );
                })()}
              </div>

              {/* Filtros */}
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 14 }}>
                {stores.length > 1 && (
                  <PanelMultiFilter
                    label="Filiais"
                    items={storeItems}
                    selected={selectedStores}
                    onChange={setSelectedStores}
                  />
                )}
                {tab === 'classes' && (
                  <PanelMultiFilter
                    label="Classes"
                    items={classItems}
                    selected={selectedClasses}
                    onChange={setSelectedClasses}
                  />
                )}
                {(selectedStores.size > 0 || selectedClasses.size > 0) && (
                  <button onClick={() => { setSelectedStores(new Set()); setSelectedClasses(new Set()); }} style={{
                    padding: '6px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                    border: '1.5px solid var(--ink-200)', borderRadius: 8, cursor: 'pointer',
                    background: 'var(--white)', color: 'var(--ink-500)',
                  }}>Limpar filtros</button>
                )}
                <button
                  onClick={() => setShowBillsPanel(p => !p)}
                  style={{
                    display: 'inline-flex', alignItems: 'center', gap: 5,
                    padding: '6px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                    border: `1.5px solid ${showBillsPanel ? '#D97706' : 'var(--ink-200)'}`,
                    borderRadius: 8, cursor: 'pointer',
                    background: showBillsPanel ? '#FEF3C7' : 'var(--white)',
                    color:      showBillsPanel ? '#92400E' : 'var(--ink-500)',
                    transition: 'all .12s',
                  }}
                >
                  <svg width="12" height="12" viewBox="0 -960 960 960" fill="currentColor" style={{ flexShrink: 0 }}>
                    <path d="M560-440q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35zM280-320q-33 0-56.5-23.5T200-400v-320q0-33 23.5-56.5T280-800h560q33 0 56.5 23.5T920-720v320q0 33-23.5 56.5T840-320H280zm80-80h400q0-33 23.5-56.5T840-480v-160q-33 0-56.5-23.5T760-720H360q0 33-23.5 56.5T280-640v160q33 0 56.5 23.5T360-400zm440 240H120q-33 0-56.5-23.5T40-240v-440h80v440h680v80zM280-400v-320 320z"/>
                  </svg>
                  Contas a Pagar
                </button>
              </div>

              {/* Chips de resumo */}
              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                <div style={{ padding: '7px 16px', background: 'var(--ink-50)', borderRadius: 10, border: '1px solid var(--ink-100)' }}>
                  <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', display: 'flex', alignItems: 'center' }}>
                    {editingLabel === 'verba'
                      ? <LabelInput field="labelVerba" />
                      : <><span>{labelVerba}</span><PencilBtn which="verba" currentLabel={labelVerba} /></>}
                  </div>
                  <div className="mono" style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-700)', marginTop: 1 }}>{BRL(displayBudget)}</div>
                  <div title={totExtra === 0 ? `Não tem '${labelExtra}'` : undefined} style={{ display: 'flex', alignItems: 'center', gap: 2, marginTop: 3, opacity: totExtra === 0 ? 0.45 : 1 }}>
                    <button onClick={() => totExtra > 0 && setIncludeExtra(v => !v)} style={{
                      display: 'inline-flex', alignItems: 'center', gap: 5,
                      background: 'none', border: 'none', cursor: totExtra > 0 ? 'pointer' : 'default', padding: 0, fontFamily: 'inherit',
                    }}>
                      <div style={{
                        width: 12, height: 12, borderRadius: 3, flexShrink: 0,
                        border: `2px solid ${includeExtra && totExtra > 0 ? '#16A34A' : 'var(--ink-300)'}`,
                        background: includeExtra && totExtra > 0 ? '#16A34A' : 'var(--white)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        transition: 'all .12s',
                      }}>
                        {includeExtra && totExtra > 0 && <svg width="7" height="5" viewBox="0 0 9 7" fill="none"><polyline points="1,3.5 3.5,6 8,1" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                      </div>
                      <span style={{ fontSize: 10.5, fontWeight: 600, color: includeExtra && totExtra > 0 ? '#16A34A' : 'var(--ink-400)', transition: 'color .12s' }}>
                        {editingLabel === 'extra' ? '' : labelExtra}
                      </span>
                    </button>
                    {editingLabel === 'extra'
                      ? <LabelInput field="labelExtra" />
                      : <PencilBtn which="extra" currentLabel={labelExtra} />}
                  </div>
                </div>
                {[
                  ['Comprado',  totReal,       'var(--brand-500)'],
                  ['Saldo',     displaySaldo,  displaySaldo >= 0 ? '#16A34A' : '#DC2626'],
                ].map(([label, val, color]) => (
                  <div key={label} style={{ padding: '7px 16px', background: 'var(--ink-50)', borderRadius: 10, border: '1px solid var(--ink-100)' }}>
                    <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em' }}>{label}</div>
                    <div className="mono" style={{ fontSize: 15, fontWeight: 700, color, marginTop: 1 }}>
                      {label === 'Saldo' && displaySaldo > 0 ? '+' : ''}{BRL(val)}
                    </div>
                  </div>
                ))}
                {tab === 'filiais' && storeRows.length === 0 && (
                  <div style={{ display: 'flex', alignItems: 'center', padding: '7px 14px', background: '#FFF8E1', borderRadius: 10, border: '1px solid #FDD835', fontSize: 12, color: '#795548' }}>
                    Metas não configuradas — o cálculo de orçamento por filial requer metas de venda.
                  </div>
                )}
              </div>
            </div>

            {/* Lista */}
            <div style={{ flex: 1, overflowY: 'auto', padding: '12px 16px 24px', display: 'flex', flexDirection: 'column', gap: 8 }}>
              {rows.length === 0 ? (
                <div style={{ padding: '48px 0', textAlign: 'center', color: 'var(--ink-400)', fontSize: 13 }}>
                  {tab === 'filiais' ? 'Metas não configuradas para este período.' : 'Sem dados de classe disponíveis.'}
                </div>
              ) : rows.map(row => {
                const isStore        = tab === 'filiais';
                const rowBudget      = includeExtra ? row.budget : row.budget - (row.extra ?? 0);
                const rowSaldo       = rowBudget - row.realizado;
                const rowUso         = rowBudget > 0 ? row.realizado / rowBudget : 0;
                const barColor       = rowUso <= 0.90 ? '#16A34A' : rowUso <= 1.10 ? '#D97706' : '#DC2626';
                return (
                  <div key={isStore ? row.id : row.name}
                    onClick={() => isStore && setSelectedRowId(prev => prev === row.id ? null : row.id)}
                    style={{ borderRadius: 12, padding: '12px 16px', cursor: isStore ? 'pointer' : 'default', transition: 'background .12s, border-color .12s',
                      background: isStore && selectedRowId === row.id ? 'var(--brand-50, #EFF6FF)' : 'var(--ink-50)',
                      border: isStore && selectedRowId === row.id ? '1.5px solid #93C5FD' : '1px solid var(--ink-100)',
                    }}>
                    {/* Nome + Vendas */}
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
                      <span style={{ fontSize: 14, fontWeight: 600, color: 'var(--ink-900)' }}>{row.name}</span>
                      {(isStore ? row.vendas : row.sale) > 0 && (
                        <div style={{ textAlign: 'right' }}>
                          <div style={{ fontSize: 9.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 1 }}>Vendas</div>
                          <span className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--brand-700, #1a4789)' }}>{BRL(isStore ? row.vendas : row.sale)}</span>
                          {isCmv && isStore && row.vendas > 0 && (
                            <div className="mono" style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', marginTop: 2 }}>
                              CMV {((row.verba / row.vendas) * 100).toFixed(1)}%
                            </div>
                          )}
                        </div>
                      )}
                    </div>

                    {/* Orçamento | Comprado | Saldo */}
                    <div style={{ display: 'flex', gap: 0, marginBottom: 8, background: 'var(--white)', borderRadius: 8, border: '1px solid var(--ink-100)', overflow: 'hidden' }}>
                      {[
                        { l: labelVerba, v: BRL(rowBudget),                             c: 'var(--ink-800)' },
                        { l: 'Comprado', v: BRL(row.realizado),                         c: 'var(--brand-600, #1a4789)' },
                        { l: 'Saldo',    v: (rowSaldo >= 0 ? '+' : '') + BRL(rowSaldo), c: rowSaldo >= 0 ? '#16A34A' : '#DC2626' },
                      ].map(({ l, v, c }, i) => (
                        <div key={l} style={{ flex: 1, padding: '7px 10px', textAlign: 'center', borderLeft: i > 0 ? '1px solid var(--ink-100)' : 'none' }}>
                          <div style={{ fontSize: 9.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 2 }}>{l}</div>
                          <div className="mono" style={{ fontSize: 12.5, fontWeight: 700, color: c }}>{v}</div>
                        </div>
                      ))}
                    </div>

                    {/* Barra + % utilizado + pill de orçamento extra na mesma linha */}
                    {(() => {
                      const hasExtra   = includeExtra && (row.extra ?? 0) > 0 && rowBudget > 0;
                      const basePct    = hasExtra ? (rowBudget - row.extra) / rowBudget * 100 : 100;
                      const fillPct    = Math.min(rowUso * 100, 100);
                      const inExtra    = hasExtra && row.realizado > (rowBudget - row.extra);
                      const overBudget = rowUso > 1;
                      const fillColor  = overBudget ? '#DC2626' : inExtra ? '#D97706' : '#16A34A';
                      return (
                        <>
                          <div style={{ position: 'relative', height: 6, background: 'var(--ink-200)', borderRadius: 99, overflow: 'hidden' }}>
                            {hasExtra && (
                              <div style={{ position: 'absolute', left: `${basePct}%`, right: 0, height: '100%', background: '#BBF7D0' }} />
                            )}
                            <div style={{ position: 'absolute', left: 0, height: '100%', borderRadius: 99, background: fillColor, width: `${fillPct}%`, transition: 'width .4s, background .3s' }} />
                            {hasExtra && (
                              <div style={{ position: 'absolute', left: `${basePct}%`, top: 0, bottom: 0, width: 2, background: 'rgba(255,255,255,0.7)', transform: 'translateX(-50%)' }} />
                            )}
                          </div>
                          <div style={{ marginTop: 4, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                            <div style={{ fontSize: 11, fontWeight: 600, color: fillColor }}>
                              {(rowUso * 100).toFixed(1)}% do orçamento utilizado
                            </div>
                            {(row.extra ?? 0) > 0 && includeExtra && (
                              <span style={{
                                display: 'inline-flex', alignItems: 'center', gap: 4,
                                padding: '2px 8px', background: '#F0FDF4', borderRadius: 20,
                                border: '1px solid #86EFAC', fontSize: 11, fontWeight: 700, color: '#16A34A',
                              }}>
                                {labelExtra} +{BRL(row.extra)}
                              </span>
                            )}
                          </div>
                        </>
                      );
                    })()}

                    {/* Entradas / Saídas em unidades + Estoque atual */}
                    {(row.soldQty > 0 || row.boughtQty > 0 || row.stockUnity > 0) && (() => {
                      const rowKey   = isStore ? row.id : row.name;
                      const expanded = expandedUnits.has(rowKey);
                      const delta    = (row.boughtQty ?? 0) - (row.soldQty ?? 0);
                      return (
                        <>
                          <div style={{ marginTop: 8, paddingTop: 7, borderTop: '1px solid var(--ink-100)', display: 'flex', justifyContent: 'flex-end' }}>
                            <button
                              onClick={e => {
                                e.stopPropagation();
                                setExpandedUnits(prev => {
                                  const next = new Set(prev);
                                  next.has(rowKey) ? next.delete(rowKey) : next.add(rowKey);
                                  return next;
                                });
                              }}
                              style={{
                                display: 'flex', alignItems: 'center', gap: 4,
                                background: 'none', border: 'none', cursor: 'pointer',
                                fontSize: 11, fontWeight: 700, color: 'var(--brand-600, #1a4789)',
                                padding: '2px 4px', borderRadius: 6,
                              }}
                            >
                              {expanded ? 'Ocultar unidades' : 'Ver unidades'}
                              <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ transition: 'transform .2s', transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}>
                                <path d="M2 3.5L5 6.5L8 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                              </svg>
                            </button>
                          </div>
                          {expanded && (
                            <div style={{ marginTop: 6, display: 'flex', gap: 0 }}>
                              <div style={{ flex: 1 }}>
                                <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 1 }}>Saídas</div>
                                <div className="mono" style={{ fontSize: 11.5, fontWeight: 700, color: '#34C759' }}>{NUM(row.soldQty ?? 0)} un</div>
                              </div>
                              <div style={{ flex: 1 }}>
                                <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 1 }}>Entradas</div>
                                <div className="mono" style={{ fontSize: 11.5, fontWeight: 700, color: '#0B69C7' }}>{NUM(row.boughtQty ?? 0)} un</div>
                              </div>
                              {(row.soldQty > 0 || row.boughtQty > 0) && (
                                <div style={{ flex: 1 }}>
                                  <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 1 }}>Saldo un.</div>
                                  <div className="mono" style={{ fontSize: 11.5, fontWeight: 700, color: delta >= 0 ? '#0B69C7' : '#FF9500' }}>
                                    {delta >= 0 ? '+' : ''}{NUM(delta)} un
                                  </div>
                                </div>
                              )}
                              {row.stockUnity > 0 && (
                                <div style={{ flex: 1, textAlign: 'right' }}>
                                  <div style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)', marginBottom: 1 }}>Estoque</div>
                                  <div className="mono" style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--ink-800)' }}>
                                    {NUM(Math.round(row.stockUnity))} un
                                    {row.stockCost > 0 && <span style={{ fontSize: 10, fontWeight: 500, color: 'var(--ink-500)', marginLeft: 4 }}>{BRL(row.stockCost)}</span>}
                                  </div>
                                </div>
                              )}
                            </div>
                          )}
                        </>
                      );
                    })()}
                  </div>
                );
              })}
            </div>
          </div>

          {/* Tooltip do donut — z-index independente, fora do wrapper */}
          {donutPanelData && donutHover && (() => {
            const hovClass = donutPanelData.classData.find(d => d.name === donutHover.name);
            if (!hovClass) return null;
            return (
              <div style={{
                position: 'fixed', zIndex: 320,
                left: donutHover.x + 16, top: donutHover.y - 16,
                background: '#0c2340', color: '#e8edf5',
                borderRadius: 10, padding: '9px 13px',
                boxShadow: '0 6px 24px rgba(0,0,0,0.35)',
                pointerEvents: 'none', whiteSpace: 'nowrap',
                fontSize: 11.5, lineHeight: 1.75,
              }}>
                <div style={{ fontWeight: 700, marginBottom: 3, color: '#fff' }}>{donutHover.name}</div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'baseline' }}>
                  <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 10.5 }}>Orçamento</span>
                  <span className="mono" style={{ fontWeight: 700 }}>{BRL(hovClass.budget)}</span>
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'baseline' }}>
                  <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 10.5 }}>Comprado</span>
                  <span className="mono" style={{ fontWeight: 700 }}>{BRL(hovClass.purc)}</span>
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'baseline' }}>
                  <span style={{ color: 'rgba(255,255,255,0.5)', fontSize: 10.5 }}>Saldo</span>
                  <span className="mono" style={{ fontWeight: 700, color: hovClass.saldo >= 0 ? '#86EFAC' : '#FCA5A5' }}>
                    {hovClass.saldo >= 0 ? '+' : ''}{BRL(hovClass.saldo)}
                  </span>
                </div>
              </div>
            );
          })()}

          {/* Coluna esquerda: rosquinha (quando filial selecionada) + contas a pagar */}
          <div style={{
            position: 'fixed', top: '4%', zIndex: 212,
            right: 'calc(50vw + min(440px, 46vw) + 8px)',
            width: 310, maxHeight: '92vh',
            display: 'flex', flexDirection: 'column', gap: 8,
            overflowY: 'auto',
            pointerEvents: 'none',
          }}>
            {/* Painel da rosquinha */}
            {donutPanelData && (() => {
              const { SW, R, SZ, classData, classColors, donutC, totalBudget, totalPurc, totalSaldo } = donutPanelData;
              return (
                <div style={{
                  pointerEvents: 'all', flexShrink: 0,
                  background: 'var(--white)', borderRadius: 20,
                  boxShadow: '0 24px 64px rgba(0,0,0,0.22)',
                  border: '1px solid var(--ink-100)',
                  padding: '16px',
                }}>
                  {/* Header */}
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
                      {_selStore.storeName || `Loja ${selectedRowId}`}
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 5, flexShrink: 0, marginLeft: 6 }}>
                      <button onClick={() => { setTab('classes'); setSelectedStores(new Set([selectedRowId])); }} style={{
                        padding: '3px 9px', borderRadius: 6, border: '1px solid var(--brand-300, #93C5FD)',
                        background: tab === 'classes' ? 'var(--brand-500, #3B82F6)' : 'var(--brand-50, #EFF6FF)',
                        color: tab === 'classes' ? '#fff' : 'var(--brand-600, #2563EB)',
                        cursor: 'pointer', fontSize: 10.5, fontWeight: 700, fontFamily: 'inherit',
                      }}>Detalhes</button>
                      <button onClick={() => setSelectedRowId(null)} style={{
                        width: 22, height: 22, borderRadius: '50%', border: '1px solid var(--ink-200)',
                        background: 'var(--ink-50)', cursor: 'pointer', fontSize: 14, color: 'var(--ink-400)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                      }}>×</button>
                    </div>
                  </div>

                  {/* Donut + resumo lado a lado */}
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
                    <div style={{ flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                      <svg width={SZ} height={SZ} style={{ overflow: 'visible' }} onMouseLeave={() => setDonutHover(null)}>
                        <g transform={`rotate(-90 ${SZ / 2} ${SZ / 2})`}>
                          {donutC.segs.length === 0
                            ? <circle cx={SZ/2} cy={SZ/2} r={R} fill="none" stroke="var(--ink-100)" strokeWidth={SW} />
                            : donutC.segs.map(seg => (
                                <circle key={seg.name} cx={SZ/2} cy={SZ/2} r={R}
                                  fill="none" stroke={seg.color} strokeWidth={SW}
                                  strokeDasharray={`${seg.len} ${donutC.circ - seg.len}`}
                                  strokeDashoffset={-seg.offset}
                                  style={{ transition: 'stroke-dasharray .4s, stroke-dashoffset .4s' }}
                                />
                              ))
                          }
                          {donutC.segs.map(seg => (
                            <circle key={`hit-${seg.name}`} cx={SZ/2} cy={SZ/2} r={R}
                              fill="none" stroke="transparent" strokeWidth={SW + 10}
                              strokeDasharray={`${seg.len} ${donutC.circ - seg.len}`}
                              strokeDashoffset={-seg.offset}
                              style={{ cursor: 'default', pointerEvents: 'stroke' }}
                              onMouseEnter={(e) => setDonutHover({ name: seg.name, x: e.clientX, y: e.clientY })}
                              onMouseMove={(e)  => setDonutHover({ name: seg.name, x: e.clientX, y: e.clientY })}
                              onMouseLeave={() => setDonutHover(null)}
                            />
                          ))}
                        </g>
                      </svg>
                      <div style={{ fontSize: 8.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-400)', marginTop: 5 }}>por classe</div>
                    </div>

                    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 5 }}>
                      {[
                        { l: labelVerba, v: BRL(totalBudget),                               c: 'var(--ink-800)' },
                        { l: 'Comprado', v: BRL(totalPurc),                                 c: 'var(--brand-600, #1a4789)' },
                        { l: 'Saldo',    v: (totalSaldo >= 0 ? '+' : '') + BRL(totalSaldo), c: totalSaldo >= 0 ? '#16A34A' : '#DC2626' },
                      ].map(({ l, v, c }) => (
                        <div key={l} style={{ background: 'var(--ink-50)', borderRadius: 8, border: '1px solid var(--ink-100)', padding: '5px 9px' }}>
                          <div style={{ fontSize: 8.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)' }}>{l}</div>
                          <div className="mono" style={{ fontSize: 12, fontWeight: 700, color: c, marginTop: 1 }}>{v}</div>
                        </div>
                      ))}
                    </div>
                  </div>

                  {/* Saldo por classe */}
                  {(() => {
                    const MAX_VISIBLE = 6;
                    const hasMore = classData.length > MAX_VISIBLE;
                    const visibleData = classesExpanded ? classData : classData.slice(0, MAX_VISIBLE);
                    const handleWheelClasses = (e) => {
                      if (classesExpanded) return;
                      if (e.deltaY > 0 && hasMore) { e.preventDefault(); setClassesExpanded(true); }
                    };
                    return (
                      <div>
                        <div
                          onWheel={handleWheelClasses}
                          style={{ display: 'flex', flexDirection: 'column', gap: 5 }}
                        >
                          {visibleData.map(d => {
                            const clr      = classColors[d.name] ?? '#CBD5E1';
                            const saldoClr = d.saldo >= 0 ? '#16A34A' : '#DC2626';
                            const barClr   = d.uso <= 0.9 ? '#16A34A' : d.uso <= 1.1 ? '#D97706' : '#DC2626';
                            const isHov    = donutHover?.name === d.name;
                            const isChk    = selectedClasses.has(d.name);
                            const toggleClass = () => setSelectedClasses(prev => {
                              const next = new Set(prev);
                              next.has(d.name) ? next.delete(d.name) : next.add(d.name);
                              return next;
                            });
                            return (
                              <div key={d.name} onClick={toggleClass} style={{
                                padding: '7px 9px', borderRadius: 9, cursor: 'pointer',
                                border: `1px solid ${isChk ? clr : isHov ? clr : 'var(--ink-100)'}`,
                                background: isChk ? 'var(--ink-50)' : isHov ? 'var(--ink-50)' : 'var(--white)',
                                transition: 'background .12s, border-color .12s',
                              }}>
                                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
                                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                                    <span style={{
                                      width: 13, height: 13, borderRadius: 4, flexShrink: 0,
                                      border: `1.5px solid ${isChk ? clr : 'var(--ink-300)'}`,
                                      background: isChk ? clr : 'var(--white)',
                                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                                      transition: 'background .12s, border-color .12s',
                                    }}>
                                      {isChk && <svg width="8" height="6" viewBox="0 0 8 6" fill="none"><path d="M1 3l2 2 4-4" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                                    </span>
                                    <span style={{ width: 7, height: 7, borderRadius: 2, background: clr, flexShrink: 0 }} />
                                    <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-800)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 110 }}>{d.name}</span>
                                  </div>
                                  <span className="mono" style={{ fontSize: 11, fontWeight: 700, color: saldoClr, flexShrink: 0 }}>
                                    {d.saldo >= 0 ? '+' : ''}{BRL(d.saldo)}
                                  </span>
                                </div>
                                <div style={{ height: 3, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden' }}>
                                  <div style={{ height: '100%', width: `${Math.min(d.uso * 100, 100)}%`, background: barClr, borderRadius: 99 }} />
                                </div>
                              </div>
                            );
                          })}
                        </div>
                        {hasMore && (
                          <button
                            onClick={() => setClassesExpanded(p => !p)}
                            style={{
                              marginTop: 6, width: '100%', padding: '5px 0',
                              background: 'none', border: '1px solid var(--ink-150, var(--ink-200))',
                              borderRadius: 8, cursor: 'pointer', fontFamily: 'inherit',
                              fontSize: 10.5, fontWeight: 700, color: 'var(--ink-400)',
                              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4,
                              transition: 'background .12s, color .12s',
                            }}
                            onMouseEnter={e => { e.currentTarget.style.background = 'var(--ink-50)'; e.currentTarget.style.color = 'var(--ink-700)'; }}
                            onMouseLeave={e => { e.currentTarget.style.background = 'none'; e.currentTarget.style.color = 'var(--ink-400)'; }}
                          >
                            {classesExpanded
                              ? (<>
                                  <svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 6.5L5 3.5L8 6.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
                                  Recolher
                                </>)
                              : (<>
                                  <svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5L8 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
                                  Ver todas ({classData.length})
                                </>)
                            }
                          </button>
                        )}
                      </div>
                    );
                  })()}
                </div>
              );
            })()}

            {/* Painel de contas a pagar — visível apenas quando showBillsPanel = true */}
            {showBillsPanel && (
              <div style={{
                pointerEvents: 'all', flexShrink: 0,
                background: 'var(--white)', borderRadius: 20,
                boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
                border: '1px solid var(--ink-100)',
                overflow: 'hidden',
              }}>
                <button
                  onClick={() => setBillsSectionOpen(p => !p)}
                  style={{
                    width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                    padding: '11px 16px',
                    background: 'none', border: 'none',
                    borderBottom: billsSectionOpen ? '1px solid var(--ink-100)' : 'none',
                    cursor: 'pointer', fontFamily: 'inherit',
                  }}
                >
                  <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
                    <svg width="14" height="14" viewBox="0 -960 960 960" fill="#D97706" style={{ flexShrink: 0 }}>
                      <path d="M560-440q-50 0-85-35t-35-85q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35zM280-320q-33 0-56.5-23.5T200-400v-320q0-33 23.5-56.5T280-800h560q33 0 56.5 23.5T920-720v320q0 33-23.5 56.5T840-320H280zm80-80h400q0-33 23.5-56.5T840-480v-160q-33 0-56.5-23.5T760-720H360q0 33-23.5 56.5T280-640v160q33 0 56.5 23.5T360-400zm440 240H120q-33 0-56.5-23.5T40-240v-440h80v440h680v80zM280-400v-320 320z"/>
                    </svg>
                    <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)' }}>
                      Contas a Pagar
                      {selectedRowId && _selStore && (
                        <span style={{ fontWeight: 400, color: 'var(--ink-500)', fontSize: 11, marginLeft: 4 }}>
                          · {_selStore.storeName || `Loja ${selectedRowId}`}
                        </span>
                      )}
                    </span>
                    {billsRaw !== null && billsMonthSummary && billsMonthSummary.reduce((s, m) => s + m.count, 0) > 0 && (
                      <span style={{ fontSize: 10, fontWeight: 700, color: '#D97706', background: '#FEF3C7', padding: '1px 6px', borderRadius: 4 }}>
                        {billsMonthSummary.reduce((s, m) => s + m.count, 0)} conta{billsMonthSummary.reduce((s, m) => s + m.count, 0) !== 1 ? 's' : ''}
                      </span>
                    )}
                  </div>
                  <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
                    style={{ transition: 'transform .2s', transform: billsSectionOpen ? 'rotate(180deg)' : 'rotate(0deg)', color: 'var(--ink-400)', flexShrink: 0 }}>
                    <path d="M2.5 4.5L6 7.5L9.5 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                </button>

                {billsSectionOpen && (
                  <div style={{ padding: '10px 14px 14px', display: 'flex', flexDirection: 'column', gap: 8 }}>
                    {(() => {
                      const curMonthStr = SF.today().slice(0, 7);
                      const curEntry    = billsMonthSummary?.find(m => m.month === curMonthStr) ?? { month: curMonthStr, total: 0, pago: 0, pendente: 0, count: 0 };
                      const secEntry    = billsMonthSummary?.find(m => m.month !== curMonthStr);
                      const skeletonCard = <div style={{ height: 60, borderRadius: 10, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />;

                      const MonthCard = ({ entry, isCurrent }) => {
                        const pct = entry.total > 0 ? entry.pago / entry.total : 0;
                        const [y, mo] = entry.month.split('-');
                        const lbl = `${MONTH_NAMES_FULL_PT[parseInt(mo) - 1]} ${y}`;
                        return (
                          <div style={{
                            borderRadius: 10,
                            border: `1px solid ${isCurrent ? '#FDE68A' : 'var(--ink-100)'}`,
                            padding: '9px 10px',
                            background: isCurrent ? '#FFFBEB' : 'var(--white)',
                          }}>
                            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
                              <div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                                <span style={{ fontSize: 11.5, fontWeight: 700, color: isCurrent ? '#92400E' : 'var(--ink-800)' }}>{lbl}</span>
                                {isCurrent && (
                                  <span style={{ fontSize: 9, fontWeight: 700, color: '#D97706', background: '#FDE68A', padding: '1px 5px', borderRadius: 3, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                                    este mês
                                  </span>
                                )}
                              </div>
                              <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-800)' }}>{BRL(entry.total)}</span>
                            </div>
                            <div style={{ height: 4, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden', marginBottom: 5 }}>
                              <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: '#16A34A', borderRadius: 99, transition: 'width .4s' }} />
                            </div>
                            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                              {entry.pago > 0
                                ? <span style={{ fontSize: 10.5, fontWeight: 600, color: '#16A34A' }}>Pago {BRL(entry.pago)}</span>
                                : <span style={{ fontSize: 10.5, fontWeight: 600, color: 'var(--ink-300)' }}>Nada pago</span>
                              }
                              {entry.pendente > 0 && (
                                <span style={{ fontSize: 10.5, fontWeight: 600, color: '#D97706' }}>Pendente {BRL(entry.pendente)}</span>
                              )}
                            </div>
                          </div>
                        );
                      };

                      return (
                        <>
                          {/* Mês atual — sempre fixo */}
                          {billsRaw === null ? skeletonCard : <MonthCard entry={curEntry} isCurrent={true} />}

                          {/* Segundo mês — aparece ao clicar em "+" */}
                          {secondMonthVisible ? (
                            <div>
                              {/* Navegação do segundo mês */}
                              <div style={{
                                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                marginBottom: 6, padding: '0 2px',
                              }}>
                                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                                  <button
                                    onClick={() => setBillsMonthOffset(o => Math.max(0, o - 1))}
                                    style={{
                                      width: 24, height: 24, borderRadius: 6,
                                      border: `1px solid ${billsMonthOffset === 0 ? 'var(--ink-150)' : 'var(--ink-300)'}`,
                                      background: billsMonthOffset === 0 ? 'var(--ink-50)' : 'var(--white)',
                                      cursor: billsMonthOffset === 0 ? 'default' : 'pointer',
                                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                                      color: billsMonthOffset === 0 ? 'var(--ink-200)' : 'var(--ink-700)',
                                      fontSize: 14, lineHeight: 1, fontFamily: 'inherit', padding: 0,
                                    }}
                                    title="Mês anterior"
                                  >‹</button>
                                  <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-500)' }}>
                                    {secEntry ? (() => { const [y, mo] = secEntry.month.split('-'); return `${MONTH_NAMES_FULL_PT[parseInt(mo) - 1]} ${y}`; })() : '—'}
                                  </span>
                                  <button
                                    onClick={() => setBillsMonthOffset(o => o + 1)}
                                    style={{
                                      width: 24, height: 24, borderRadius: 6,
                                      border: '1px solid var(--ink-300)', background: 'var(--white)',
                                      cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                                      color: 'var(--ink-700)', fontSize: 14, lineHeight: 1, fontFamily: 'inherit', padding: 0,
                                    }}
                                    title="Próximo mês"
                                  >›</button>
                                </div>
                                <button
                                  onClick={() => { setSecondMonthVisible(false); setBillsMonthOffset(0); }}
                                  style={{
                                    background: 'none', border: 'none', cursor: 'pointer',
                                    fontSize: 14, color: 'var(--ink-400)', lineHeight: 1, padding: '0 2px',
                                  }}
                                  title="Fechar"
                                >×</button>
                              </div>
                              {billsRawSecond === null ? skeletonCard : <MonthCard entry={secEntry} isCurrent={false} />}
                            </div>
                          ) : (
                            <button
                              onClick={() => setSecondMonthVisible(true)}
                              style={{
                                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                                width: '100%', padding: '8px 0',
                                border: '1.5px dashed var(--ink-200)', borderRadius: 10,
                                background: 'none', cursor: 'pointer', fontFamily: 'inherit',
                                fontSize: 12, fontWeight: 600, color: 'var(--ink-500)',
                                transition: 'border-color .12s, color .12s',
                              }}
                              onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--ink-400)'; e.currentTarget.style.color = 'var(--ink-700)'; }}
                              onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--ink-200)'; e.currentTarget.style.color = 'var(--ink-500)'; }}
                            >
                              <span style={{ fontSize: 16, lineHeight: 1, fontWeight: 400 }}>+</span>
                              Ver outro mês
                            </button>
                          )}
                        </>
                      );
                    })()}
                  </div>
                )}
              </div>
            )}
          </div>

          {/* Balão de informação — fora do modal para não ser preso pelo transform */}
          {infoOpen && (() => {
            const isDark      = document.documentElement.getAttribute('data-theme') === 'dark';
            const bg          = isDark ? '#0c2340'                  : '#ffffff';
            const border      = isDark ? 'none'                     : '1px solid #e2e8f0';
            const textPrimary = isDark ? '#e8edf5'                  : '#1a2744';
            const textMuted   = isDark ? 'rgba(255,255,255,0.45)'   : '#64748b';
            const blockBg     = isDark ? 'rgba(255,255,255,0.07)'   : '#f1f5f9';
            const divider     = isDark ? 'rgba(255,255,255,0.1)'    : '#e2e8f0';
            const closeBg     = isDark ? 'rgba(255,255,255,0.08)'   : '#f1f5f9';
            const closeColor  = isDark ? 'rgba(255,255,255,0.55)'   : '#64748b';
            const blue        = isDark ? '#7EC8FA'                  : '#1a4789';
            const green       = isDark ? '#86EFAC'                  : '#15803d';
            const red         = isDark ? '#FCA5A5'                  : '#b91c1c';

            const totalVendas = storeRows.reduce((s, r) => s + (r.vendas ?? 0), 0);
            const storeComExtra = storeRows.find(r => (r.extra ?? 0) > 0);
            const exVendas  = storeComExtra ? storeComExtra.vendas  : totalVendas;
            const exMeta    = storeComExtra ? (storeComExtra.vendas - storeComExtra.extra / rate) : (totalVendas * 0.9);
            const exExtra   = storeComExtra ? storeComExtra.extra   : totExtra;

            return (
              <div style={{
                position: 'fixed', top: '50%', right: 20, transform: 'translateY(-50%)',
                width: 252, maxHeight: '80vh', overflowY: 'auto',
                background: bg, border, borderRadius: 16,
                boxShadow: isDark ? '0 12px 40px rgba(0,0,0,0.45)' : '0 8px 32px rgba(0,0,0,0.12)',
                zIndex: 220, padding: '20px 18px',
                display: 'flex', flexDirection: 'column', gap: 16,
              }}>
                <div style={{ fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: textMuted }}>Como é calculado</div>

                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: blue, marginBottom: 6 }}>{labelVerba}</div>
                  <div style={{ fontSize: 12, lineHeight: 1.6, color: textPrimary }}>
                    {isCmv
                      ? <>O orçamento de cada filial é igual ao seu <strong>CMV</strong> (custo das mercadorias vendidas) no período.</>
                      : <>O quanto você pode comprar com base no que <strong>vendeu</strong> no período.</>}
                  </div>
                  <div style={{ marginTop: 10, padding: '8px 12px', background: blockBg, borderRadius: 10, fontFamily: 'monospace', fontSize: 12 }}>
                    <div style={{ color: textMuted, marginBottom: 3 }}>fórmula</div>
                    {isCmv
                      ? <div style={{ color: blue }}>CMV de cada filial</div>
                      : <>
                          <div style={{ color: blue }}>Vendas * {Math.round(rate * 100)}%</div>
                          <div style={{ marginTop: 5, color: textMuted, fontSize: 11 }}>
                            CMV% configurado: <span style={{ color: textPrimary, fontWeight: 700 }}>{Math.round(rate * 100)}%</span>
                          </div>
                        </>}
                  </div>
                  <div style={{ marginTop: 7, padding: '7px 10px', background: blockBg, borderRadius: 8, fontSize: 11, color: textMuted, lineHeight: 1.6 }}>
                    <span style={{ color: textPrimary, fontWeight: 600 }}>No período atual:</span><br />
                    {isCmv
                      ? <>CMV total: <span style={{ color: blue, fontWeight: 700 }}>{BRL(totBudget)}</span></>
                      : <>{BRL(totalVendas)} * {Math.round(rate * 100)}% =<span style={{ color: blue, fontWeight: 700 }}>{BRL(totBudget - totExtra)}</span></>}
                  </div>
                </div>

                {!isCmv && (
                  <>
                    <div style={{ height: 1, background: divider }} />

                    <div>
                      <div style={{ fontSize: 13, fontWeight: 700, color: green, marginBottom: 6 }}>{labelExtra}</div>
                      <div style={{ fontSize: 12, lineHeight: 1.6, color: textPrimary }}>
                        Quando as vendas superam a meta, o excedente gera verba adicional.
                      </div>
                      <div style={{ marginTop: 10, padding: '8px 12px', background: blockBg, borderRadius: 10, fontFamily: 'monospace', fontSize: 12 }}>
                        <div style={{ color: textMuted, marginBottom: 3 }}>fórmula</div>
                        <div style={{ color: green }}>(Vendas - Meta) * {Math.round(rate * 100)}%</div>
                        <div style={{ marginTop: 4, color: textMuted, fontSize: 11 }}>apenas quando Vendas &gt; Meta</div>
                      </div>
                      {totExtra > 0 ? (
                        <div style={{ marginTop: 7, padding: '7px 10px', background: blockBg, borderRadius: 8, fontSize: 11, color: textMuted, lineHeight: 1.6 }}>
                          <span style={{ color: textPrimary, fontWeight: 600 }}>{storeComExtra ? `${storeComExtra.name}:` : 'No período:'}</span><br />
                          ({BRL(exVendas)} - {BRL(exMeta)}) * {Math.round(rate * 100)}% =<span style={{ color: green, fontWeight: 700 }}>{BRL(exExtra)}</span>
                        </div>
                      ) : (
                        <div style={{ marginTop: 7, fontSize: 11, color: textMuted }}>Nenhuma filial superou a meta no período.</div>
                      )}
                    </div>
                  </>
                )}

                <div style={{ height: 1, background: divider }} />

                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: red, marginBottom: 6 }}>Saldo</div>
                  <div style={{ fontSize: 12, lineHeight: 1.6, color: textPrimary }}>
                    Diferença entre o orçamento disponível e o que foi comprado.
                  </div>
                  <div style={{ marginTop: 10, padding: '8px 12px', background: blockBg, borderRadius: 10, fontFamily: 'monospace', fontSize: 12 }}>
                    <div style={{ color: textMuted, marginBottom: 3 }}>fórmula</div>
                    <div style={{ color: red }}>{labelVerba} − Comprado</div>
                  </div>
                  <div style={{ marginTop: 7, padding: '7px 10px', background: blockBg, borderRadius: 8, fontSize: 11, color: textMuted, lineHeight: 1.6 }}>
                    <span style={{ color: textPrimary, fontWeight: 600 }}>No período atual:</span><br />
                    {BRL(displayBudget)} − {BRL(totReal)} = <span style={{ color: displaySaldo >= 0 ? green : red, fontWeight: 700 }}>{displaySaldo >= 0 ? '+' : ''}{BRL(displaySaldo)}</span>
                  </div>
                </div>

                <button onClick={() => setInfoOpen(false)} style={{
                  marginTop: 2, padding: '7px 0', background: closeBg, border: 'none',
                  borderRadius: 8, color: closeColor, fontSize: 12, fontWeight: 600,
                  cursor: 'pointer', fontFamily: 'inherit',
                }}>Fechar</button>
              </div>
            );
          })()}
        </>
      )}
    </>
  );
}

// ─── Planejamento de Compras por Filial ──────────────────────────────────────
function PurchasesStoreBudgetSection({ stores, goalFactor, cmvGreen = 55, loading }) {
  const [expandedRows, setExpandedRows] = React.useState(new Set());
  const toggleRow = (id) => setExpandedRows(prev => {
    const next = new Set(prev);
    next.has(id) ? next.delete(id) : next.add(id);
    return next;
  });

  const rows = React.useMemo(() => {
    if (goalFactor == null) return [];
    return stores
      .map(s => {
        const goal      = safeN(s.goalAccum) > 0 ? safeN(s.goalAccum) : safeN(s.totalGoal) * goalFactor;
        const verba     = (goal > 0 ? Math.min(s.total, goal) : s.total) * (cmvGreen / 100);
        const extra     = goal > 0 ? Math.max(0, s.total - goal) * (cmvGreen / 100) : 0;
        const budget    = verba + extra; // = s.total * cmvGreen%
        const realizado = safeN(s.purchases?.total);
        const saldo     = budget - realizado;
        const uso       = budget > 0 ? realizado / budget : 0;
        return { id: s.storeId, name: s.storeName || `Loja ${s.storeId}`, goal, verba, extra, budget, realizado, saldo, uso, vendas: s.total };
      })
      .filter(r => r.verba > 0 || r.realizado > 0)
      .sort((a, b) => b.budget - a.budget);
  }, [stores, goalFactor, cmvGreen]);

  if (loading) return (
    <div className="card" style={{ padding: 24, height: 120, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
  );
  if (!rows.length) return null;

  const totVerba     = rows.reduce((s, r) => s + r.budget, 0);
  const totRealizado = rows.reduce((s, r) => s + r.realizado, 0);
  const totSaldo     = totVerba - totRealizado;

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '16px 20px 12px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid var(--ink-100)' }}>
        <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>Planejamento por Filial</div>
        <div style={{ display: 'flex', gap: 20 }}>
          <div style={{ textAlign: 'right' }}>
            <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Verba Total</div>
            <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)' }}>{BRL(totVerba)}</div>
          </div>
          <div style={{ textAlign: 'right' }}>
            <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Saldo</div>
            <div className="mono" style={{ fontSize: 13, fontWeight: 700, color: totSaldo >= 0 ? '#16A34A' : '#DC2626' }}>
              {totSaldo >= 0 ? '+' : ''}{BRL(totSaldo)}
            </div>
          </div>
        </div>
      </div>

      <div style={{ padding: '10px 12px 14px', display: 'flex', flexDirection: 'column', gap: 6 }}>
        {rows.map(row => {
          const barColor  = row.uso <= 0.90 ? '#16A34A' : row.uso <= 1.10 ? '#D97706' : '#DC2626';
          const isExpanded = expandedRows.has(row.id);
          return (
            <div key={row.id} onClick={() => toggleRow(row.id)} style={{ borderRadius: 10, padding: '10px 14px', background: 'var(--ink-50)', border: '1px solid var(--ink-100)', cursor: 'pointer', transition: 'background .12s' }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--ink-100)'}
              onMouseLeave={e => e.currentTarget.style.background = 'var(--ink-50)'}
            >
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 5 }}>
                <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)' }}>{row.name}</span>
                <span style={{ fontSize: 12, fontWeight: 700, color: row.saldo >= 0 ? '#16A34A' : '#DC2626' }}>
                  {row.saldo >= 0 ? '+' : ''}{BRL(row.saldo)}
                </span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--ink-500)', marginBottom: 5 }}>
                <span className="mono">Comprado {BRL(row.realizado)}</span>
                <span className="mono">
                  Verba {BRL(row.verba)}
                  {row.extra > 0 && <span style={{ color: 'var(--brand-500)' }}> + extra {BRL(row.extra)}</span>}
                </span>
              </div>
              <div style={{ height: 4, background: 'var(--ink-200)', borderRadius: 99, overflow: 'hidden' }}>
                <div style={{ height: '100%', borderRadius: 99, background: barColor, width: `${Math.min(row.uso * 100, 100)}%`, transition: 'width .4s' }} />
              </div>
              {row.budget > 0 && (
                <div style={{ marginTop: 3, fontSize: 10, fontWeight: 600, color: barColor, textAlign: 'right' }}>
                  {(row.uso * 100).toFixed(1)}% da verba utilizada
                </div>
              )}
              {isExpanded && (
                <div style={{ marginTop: 8, paddingTop: 8, borderTop: '1px solid var(--ink-200)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <span style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--ink-400)' }}>Compras / Vendas</span>
                  <span className="mono" style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-700)' }}>
                    {BRL(row.realizado)} / {BRL(row.vendas)}
                  </span>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Compras por Classe / Grupo (mesmo padrão visual do ClassGroupSection de vendas) ─
function PurchasesClassGroupSection({ stores, purchaseGroups, classRanking = [], cmvGreen = 55, cmvYellow = 65, cmvOrange = 70, loading }) {
  const classMap = {};
  for (const s of stores) {
    for (const c of Object.values(s.purchases?.classifications ?? {})) {
      if (!c.className) continue;
      const ex = classMap[c.className] ?? { name: c.className, total: 0, quantity: 0 };
      ex.total    += c.total ?? 0;
      ex.quantity += c.quantity ?? 0;
      classMap[c.className] = ex;
    }
  }
  const classes = Object.values(classMap);

  // Mapa de vendas por classe (para CMV)
  const salesByClass = {};
  for (const c of classRanking) {
    if (c.name) salesByClass[c.name] = (salesByClass[c.name] ?? 0) + (c.total ?? 0);
  }
  // Grupos vêm de data.stock[storeId].groups (igual ao mobile: getGroupsLatest)
  // Mapeamos totalCost→total e unity→quantity para usar o mesmo sort/display
  const groups = (purchaseGroups ?? []).map(g => ({
    name:     g.name,
    total:    g.totalCost,
    quantity: g.unity,
  }));
  const hasClasses = classes.length > 0;
  const hasGroups  = groups.length  > 0;

  const [tab,   setTab]   = React.useState(hasClasses ? 'classes' : 'grupos');
  const [sort,  setSort]  = React.useState('total');
  const [limit, setLimit] = React.useState(10);

  React.useEffect(() => {
    if (!hasClasses && hasGroups) setTab('grupos');
    if (!hasGroups  && hasClasses) setTab('classes');
  }, [hasGroups, hasClasses]);
  React.useEffect(() => { setLimit(10); }, [tab, sort]);

  const rows = React.useMemo(() => {
    const src = tab === 'classes' ? classes : groups;
    return [...src].sort((a, b) => b[sort] - a[sort]);
  }, [tab, sort, classes.length, groups.length]);

  const grandTotal    = rows.reduce((s, r) => s + r.total,    0);
  const grandQuantity = rows.reduce((s, r) => s + r.quantity, 0);
  const visible = rows.slice(0, limit);

  if (loading) return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '20px 24px 14px', fontSize: 16, fontWeight: 700 }}>Classes / Grupos</div>
      <div style={{ padding: '0 16px 16px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {[...Array(5)].map((_, i) => <div key={i} style={{ height: 72, borderRadius: 12, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>)}
      </div>
    </div>
  );

  if (!hasClasses && !hasGroups) return null;

  return (
    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>

      {/* Segment control centralizado — igual às vendas */}
      {hasClasses && hasGroups && (
        <div style={{ padding: '14px 16px 0', display: 'flex', justifyContent: 'center' }}>
          <div style={{ display: 'flex', background: '#E8EEF8', borderRadius: 10, padding: 3 }}>
            {[['classes','Classes'],['grupos','Grupos']].map(([v, l]) => (
              <button key={v} onClick={() => setTab(v)} style={{
                padding: '6px 20px', fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
                border: 0, borderRadius: 8, cursor: 'pointer',
                background: tab === v ? 'var(--white)' : 'transparent',
                color:      tab === v ? 'var(--brand-600, var(--brand-700))' : '#888',
                boxShadow:  tab === v ? '0 1px 4px rgba(0,0,0,0.08)' : 'none',
                transition: 'all .12s',
              }}>{l}</button>
            ))}
          </div>
        </div>
      )}

      {/* Cabeçalho + pills R$/Qtd */}
      <div style={{ padding: '12px 16px 10px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>
          {tab === 'classes' ? 'Compras por Classe' : 'Compras por Grupo'}
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {[['total','R$'],['quantity','Qtd']].map(([k, l]) => (
            <button key={k} onClick={() => setSort(k)} style={{
              padding: '3px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
              border: 0, borderRadius: 999, cursor: 'pointer',
              background: sort === k ? 'var(--brand-500)' : '#E8EEF8',
              color:      sort === k ? '#fff' : '#666',
              transition: 'all .12s',
            }}>{l}</button>
          ))}
        </div>
      </div>

      {/* Lista de itens — cards idênticos ao ClassGroupSection de vendas */}
      {!rows.length
        ? <EmptyState text="Sem dados para este período." style={{ padding: '0 16px 20px' }} />
        : (
          <div style={{ padding: '0 12px 14px', display: 'flex', flexDirection: 'column', gap: 8 }}>
            {visible.map((row, i) => {
              const pct      = sort === 'total'
                ? (grandTotal    > 0 ? row.total    / grandTotal    : 0)
                : (grandQuantity > 0 ? row.quantity / grandQuantity : 0);
              const medal    = CG_MEDALS[i] ?? null;
              const barColor = medal?.dot ?? CG_BLUE;
              const cardBg   = medal?.bg  ?? 'var(--white)';
              const cardBd   = medal?.border ?? 'rgba(0,0,0,0.1)';
              const saleCls  = tab === 'classes' ? (salesByClass[row.name] ?? 0) : 0;
              const cmv      = saleCls > 0 ? row.total / saleCls * 100 : null;
              const cmvColor = cmv != null
                ? (cmv > cmvOrange ? '#FF3B30' : cmv > cmvYellow ? '#FF6B00' : cmv > cmvGreen ? '#F5A623' : '#34C759')
                : null;
              const cmvLabel = cmv != null
                ? (cmv > cmvOrange ? 'Acima do recomendado' : cmv > cmvYellow ? 'Margem apertando' : cmv > cmvGreen ? 'Dentro do padrão' : 'Compra eficiente')
                : null;

              return (
                <div key={row.name} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
                  <div style={{ width: 4, borderRadius: 4, flexShrink: 0, background: barColor }}/>
                  <div style={{ flex: 1, borderRadius: 12, padding: '10px 16px', background: cardBg, border: `0.5px solid ${cardBd}` }}>
                    <div style={{ fontSize: 12, color: '#6B7280', marginBottom: 5 }}>
                      {sort === 'total' ? `${NUM(row.quantity)} itens` : BRL(row.total)}
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
                        <div style={{
                          width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
                          background: medal ? medal.dot + '30' : '#F0F0F0',
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontSize: 12, fontWeight: 700, color: medal?.dot ?? '#999',
                        }}>{i + 1}</div>
                        <span style={{
                          fontSize: 13.5, fontWeight: 600, color: 'var(--ink-900)',
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        }} title={row.name}>{row.name}</span>
                      </div>
                      <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 8 }}>
                        <div className="mono" style={{ fontSize: 14, fontWeight: 500, color: 'var(--brand-500)' }}>
                          {sort === 'total' ? BRL(row.total) : NUM(row.quantity)}
                        </div>
                        {saleCls > 0 && (
                          <div style={{ marginTop: 3, textAlign: 'right' }}>
                            <div style={{ fontSize: 11.5, color: '#6B7280', fontWeight: 500 }}>Venda {BRL(saleCls)}</div>
                            <div style={{ fontSize: 11, fontWeight: 700, color: cmvColor, marginTop: 1 }}>
                              CMV {cmv.toFixed(1)}% · {cmvLabel}
                            </div>
                            {tab === 'classes' && (() => {
                              const verba = saleCls * (cmvGreen / 100);
                              const saldo = verba - row.total;
                              return (
                                <div style={{ fontSize: 11, fontWeight: 700, marginTop: 2, color: saldo >= 0 ? '#16A34A' : '#DC2626' }}>
                                  {saldo >= 0 ? '+' : ''}{BRL(saldo)} de verba
                                </div>
                              );
                            })()}
                          </div>
                        )}
                        <div style={{ fontSize: 11.5, color: CG_PCT_GREEN, fontWeight: 600, marginTop: 1 }}>
                          {(pct * 100).toFixed(1)}% {sort === 'total' ? 'do total de compras' : 'dos itens'}
                        </div>
                      </div>
                    </div>
                    <div style={{ marginTop: 8, height: 4, background: 'rgba(0,0,0,0.07)', borderRadius: 99, overflow: 'hidden' }}>
                      <div style={{ height: '100%', borderRadius: 99, background: barColor, width: `${pct * 100}%`, transition: 'width .4s' }}/>
                    </div>
                  </div>
                </div>
              );
            })}
            {rows.length > limit && (
              <button onClick={() => setLimit(l => l + 10)} style={{
                width: '100%', padding: '10px', background: 'var(--ink-50)',
                border: '1px solid var(--ink-100)', borderRadius: 10,
                cursor: 'pointer', fontSize: 13, fontWeight: 600,
                color: 'var(--brand-500)', fontFamily: 'inherit',
              }}>
                Mostrar mais ({rows.length - limit} restantes)
              </button>
            )}
          </div>
        )
      }
    </div>
  );
}

function PurchasesManufacturers({ stores, loading, mfgFilter }) {
  const [limit,  setLimit]  = React.useState(20);
  const [sortBy, setSortBy] = React.useState('brl');
  const [search, setSearch] = React.useState('');

  const mfgMap = {};
  for (const s of stores) {
    for (const m of Object.values(s.purchases?.manufacturers ?? {})) {
      const key  = m.distributorName || m.manufacturerName || '—';
      const ex   = mfgMap[key] ?? { name: key, total: 0, quantity: 0 };
      ex.total    += m.total ?? 0;
      ex.quantity += m.quantity ?? 0;
      mfgMap[key] = ex;
    }
  }
  const mfgsAll = Object.values(mfgMap).sort((a, b) => sortBy === 'brl' ? b.total - a.total : b.quantity - a.quantity);
  const mfgs = mfgsAll.filter(m => {
    if (mfgFilter && !mfgFilter.has(m.name)) return false;
    if (search.trim() && !m.name.toLowerCase().includes(search.trim().toLowerCase())) return false;
    return true;
  });
  const grandTotal = mfgsAll.reduce((s, m) => s + m.total, 0);
  const visible = mfgs.slice(0, limit);

  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div style={{ padding: '18px 20px 12px', borderBottom: '1px solid var(--ink-100)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
          <div>
            <div style={{ fontSize: 15, fontWeight: 700 }}>Fornecedores</div>
            <div style={{ fontSize: 12.5, color: 'var(--ink-500)', marginTop: 2 }}>
              {search.trim() ? `${mfgs.length} de ${mfgsAll.length}` : mfgsAll.length} fornecedor(es)
            </div>
          </div>
          <div style={{ display: 'flex', gap: 4, background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
            {[{ v: 'brl', l: 'R$' }, { v: 'qty', l: 'Qtd' }].map(({ v, l }) => (
              <button key={v} onClick={() => { setSortBy(v); setLimit(20); }} style={{
                padding: '4px 14px', fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                background: sortBy === v ? 'var(--white)' : 'transparent',
                color: sortBy === v ? 'var(--ink-900)' : 'var(--ink-500)',
                border: 'none', borderRadius: 6, cursor: 'pointer',
                boxShadow: sortBy === v ? 'var(--shadow-sm)' : 'none', transition: 'all .15s',
              }}>{l}</button>
            ))}
          </div>
        </div>
        <div style={{ position: 'relative' }}>
          <div style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', color: 'var(--ink-400)' }}>
            <Icon name="search" size={14} />
          </div>
          <input
            value={search}
            onChange={e => { setSearch(e.target.value); setLimit(20); }}
            placeholder="Buscar fornecedor..."
            style={{
              width: '100%', padding: '7px 10px 7px 32px',
              fontSize: 13, fontFamily: 'inherit',
              background: 'var(--ink-50)', border: '1px solid var(--ink-200)',
              borderRadius: 8, color: 'var(--ink-900)',
              outline: 'none',
            }}
            onFocus={e => e.target.style.borderColor = 'var(--brand-500)'}
            onBlur={e  => e.target.style.borderColor = 'var(--ink-200)'}
          />
          {search && (
            <button onClick={() => setSearch('')} style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-400)', fontSize: 16, lineHeight: 1, padding: 0 }}>×</button>
          )}
        </div>
      </div>
      {loading
        ? <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
            {[...Array(5)].map((_, i) => <div key={i} style={{ height: 40, borderRadius: 6, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />)}
          </div>
        : !mfgs.length
        ? <EmptyState text="Sem dados de fornecedores no período." />
        : <div style={{ display: 'grid', gridTemplateColumns: '1fr 80px auto' }}>
            {['Fornecedor', 'Qtd', 'Valor'].map((h, i) => (
              <div key={h} style={{ padding: '8px 16px', fontSize: 10.5, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.07em', textAlign: i > 0 ? 'right' : 'left', borderBottom: '1px solid var(--ink-200)', background: 'var(--ink-50)' }}>{h}</div>
            ))}
            {visible.map((m, i) => {
              const pct = grandTotal > 0 ? (m.total / grandTotal * 100) : 0;
              const avgItem = m.quantity > 0 ? m.total / m.quantity : 0;
              const rowBg = i % 2 === 0 ? 'transparent' : 'var(--ink-50)';
              const bdr = i < visible.length - 1 ? '1px solid var(--ink-100)' : 'none';
              return (
                <React.Fragment key={m.name}>
                  <div style={{ minWidth: 0, padding: '10px 16px', borderBottom: bdr, background: rowBg }}>
                    <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--ink-800)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={m.name}>{m.name}</div>
                    <div style={{ display: 'flex', gap: 4, marginTop: 4, flexWrap: 'wrap' }}>
                      <span style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--brand-600, #2563EB)', background: 'var(--brand-50, #EFF6FF)', border: '1px solid var(--brand-200, #BFDBFE)', borderRadius: 20, padding: '1px 7px', whiteSpace: 'nowrap' }}>{pct.toFixed(1)}% do total</span>
                      <span style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-600)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 7px', whiteSpace: 'nowrap' }}>R$ {avgItem.toFixed(2)}/item</span>
                    </div>
                  </div>
                  <div className="mono" style={{ padding: '10px 16px', fontSize: 12.5, color: 'var(--ink-600)', textAlign: 'right', borderBottom: bdr, background: rowBg, display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>{NUM(m.quantity)}</div>
                  <div className="mono" style={{ padding: '10px 16px', fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', textAlign: 'right', borderBottom: bdr, background: rowBg, display: 'flex', alignItems: 'center', justifyContent: 'flex-end', whiteSpace: 'nowrap' }}>{BRL(m.total)}</div>
                </React.Fragment>
              );
            })}
            {mfgs.length > limit && (
              <button onClick={() => setLimit(l => l + 20)} style={{ gridColumn: '1 / -1', width: '100%', padding: '11px', background: 'var(--ink-50)', border: 'none', borderTop: '1px solid var(--ink-100)', cursor: 'pointer', fontSize: 13, fontWeight: 600, color: 'var(--brand-500)', fontFamily: 'inherit' }}>
                Mostrar mais ({mfgs.length - limit} restantes)
              </button>
            )}
          </div>
      }
    </div>
  );
}

// ─── Visão de Compras ─────────────────────────────────────────────────────────
function PurchasesView({ data, loading, syncAt, filterDim, filterSel, period, customStart, customEnd, setPeriod, setCustomRange, user, metaCompras, metaComprasMode, cmvGreen = 55, cmvYellow = 65, cmvOrange = 70 }) {
  const stores = data?.stores ?? [];
  const totals = data?.totals;

  const periodStart = React.useMemo(() => {
    const t = SF.today();
    if (period === 'hoje')   return t;
    if (period === '7d')     return SF.subtractDays(6);
    if (period === '30d')    return SF.subtractDays(29);
    if (period === 'mtd')    return SF.startOfMonth();
    if (period === 'custom') return customStart ?? t;
    return t;
  }, [period, customStart]);

  const periodEnd = period === 'custom' ? (customEnd ?? SF.today()) : SF.today();
  const mfgFilter = filterDim === 'fornecedores' && filterSel?.length > 0 ? new Set(filterSel) : null;

  // siteConfig levantado aqui para que PurchasesPlanningCard reaja ao marcar verba como usada
  const [siteConfig, setSiteConfig] = React.useState(null);
  React.useEffect(() => {
    const cnpj = user?.cnpj;
    if (!cnpj) return;
    let cancelled = false;
    SF.db.collection('loja').doc(cnpj).collection('config').doc('Site')
      .get()
      .then(snap => { if (!cancelled) setSiteConfig(snap.exists ? (snap.data() ?? {}) : {}); })
      .catch(() => { if (!cancelled) setSiteConfig({}); });
    return () => { cancelled = true; };
  }, [user?.cnpj]);

  // Verba extra total (todas as filiais que superaram, sem descontar o já utilizado)
  const extraBudget = React.useMemo(() => {
    const now = new Date();
    const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
    const currentDay  = now.getDate();
    let goalFactor;
    if      (period === 'hoje')                                goalFactor = 1 / daysInMonth;
    else if (period === '7d')                                  goalFactor = 7 / daysInMonth;
    else if (period === '30d')                                 goalFactor = 30 / daysInMonth;
    else if (period === 'mtd')                                 goalFactor = currentDay / daysInMonth;
    else if (period === 'custom' && customStart && customEnd)  goalFactor = (SF.daysBetween(customStart, customEnd) + 1) / daysInMonth;
    else                                                       goalFactor = null;
    if (goalFactor === null) return 0;
    const ratio = (totals?.total ?? 0) > 0 ? (totals?.purchaseTotal ?? 0) / (totals?.total ?? 1) : 0;
    return stores.reduce((sum, s) => {
      const goal   = safeN(s.goalAccum) > 0 ? safeN(s.goalAccum) : safeN(s.totalGoal) * goalFactor;
      if (goal <= 0) return sum;
      const excess = s.total - goal;
      if (excess <= 0) return sum;
      const stRatio = s.total > 0 ? safeN(s.purchases?.total) / s.total : ratio;
      return sum + excess * (stRatio > 0 ? stRatio : ratio);
    }, 0);
  }, [stores, totals, period, customStart, customEnd]);

  // Verba já marcada como utilizada no período atual → desconta do disponível
  const usedExtra = React.useMemo(() => {
    if (!siteConfig || !periodStart || !periodEnd) return 0;
    const periodBase = `${periodStart}_${periodEnd}`;
    return Object.entries(siteConfig?.verbas ?? {})
      .filter(([key]) => key.startsWith(`${periodBase}_`))
      .reduce((sum, [, val]) => sum + (val?.amount ?? 0), 0);
  }, [siteConfig, periodStart, periodEnd]);

  const availableExtra = Math.max(0, extraBudget - usedExtra);

  // Dados do gráfico filtrados pelo fornecedor selecionado no filtro do topo
  const chartPurchaseData = React.useMemo(() => {
    if (!mfgFilter || !data?.mfgPurchaseSeries) return data?.purchaseDayData;
    const matching = Object.values(data.mfgPurchaseSeries).filter(s => mfgFilter.has(s.name));
    if (!matching.length) return data?.purchaseDayData;
    const base = data?.purchaseDayData ?? [];
    return base.map((d, i) => {
      const sumV = matching.reduce((s, m) => s + (m.data[i]?.v ?? 0), 0);
      const sumQ = matching.reduce((s, m) => s + (m.data[i]?.q ?? 0), 0);
      const sumU = matching.reduce((s, m) => s + (m.data[i]?.u ?? 0), 0);
      return { ...d, v: sumV, q: sumQ, u: sumU, pct: d.v > 0 ? (sumV / d.v) * 100 : 0 };
    });
  }, [mfgFilter, data?.mfgPurchaseSeries, data?.purchaseDayData]);

  const purchaseSpark = data?.purchaseDayData?.map(d => d.v) ?? [0];
  const kpiTotal = totals
    ? { label: 'Total compras', value: BRL(totals.purchaseTotal), delta: 0, spark: purchaseSpark, primary: true, noFlash: true }
    : null;

  return (
    <>
      {/* Total compras + planejamento lado a lado */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 3fr', gap: 16, alignItems: 'start' }}>
        <KpiCard {...(kpiTotal ?? { label: 'Total compras', value: '—', delta: 0, spark: [0], primary: true })} loading={loading} syncAt={syncAt} />
        <PurchasesPlanningCard totals={totals} loading={loading} metaCompras={metaCompras ?? 0.70} metaComprasMode={metaComprasMode ?? 'pct'} extraBudget={availableExtra} extraTotal={extraBudget} />
      </div>

      {/* Gráfico + fornecedores / detalhes */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.65fr 1fr', gap: 20, marginTop: 20, alignItems: 'start' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          {/* <PurchasesExtraBudgetCard stores={stores} totals={totals} loading={loading} period={period} customStart={customStart} customEnd={customEnd} user={user} periodStart={periodStart} periodEnd={periodEnd} siteConfig={siteConfig} setSiteConfig={setSiteConfig} /> */}
          <PurchasesChart data={chartPurchaseData} loading={loading} mfgSeries={data?.mfgPurchaseSeries} externalMfgFilter={mfgFilter} />
          {/* <PurchasesHeroCard totals={totals} loading={loading} cmvGreen={cmvGreen} cmvYellow={cmvYellow} cmvOrange={cmvOrange} /> */}
          <PurchasesStoreRanking stores={stores} totalSales={totals?.total} loading={loading} cmvGreen={cmvGreen} cmvYellow={cmvYellow} cmvOrange={cmvOrange} />
          <PurchasesClassGroupSection stores={stores} purchaseGroups={data?.purchaseGroups ?? []} classRanking={data?.classRanking ?? []} cmvGreen={cmvGreen} cmvYellow={cmvYellow} cmvOrange={cmvOrange} loading={loading} />
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          <PurchasesManufacturers stores={stores} loading={loading} mfgFilter={mfgFilter} />
        </div>
      </div>
      <PurchasesBudgetFloatingPanel stores={stores} classRanking={data?.classRanking ?? []} stockByStore={data?.stockByStore ?? []} goalFactor={totals?.goalFactor} cmvGreen={cmvGreen} metaCompras={metaCompras} metaComprasMode={metaComprasMode}
        period={period} setPeriod={setPeriod} customStart={customStart} customEnd={customEnd} setCustomRange={setCustomRange}
        siteConfig={siteConfig} setSiteConfig={setSiteConfig} user={user} />
    </>
  );
}

// ─── Visão de Estoque ─────────────────────────────────────────────────────────
const SK_BLUE      = '#0B69C7';
const SK_CARD_BG   = '#EEF5FF';
const SK_CARD_BRD  = '#B5D4F4';
const SK_VAL_COLOR = '#1A4F8A';
const SK_MEDAL_DOT = ['#FFD700', '#C0C0C0', '#CD7F32'];
const SK_MEDAL_BG  = ['rgba(255,215,0,0.10)', 'rgba(192,192,192,0.10)', 'rgba(205,127,50,0.10)'];
const SK_MEDAL_BRD = ['rgba(255,215,0,0.40)', 'rgba(192,192,192,0.40)', 'rgba(205,127,50,0.40)'];

const skBRL = (n) => (n ?? 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const skNum = (n) => Math.round(n ?? 0).toLocaleString('pt-BR');

function stockAggregated(stockList, filterDim, filterSel) {
  if (!stockList.length) return null;

  const hasFilter = filterSel && filterSel.length > 0;
  const selSet    = new Set(filterSel ?? []);
  const byGroup   = hasFilter && filterDim === 'grupos';
  const byClass   = hasFilter && filterDim === 'classes';

  let totalSale = 0, totalCost = 0, totalUnity = 0;

  if (byClass || byGroup) {
    for (const store of stockList) {
      const items = byGroup ? (store.groups ?? []) : (store.classifications ?? []);
      for (const c of items) {
        const name = byGroup ? c.groupName : c.className;
        if (!selSet.has(name)) continue;
        totalSale  += c.totalSale;
        totalCost  += c.totalCost;
        totalUnity += c.unity;
      }
    }
  } else {
    totalSale  = stockList.reduce((s, st) => s + st.totalSale,  0);
    totalCost  = stockList.reduce((s, st) => s + st.totalCost,  0);
    totalUnity = stockList.reduce((s, st) => s + st.totalUnity, 0);
  }

  const regular  = stockList.filter(s => s.storeId < 10001);
  const external = stockList.filter(s => s.storeId >= 10001);
  const totalSku = (regular.length  ? Math.max(...regular.map(s => s.totalSku ?? 0)) : 0)
                 + (external.length ? external.reduce((s, st) => s + (st.totalSku ?? 0), 0) : 0);

  // Agrega classificações ou grupos conforme filtro ativo
  const itemMap = {};
  for (const store of stockList) {
    const items = byGroup ? (store.groups ?? []) : (store.classifications ?? []);
    for (const c of items) {
      const name = byGroup ? c.groupName : c.className;
      if (!byGroup && !isValidClassName(name)) continue;
      if (hasFilter && !selSet.has(name)) continue;
      if (!itemMap[name]) itemMap[name] = { className: name, unity: 0, totalCost: 0, totalSale: 0 };
      itemMap[name].unity     += c.unity;
      itemMap[name].totalCost += c.totalCost;
      itemMap[name].totalSale += c.totalSale;
    }
  }

  return { totalSale, totalCost, totalUnity, totalSku, classifications: Object.values(itemMap) };
}

function StockView({ data, loading, syncAt, filterDim, filterSel, selectedStoreIds }) {
  const stockList      = data?.stockByStore ?? [];
  const evoData        = data?.stockEvolution ?? { points: [], gran: 'day' };
  const stockEvolution = evoData.points ?? evoData;
  const stockGran      = evoData.gran ?? 'day';
  const agg            = stockAggregated(stockList, filterDim, filterSel);

  const allStores  = data?.allStores ?? [];
  const storeLabel = React.useMemo(() => {
    const sel = selectedStoreIds ?? [];
    if (sel.length === 0) return 'da Rede';
    const names = sel.map(id => {
      const found = allStores.find(s => s.storeId === id);
      return found?.storeName || `Filial ${id}`;
    });
    const hasFilial = (n) => /filial/i.test(n);
    if (names.length === 1) return hasFilial(names[0]) ? `da ${names[0]}` : `da Filial ${names[0]}`;
    return `das Filiais (${names.join(', ')})`;
  }, [selectedStoreIds, allStores]);

  const dias = stockEvolution.length * (stockGran === 'day' ? 1 : stockGran === 'week' ? 7 : 30) || 1;

  const perStoreCoverage = React.useMemo(() => {
    if (!dias) return [];
    return stockList
      .map(st => {
        const storeSales  = (data?.stores ?? []).find(s => s.storeId === st.storeId);
        const costOfSales = safeN(storeSales?.cost);
        const dailyCMV    = costOfSales / dias;
        const cobertura   = dailyCMV > 0 ? Math.round(st.totalCost / dailyCMV) : null;
        return { storeId: st.storeId, storeName: st.storeName || `Filial ${st.storeId}`, cobertura };
      })
      .filter(s => s.cobertura !== null && s.cobertura > 0 && s.cobertura < 9999)
      .sort((a, b) => a.cobertura - b.cobertura);
  }, [stockList, data?.stores, dias]);

  // Cobertura da rede pela mesma fórmula das filiais (soma só quem tem custo de vendas)
  // Evita distorção por filiais externas cujas compras não são rastreadas nos docs diários
  const networkCobertura = React.useMemo(() => {
    if (!dias) return null;
    let totalStock = 0, totalCostOfSales = 0;
    for (const st of stockList) {
      const storeSales = (data?.stores ?? []).find(s => s.storeId === st.storeId);
      const cost = safeN(storeSales?.cost);
      if (cost <= 0) continue; // exclui filiais sem custo rastreado
      totalStock       += st.totalCost;
      totalCostOfSales += cost;
    }
    return totalCostOfSales > 0 ? Math.round(totalStock / (totalCostOfSales / dias)) : null;
  }, [stockList, data?.stores, dias]);

  const salesByClassAgg = React.useMemo(() => {
    const map = {};
    for (const st of (data?.stores ?? [])) {
      for (const [name, c] of Object.entries(st.salesByClass ?? {})) {
        const ex = map[name] ?? { total: 0, quantity: 0 };
        ex.total    += safeN(c.total);
        ex.quantity += safeN(c.quantity);
        map[name] = ex;
      }
    }
    return map;
  }, [data?.stores]);

  const purchasesByClassAgg = React.useMemo(() => {
    const map = {};
    for (const st of (data?.stores ?? [])) {
      for (const [, c] of Object.entries(st.purchases?.classifications ?? {})) {
        if (!c.className) continue;
        const ex = map[c.className] ?? { total: 0, quantity: 0 };
        ex.total    += safeN(c.total);
        ex.quantity += safeN(c.quantity);
        map[c.className] = ex;
      }
    }
    return map;
  }, [data?.stores]);

  const kpis = agg ? [
    { label: 'Valor de venda',  value: BRL(agg.totalSale),  delta: 0, spark: [agg.totalSale],  primary: true },
    { label: 'Valor de custo',  value: BRL(agg.totalCost),  delta: 0, spark: [agg.totalCost]  },
    { label: 'Unidades',        value: NUM(agg.totalUnity), delta: 0, spark: [agg.totalUnity] },
    { label: 'Nº de produtos',  value: NUM(agg.totalSku),   delta: 0, spark: [agg.totalSku]   },
  ] : null;

  return (
    <>
      <KpiRow kpis={kpis} loading={loading} syncAt={syncAt} />
      <div style={{ marginTop: 20 }}>
        {loading
          ? <StockSummarySkeleton />
          : !agg
          ? <EmptyState text="Sem dados de estoque disponíveis." />
          : <StockSummaryCard agg={agg} evolution={stockEvolution} gran={stockGran} storeLabel={storeLabel} salesByClass={salesByClassAgg} purchasesByClass={purchasesByClassAgg} perStoreCoverage={perStoreCoverage} dias={dias} networkCobertura={networkCobertura} />
        }
      </div>
    </>
  );
}

function StockSummarySkeleton() {
  return (
    <div className="card" style={{ padding: 20, display: 'flex', flexDirection: 'column', gap: 10 }}>
      {[180, 70, 200].map((h, i) => (
        <div key={i} style={{ height: h, borderRadius: 12, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
      ))}
    </div>
  );
}

function StockSummaryCard({ agg, evolution, gran, storeLabel, salesByClass, purchasesByClass, perStoreCoverage, dias, networkCobertura }) {
  const periodLabel = React.useMemo(() => {
    const pts = evolution;
    if (!pts || pts.length === 0) return null;
    if (pts.length === 1) return pts[0].label;
    return `${pts[0].label} – ${pts[pts.length - 1].label}`;
  }, [evolution]);

  // Estoque é snapshot em unidades: primeiro → último (igual ao mobile)
  const stockTrendPct = React.useMemo(() => {
    const vals = evolution.map(p => p.stockUnits ?? 0).filter(v => v > 0);
    if (vals.length < 2) return null;
    const first = vals[0], last = vals[vals.length - 1];
    return first > 0 ? (last - first) / first * 100 : null;
  }, [evolution]);

  const giro = React.useMemo(() => {
    const pts = evolution.filter(p => (p.stockCost ?? 0) > 0);
    if (pts.length < 2) return null;
    const estoqueInicio  = pts[0].stockCost;
    const estoqueFim     = pts[pts.length - 1].stockCost;
    const comprasTotal   = evolution.reduce((s, p) => s + p.purchasedValue, 0);
    const vendasTotal    = evolution.reduce((s, p) => s + p.soldValue, 0);
    const cmv              = estoqueInicio + comprasTotal - estoqueFim;
    if (cmv <= 0 || estoqueFim <= 0) return null;
    // dias reais do período (não o nº de snapshots de estoque, que pode ser 2-3 para 30d)
    const diasReais        = dias > 0 ? dias : pts.length;
    const cmvDiario        = cmv / diasReais;
    const comprasDiarias   = comprasTotal / diasReais;
    const vendasDiarias    = vendasTotal / diasReais;
    const cobertura        = Math.round(estoqueFim / cmvDiario);
    const saldoDiario       = comprasDiarias - cmvDiario;
    const soldQtyTotal      = evolution.reduce((s, p) => s + (p.soldQty      ?? 0), 0);
    const purchasedQtyTotal = evolution.reduce((s, p) => s + (p.purchasedQty ?? 0), 0);
    const saidasUnDia       = soldQtyTotal      / diasReais;
    const entradasUnDia     = purchasedQtyTotal / diasReais;
    const saldoUnDia        = entradasUnDia - saidasUnDia;
    return { cobertura, cmvDiario, comprasDiarias, vendasDiarias, saldoDiario, saidasUnDia, entradasUnDia, saldoUnDia };
  }, [evolution, dias]);

  return (
    <div className="card" style={{ padding: 20 }}>
      {evolution.length >= 2 && <StockEvolutionChart points={evolution} gran={gran} />}
      {giro !== null && (() => {
        const fmtK = (v) => BRL(v);
        const saldoPos = giro.saldoDiario >= 0;
        return (
          <div style={{ marginBottom: 20, padding: '14px 18px', background: 'var(--ink-50)', borderRadius: 12, border: '1px solid var(--ink-200)' }}>
            <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 12 }}>Cobertura de Estoque {storeLabel}</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
              {/* Dias em destaque */}
              <div style={{ textAlign: 'center', flexShrink: 0 }}>
                <div style={{ fontSize: 42, fontWeight: 800, color: 'var(--ink-900)', lineHeight: 1 }}>{networkCobertura ?? giro.cobertura}</div>
                <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', marginTop: 2 }}>dias</div>
              </div>
              <div style={{ width: 1, background: 'var(--ink-200)', alignSelf: 'stretch' }} />
              {/* Detalhes diários */}
              <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '6px 16px' }}>
                <div>
                  <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 2 }}>SAEM POR DIA</div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: EVO_GREEN }}>{fmtK(giro.cmvDiario)}</div>
                  {giro.saidasUnDia > 0 && <div style={{ fontSize: 11, fontWeight: 600, color: EVO_GREEN }}>{Math.round(giro.saidasUnDia).toLocaleString('pt-BR')} un.</div>}
                  <div style={{ fontSize: 10, color: 'var(--ink-400)' }}>custo médio</div>
                </div>
                <div>
                  <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 2 }}>ENTRAM POR DIA</div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: EVO_BLUE }}>{fmtK(giro.comprasDiarias)}</div>
                  {giro.entradasUnDia > 0 && <div style={{ fontSize: 11, fontWeight: 600, color: EVO_BLUE }}>{Math.round(giro.entradasUnDia).toLocaleString('pt-BR')} un.</div>}
                  <div style={{ fontSize: 10, color: 'var(--ink-400)' }}>compras médias</div>
                </div>
                <div>
                  <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 2 }}>SALDO DIÁRIO {(storeLabel ?? '').toUpperCase()}</div>
                  <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)' }}>
                    {saldoPos ? '+' : ''}{fmtK(giro.saldoDiario)}
                  </div>
                  {giro.saldoUnDia !== 0 && Math.abs(giro.saldoUnDia) >= 0.5 && (
                    <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-700)' }}>
                      {saldoPos ? '+' : ''}{Math.round(giro.saldoUnDia).toLocaleString('pt-BR')} un./dia
                    </div>
                  )}
                  <div style={{ fontSize: 10, color: saldoPos ? EVO_BLUE : EVO_ORANGE, fontWeight: 600 }}>
                    {saldoPos ? '↑ crescendo' : '↓ reduzindo'}
                  </div>
                </div>
              </div>
            </div>
          </div>
        );
      })()}
      {perStoreCoverage?.length > 1 && (
        <StockStoreCoverageCard stores={perStoreCoverage} />
      )}
      {agg.classifications.length > 0
        ? <StockClassifications items={agg.classifications} totalSale={agg.totalSale} salesByClass={salesByClass ?? {}} purchasesByClass={purchasesByClass ?? {}} periodLabel={periodLabel} stockTrendPct={stockTrendPct} dias={dias ?? 1} />
        : <EmptyState text="Sem classificações disponíveis." />
      }
    </div>
  );
}

// ─── Cobertura por Filial ─────────────────────────────────────────────────────
const coverageColor = (d) =>
  d > 60 ? '#22C55E' : d > 30 ? '#0B69C7' : d > 15 ? '#FF9500' : '#EF4444';

function StockStoreCoverageCard({ stores }) {
  return (
    <div style={{ marginTop: 20, padding: '16px 18px', background: 'var(--ink-50)', borderRadius: 12, border: '1px solid var(--ink-200)' }}>
      <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 14 }}>
        Cobertura por Filial
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(110px, 1fr))', gap: 10 }}>
        {stores.map(s => {
          const c = coverageColor(s.cobertura);
          return (
            <div key={s.storeId} style={{
              background: 'var(--white)', borderRadius: 10,
              border: `1px solid var(--ink-200)`, padding: '12px 10px',
              textAlign: 'center', display: 'flex', flexDirection: 'column', gap: 2,
            }}>
              <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {s.storeName}
              </div>
              <div style={{ fontSize: 28, fontWeight: 800, color: c, lineHeight: 1.1, fontFeatureSettings: '"tnum"' }}>
                {s.cobertura}
              </div>
              <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-500)' }}>dias</div>
            </div>
          );
        })}
      </div>
      <div style={{ display: 'flex', gap: 14, marginTop: 12, flexWrap: 'wrap' }}>
        {[['> 60 dias', '#22C55E'], ['30–60 dias', '#0B69C7'], ['15–30 dias', '#FF9500'], ['< 15 dias', '#EF4444']].map(([label, color]) => (
          <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
            <div style={{ width: 8, height: 8, borderRadius: '50%', background: color, flexShrink: 0 }} />
            <span style={{ fontSize: 10, color: 'var(--ink-500)' }}>{label}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Gráfico de Evolução do Estoque ──────────────────────────────────────────
const EVO_ORANGE = '#FF9500';
const EVO_GREEN  = '#34C759';
const EVO_BLUE   = '#0B69C7';

function StockEvolutionChart({ points, gran = 'day' }) {
  const svgRef                        = React.useRef(null);
  const [hover,     setHover]         = React.useState(null);
  const [dragStart, setDragStart]     = React.useState(null);
  const [dragCur,   setDragCur]       = React.useState(null);
  const [selection, setSelection]     = React.useState(null);
  React.useEffect(() => { setHover(null); setSelection(null); }, [points.length]);

  const visiblePoints = points;

  const w = 760, h = 260, padL = 56, padR = 16, padT = 16, padB = 36;
  const innerW = w - padL - padR, innerH = h - padT - padB;
  const N = visiblePoints.length;

  const step = N > 1 ? innerW / (N - 1) : innerW;
  const ptX  = (i) => N > 1 ? padL + i * step : padL + innerW / 2;

  const isDragging = dragStart !== null;

  const getIdx = (e) => {
    const svg = svgRef.current; if (!svg || N === 0) return null;
    const pt = svg.createSVGPoint(); pt.x = e.clientX; pt.y = e.clientY;
    const svgX = pt.matrixTransform(svg.getScreenCTM().inverse()).x;
    return N === 1 ? 0 : Math.max(0, Math.min(N - 1, Math.round((svgX - padL) / step)));
  };

  const onMouseMove = (e) => {
    const idx = getIdx(e); if (idx === null) return;
    if (isDragging) { setDragCur(idx); }
    else if (!selection) { setHover({ i: idx, d: visiblePoints[idx] }); }
  };
  const onMouseDown = (e) => {
    if (N < 2) return; e.preventDefault();
    const idx = getIdx(e); if (idx === null) return;
    setDragStart(idx); setDragCur(idx); setSelection(null); setHover(null);
  };
  const onMouseUp = (e) => {
    if (!isDragging) return;
    const idx = getIdx(e) ?? dragCur;
    const from = Math.min(dragStart, idx ?? dragStart);
    const to   = Math.max(dragStart, idx ?? dragStart);
    if (from === to) { setHover({ i: from, d: visiblePoints[from] }); setSelection(null); }
    else             { setSelection({ from, to }); }
    setDragStart(null); setDragCur(null);
  };
  const onMouseLeave = () => {
    if (isDragging) { setDragStart(null); setDragCur(null); }
    setHover(null);
  };

  const activeSel = isDragging && dragStart !== null && dragCur !== null
    ? { from: Math.min(dragStart, dragCur), to: Math.max(dragStart, dragCur) }
    : selection;

  const selStats = activeSel && activeSel.to > activeSel.from ? (() => {
    const slice = visiblePoints.slice(activeSel.from, activeSel.to + 1);
    const soldTotal  = slice.reduce((s, d) => s + d.soldValue, 0);
    const purchTotal = slice.reduce((s, d) => s + d.purchasedValue, 0);
    const stockFirst = slice[0].stockValue, stockLast = slice[slice.length - 1].stockValue;
    const stockChg   = stockFirst > 0 ? ((stockLast - stockFirst) / stockFirst) * 100 : null;
    return { soldTotal, purchTotal, stockChg, days: slice.length, from: slice[0].label, to: slice[slice.length - 1].label };
  })() : null;

  // ── Duas zonas independentes (igual ao mobile) ──────────────────────────────
  // Zona superior (~55%): estoque — escala própria
  // Zona inferior (~45%): vendido + comprado — escala compartilhada
  const STOCK_RATIO = 0.55;
  const STOCK_H  = Math.round(innerH * STOCK_RATIO);
  const SEP_H    = 10; // separador entre zonas
  const FLOW_TOP = padT + STOCK_H + SEP_H;
  const FLOW_H   = innerH - STOCK_H - SEP_H;

  const makeRange = (vals) => {
    const v = vals.filter(x => x > 0);
    if (!v.length) return null;
    const mn = Math.min(...v), mx = Math.max(...v);
    const pad = Math.max((mx - mn) * 0.15, mx * 0.05, 1);
    return { min: Math.max(0, mn - pad), max: mx + pad };
  };
  const makeYFn = (range, top, h) => (v) => {
    if (!range || v < 0) return top + h;
    if (v === 0) return top + h;
    return top + h * (1 - (v - range.min) / (range.max - range.min));
  };

  const stockRange = makeRange(visiblePoints.map(p => p.stockValue));
  const flowRange  = makeRange([...visiblePoints.map(p => p.soldValue), ...visiblePoints.map(p => p.purchasedValue)]);

  const yStock = makeYFn(stockRange, padT,    STOCK_H);
  const yFlow  = makeYFn(flowRange,  FLOW_TOP, FLOW_H);

  const buildPathFn = (vals, yFn) =>
    vals.map((v, i) => `${i === 0 ? 'M' : 'L'}${ptX(i).toFixed(1)},${yFn(v).toFixed(1)}`).join(' ');

  const fmtAxis = (v) => v >= 1e6 ? `${(v/1e6).toFixed(1)}M` : v >= 1e3 ? `${(v/1e3).toFixed(0)}k` : `${Math.round(v)}`;
  const fmtVal  = (v) => v >= 1e6 ? `R$${(v/1e6).toFixed(1)}M` : v >= 1e3 ? `R$${(v/1e3).toFixed(0)}k` : `R$${Math.round(v)}`;
  const fmtPct  = (p) => `${p >= 0 ? '+' : ''}${p.toFixed(1)}%`;
  const showLabel = (i) => N <= 10 ? true : N <= 31 ? i % 3 === 0 || i === N - 1 : i % 7 === 0 || i === N - 1;

  // Ticks por zona
  const stockTicks = stockRange
    ? [stockRange.min, stockRange.min + (stockRange.max - stockRange.min) * 0.5, stockRange.max]
    : [];
  const flowTicks = flowRange
    ? [flowRange.min, flowRange.min + (flowRange.max - flowRange.min) * 0.5, flowRange.max]
    : [];

  const SERIES = [
    { key: 'stockValue',     color: EVO_ORANGE, label: 'Estoque',  dashed: true,  yFn: yStock },
    { key: 'soldValue',      color: EVO_GREEN,  label: 'Vendido',  dashed: true,  yFn: yFlow  },
    { key: 'purchasedValue', color: EVO_BLUE,   label: 'Comprado', dashed: false, yFn: yFlow  },
  ];

  // Estoque é snapshot: compara primeiro → último valor não-zero (igual ao mobile)
  const snapshotTrend = (key) => {
    const vals = points.map(p => p[key]).filter(v => v > 0);
    if (vals.length < 2) return null;
    const first = vals[0], last = vals[vals.length - 1];
    return first > 0 ? ((last - first) / first) * 100 : null;
  };
  // Vendas/Compras são fluxo: média 1ª metade vs 2ª metade (igual ao mobile)
  const flowTrend = (key) => {
    const vals = points.map(p => p[key]).filter(v => v > 0);
    if (vals.length < 3) return null;
    const mid = Math.ceil(vals.length / 2);
    const a1  = vals.slice(0, mid).reduce((s, v) => s + v, 0) / mid;
    const a2  = vals.slice(-mid).reduce((s, v) => s + v, 0) / mid;
    return a1 > 0 ? ((a2 - a1) / a1) * 100 : null;
  };
  const trends = {
    stockValue:     snapshotTrend('stockUnits'),
    soldValue:      flowTrend('soldQty'),
    purchasedValue: flowTrend('purchasedQty'),
  };

  return (
    <div style={{ marginBottom: 24 }}>
      {/* Header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
        <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--ink-900)' }}>Evolução do Estoque</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          {SERIES.map(({ color, label, dashed, key }) => {
            const t = trends[key];
            const pos = t !== null && t >= 0;
            return (
            <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
              {dashed
                ? <svg width={16} height={4} style={{ flexShrink: 0 }}><line x1={0} y1={2} x2={16} y2={2} stroke={color} strokeWidth={2} strokeDasharray="4,2" /></svg>
                : <div style={{ width: 16, height: 2, background: color, borderRadius: 99 }} />
              }
              <span style={{ fontSize: 11, color: 'var(--ink-500)' }}>{label}</span>
              {t !== null && (
                <span style={{ fontSize: 10, fontWeight: 700, color: pos ? '#1A9E44' : '#FF3B30' }}>
                  {pos ? '▲' : '▼'} {Math.abs(t).toFixed(2)}%
                </span>
              )}
            </div>
            );
          })}
        </div>
      </div>

      {/* SVG */}
      <div style={{ position: 'relative', userSelect: 'none' }}>
        <svg ref={svgRef} viewBox={`0 0 ${w} ${h}`}
          style={{ width: '100%', height: 260, display: 'block', cursor: isDragging ? 'col-resize' : 'crosshair' }}
          onMouseDown={onMouseDown} onMouseMove={onMouseMove} onMouseUp={onMouseUp} onMouseLeave={onMouseLeave}>
          <defs>
            <linearGradient id="evo-grad-stock" x1="0" x2="0" y1="0" y2="1">
              <stop offset="0%" stopColor={EVO_ORANGE} stopOpacity="0.12" />
              <stop offset="100%" stopColor={EVO_ORANGE} stopOpacity="0" />
            </linearGradient>
          </defs>

          {/* Grid + ticks zona estoque */}
          {stockTicks.map((v, i) => {
            const y = yStock(v);
            return (
              <g key={`st${i}`}>
                <line x1={padL} x2={padL + innerW} y1={y} y2={y} stroke="var(--ink-100)" />
                <text x={padL - 6} y={y + 4} fontSize="10" fill={EVO_ORANGE} textAnchor="end" opacity="0.8">{fmtAxis(v)}</text>
              </g>
            );
          })}

          {/* Separador de zonas */}
          <line x1={padL} x2={padL + innerW} y1={FLOW_TOP - SEP_H / 2} y2={FLOW_TOP - SEP_H / 2}
                stroke="var(--ink-200)" strokeWidth="1" strokeDasharray="4,3" />

          {/* Grid + ticks zona fluxo */}
          {flowTicks.map((v, i) => {
            const y = yFlow(v);
            return (
              <g key={`ft${i}`}>
                <line x1={padL} x2={padL + innerW} y1={y} y2={y} stroke="var(--ink-100)" />
                <text x={padL - 6} y={y + 4} fontSize="10" fill="var(--ink-500)" textAnchor="end">{fmtAxis(v)}</text>
              </g>
            );
          })}

          {/* Labels X */}
          {visiblePoints.map((p, i) => showLabel(i) &&
            <text key={i} x={ptX(i)} y={h - 8} fontSize="10.5" fill="var(--ink-500)" textAnchor="middle">{p.label}</text>
          )}

          {/* Seleção */}
          {activeSel && (
            <rect x={ptX(activeSel.from)} y={padT} width={ptX(activeSel.to) - ptX(activeSel.from)} height={innerH}
                  fill={EVO_BLUE} fillOpacity="0.05" />
          )}
          {activeSel && [activeSel.from, activeSel.to].map((idx, k) => (
            <line key={k} x1={ptX(idx)} x2={ptX(idx)} y1={padT} y2={padT + innerH}
                  stroke={EVO_BLUE} strokeOpacity="0.25" strokeWidth="1" strokeDasharray="3 3" />
          ))}

          {/* Hover vertical */}
          {hover && !isDragging && (
            <line x1={ptX(hover.i)} x2={ptX(hover.i)} y1={padT} y2={padT + innerH}
                  stroke="var(--ink-300)" strokeWidth="1" strokeDasharray="3 3" />
          )}

          {/* Área sob estoque */}
          <path d={buildPathFn(visiblePoints.map(p => p.stockValue), yStock) + ` L${ptX(N-1).toFixed(1)},${padT + STOCK_H} L${ptX(0).toFixed(1)},${padT + STOCK_H} Z`}
                fill="url(#evo-grad-stock)" />

          {/* Linhas das 3 séries — cada uma na sua zona */}
          <path d={buildPathFn(visiblePoints.map(p => p.stockValue),     yStock)} fill="none" stroke={EVO_ORANGE} strokeWidth="2.5" strokeDasharray="6,3" strokeLinejoin="round" />
          <path d={buildPathFn(visiblePoints.map(p => p.soldValue),      yFlow)}  fill="none" stroke={EVO_GREEN}  strokeWidth="2"   strokeDasharray="5,3" strokeLinejoin="round" />
          <path d={buildPathFn(visiblePoints.map(p => p.purchasedValue), yFlow)}  fill="none" stroke={EVO_BLUE}   strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" />

          {/* Pontos hover/seleção */}
          {SERIES.map(({ key, color, yFn }) =>
            visiblePoints.map((p, i) => {
              const isEndpoint = activeSel && (i === activeSel.from || i === activeSel.to);
              const isHover    = hover?.i === i;
              if (!isEndpoint && !isHover) return null;
              return <circle key={`${key}-${i}`} cx={ptX(i)} cy={yFn(p[key])} r={isEndpoint ? 4.5 : 3.5}
                             fill={isEndpoint ? color : 'var(--white)'} stroke={color} strokeWidth="2"
                             style={{ pointerEvents: 'none' }} />;
            })
          )}
        </svg>

        {/* Tooltip hover */}
        {hover && !isDragging && !selection && (
          <div style={{
            position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)',
            background: 'var(--white)', border: '1px solid var(--ink-200)',
            borderRadius: 10, padding: '10px 16px', boxShadow: 'var(--shadow-md)',
            pointerEvents: 'none', zIndex: 10, minWidth: 220,
          }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', marginBottom: 8, textAlign: 'center' }}>
              {gran === 'week' ? `Semana de ${hover.d.label}` : hover.d.label}
            </div>
            <div style={{ display: 'flex', gap: 16 }}>
              {SERIES.map(({ key, color, label }) => {
                const periodTag = gran === 'week' ? ' (semana)' : gran === 'month' ? ' (mês)' : '';
                const isStock   = key === 'stockValue';
                return (
                  <div key={key} style={{ flex: 1, textAlign: 'center' }}>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4, marginBottom: 2 }}>
                      <div style={{ width: 7, height: 7, borderRadius: '50%', background: color }} />
                      <span style={{ fontSize: 10, color: 'var(--ink-500)' }}>{label}{!isStock ? periodTag : ''}</span>
                    </div>
                    <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)' }}>{fmtVal(hover.d[key])}</div>
                    {isStock && hover.d.stockUnits > 0 && (
                      <div style={{ fontSize: 10, color: 'var(--ink-400)' }}>{Math.round(hover.d.stockUnits).toLocaleString('pt-BR')} un.</div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        )}
      </div>

      {/* Stats de seleção */}
      {selStats && (
        <div style={{ marginTop: 10, padding: '12px 16px', background: 'var(--ink-50)', borderRadius: 10, border: '1px solid var(--ink-200)' }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-700)', marginBottom: 8 }}>
            {selStats.from} → {selStats.to} ({selStats.days} {gran === 'month' ? (selStats.days === 1 ? 'mês' : 'meses') : gran === 'week' ? (selStats.days === 1 ? 'semana' : 'semanas') : (selStats.days === 1 ? 'dia' : 'dias')})
          </div>
          <div style={{ display: 'flex', gap: 24, flexWrap: 'wrap' }}>
            {[
              { color: EVO_GREEN,  label: 'Total vendido',  value: fmtVal(selStats.soldTotal) },
              { color: EVO_BLUE,   label: 'Total comprado', value: fmtVal(selStats.purchTotal) },
              selStats.stockChg !== null && { color: EVO_ORANGE, label: 'Variação estoque', value: fmtPct(selStats.stockChg) },
            ].filter(Boolean).map(({ color, label, value }) => (
              <div key={label}>
                <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 2 }}>{label}</div>
                <div style={{ fontSize: 14, fontWeight: 700, color }}>{value}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}


function StockClassifications({ items, totalSale, salesByClass = {}, purchasesByClass = {}, periodLabel, stockTrendPct = null, dias = 1 }) {
  const sorted = [...items].sort((a, b) => b.totalSale - a.totalSale);
  const hasPeriodAny = Object.keys(salesByClass).length > 0 || Object.keys(purchasesByClass).length > 0;

  // Estimativa de delta por classe (qty × preço médio atual) para normalização proporcional
  const estimatedDeltas = React.useMemo(() => {
    const map = {};
    let total = 0;
    for (const item of items) {
      const soldQty   = safeN(salesByClass[item.className]?.quantity);
      const boughtQty = safeN(purchasesByClass[item.className]?.quantity);
      const avg       = item.unity > 0 ? item.totalSale / item.unity : 0;
      const v         = (boughtQty - soldQty) * avg;
      map[item.className] = v;
      total += v;
    }
    return { map, total };
  }, [items, salesByClass, purchasesByClass]);

  return (
    <div style={{ marginTop: 16 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
          Classificações
        </div>
        {hasPeriodAny && periodLabel && (
          <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-600)', background: 'var(--ink-100)', borderRadius: 6, padding: '2px 8px' }}>
            {periodLabel}
          </div>
        )}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {sorted.map((item, i) => {
          const isMedal  = i < 3;
          const dot      = isMedal ? SK_MEDAL_DOT[i] : SK_BLUE;
          const bg       = isMedal ? SK_MEDAL_BG[i]  : 'var(--white)';
          const brd      = isMedal ? SK_MEDAL_BRD[i] : 'var(--ink-200)';
          const pct      = totalSale > 0 ? (item.totalSale / totalSale) * 100 : 0;
          const pctColor = i === 0 ? '#B8860B' : dot;

          const sold        = salesByClass[item.className];
          const bought      = purchasesByClass[item.className];
          const soldQty     = safeN(sold?.quantity);
          const boughtQty   = safeN(bought?.quantity);
          const soldVal     = safeN(sold?.total);
          const boughtVal   = safeN(bought?.total);
          const deltaQty    = boughtQty - soldQty;
          const hasPeriod   = soldQty > 0 || boughtQty > 0;
          const classEst    = estimatedDeltas.map[item.className] ?? 0;
          // Distribui o % real do gráfico (stockTrendPct) proporcionalmente entre as classes
          const deltaPct    = (stockTrendPct !== null && estimatedDeltas.total !== 0)
            ? (classEst / estimatedDeltas.total) * stockTrendPct
            : null;
          // Cobertura em dias: unidades no estoque ÷ vendas diárias da classe no período
          const coberturaClass = soldQty > 0 && dias > 0
            ? Math.round(item.unity / (soldQty / dias))
            : null;

          return (
            <div key={item.className} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
              {isMedal && <div style={{ width: 4, borderRadius: 4, background: dot, flexShrink: 0 }} />}
              <div style={{ flex: 1, borderRadius: 12, border: `0.5px solid ${brd}`, background: bg, padding: '12px 14px' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
                  <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginRight: 8 }}>
                    {item.className}
                  </span>
                  <div style={{ display: 'flex', alignItems: 'center', flexShrink: 0, gap: 4 }}>
                    {deltaPct !== null && (
                      <span style={{ fontSize: 10, fontWeight: 700, color: deltaPct >= 0 ? '#22C55E' : '#EF4444' }}>
                        {deltaPct >= 0 ? '▲' : '▼'} {Math.abs(deltaPct).toFixed(2)}%
                      </span>
                    )}
                    <span style={{ fontSize: 12, fontWeight: 700, color: pctColor }}>
                      {pct.toFixed(1)}%
                    </span>
                  </div>
                </div>
                <div style={{ height: 3, background: 'var(--ink-200)', borderRadius: 99, overflow: 'hidden', marginBottom: 10 }}>
                  <div style={{ height: '100%', borderRadius: 99, background: dot, width: `${Math.min(pct, 100)}%`, transition: 'width .6s' }} />
                </div>
                <div style={{ display: 'flex' }}>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Unidades</div>
                    <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-800)' }}>{skNum(item.unity)}</div>
                  </div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Custo</div>
                    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 2 }}>
                      <span style={{ fontSize: 10, color: 'var(--ink-500)', marginBottom: 1 }}>R$</span>
                      <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-800)' }}>{skBRL(item.totalCost)}</span>
                    </div>
                  </div>
                  <div style={{ flex: 1, textAlign: 'right' }}>
                    <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Venda</div>
                    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 2, justifyContent: 'flex-end' }}>
                      <span style={{ fontSize: 10, color: SK_BLUE, marginBottom: 1 }}>R$</span>
                      <span style={{ fontSize: 12, fontWeight: 600, color: SK_BLUE }}>{skBRL(item.totalSale)}</span>
                    </div>
                  </div>
                  {coberturaClass !== null && (
                    <div style={{ flex: 1, textAlign: 'right' }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Cobertura</div>
                      <div style={{ fontSize: 14, fontWeight: 800, color: coverageColor(coberturaClass), fontFeatureSettings: '"tnum"', lineHeight: 1 }}>
                        {coberturaClass}
                      </div>
                      <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-500)' }}>dias</div>
                    </div>
                  )}
                </div>
                {hasPeriod && (
                  <div style={{ marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--ink-200)', display: 'flex', gap: 0 }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Vendido</div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: EVO_GREEN }}>{skNum(soldQty)} un</div>
                      <div style={{ fontSize: 10, fontWeight: 500, color: 'var(--ink-600)' }}>R$ {skBRL(soldVal)}</div>
                    </div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Comprado</div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: EVO_BLUE }}>{skNum(boughtQty)} un</div>
                      <div style={{ fontSize: 10, fontWeight: 500, color: 'var(--ink-600)' }}>R$ {skBRL(boughtVal)}</div>
                    </div>
                    <div style={{ flex: 1, textAlign: 'right' }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 2 }}>Saldo</div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: deltaQty >= 0 ? EVO_BLUE : EVO_ORANGE }}>
                        {deltaQty >= 0 ? '+' : ''}{skNum(deltaQty)} un
                      </div>
                      <div style={{ fontSize: 10, fontWeight: 600, color: deltaQty >= 0 ? EVO_BLUE : EVO_ORANGE }}>
                        {deltaQty >= 0 ? 'crescendo' : 'reduzindo'}
                      </div>
                    </div>
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Visão de Metas ───────────────────────────────────────────────────────────
// ─── Goals helpers ────────────────────────────────────────────────────────────
const G_MONTH = '#0B69C7';
const G_WEEK  = '#34C759';
const G_TODAY = '#FF9500';
const G_MEDAL_DOT = ['#F59E0B', '#94A3B8', '#CD7F32'];
const G_MEDAL_BG  = ['rgba(245,158,11,0.09)',  'rgba(148,163,184,0.09)', 'rgba(205,127,50,0.09)'];
const G_MEDAL_BRD = ['rgba(245,158,11,0.30)',  'rgba(148,163,184,0.30)', 'rgba(205,127,50,0.30)'];

const gFmtBRL = (n) => {
  if (n >= 1e6) return `R$ ${(n/1e6).toFixed(1)}M`;
  if (n >= 1e3) return `R$ ${(n/1e3).toFixed(1)}k`;
  return BRL(n);
};
const gFmtNum = (n) => n >= 1000 ? `${(n/1000).toFixed(1)}k` : `${Math.round(n)}`;
const gStatusColor = (pct) =>
  pct >= 1 ? G_WEEK : pct >= 0.75 ? G_MONTH : pct >= 0.5 ? G_TODAY : '#FF3B30';

const gLastDayOfMonth = (monthStr) => {
  const [y, m] = monthStr.split('-').map(Number);
  return new Date(y, m, 0).getDate();
};
const gMonthName = (monthStr) => {
  const [y, m] = monthStr.split('-').map(Number);
  return `${MONTH_NAMES_FULL_PT[m-1]} de ${y}`;
};

function gWeekInfo(monthStr, currentDay) {
  const [y, m] = monthStr.split('-').map(Number);
  const daysInMonth = gLastDayOfMonth(monthStr);
  const today = new Date(y, m - 1, currentDay);
  const dow = today.getDay(); // 0=Sun
  const iso = dow === 0 ? 7 : dow; // 1=Seg … 7=Dom (ISO)

  // Seg e Dom da semana ISO atual
  const monday = new Date(today); monday.setDate(today.getDate() - (iso - 1));
  const sunday = new Date(today); sunday.setDate(today.getDate() + (7 - iso));

  // Recorta nos limites do mês (igual ao mobile: moment.max/min)
  const wsDay    = (monday.getFullYear() < y || monday.getMonth() < m - 1) ? 1 : monday.getDate();
  const weDayNum = (sunday.getFullYear() > y || sunday.getMonth() > m - 1) ? daysInMonth : sunday.getDate();

  return { wsDay, weDayNum };
}

function buildGoalsData(docs, allowedStores, storeFilter, monthStr) {
  const [year, monthNum] = monthStr.split('-').map(Number);
  const daysInMonth = gLastDayOfMonth(monthStr);
  const todayStr = SF.today();
  const isCurrentMonth = todayStr.startsWith(monthStr);
  const currentDay = isCurrentMonth ? parseInt(todayStr.slice(8)) : daysInMonth;
  const filialKeys = new Set(Object.keys(SF.getFiliaisMap()));
  const allowed = allowedStores == null || typeof allowedStores === 'boolean' ? null : allowedStores;
  const hasFilter = storeFilter.length > 0;
  const storeMap = {};

  for (const { date, data } of docs) {
    if (!data?.stores) continue;
    const day = parseInt(date.slice(8));
    for (const [key, s] of Object.entries(data.stores)) {
      const sid = SF.parseStoreKey(key);
      if (allowed && !filialKeys.has(key) && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      if (!storeMap[sid]) storeMap[sid] = {
        storeId: sid, storeName: s.storeName ?? '',
        totalGoal: 0, avgTicketGoal: 0, clientGoal: 0, unityGoal: 0,
        history: [], sellers: {}, todaySellersRaw: {},
      };
      const acc = storeMap[sid];
      if (!acc.storeName && s.storeName) acc.storeName = s.storeName;
      if (safeN(s.totalGoal) > 0) {
        acc.totalGoal = safeN(s.totalGoal);
        acc.avgTicketGoal = safeN(s.avgTicketGoal);
        acc.clientGoal = safeN(s.clientGoal);
        acc.unityGoal = safeN(s.unityGoal);
      }
      acc.history.push({
        day,
        total:      safeN(s.total),
        quantity:   safeN(s.quantity || s.unityCount),
        client:     safeN(s.clientCount || s.salesCount),
        ticket:     safeN(s.avgTicket),
        salesCount: safeN(s.salesCount),
      });
      for (const [id, seller] of Object.entries(s.sellers ?? {})) {
        const sid2 = parseInt(id) || 0;
        if (!acc.sellers[sid2]) acc.sellers[sid2] = {
          sellerId: sid2,
          sellerName: seller.sellerName || `Vendedor ${sid2}`,
          total: 0, client: 0, quantity: 0, sales: 0,
          sellerGoal: safeN(seller.sellerGoal),
        };
        const sel = acc.sellers[sid2];
        sel.total    += safeN(seller.total);
        sel.client   += safeN(seller.client);
        sel.quantity += safeN(seller.quantity);
        sel.sales    += safeN(seller.sales);
        if (safeN(seller.sellerGoal) > 0) sel.sellerGoal = safeN(seller.sellerGoal);
      }
      if (day === currentDay) acc.todaySellersRaw = s.sellers ?? {};
    }
  }

  const goals = Object.values(storeMap).map(acc => {
    const history   = acc.history.sort((a, b) => a.day - b.day);
    const totalAcc  = history.reduce((s, h) => s + h.total, 0);
    const unityAcc  = history.reduce((s, h) => s + h.quantity, 0);
    const clientAcc = history.reduce((s, h) => s + h.client, 0);
    const totalSalesCount = history.reduce((s, h) => s + (h.salesCount ?? 0), 0);
    const avgTicketAcc    = totalSalesCount > 0 ? totalAcc / totalSalesCount : 0;
    const sellers = Object.values(acc.sellers).map(s => ({
      ...s,
      ticket: s.sales > 0 ? s.total / s.sales : (s.client > 0 ? s.total / s.client : 0),
    })).sort((a, b) => b.total - a.total);
    const todaySellers = Object.entries(acc.todaySellersRaw).map(([id, sel]) => {
      const sid2 = parseInt(id) || 0;
      const sales = safeN(sel.sales);
      const client = safeN(sel.client);
      const total = safeN(sel.total);
      return {
        sellerId: sid2,
        sellerName: sel.sellerName || `Vendedor ${sid2}`,
        total, client, quantity: safeN(sel.quantity), sales,
        sellerGoal: safeN(sel.sellerGoal),
        ticket: sales > 0 ? total / sales : (client > 0 ? total / client : 0),
      };
    }).sort((a, b) => b.total - a.total);
    return {
      storeId: acc.storeId, storeName: acc.storeName,
      totalGoal: acc.totalGoal, avgTicketGoal: acc.avgTicketGoal,
      clientGoal: acc.clientGoal, unityGoal: acc.unityGoal,
      totalAccumulated: totalAcc, unityAccumulated: unityAcc,
      clientAccumulated: clientAcc, avgTicketAccumulated: avgTicketAcc,
      goalPercentage: acc.totalGoal > 0 ? (totalAcc / acc.totalGoal) * 100 : 0,
      history, sellers, todaySellers,
    };
  }).filter(g => g.totalGoal > 0);

  const dayMap = {};
  for (const g of goals) {
    for (const h of g.history) {
      if (!dayMap[h.day]) dayMap[h.day] = { day: h.day, total: 0, quantity: 0, client: 0, salesCount: 0 };
      dayMap[h.day].total    += h.total;
      dayMap[h.day].quantity += h.quantity;
      dayMap[h.day].client   += h.client;
      dayMap[h.day].salesCount += h.salesCount;
    }
  }
  const aggHistory = Object.values(dayMap).sort((a, b) => a.day - b.day);

  const sellerMap = {};
  for (const g of goals) {
    for (const s of g.sellers) {
      const k = s.sellerName;
      if (!sellerMap[k]) sellerMap[k] = { ...s, total: 0, client: 0, quantity: 0, sales: 0 };
      sellerMap[k].total    += s.total;
      sellerMap[k].client   += s.client;
      sellerMap[k].quantity += s.quantity;
      sellerMap[k].sales    += s.sales || 0;
    }
  }
  const aggSellers = Object.values(sellerMap).map(s => ({
    ...s, ticket: s.sales > 0 ? s.total / s.sales : (s.client > 0 ? s.total / s.client : 0),
  })).sort((a, b) => b.total - a.total);

  const totalGoal  = goals.reduce((s, g) => s + g.totalGoal, 0);
  const totalAcc   = goals.reduce((s, g) => s + g.totalAccumulated, 0);
  const unityGoal  = goals.reduce((s, g) => s + g.unityGoal, 0);
  const unityAcc   = goals.reduce((s, g) => s + g.unityAccumulated, 0);
  const clientGoal      = goals.reduce((s, g) => s + g.clientGoal, 0);
  const clientAcc       = goals.reduce((s, g) => s + g.clientAccumulated, 0);
  const avgTicketGoal   = goals.length > 0 ? goals.reduce((s, g) => s + (g.avgTicketGoal ?? 0), 0) / goals.length : 0;
  const aggSalesCount   = aggHistory.reduce((s, h) => s + (h.salesCount ?? 0), 0);
  const avgTicketAcc    = aggSalesCount > 0 ? totalAcc / aggSalesCount : 0;

  const aggregate = {
    storeId: 0, storeName: 'Visão Geral',
    totalGoal, totalAccumulated: totalAcc,
    unityGoal, unityAccumulated: unityAcc,
    clientGoal, clientAccumulated: clientAcc,
    avgTicketGoal, avgTicketAccumulated: avgTicketAcc,
    goalPercentage: totalGoal > 0 ? (totalAcc / totalGoal) * 100 : 0,
    history: aggHistory, sellers: aggSellers,
  };

  return { goals, aggregate, daysInMonth, currentDay, isCurrentMonth, year, month: monthNum - 1, monthStr };
}

function useGoalsData(user, selectedStoreIds, monthStr) {
  const [goalsData, setGoalsData] = React.useState(null);
  const [loading,   setLoading]   = React.useState(true);

  React.useEffect(() => {
    if (!user?.cnpj) return;
    let cancelled = false, unsubLive = null;
    const cnpj = user.cnpj;
    setLoading(true); setGoalsData(null);
    const todayStr = SF.today();
    const isCurrent = todayStr.startsWith(monthStr);
    const startDate = `${monthStr}-01`;
    const daysInMonth = gLastDayOfMonth(monthStr);
    const pad2 = n => String(n).padStart(2, '0');
    const endDate = isCurrent ? todayStr : `${monthStr}-${pad2(daysInMonth)}`;

    (async () => {
      await SF.loadFiliaisMap(cnpj);
      if (cancelled) return;
      const pastEnd = isCurrent ? SF.subtractDays(1) : endDate;
      let pastDocs = [];
      if (startDate <= pastEnd) {
        pastDocs = await SF.fetchDailyRangeMerged(cnpj, startDate, pastEnd);
        if (cancelled) return;
      }
      if (!isCurrent) {
        if (!cancelled) { setGoalsData(buildGoalsData(pastDocs, user.allowedStores, selectedStoreIds, monthStr)); setLoading(false); }
        return;
      }
      const build = (todayData) => buildGoalsData(
        [...pastDocs, { date: todayStr, data: todayData }],
        user.allowedStores, selectedStoreIds, monthStr
      );
      setGoalsData(build(null)); setLoading(false);
      unsubLive = SF.listenDailyDocMerged(cnpj, todayStr,
        (d) => { if (!cancelled) setGoalsData(build(d)); }, () => {}
      );
    })();
    return () => { cancelled = true; unsubLive?.(); };
  }, [user?.cnpj, selectedStoreIds.join(','), monthStr]);

  return { goalsData, loading };
}

// ─── Goals: componentes visuais ───────────────────────────────────────────────

function GoalCirclesSVG({ monthPct, weekPct, dayPct }) {
  const SIZE = 220, CX = 110, CY = 110;
  const RINGS = [
    { label: 'Mês',    r: 95, sw: 15, color: G_MONTH, bg: 'rgba(11,105,199,0.12)',  pct: monthPct / 100 },
    { label: 'Semana', r: 71, sw: 13, color: G_WEEK,  bg: 'rgba(52,199,89,0.12)',   pct: weekPct  / 100 },
    { label: 'Hoje',   r: 48, sw: 12, color: G_TODAY, bg: 'rgba(255,149,0,0.12)',   pct: dayPct   / 100 },
  ];
  const arc = (r, pct) => {
    const p = Math.min(Math.max(pct, 0), 0.9999);
    const angle = p * 360;
    const toRad = deg => ((deg - 90) * Math.PI) / 180;
    const sx = CX + r * Math.cos(toRad(0));
    const sy = CY + r * Math.sin(toRad(0));
    const ex = CX + r * Math.cos(toRad(angle));
    const ey = CY + r * Math.sin(toRad(angle));
    return `M ${sx.toFixed(2)} ${sy.toFixed(2)} A ${r} ${r} 0 ${angle > 180 ? 1 : 0} 1 ${ex.toFixed(2)} ${ey.toFixed(2)}`;
  };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
      <div style={{ position: 'relative', width: SIZE, height: SIZE, flexShrink: 0 }}>
        <svg width={SIZE} height={SIZE} viewBox={`0 0 ${SIZE} ${SIZE}`}>
          {RINGS.map((ring) => (
            <React.Fragment key={ring.label}>
              <circle cx={CX} cy={CY} r={ring.r} style={{ stroke: ring.bg }} strokeWidth={ring.sw} fill="none" />
              {ring.pct > 0 && <path d={arc(ring.r, ring.pct)} stroke={ring.color} strokeWidth={ring.sw} strokeLinecap="round" fill="none" />}
            </React.Fragment>
          ))}
        </svg>
        <div style={{ position: 'absolute', top: 0, left: 0, width: SIZE, height: SIZE, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
          <div style={{ fontSize: 32, fontWeight: 800, color: G_MONTH, lineHeight: 1 }}>{Math.round(monthPct)}%</div>
          <span style={{ display: 'inline-block', fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 8px', marginTop: 4 }}>do mês</span>
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {RINGS.map(ring => (
          <div key={ring.label} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 16, height: 16, borderRadius: '50%', background: ring.color, flexShrink: 0 }} />
            <div>
              <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>{ring.label}</div>
              <div style={{ fontSize: 13, fontWeight: 700, color: ring.color }}>{Math.round(ring.pct * 100)}%</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function GoalValueCards({ goal, daysInMonth, currentDay, year, month }) {
  const dayGoal = goal.totalGoal > 0 ? goal.totalGoal / daysInMonth : 0;
  const { wsDay, weDayNum } = gWeekInfo(`${year}-${String(month+1).padStart(2,'0')}`, currentDay);
  const daysInWeek = Math.max(weDayNum - wsDay + 1, 1);
  const weekGoal = dayGoal * daysInWeek;
  const weekAcc  = goal.history.filter(h => h.day >= wsDay && h.day <= currentDay).reduce((s, h) => s + h.total, 0);
  const dayAcc   = goal.history.find(h => h.day === currentDay)?.total ?? 0;

  const cards = [
    { label: 'Mês',    acc: goal.totalAccumulated, g: goal.totalGoal, color: G_MONTH },
    { label: 'Semana', acc: weekAcc,                g: weekGoal,       color: G_WEEK  },
    { label: 'Hoje',   acc: dayAcc,                 g: dayGoal,        color: G_TODAY },
  ];
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, marginTop: 12 }}>
      {cards.map(({ label, acc, g, color }) => {
        const pct = g > 0 ? Math.min(acc / g, 9.99) : 0;
        return (
          <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 14, padding: 10 }}>
            <div style={{ width: 10, height: 10, borderRadius: '50%', background: color, marginBottom: 6 }} />
            <div style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 2 }}>{label}</div>
            <div style={{ fontSize: 15, fontWeight: 800, color, marginBottom: 2 }}>{gFmtBRL(acc)}</div>
            <span style={{ display: 'inline-block', fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 7px', marginBottom: 6 }}>meta {gFmtBRL(g)}</span>
            <div style={{ height: 4, background: 'var(--ink-100)', borderRadius: 2, overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: color, borderRadius: 2 }} />
            </div>
            <div style={{ fontSize: 11, fontWeight: 700, color, marginTop: 4 }}>{Math.round(pct * 100)}%</div>
          </div>
        );
      })}
    </div>
  );
}

function GoalMetricCards({ goal }) {
  const metrics = [
    { label: 'Unidades',     acc: goal.unityAccumulated      ?? 0, g: goal.unityGoal      ?? 0, fmt: NUM },
    { label: 'Ticket médio', acc: goal.avgTicketAccumulated  ?? 0, g: goal.avgTicketGoal  ?? 0, fmt: gFmtBRL },
  ];
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginTop: 16 }}>
      {metrics.map(({ label, acc, g, fmt }) => {
        const pct   = g > 0 ? acc / g : 0;
        const color = gStatusColor(pct);
        return (
          <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 12, padding: 8 }}>
            <div style={{ fontSize: 9, color: 'var(--ink-400)', fontWeight: 600, marginBottom: 3 }}>{label}</div>
            <div style={{ fontSize: 15, fontWeight: 800, color, marginBottom: 1 }}>{fmt(acc)}</div>
            <span style={{ display: 'inline-block', fontSize: 9, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 6px', marginBottom: 6 }}>meta {fmt(g)}</span>
            <div style={{ height: 4, background: 'var(--ink-100)', borderRadius: 2, overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: color, borderRadius: 2 }} />
            </div>
            <div style={{ fontSize: 10, fontWeight: 700, color, marginTop: 3 }}>{Math.round(pct * 100)}%</div>
          </div>
        );
      })}
    </div>
  );
}

function GoalStoreRanking({ goals }) {
  const sorted = [...goals].sort((a, b) => b.goalPercentage - a.goalPercentage);
  if (!sorted.length) return null;
  return (
    <div style={{ marginTop: 24 }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 12 }}>Ranking das Filiais</div>
      {sorted.map((g, i) => {
        const pct   = g.goalPercentage / 100;
        const color = gStatusColor(pct);
        const dot   = i < 3 ? G_MEDAL_DOT[i] : 'transparent';
        const bg    = i < 3 ? G_MEDAL_BG[i]  : 'var(--ink-50)';
        const brd   = i < 3 ? G_MEDAL_BRD[i] : 'transparent';
        return (
          <div key={g.storeId} style={{ display: 'flex', alignItems: 'stretch', gap: 8, marginBottom: 10 }}>
            <div style={{ width: 4, borderRadius: 4, background: dot, flexShrink: 0 }} />
            <div style={{ flex: 1, borderRadius: 12, padding: '11px 14px', background: bg, border: `0.5px solid ${brd}` }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
                <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
                  {g.storeName || `Loja ${g.storeId}`}
                </div>
                <div style={{ fontSize: 14, fontWeight: 800, color, marginLeft: 8, flexShrink: 0 }}>{Math.round(g.goalPercentage)}%</div>
              </div>
              <div style={{ height: 6, background: 'var(--ink-100)', borderRadius: 3, overflow: 'hidden', marginBottom: 5 }}>
                <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: color, borderRadius: 3 }} />
              </div>
              <div style={{ display: 'inline-block', background: 'var(--ink-50)', borderRadius: 8, padding: '2px 7px' }}>
                <span style={{ fontSize: 14, color: 'var(--brand-500)', fontWeight: 700 }}>
                  {gFmtBRL(g.totalAccumulated)} / {gFmtBRL(g.totalGoal)}
                </span>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function GoalHistoryBars({ history, currentDay, year, month }) {
  const DAY_NAMES = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'];
  const days = [];
  for (let i = 6; i >= 0; i--) {
    const d = currentDay - i;
    if (d < 1) continue;
    const date = new Date(year, month, d);
    const h = history.find(x => x.day === d);
    days.push({ day: d, label: DAY_NAMES[date.getDay()], total: h?.total ?? 0 });
  }
  const maxTotal = Math.max(...days.map(d => d.total), 1);
  return (
    <div style={{ marginTop: 24 }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 12 }}>Histórico Recente (últimos 7 dias)</div>
      <div style={{ display: 'flex', alignItems: 'flex-end', height: 160, gap: 8 }}>
        {days.map(({ day, label, total }) => {
          const h = total / maxTotal;
          const isToday = day === currentDay;
          return (
            <div key={day} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%', justifyContent: 'flex-end' }}>
              <div style={{ fontSize: 10, color: isToday ? G_MONTH : 'var(--ink-400)', fontWeight: isToday ? 700 : 400, marginBottom: 3, textAlign: 'center' }}>
                {total > 0 ? gFmtBRL(total).replace('R$ ', '') : ''}
              </div>
              <div style={{ width: '100%', flex: 1, display: 'flex', alignItems: 'flex-end' }}>
                <div style={{ width: '100%', height: `${Math.max(h * 100, total > 0 ? 4 : 0)}%`, background: isToday ? G_MONTH : 'var(--ink-200)', borderRadius: '4px 4px 0 0' }} />
              </div>
              <div style={{ height: 1, width: '100%', background: 'var(--ink-100)', marginBottom: 4 }} />
              <div style={{ fontSize: 10, color: isToday ? G_MONTH : 'var(--ink-400)', fontWeight: isToday ? 700 : 400 }}>{label}</div>
              <div style={{ fontSize: 9, color: 'var(--ink-300)' }}>{day}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function GoalSellersList({ sellers, totalAccumulated }) {
  const [expandedId, setExpandedId] = React.useState(null);
  if (!sellers?.length) return null;
  // Todos os vendedores, sem corte: cada um compõe a venda do período
  const visible = sellers;
  return (
    <div style={{ marginTop: 24 }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 12 }}>Ranking Vendedores</div>
      {visible.map((s, i) => {
        const hasGoal  = (s.sellerGoal ?? 0) > 0;
        const pct      = hasGoal ? s.total / s.sellerGoal : 0;
        const color    = hasGoal ? gStatusColor(pct) : G_MONTH;
        const sharePct = totalAccumulated > 0 ? (s.total / totalAccumulated) * 100 : 0;
        const medalDot = i < 3 ? G_MEDAL_DOT[i] : 'transparent';
        const bg       = i < 3 ? G_MEDAL_BG[i]  : 'var(--ink-50)';
        const brd      = i < 3 ? G_MEDAL_BRD[i] : 'transparent';
        const rowKey   = `${s.sellerId}-${s.sellerName}`;
        const isOpen   = expandedId === rowKey;
        return (
          <div key={rowKey} style={{ display: 'flex', alignItems: 'stretch', gap: 8, marginBottom: 10 }}>
            <div style={{ width: 4, borderRadius: 4, background: hasGoal ? color : (medalDot || 'transparent'), flexShrink: 0 }} />
            <div style={{ flex: 1, borderRadius: 12, padding: '10px 14px', background: bg, border: `0.5px solid ${brd}`, cursor: 'pointer' }}
                 onClick={() => setExpandedId(isOpen ? null : rowKey)}>
              <div style={{ display: 'flex', alignItems: 'center', marginBottom: hasGoal ? 6 : 0 }}>
                <div style={{ width: 22, height: 22, borderRadius: '50%', background: i < 3 ? G_MEDAL_DOT[i] + '30' : 'var(--ink-100)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginRight: 8, flexShrink: 0 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: i < 3 ? G_MEDAL_DOT[i] : 'var(--ink-500)' }}>{i+1}</span>
                </div>
                <span style={{ flex: 1, fontSize: 13, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.sellerName}</span>
                {hasGoal
                  ? <span style={{ fontSize: 14, fontWeight: 800, color, marginLeft: 8, flexShrink: 0 }}>{Math.round(pct * 100)}%</span>
                  : <span className="mono" style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', marginLeft: 8, flexShrink: 0 }}>{BRL(s.total)}</span>
                }
              </div>
              {!hasGoal && (
                <div style={{ display: 'inline-block', background: 'rgba(255,149,0,0.1)', border: '1px solid rgba(255,149,0,0.4)', borderRadius: 8, padding: '3px 8px', marginBottom: 4 }}>
                  <span style={{ fontSize: 10, fontWeight: 600, color: '#CC6600' }}>Sem meta nesta filial</span>
                </div>
              )}
              {hasGoal && (
                <>
                  <div style={{ height: 6, background: 'var(--ink-100)', borderRadius: 3, overflow: 'hidden', marginBottom: 6 }}>
                    <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: color, borderRadius: 3 }} />
                  </div>
                  <div style={{ display: 'flex', marginTop: 4 }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 1 }}>Vendido</div>
                      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)' }}>{BRL(s.total)}</div>
                    </div>
                    <div style={{ textAlign: 'right' }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 1 }}>Meta</div>
                      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)' }}>{BRL(s.sellerGoal)}</div>
                    </div>
                  </div>
                </>
              )}
              {isOpen && (
                <div style={{ display: 'flex', flexWrap: 'wrap', marginTop: 10, paddingTop: 10, borderTop: '1px solid var(--ink-200)' }}>
                  {[
                    ['Itens',         NUM(s.quantity)],
                    ['Clientes',      s.client],
                    s.ticket > 0 && ['Ticket médio', BRL(s.ticket)],
                    ['Participação',  `${sharePct.toFixed(1)}%`],
                  ].filter(Boolean).map(([label, val]) => (
                    <div key={label} style={{ width: '50%', paddingBottom: 4 }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 1 }}>{label}</div>
                      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)' }}>{val}</div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── GoalTopSellersRanking: top 3 vendedores por participação (visão geral) ───
function GoalTopSellersRanking({ goals }) {
  const top3 = React.useMemo(() => {
    const all = [];
    goals.forEach(goal => {
      if (!goal.sellers?.length || goal.totalAccumulated <= 0) return;
      goal.sellers.forEach(s => {
        all.push({
          sellerId:   s.sellerId,
          sellerName: s.sellerName,
          storeName:  goal.storeName || `Loja ${goal.storeId}`,
          total:      s.total,
          quantity:   s.quantity,
          client:     s.client,
          ticket:     s.ticket,
          sharePct:   (s.total / goal.totalAccumulated) * 100,
        });
      });
    });
    return all.sort((a, b) => b.sharePct - a.sharePct).slice(0, 3);
  }, [goals]);

  if (!top3.length) return null;

  return (
    <div style={{ marginTop: 24 }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 2 }}>Top Vendedores por Participação</div>
      <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 12 }}>% sobre as vendas — comparação entre as lojas</div>
      {top3.map((s, i) => {
        const isMedal    = i < 3;
        const barColor   = isMedal ? G_MEDAL_DOT[i] : G_MONTH;
        const cardBg     = isMedal ? G_MEDAL_BG[i]  : 'var(--white)';
        const cardBorder = isMedal ? G_MEDAL_BRD[i] : 'rgba(0,0,0,0.08)';
        return (
          <GoalTopSellerCard key={`${s.sellerId}-${s.storeName}`} seller={s} rank={i} barColor={barColor} cardBg={cardBg} cardBorder={cardBorder} />
        );
      })}
    </div>
  );
}

function GoalTopSellerCard({ seller, rank, barColor, cardBg, cardBorder }) {
  const [expanded, setExpanded] = React.useState(false);
  const fmtCur = (n) => n >= 1e6 ? `R$ ${(n/1e6).toFixed(1)}M` : `R$ ${(n/1000).toFixed(1)}k`;
  const fmtNum = (n) => n >= 1000 ? `${(n/1000).toFixed(1)}k` : `${Math.round(n)}`;
  const fmtTicket = (n) => n.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', minimumFractionDigits: 2 });
  return (
    <div style={{ display: 'flex', alignItems: 'stretch', gap: 8, marginBottom: 10 }}>
      <div style={{ width: 4, borderRadius: 4, background: barColor, flexShrink: 0 }} />
      <div
        onClick={() => setExpanded(p => !p)}
        style={{ flex: 1, borderRadius: 12, padding: '12px 14px', background: cardBg, border: `0.5px solid ${cardBorder}`, cursor: 'pointer' }}
      >
        <div style={{ display: 'flex', alignItems: 'center' }}>
          <div style={{ width: 26, height: 26, borderRadius: '50%', background: barColor + '30', display: 'flex', alignItems: 'center', justifyContent: 'center', marginRight: 10, flexShrink: 0 }}>
            <span style={{ fontSize: 12, fontWeight: 700, color: barColor }}>{rank + 1}</span>
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{seller.sellerName}</div>
            <span style={{ display: 'inline-block', fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 7px', marginTop: 3, maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{seller.storeName}</span>
          </div>
          <div style={{ textAlign: 'right', marginLeft: 8, flexShrink: 0 }}>
            <div style={{ fontSize: 18, fontWeight: 800, color: G_MONTH }}>{seller.sharePct.toFixed(1)}%</div>
            <span style={{ display: 'inline-block', fontSize: 10, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 7px', marginTop: 2 }}>participação</span>
          </div>
        </div>
        {expanded && (
          <div style={{ marginTop: 10, paddingTop: 10, borderTop: `1px solid ${barColor}40` }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
              {[
                ['Vendido',      BRL(seller.total)],
                ['Itens',        NUM(seller.quantity)],
                ['Clientes',     fmtNum(seller.client)],
                ['Ticket médio', fmtTicket(seller.ticket)],
              ].map(([label, val]) => (
                <div key={label} style={{ textAlign: 'center', flex: 1 }}>
                  <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 2 }}>{label}</div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-900)' }}>{val}</div>
                </div>
              ))}
            </div>
            <div style={{ fontSize: 10, color: 'var(--ink-400)', fontStyle: 'italic', textAlign: 'center' }}>
              % calculado sobre as vendas da filial {seller.storeName}, independente da meta.
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── GoalCircleBars: substitui a rosquinha — mesmos dots/fontes/cores, barras no lugar dos anéis
function GoalCircleBars({ goal, daysInMonth, currentDay, year, month }) {
  const monthStr = year + '-' + String(month + 1).padStart(2, '0');
  const dayGoal  = goal.totalGoal > 0 ? goal.totalGoal / daysInMonth : 0;
  const { wsDay, weDayNum } = gWeekInfo(monthStr, currentDay);
  const daysInWeek = Math.max(weDayNum - wsDay + 1, 1);
  const weekGoal = dayGoal * daysInWeek;
  const weekAcc  = goal.history.filter(h => h.day >= wsDay && h.day <= currentDay).reduce((s, h) => s + h.total, 0);
  const dayAcc   = goal.history.find(h => h.day === currentDay)?.total ?? 0;

  const monthPct = goal.totalGoal > 0 ? (goal.totalAccumulated / goal.totalGoal) * 100 : 0;
  const weekPct  = weekGoal > 0 ? (weekAcc / weekGoal) * 100 : 0;
  const dayPct   = dayGoal  > 0 ? (dayAcc  / dayGoal)  * 100 : 0;

  const items = [
    { label: 'Mês',    pct: monthPct, color: G_MONTH, bg: 'rgba(11,105,199,0.12)' },
    { label: 'Semana', pct: weekPct,  color: G_WEEK,  bg: 'rgba(52,199,89,0.12)'  },
    { label: 'Hoje',   pct: dayPct,   color: G_TODAY, bg: 'rgba(255,149,0,0.12)'  },
  ];

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 20, paddingBottom: 16 }}>
      <div style={{ textAlign: 'center', flexShrink: 0 }}>
        <div style={{ fontSize: 32, fontWeight: 800, color: G_MONTH, lineHeight: 1 }}>{Math.round(monthPct)}%</div>
        <div style={{ fontSize: 12, color: 'var(--ink-400)', marginTop: 2 }}>do mês</div>
      </div>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 10 }}>
        {items.map(({ label, pct, color, bg }) => (
          <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 16, height: 16, borderRadius: '50%', background: color, flexShrink: 0 }} />
            <div style={{ fontSize: 12, color: 'var(--ink-500)', width: 52, flexShrink: 0 }}>{label}</div>
            <div style={{ flex: 1, height: 10, background: bg, borderRadius: 5, overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${Math.min(pct, 100)}%`, background: color, borderRadius: 5 }} />
            </div>
            <div style={{ fontSize: 13, fontWeight: 700, color, width: 44, textAlign: 'right', flexShrink: 0 }}>{Math.round(pct)}%</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── GoalStatsHeader: 5 KPI cards horizontais (substitui anéis + cards do mobile) ─

function GoalStatsHeader({ goal, daysInMonth, currentDay, year, month }) {
  const monthStr = `${year}-${String(month + 1).padStart(2, '0')}`;
  const dayGoal  = goal.totalGoal > 0 ? goal.totalGoal / daysInMonth : 0;
  const { wsDay, weDayNum } = gWeekInfo(monthStr, currentDay);
  const daysInWeek = Math.max(weDayNum - wsDay + 1, 1);
  const weekGoal = dayGoal * daysInWeek;
  const weekAcc  = goal.history.filter(h => h.day >= wsDay && h.day <= currentDay).reduce((s, h) => s + h.total, 0);
  const dayAcc   = goal.history.find(h => h.day === currentDay)?.total ?? 0;

  const items = [
    { label: 'Mês',      acc: goal.totalAccumulated,  g: goal.totalGoal,  color: G_MONTH, fmt: gFmtBRL },
    { label: 'Semana',   acc: weekAcc,                 g: weekGoal,        color: G_WEEK,  fmt: gFmtBRL },
    { label: 'Hoje',     acc: dayAcc,                  g: dayGoal,         color: G_TODAY, fmt: gFmtBRL },
    { label: 'Unidades', acc: goal.unityAccumulated,   g: goal.unityGoal,  color: null,    fmt: NUM },
    { label: 'Ticket médio', acc: goal.avgTicketAccumulated ?? 0, g: goal.avgTicketGoal ?? 0, color: null, fmt: gFmtBRL },
  ];

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 10, marginBottom: 20 }}>
      {items.map(({ label, acc, g, color: fixedColor, fmt }) => {
        const pct   = g > 0 ? Math.min(acc / g, 9.99) : 0;
        const color = fixedColor ?? gStatusColor(pct);
        return (
          <div key={label} style={{ background: 'var(--ink-50)', borderRadius: 12, padding: '10px 12px', borderLeft: `3px solid ${color}` }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 4 }}>
              <span style={{ fontSize: 10, color: 'var(--ink-400)', fontWeight: 600 }}>{label}</span>
              <span style={{ fontSize: 18, fontWeight: 800, color, lineHeight: 1 }}>{Math.round(pct * 100)}%</span>
            </div>
            <div style={{ fontSize: 14, fontWeight: 800, color, marginBottom: 3 }}>{fmt(acc)}</div>
            <span style={{ display: 'inline-block', fontSize: 9, fontWeight: 600, color: 'var(--ink-500)', background: 'var(--ink-100)', border: '1px solid var(--ink-200)', borderRadius: 20, padding: '1px 6px', marginBottom: 6 }}>meta {fmt(g)}</span>
            <div style={{ height: 4, background: 'var(--ink-100)', borderRadius: 2, overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${Math.min(pct * 100, 100)}%`, background: color, borderRadius: 2 }} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── GoalStorePage: view de uma loja específica ───────────────────────────────
function GoalStorePage({ goal, gData }) {
  const { daysInMonth, currentDay, year, month, isCurrentMonth } = gData;
  const dayAcc = goal.history.find(h => h.day === currentDay)?.total ?? 0;

  const todaySellers = goal.todaySellers ?? [];
  const showToggle = isCurrentMonth && todaySellers.length > 0 && goal.sellers.length > 0;
  const [sellerMode, setSellerMode] = React.useState('acumulado');
  React.useEffect(() => { setSellerMode('acumulado'); }, [goal.storeId]);

  const hoieSellers = todaySellers.map(s =>
    s.sellerGoal > 0 ? { ...s, sellerGoal: s.sellerGoal / daysInMonth } : s
  );
  const displaySellers = sellerMode === 'hoje' && hoieSellers.length > 0 ? hoieSellers : goal.sellers;
  const displayTotal   = sellerMode === 'hoje' ? dayAcc : goal.totalAccumulated;

  return (
    <div>
      <GoalCircleBars goal={goal} daysInMonth={daysInMonth} currentDay={currentDay} year={year} month={month} />
      <GoalValueCards goal={goal} daysInMonth={daysInMonth} currentDay={currentDay} year={year} month={month} />
      <GoalMetricCards goal={goal} />
      {goal.sellers.length > 0 && (
        <>
          {showToggle && (
            <div style={{ display: 'flex', justifyContent: 'center', marginTop: 4, marginBottom: 4 }}>
              <div style={{ display: 'flex', background: 'var(--ink-100)', borderRadius: 10, padding: 3, gap: 2 }}>
                {['acumulado', 'hoje'].map(mode => (
                  <button key={mode} onClick={() => setSellerMode(mode)} style={{
                    padding: '6px 18px', borderRadius: 8, border: 'none', cursor: 'pointer', fontFamily: 'inherit',
                    background: sellerMode === mode ? G_MONTH : 'transparent',
                    color: sellerMode === mode ? '#FFF' : 'var(--ink-500)',
                    fontSize: 12, fontWeight: 600,
                  }}>
                    {mode.charAt(0).toUpperCase() + mode.slice(1)}
                  </button>
                ))}
              </div>
            </div>
          )}
          <GoalSellersList sellers={displaySellers} totalAccumulated={displayTotal} />
        </>
      )}
    </div>
  );
}

// ─── GoalOverviewPage: visão geral de todas as filiais ────────────────────────
function GoalOverviewPage({ gData }) {
  const { aggregate, goals, daysInMonth, currentDay, year, month } = gData;
  const [showFullRanking, setShowFullRanking] = React.useState(false);
  React.useEffect(() => { setShowFullRanking(false); }, [gData.monthStr]);

  return (
    <div>
      <GoalCircleBars goal={aggregate} daysInMonth={daysInMonth} currentDay={currentDay} year={year} month={month} />
      <GoalValueCards goal={aggregate} daysInMonth={daysInMonth} currentDay={currentDay} year={year} month={month} />
      <GoalMetricCards goal={aggregate} />
      {goals.length > 1 ? (
        <>
          <GoalTopSellersRanking goals={goals} />
          {aggregate.sellers?.length > 0 && (
            <div style={{ marginTop: 16 }}>
              <button
                onClick={() => setShowFullRanking(v => !v)}
                style={{
                  width: '100%', padding: 12, background: 'var(--ink-50)', border: '1px solid var(--ink-200)',
                  borderRadius: 10, cursor: 'pointer', fontSize: 13, fontWeight: 700, color: G_MONTH, fontFamily: 'inherit',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                }}
              >
                {showFullRanking ? 'Ocultar ranking completo' : `Ver ranking completo (${aggregate.sellers.length} vendedores)`}
                <Icon name="chevron-down" size={13} style={{ transform: showFullRanking ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
              </button>
              {showFullRanking && (
                <GoalSellersList sellers={aggregate.sellers} totalAccumulated={aggregate.totalAccumulated} />
              )}
            </div>
          )}
        </>
      ) : (
        // Loja única: sem Top 3 destacado, mostra o ranking direto (comportamento igual ao GoalStorePage)
        <GoalSellersList sellers={aggregate.sellers} totalAccumulated={aggregate.totalAccumulated} />
      )}
    </div>
  );
}

// ─── GoalMonthPicker: dropdown com grid meses + nav de ano ───────────────────
function GoalMonthPicker({ monthStr, onSelect, onClose }) {
  const now    = new Date();
  const nowStr = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}`;
  const [pickYear, setPickYear] = React.useState(() => parseInt(monthStr.split('-')[0]));
  const MONTHS = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
  return (
    <div style={{
      position: 'absolute', top: '110%', left: '50%', transform: 'translateX(-50%)',
      background: 'var(--white)', borderRadius: 12, padding: '12px 10px',
      boxShadow: '0 8px 32px rgba(0,0,0,0.16)', zIndex: 99, width: 220,
      border: '1px solid var(--ink-200)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, padding: '0 2px' }}>
        <button onClick={() => setPickYear(y => y - 1)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 20, color: G_MONTH, padding: '0 6px', lineHeight: 1, fontFamily: 'inherit' }}>‹</button>
        <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-900)' }}>{pickYear}</span>
        <button onClick={() => setPickYear(y => y + 1)} disabled={pickYear >= now.getFullYear()} style={{ background: 'none', border: 'none', cursor: pickYear < now.getFullYear() ? 'pointer' : 'default', fontSize: 20, color: pickYear < now.getFullYear() ? G_MONTH : 'var(--ink-200)', padding: '0 6px', lineHeight: 1, fontFamily: 'inherit' }}>›</button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }}>
        {MONTHS.map((name, i) => {
          const ms = `${pickYear}-${String(i+1).padStart(2,'0')}`;
          const isSelected = ms === monthStr;
          const isFuture   = ms > nowStr;
          return (
            <button key={i} disabled={isFuture} onClick={() => { onSelect(ms); onClose(); }} style={{
              padding: '8px 4px', borderRadius: 8, border: 'none',
              cursor: isFuture ? 'default' : 'pointer', fontFamily: 'inherit',
              background: isSelected ? G_MONTH : 'transparent',
              color: isSelected ? '#fff' : isFuture ? '#CCC' : '#333',
              fontSize: 12, fontWeight: isSelected ? 700 : 400,
            }}>{name}</button>
          );
        })}
      </div>
    </div>
  );
}

// ─── GoalsView principal ──────────────────────────────────────────────────────
function GoalsView({ user, selectedStoreIds }) {
  const now = new Date();
  const [monthStr, setMonthStr] = React.useState(
    `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}`
  );
  const [selectedStore,   setSelectedStore]   = React.useState(null);
  const [monthPickerOpen, setMonthPickerOpen] = React.useState(false);

  const selectMonth = React.useCallback((ms) => {
    setMonthStr(ms);
    setSelectedStore(null);
    setMonthPickerOpen(false);
  }, []);

  const { goalsData, loading } = useGoalsData(user, selectedStoreIds ?? [], monthStr);

  const canNext = monthStr < `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}`;

  const changeMonth = (delta) => {
    const [y, m] = monthStr.split('-').map(Number);
    const d = new Date(y, m - 1 + delta, 1);
    setMonthStr(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);
    setSelectedStore(null);
  };

  const storeList = goalsData?.goals ?? [];
  const activeGoal = selectedStore !== null ? storeList.find(g => g.storeId === selectedStore) : null;

  if (loading) return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ display: 'flex', gap: 12, marginBottom: 24, alignItems: 'center', justifyContent: 'center' }}>
        {[...Array(3)].map((_,i) => <div key={i} style={{ height: 28, width: 80, borderRadius: 8, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />)}
      </div>
      {[...Array(4)].map((_,i) => <div key={i} style={{ height: 60, borderRadius: 10, background: 'var(--ink-100)', marginBottom: 10, animation: 'sf-pulse 1.4s ease-in-out infinite' }} />)}
    </div>
  );

  if (!goalsData || (!goalsData.goals.length && !loading)) return (
    <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--ink-400)' }}>
      Sem metas configuradas para {gMonthName(monthStr)}.
    </div>
  );

  return (
    <div style={{ display: 'grid', gridTemplateColumns: storeList.length > 1 ? '280px 1fr' : '1fr', gap: 20, alignItems: 'start' }}>

      {/* Sidebar de navegação (só quando >1 filial) */}
      {storeList.length > 1 && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {/* Navegador de mês */}
          <div className="card" style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <button onClick={() => changeMonth(-1)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-600)', fontSize: 18, padding: '0 4px', fontFamily: 'inherit' }}>‹</button>
            <div style={{ flex: 1, textAlign: 'center', position: 'relative' }}>
              <button onClick={() => setMonthPickerOpen(p => !p)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 700, color: G_MONTH, fontFamily: 'inherit', padding: '2px 6px', borderRadius: 6 }}>
                {gMonthName(monthStr)} ▾
              </button>
              {monthPickerOpen && (
                <>
                  <div style={{ position: 'fixed', inset: 0, zIndex: 98 }} onClick={() => setMonthPickerOpen(false)} />
                  <GoalMonthPicker monthStr={monthStr} onSelect={selectMonth} onClose={() => setMonthPickerOpen(false)} />
                </>
              )}
            </div>
            <button onClick={() => changeMonth(1)} disabled={!canNext} style={{ background: 'none', border: 'none', cursor: canNext ? 'pointer' : 'default', color: canNext ? 'var(--ink-600)' : 'var(--ink-200)', fontSize: 18, padding: '0 4px', fontFamily: 'inherit' }}>›</button>
          </div>

          {/* Botão visão geral */}
          <button
            onClick={() => setSelectedStore(null)}
            style={{
              width: '100%', padding: '10px 14px', textAlign: 'left',
              background: selectedStore === null ? 'var(--brand-50)' : 'var(--white)',
              color: selectedStore === null ? 'var(--brand-700)' : 'var(--ink-700)',
              border: `1px solid ${selectedStore === null ? 'var(--brand-200)' : 'var(--ink-200)'}`,
              borderRadius: 10, cursor: 'pointer', fontFamily: 'inherit',
              fontSize: 13, fontWeight: 600,
            }}
          >
            Visão Geral
            <span style={{ fontSize: 11, fontWeight: 400, color: 'var(--ink-400)', marginLeft: 6 }}>({storeList.length} filiais)</span>
          </button>

          {/* Botões por loja */}
          {storeList.sort((a,b) => b.goalPercentage - a.goalPercentage).map(g => {
            const color = gStatusColor(g.goalPercentage / 100);
            const isActive = selectedStore === g.storeId;
            return (
              <button key={g.storeId} onClick={() => setSelectedStore(g.storeId)} style={{
                width: '100%', padding: '10px 14px', textAlign: 'left',
                background: isActive ? 'var(--brand-50)' : 'var(--white)',
                color: isActive ? 'var(--brand-700)' : 'var(--ink-700)',
                border: `1px solid ${isActive ? 'var(--brand-200)' : 'var(--ink-200)'}`,
                borderRadius: 10, cursor: 'pointer', fontFamily: 'inherit',
              }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
                  <span style={{ fontSize: 13, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1, marginRight: 8 }}>
                    {g.storeName || `Loja ${g.storeId}`}
                  </span>
                  <span style={{ fontSize: 12, fontWeight: 800, color, flexShrink: 0 }}>{Math.round(g.goalPercentage)}%</span>
                </div>
                <div style={{ height: 3, background: 'var(--ink-100)', borderRadius: 99 }}>
                  <div style={{ height: '100%', width: `${Math.min(g.goalPercentage, 100)}%`, background: color, borderRadius: 99 }} />
                </div>
              </button>
            );
          })}
        </div>
      )}

      {/* Conteúdo principal */}
      <div className="card" style={{ padding: 24 }}>
        {/* Título + navegador de mês (quando filial única) */}
        {storeList.length <= 1 && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
            <button onClick={() => changeMonth(-1)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-600)', fontSize: 20, padding: '0 4px', fontFamily: 'inherit' }}>‹</button>
            <div style={{ position: 'relative', textAlign: 'center' }}>
              <button onClick={() => setMonthPickerOpen(p => !p)} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 15, fontWeight: 700, color: G_MONTH, fontFamily: 'inherit', padding: '2px 6px', borderRadius: 6 }}>
                {gMonthName(monthStr)} ▾
              </button>
              {monthPickerOpen && (
                <>
                  <div style={{ position: 'fixed', inset: 0, zIndex: 98 }} onClick={() => setMonthPickerOpen(false)} />
                  <GoalMonthPicker monthStr={monthStr} onSelect={selectMonth} onClose={() => setMonthPickerOpen(false)} />
                </>
              )}
            </div>
            <button onClick={() => changeMonth(1)} disabled={!canNext} style={{ background: 'none', border: 'none', cursor: canNext ? 'pointer' : 'default', color: canNext ? 'var(--ink-600)' : 'var(--ink-200)', fontSize: 20, padding: '0 4px', fontFamily: 'inherit' }}>›</button>
          </div>
        )}

        {/* Título da filial/visão quando multi */}
        {storeList.length > 1 && (
          <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 20 }}>
            {activeGoal ? (activeGoal.storeName || `Loja ${activeGoal.storeId}`) : 'Visão Geral'}
          </div>
        )}

        {activeGoal
          ? <GoalStorePage   goal={activeGoal}       gData={goalsData} />
          : <GoalOverviewPage                         gData={goalsData} />
        }
      </div>
    </div>
  );
}

// ─── Hook: live listeners para todas as datas do período (até 60 dias) ───────
function useBillsLive(cnpj, allowedStores, storeFilter, periodStart, periodEnd, dateMode = 'vencimento') {
  const [liveBills, setLiveBills] = React.useState(null); // null = aguardando 1ª carga

  React.useEffect(() => {
    if (!cnpj || !periodStart || !periodEnd) return;

    const MAX_DAYS = 60;
    const allDates = SF.datesInRange(periodStart, periodEnd);
    if (!allDates.length || allDates.length > MAX_DAYS) { setLiveBills(null); return; }

    // Os docs diários são keyados pelo VENCIMENTO da conta (coletor: WHERE DT_VENCIMENTO = data).
    // Filtrando por emissão/entrada, uma conta emitida/lançada no período pode vencer ANOS
    // depois (já vimos vencimento digitado pra 2029) ou já ter vencido antes — busca docs
    // extras (one-shot, sem listener) em volta do período. Pra frente é sem limite: a range
    // query só devolve docs que existem, então "até 9999" não custa leituras extras.
    const addDays = (dateStr, n) => {
      const d = new Date(dateStr + 'T00:00:00'); d.setDate(d.getDate() + n);
      return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    };
    const EXT_BACK = 60, EXT_FWD_END = '9999-12-31';
    const needsExt = dateMode !== 'vencimento';

    const safeN   = v => { const n = Number(v); return isNaN(n) ? 0 : n; };
    const allowed = (allowedStores === true || !allowedStores) ? null : allowedStores;
    const hasSF   = storeFilter.length > 0;
    const docData = {};
    let   extDates = [];
    const ready   = new Set();
    const readyTarget = allDates.length + (needsExt ? 1 : 0);
    let cancelled = false;

    const rebuild = () => {
      if (cancelled || ready.size < readyTarget) return;
      const merged = {};
      for (const date of [...extDates, ...allDates]) {
        const doc = docData[date];
        if (!doc?.bills) continue;
        for (const [id, b] of Object.entries(doc.bills)) {
          const sid = b.collectorStoreId
            ? b.collectorStoreId * 10000 + Number(b.storeId)
            : Number(b.storeId);
          if (allowed && sid < 10001 && !SF.isAllowedStore(sid, allowed)) continue;
          if (hasSF && !storeFilter.includes(sid)) continue;
          merged[id] = {
            storeId:          sid,
            storeName:        b.storeName        ?? '',
            manufacturerName: b.manufacturerName ?? '',
            billingValue:     safeN(b.billingValue),
            paidValue:        safeN(b.paidValue),
            status:           b.status           ?? '',
            dueDate:          b.dueDate          ?? null,
            issueDate:        b.issueDate        ?? null,
            emissionDate:     b.emissionDate     ?? null,
          };
        }
      }
      setLiveBills(Object.values(merged));
    };

    const unsubs = allDates.map(date =>
      SF.listenDailyDocMerged(cnpj, date,
        (data) => { docData[date] = data; ready.add(date); rebuild(); },
        ()     => {                       ready.add(date); rebuild(); }
      )
    );

    if (needsExt) {
      Promise.allSettled([
        SF.fetchBillsRangeMerged(cnpj, addDays(periodStart, -EXT_BACK), addDays(periodStart, -1)),
        SF.fetchBillsRangeMerged(cnpj, addDays(periodEnd, 1), EXT_FWD_END),
      ]).then(results => {
        for (const r of results) {
          if (r.status !== 'fulfilled') continue;
          for (const [date, doc] of Object.entries(r.value)) docData[date] = doc;
        }
        extDates = Object.keys(docData).filter(d => !allDates.includes(d)).sort();
        ready.add('__ext__'); rebuild();
      });
    }

    return () => { cancelled = true; setLiveBills(null); unsubs.forEach(u => u?.()); };
  }, [cnpj, allowedStores, storeFilter.join(','), periodStart, periodEnd, dateMode]);

  return liveBills;
}

// Componente isolado: estado de hover interno → re-render não afeta BillsTable
function BillsMfgDonut({ data }) {
  const [hov, setHov] = React.useState(null);
  const { segs, total, R, SW, SZ } = data;
  const circ = 2 * Math.PI * R;
  return (
    <div className="card" style={{ marginTop: 20, padding: '20px 24px' }}>
      <div style={{ fontSize: 13, fontWeight: 700, marginBottom: 16, color: 'var(--ink-900)' }}>
        VALORES A PAGAR
        <span style={{ fontSize: 11, fontWeight: 400, color: 'var(--ink-400)', marginLeft: 8 }}></span>
      </div>
      <div style={{ display: 'flex', gap: 32, alignItems: 'center', flexWrap: 'wrap' }}>
        <div style={{ flexShrink: 0 }}>
          <svg width={SZ} height={SZ} style={{ display: 'block' }}>
            <g transform={`translate(${SZ / 2},${SZ / 2}) rotate(-90)`}>
              {segs.map((seg, i) => (
                <circle key={i}
                  cx="0" cy="0" r={R}
                  fill="none"
                  stroke={seg.color}
                  strokeWidth={SW}
                  strokeDasharray={`${seg.len} ${circ - seg.len}`}
                  strokeDashoffset={-seg.offset}
                  opacity={hov === null || hov === seg.name ? 1 : 0.2}
                  style={{ cursor: 'pointer' }}
                  onMouseEnter={() => setHov(seg.name)}
                  onMouseLeave={() => setHov(null)}
                />
              ))}
            </g>
            {hov ? (() => {
              const seg = segs.find(s => s.name === hov);
              if (!seg) return null;
              const pct = total > 0 ? ((seg.value / total) * 100).toFixed(1) : '0';
              return (<>
                <text x={SZ / 2} y={SZ / 2 - 8} textAnchor="middle" style={{ fontSize: 22, fontWeight: 700, fill: 'var(--ink-900)', fontFamily: 'inherit' }}>{pct}%</text>
                <text x={SZ / 2} y={SZ / 2 + 12} textAnchor="middle" style={{ fontSize: 11, fill: 'var(--ink-500)', fontFamily: 'inherit' }}>{BRL(seg.value)}</text>
              </>);
            })() : (<>
              <text x={SZ / 2} y={SZ / 2 - 8} textAnchor="middle" style={{ fontSize: 11, fill: 'var(--ink-400)', fontFamily: 'inherit' }}>Total</text>
              <text x={SZ / 2} y={SZ / 2 + 12} textAnchor="middle" style={{ fontSize: 15, fontWeight: 700, fill: 'var(--ink-900)', fontFamily: 'inherit' }}>{BRL(total)}</text>
            </>)}
          </svg>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          {segs.map((seg, i) => {
            const pct = total > 0 ? ((seg.value / total) * 100).toFixed(1) : '0';
            const isHov = hov === seg.name;
            return (
              <div key={i}
                onMouseEnter={() => setHov(seg.name)}
                onMouseLeave={() => setHov(null)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10, cursor: 'default',
                  opacity: hov === null || isHov ? 1 : 0.3,
                  padding: '4px 8px', borderRadius: 7,
                  background: isHov ? 'var(--ink-50)' : 'transparent',
                }}>
                <span style={{ width: 11, height: 11, borderRadius: 3, background: seg.color, flexShrink: 0 }} />
                <span style={{ fontSize: 12.5, color: 'var(--ink-800)', width: 220, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={seg.name}>{seg.name}</span>
                <span className="mono" style={{ fontSize: 12, color: 'var(--ink-500)', width: 110, textAlign: 'right' }}>{BRL(seg.value)}</span>
                <span style={{ fontSize: 12, fontWeight: 700, color: seg.color, width: 42, textAlign: 'right' }}>{pct}%</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ─── Visão de Contas a Pagar ──────────────────────────────────────────────────
function BillsView({ data, loading, syncAt, filterDim, filterSel, billsParams, period, customStart, customEnd, user, selectedStoreIds }) {

  const periodRange = React.useMemo(() => {
    const today = SF.today();
    switch (period) {
      case 'custom': return { start: customStart, end: customEnd };
      case 'mtd':    return { start: SF.startOfMonth(),    end: today };
      case 'ytd':    return { start: SF.startOfYear(),     end: today };
      case '30d':    return { start: SF.subtractDays(29),  end: today };
      case '7d':     return { start: SF.subtractDays(6),   end: today };
      default:       return { start: today,                end: today };
    }
  }, [period, customStart, customEnd]);

  const liveBills = useBillsLive(
    user?.cnpj, user?.allowedStores, selectedStoreIds ?? [],
    periodRange.start, periodRange.end,
    billsParams?.dateMode ?? 'vencimento'
  );
  const allBills = liveBills ?? (data?.bills ?? []);
  const isLive   = liveBills !== null;

  // Espelha EntryRow do mobile: só status "D" é cancelado; pago = paidValue > 0
  const categorize = (status, paidValue) => {
    if (status === 'D') return 'cancelado';
    if (paidValue > 0) return 'pago';
    return 'pendente';
  };

  const bills = React.useMemo(() => {
    // Pré-filtro: espelha hero filter do mobile — só A (aberto) e P (parcial) contam;
    // D (deletado) fica como cancelado; B (baixado) e outros são ignorados como no app.
    let list = allBills.filter(b => b.status === 'A' || b.status === 'P' || b.status === 'D');
    // Filtro global de fornecedor
    if (filterDim === 'fornecedores' && filterSel?.length) {
      const sel = new Set(filterSel);
      list = list.filter(b => sel.has(b.manufacturerName));
    }
    // Filtros de parâmetros (vêm do FilterPanel > aba Parâmetros)
    if (billsParams) {
      list = list.filter(b => {
        const cat = categorize(b.status, b.paidValue);
        return (cat === 'pago'      && billsParams.statusPago)      ||
               (cat === 'cancelado' && billsParams.statusCancelado) ||
               (cat === 'pendente'  && billsParams.statusPendente);
      });
      const activeNum = (v) => { const n = parseFloat(v); return !isNaN(n) && n !== 0 ? n : null; };
      const minB = activeNum(billsParams.minBilling), maxB = activeNum(billsParams.maxBilling);
      const minP = activeNum(billsParams.minPaid),    maxP = activeNum(billsParams.maxPaid);
      if (minB !== null) list = list.filter(b => b.billingValue >= minB);
      if (maxB !== null) list = list.filter(b => b.billingValue <= maxB);
      if (minP !== null) list = list.filter(b => b.paidValue    >= minP);
      if (maxP !== null) list = list.filter(b => b.paidValue    <= maxP);
      if (billsParams.dateMode === 'lancamento' && periodRange.start && periodRange.end) {
        list = list.filter(b => b.issueDate && b.issueDate >= periodRange.start && b.issueDate <= periodRange.end);
      }
      if (billsParams.dateMode === 'emissao' && periodRange.start && periodRange.end) {
        list = list.filter(b => b.emissionDate && b.emissionDate >= periodRange.start && b.emissionDate <= periodRange.end);
      }
    }
    return list;
  }, [allBills, filterDim, filterSel, billsParams, periodRange]);

  const kpis = loading ? null : [
    { label: 'Total pago',    value: BRL(bills.reduce((s, b) => s + b.paidValue,     0)), delta: 0, spark: [0] },
    { label: 'Total a pagar', value: BRL(bills.reduce((s, b) => s + b.billingValue, 0)), delta: 0, spark: [0], primary: true },
    { label: 'A vencer',      value: BRL(Math.max(bills.reduce((s, b) => s + b.billingValue - b.paidValue, 0), 0)), delta: 0, spark: [0] },
    { label: 'Qtd. contas',   value: NUM(bills.length), delta: 0, spark: [0] },
  ];

  // Donut por fornecedor — dados computados aqui, render isolado em BillsMfgDonut
  const mfgDonutData = React.useMemo(() => {
    if (!bills.length) return null;
    const map = {};
    for (const b of bills) {
      if (b.status === 'D') continue;
      const name = b.manufacturerName || 'Sem fornecedor';
      map[name] = (map[name] ?? 0) + b.billingValue;
    }
    const sorted = Object.entries(map).sort((a, b) => b[1] - a[1]);
    const top    = sorted.slice(0, 8);
    const rest   = sorted.slice(8).reduce((s, [, v]) => s + v, 0);
    const items  = rest > 0 ? [...top, ['Outros', rest]] : top;
    const total  = items.reduce((s, [, v]) => s + v, 0);
    const R = 80, SW = 22, circ = 2 * Math.PI * R;
    let off = 0;
    const segs = items.map(([name, value], i) => {
      const len = total > 0 ? (value / total) * circ : 0;
      const color = name === 'Outros' ? '#CBD5E1' : GRP_PALETTE[i % GRP_PALETTE.length];
      const seg = { name, value, len, offset: off, color };
      off += len;
      return seg;
    });
    return { segs, total, R, SW, SZ: (R + SW / 2 + 4) * 2 };
  }, [bills]);

  return (
    <>
      <KpiRow kpis={kpis} loading={loading} syncAt={syncAt} />
      {isLive && !loading && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginTop: 4, marginBottom: -4 }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--positive)', display: 'inline-block' }} />
          <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--positive)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>ao vivo</span>
        </div>
      )}

      {/* Rosquinha por fornecedor */}
      {!loading && mfgDonutData && <BillsMfgDonut data={mfgDonutData} />}

      <div style={{ marginTop: 20 }}>
        <BillsTable bills={bills} allCount={allBills.length} loading={loading} dateMode={billsParams?.dateMode ?? 'vencimento'} />
      </div>
    </>
  );
}

function BillsTable({ bills, allCount, loading, dateMode = 'vencimento' }) {
  const [expandedGroups, setExpandedGroups] = React.useState(new Set());
  const [showAll,        setShowAll]        = React.useState(false);
  const MAX_GROUPS = 50;

  const extraCol = dateMode === 'lancamento' ? { label: 'Emissão',  field: 'issueDate'    }
                 : dateMode === 'emissao'    ? { label: 'Entrada',  field: 'emissionDate' }
                 : null;

  const categorize = (status, paidValue) => {
    if (status === 'D') return 'cancelado';
    if (paidValue > 0) return 'pago';
    return 'pendente';
  };
  const fmtDueDate = (d) => {
    if (!d) return '—';
    if (typeof d === 'string' && /^\d{4}-\d{2}-\d{2}/.test(d)) {
      const [y, m, day] = d.slice(0, 10).split('-');
      return `${day}/${m}/${y}`;
    }
    try {
      const dt = d?.toDate ? d.toDate() : d?.seconds ? new Date(d.seconds * 1000) : new Date(d);
      if (isNaN(dt.getTime())) return '—';
      return dt.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' });
    } catch { return '—'; }
  };
  const fmtShort = (d) => {
    if (!d) return '—';
    if (typeof d === 'string' && /^\d{4}-\d{2}-\d{2}/.test(d)) {
      const [, m, day] = d.slice(0, 10).split('-');
      return `${day}/${m}`;
    }
    return fmtDueDate(d);
  };
  const statusColor = (cat) => ({ pago: 'var(--positive)', cancelado: 'var(--danger)', pendente: 'var(--warning)' })[cat] ?? 'var(--ink-500)';
  const statusBg    = (cat) => ({ pago: 'var(--positive-bg)', cancelado: 'var(--danger-bg)', pendente: 'var(--warning-bg)' })[cat] ?? 'var(--ink-100)';
  const statusLabel = (cat) => ({ pago: 'Pago', cancelado: 'Cancelado', pendente: 'Pendente' })[cat] ?? cat;

  const groups = React.useMemo(() => {
    const map = {};
    for (const b of bills) {
      const name = b.manufacturerName || '—';
      if (!map[name]) map[name] = { name, items: [] };
      map[name].items.push(b);
    }
    return Object.values(map).sort((a, b) => {
      const ta = a.items.reduce((s, x) => s + x.billingValue, 0);
      const tb = b.items.reduce((s, x) => s + x.billingValue, 0);
      return tb - ta;
    });
  }, [bills]);

  const visibleGroups = showAll ? groups : groups.slice(0, MAX_GROUPS);
  const remaining     = groups.length - MAX_GROUPS;
  const toggle = (name) => setExpandedGroups(prev => { const n = new Set(prev); n.has(name) ? n.delete(name) : n.add(name); return n; });

  const renderBillRow = (b, i, indent) => {
    const cat = categorize(b.status, b.paidValue);
    const pad = indent ? '9px 20px' : '11px 20px';
    return (
      <tr key={`${b.storeId}-${i}`} style={{ background: indent ? (i % 2 === 0 ? 'var(--ink-50)' : 'rgba(0,0,0,0.018)') : (i % 2 === 0 ? 'var(--white)' : 'var(--ink-50)'), borderTop: indent ? '1px solid var(--ink-100)' : undefined }}>
        <td style={{ padding: indent ? '9px 20px 9px 36px' : pad, fontWeight: indent ? 400 : 600, maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {indent ? '' : (b.manufacturerName || '—')}
        </td>
        <td style={{ padding: pad, color: 'var(--ink-600)', fontSize: indent ? 12 : 12.5 }}>{b.storeName || `Loja ${b.storeId}`}</td>
        <td className="mono" style={{ padding: pad, textAlign: 'right', fontSize: 12, color: 'var(--ink-600)' }}>{fmtDueDate(b.dueDate)}</td>
        {extraCol && (
          <td className="mono" style={{ padding: pad, textAlign: 'right', fontSize: 12, color: 'var(--ink-500)' }}>{fmtDueDate(b[extraCol.field])}</td>
        )}
        <td className="mono" style={{ padding: pad, textAlign: 'right', fontWeight: 700 }}>{BRL(b.billingValue)}</td>
        <td className="mono" style={{ padding: pad, textAlign: 'right' }}>{BRL(b.paidValue)}</td>
        <td style={{ padding: pad, textAlign: 'right' }}>
          <span style={{ fontSize: 11.5, fontWeight: 600, color: statusColor(cat), background: statusBg(cat), padding: '3px 8px', borderRadius: 5 }}>
            {statusLabel(cat)}
          </span>
        </td>
      </tr>
    );
  };

  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div style={{ padding: '20px 24px 16px' }}>
        <div style={{ fontSize: 16, fontWeight: 700 }}>Contas a pagar</div>
        <div style={{ fontSize: 13, color: 'var(--ink-500)', marginTop: 2 }}>
          {!loading && (bills.length < allCount
            ? `${bills.length} de ${allCount} contas (filtros ativos)`
            : `${bills.length} conta${bills.length !== 1 ? 's' : ''} · snapshot do dia`)}
        </div>
      </div>
      {loading
        ? <div style={{ padding: '0 24px 24px', display: 'flex', flexDirection: 'column', gap: 10 }}>
            {[...Array(5)].map((_, i) => <div key={i} style={{ height: 44, borderRadius: 6, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>)}
          </div>
        : !bills.length
        ? <EmptyState text={allCount ? 'Nenhuma conta corresponde aos filtros.' : 'Sem contas a pagar no período.'} style={{ padding: '0 24px 24px' }} />
        : (
          <>
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
              <thead>
                <tr style={{ background: 'var(--ink-50)', borderTop: '1px solid var(--ink-200)', borderBottom: '1px solid var(--ink-200)' }}>
                  {['Fornecedor','Loja','Vencimento', ...(extraCol ? [extraCol.label] : []), 'Valor','Pago','Status'].map((h, i) => (
                    <th key={i} style={{ textAlign: i < 2 ? 'left' : 'right', padding: '10px 20px', fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {visibleGroups.map((group) => {
                  if (group.items.length === 1) {
                    return renderBillRow(group.items[0], 0, false);
                  }

                  const isExpanded   = expandedGroups.has(group.name);
                  const totalBilling = group.items.reduce((s, b) => s + b.billingValue, 0);
                  const totalPaid    = group.items.reduce((s, b) => s + b.paidValue, 0);
                  const statusCounts = {};
                  for (const b of group.items) {
                    const cat = categorize(b.status, b.paidValue);
                    statusCounts[cat] = (statusCounts[cat] ?? 0) + 1;
                  }
                  const storeIds  = new Set(group.items.map(b => b.storeId));
                  const lojaLabel = storeIds.size === 1
                    ? (group.items[0].storeName || `Loja ${group.items[0].storeId}`)
                    : `${storeIds.size} lojas`;
                  const dates     = group.items.map(b => b.dueDate).filter(Boolean).sort();
                  const dateLabel = dates.length === 0 ? '—'
                    : dates[0] === dates[dates.length - 1] ? fmtDueDate(dates[0])
                    : `${fmtShort(dates[0])} – ${fmtShort(dates[dates.length - 1])}`;

                  return (
                    <React.Fragment key={group.name}>
                      <tr
                        onClick={() => toggle(group.name)}
                        style={{ cursor: 'pointer', borderTop: '1px solid var(--ink-200)', background: isExpanded ? 'var(--ink-50)' : 'var(--white)', transition: 'background .1s' }}
                        onMouseEnter={e => { if (!isExpanded) e.currentTarget.style.background = 'var(--ink-50)'; }}
                        onMouseLeave={e => { if (!isExpanded) e.currentTarget.style.background = 'var(--white)'; }}
                      >
                        <td style={{ padding: '11px 20px', fontWeight: 700, maxWidth: 220 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
                            <svg width="9" height="9" viewBox="0 0 9 9" fill="none"
                              style={{ flexShrink: 0, transition: 'transform .15s', transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)', color: 'var(--ink-400)' }}>
                              <path d="M2.5 1.5L6.5 4.5L2.5 7.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                            </svg>
                            <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{group.name}</span>
                            <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-500)', background: 'var(--ink-100)', borderRadius: 20, padding: '1px 7px', flexShrink: 0 }}>
                              {group.items.length}
                            </span>
                          </div>
                        </td>
                        <td style={{ padding: '11px 20px', color: 'var(--ink-600)', fontSize: 12.5 }}>{lojaLabel}</td>
                        <td className="mono" style={{ padding: '11px 20px', textAlign: 'right', fontSize: 12, color: 'var(--ink-500)' }}>{dateLabel}</td>
                        {extraCol && <td />}
                        <td className="mono" style={{ padding: '11px 20px', textAlign: 'right', fontWeight: 700 }}>{BRL(totalBilling)}</td>
                        <td className="mono" style={{ padding: '11px 20px', textAlign: 'right' }}>{BRL(totalPaid)}</td>
                        <td style={{ padding: '11px 20px', textAlign: 'right' }}>
                          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 3, flexWrap: 'wrap' }}>
                            {['pendente','pago','cancelado'].filter(c => statusCounts[c]).map(c => (
                              <span key={c} style={{ fontSize: 11, fontWeight: 600, color: statusColor(c), background: statusBg(c), padding: '2px 7px', borderRadius: 5, whiteSpace: 'nowrap' }}>
                                {statusCounts[c] > 1 ? `${statusCounts[c]}× ` : ''}{statusLabel(c)}
                              </span>
                            ))}
                          </div>
                        </td>
                      </tr>
                      {isExpanded && group.items.map((b, i) => renderBillRow(b, i, true))}
                    </React.Fragment>
                  );
                })}
              </tbody>
            </table>
            {!showAll && remaining > 0 && (
              <div style={{ padding: '14px 24px', borderTop: '1px solid var(--ink-100)', display: 'flex', justifyContent: 'center' }}>
                <button
                  onClick={() => setShowAll(true)}
                  style={{ padding: '8px 28px', borderRadius: 8, border: '1px solid var(--ink-200)', background: 'var(--white)', cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 600, color: 'var(--ink-600)' }}
                  onMouseEnter={e => e.currentTarget.style.background = 'var(--ink-50)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'var(--white)'}
                >
                  Mostrar mais {remaining}
                </button>
              </div>
            )}
          </>
        )
      }
    </div>
  );
}

// ─── Visão Geral ──────────────────────────────────────────────────────────────
function OverviewView({ data, loading, syncAt, period }) {
  const t = data?.totals;
  const summaryCards = t ? [
    { label: 'Vendas', value: BRL(t.total),          icon: 'cart',    color: 'var(--brand-500)' },
    { label: 'Compras', value: BRL(t.purchaseTotal), icon: 'bag',     color: 'var(--ink-700)' },
    { label: 'Margem',  value: `${(t.margin ?? 0).toFixed(1)}%`, icon: 'trend', color: t.margin >= 0 ? 'var(--positive)' : 'var(--danger)' },
    { label: 'Contas',  value: BRL(t.billTotal ?? 0), icon: 'money', color: 'var(--warning)' },
  ] : null;

  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
        {loading
          ? [...Array(4)].map((_, i) => (
              <div key={i} className="card" style={{ padding: 20, height: 100, animation: 'sf-pulse 1.4s ease-in-out infinite' }}/>
            ))
          : (summaryCards ?? [
              { label: 'Vendas',  value: '—', icon: 'cart',  color: 'var(--brand-500)' },
              { label: 'Compras', value: '—', icon: 'bag',   color: 'var(--ink-700)' },
              { label: 'Margem',  value: '—', icon: 'trend', color: 'var(--positive)' },
              { label: 'Contas',  value: '—', icon: 'money', color: 'var(--warning)' },
            ]).map((c, i) => (
              <div key={i} className="card" style={{ padding: 20, display: 'flex', alignItems: 'center', gap: 16 }}>
                <div style={{ width: 44, height: 44, borderRadius: 10, background: 'var(--ink-50)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: c.color, flexShrink: 0 }}>
                  <Icon name={c.icon} size={20} />
                </div>
                <div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{c.label}</div>
                  <div className="mono" style={{ fontSize: 20, fontWeight: 700, marginTop: 3, color: c.color }}>{c.value}</div>
                </div>
              </div>
            ))
        }
      </div>

      {!loading && data && (
        <div style={{ display: 'grid', gridTemplateColumns: '1.65fr 1fr', gap: 20, marginTop: 20 }}>
          <SalesChart data={data.dayData} loading={loading} />
          <StoreLeaderboard stores={data.topStores} loading={loading} period={period} />
        </div>
      )}
    </>
  );
}

// ─── Comparativo ─────────────────────────────────────────────────────────────
const COMP_PRIMARY     = '#0B69C7';
const COMP_MONTHS      = ['janeiro','fevereiro','março','abril','maio','junho','julho','agosto','setembro','outubro','novembro','dezembro'];
const COMP_MEDAL_BAR   = ['#FFD700','#C0C0C0','#CD7F32'];
const COMP_MEDAL_BG    = ['rgba(255,215,0,0.10)','rgba(192,192,192,0.10)','rgba(205,127,50,0.10)'];
const COMP_MEDAL_BRD   = ['rgba(255,215,0,0.40)','rgba(192,192,192,0.40)','rgba(205,127,50,0.40)'];
const COMP_MEDAL_PCT   = ['#B8860B','#666','#8B4513'];

const compFmtCompact = (v) => {
  if (v >= 1e6) return `R$ ${(v/1e6).toFixed(1).replace('.',',')}M`;
  if (v >= 1e3) return `R$ ${(v/1e3).toFixed(1).replace('.',',')}k`;
  return `R$ ${Math.round(v)}`;
};
const compFmtFull = (v) => BRL(v);
const compProfit  = (c) => c.totalPurchase === 0 ? 100 : parseFloat(((c.totalSale - c.totalPurchase) / c.totalPurchase * 100).toFixed(1));

function compPrevDates(period, customStart, customEnd) {
  const fmt = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  // Subtrai 1 mês mantendo o mesmo dia — igual ao mobile (period.subtract(1, "month"))
  const subMonth = (dateStr) => {
    const [y, m, d] = dateStr.split('-').map(Number);
    const dt = new Date(y, m - 2, d); // m-2 = 0-indexed mês anterior
    // Se o dia extrapolou (ex: 31 de março → fevereiro não tem 31), usa o último dia do mês
    if (dt.getMonth() !== (m - 2 + 12) % 12) dt.setDate(0);
    return fmt(dt);
  };

  const today = SF.today();
  const startStr = (() => {
    if (period === 'hoje')   return today;
    if (period === '7d')     return SF.subtractDays(6);
    if (period === '30d')    return SF.subtractDays(29);
    if (period === 'mtd')    return SF.startOfMonth();
    if (period === 'custom') return customStart ?? today;
    return today;
  })();

  return { s: subMonth(startStr), e: subMonth(customEnd ?? today) };
}

function compPeriodLabel(startStr, endStr) {
  if (!startStr) return '';
  const [sy, sm, sd] = startStr.split('-').map(Number);
  const [ey, em, ed] = endStr.split('-').map(Number);
  const smName = COMP_MONTHS[sm-1], emName = COMP_MONTHS[em-1];
  if (startStr === endStr) return `${sd} de ${smName} de ${sy}`;
  if (sy === ey && sm === em) return `${sd} a ${ed} de ${smName} de ${sy}`;
  return `${sd} de ${smName} a ${ed} de ${emName} de ${ey}`;
}

function compCompLabel(period, customStart, customEnd) {
  const prev = compPrevDates(period, customStart, customEnd);
  if (!prev) return '';
  return `comparando com ${compPeriodLabel(prev.s, prev.e)}`;
}

function buildCompFromStores(stores) {
  return (stores ?? []).map(store => {
    const clsMap = {};
    for (const [name, g] of Object.entries(store.salesByClass ?? {})) {
      clsMap[name] = { className: name, totalSale: g.total ?? 0, totalPurchase: 0 };
    }
    for (const [, c] of Object.entries(store.purchases?.classifications ?? {})) {
      const n = c.className; if (!n) continue;
      clsMap[n] = clsMap[n] ?? { className: n, totalSale: 0, totalPurchase: 0 };
      clsMap[n].totalPurchase += c.total ?? 0;
    }
    return { storeId: store.storeId, storeName: store.storeName, totalSale: store.total ?? 0, totalPurchase: safeN(store.purchases?.total), classification: Object.values(clsMap) };
  }).filter(c => c.totalSale > 0 || c.totalPurchase > 0);
}

function buildCompFromDocs(docs, allowedStores, storeFilter) {
  if (!docs?.length) return [];
  const allowed   = (allowedStores == null || typeof allowedStores === 'boolean') ? null : allowedStores;
  const hasFilter = storeFilter?.length > 0;
  const storeMap  = {};
  for (const { data } of docs) {
    if (!data?.stores) continue;
    for (const [key, s] of Object.entries(data.stores)) {
      const sid = SF.parseStoreKey(key);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      if (!storeMap[sid]) storeMap[sid] = { storeId: sid, storeName: '', totalSale: 0, totalPurchase: 0, clsMap: {}, purMap: {} };
      const acc = storeMap[sid];
      if (!acc.storeName && s.storeName) acc.storeName = s.storeName;
      acc.totalSale     += safeN(s.total);
      // Compras reais (entrada de mercadoria), igual à tela de Compras: soma das
      // classes com fallback para purchases.total. Antes usava s.cost (CMV = custo
      // do que foi VENDIDO), o que não batia com a view de Compras.
      const _purTotal = Object.values(s.purchases?.classifications ?? {}).reduce((a, c) => a + safeN(c.total), 0);
      acc.totalPurchase += _purTotal > 0 ? _purTotal : safeN(s.purchases?.total);
      for (const [, g] of Object.entries(s.salesByClass ?? {})) {
        const name = g.className; if (!name) continue;
        acc.clsMap[name] = (acc.clsMap[name] ?? 0) + safeN(g.total);
      }
      for (const [, c] of Object.entries(s.purchases?.classifications ?? {})) {
        if (!c.className) continue;
        acc.purMap[c.className] = (acc.purMap[c.className] ?? 0) + safeN(c.total);
      }
    }
  }
  return Object.values(storeMap)
    .filter(s => s.totalSale > 0 || s.totalPurchase > 0)
    .map(s => ({
      storeId: s.storeId, storeName: s.storeName,
      totalSale: s.totalSale, totalPurchase: s.totalPurchase,
      classification: [...new Set([...Object.keys(s.clsMap), ...Object.keys(s.purMap)])].map(name => ({
        className: name, totalSale: s.clsMap[name] ?? 0, totalPurchase: s.purMap[name] ?? 0,
      })),
    }));
}

function sumComps(comps) {
  if (!comps.length) return null;
  const clsMap = {};
  for (const c of comps) {
    for (const cls of c.classification) {
      clsMap[cls.className] = clsMap[cls.className] ?? { className: cls.className, totalSale: 0, totalPurchase: 0 };
      clsMap[cls.className].totalSale     += cls.totalSale;
      clsMap[cls.className].totalPurchase += cls.totalPurchase;
    }
  }
  return {
    storeId: 0, storeName: 'Visão Geral',
    totalSale: comps.reduce((s, c) => s + c.totalSale, 0),
    totalPurchase: comps.reduce((s, c) => s + c.totalPurchase, 0),
    classification: Object.values(clsMap),
  };
}

// ── Trend chip ────────────────────────────────────────────────────────────────
function CompTrend({ current, previous, isPoints, invertColor }) {
  if (previous == null || previous === 0) return null;
  const delta = isPoints ? current - previous : ((current - previous) / Math.abs(previous)) * 100;
  const up    = delta >= 0;
  const good  = invertColor ? !up : up;
  return (
    <span style={{ fontSize: 10, fontWeight: 700, color: good ? '#22C55E' : '#EF4444', marginLeft: 4 }}>
      {up ? '▲' : '▼'} {Math.abs(delta).toFixed(1)}%
    </span>
  );
}

// ── MetricCard ────────────────────────────────────────────────────────────────
function CompMetricCard({ label, value, prevValue, isPercent, isActive, invertColor, onClick, primary }) {
  const fmtValue = isPercent ? `${value}%` : BRL(value);
  const hasTrend = prevValue != null && prevValue !== 0;
  const delta    = hasTrend
    ? (isPercent ? value - prevValue : ((value - prevValue) / Math.abs(prevValue)) * 100)
    : null;
  const up   = delta !== null && delta >= 0;
  const good = invertColor ? !up : up;

  return (
    <div
      onClick={onClick}
      className="card"
      style={{
        flex: 1, padding: 20, cursor: onClick ? 'pointer' : 'default',
        position: 'relative', overflow: 'hidden',
        background: primary ? 'linear-gradient(160deg, #1a4789 0%, #0c2340 100%)' : 'var(--white)',
        color: primary ? '#ffffff' : 'var(--ink-900)',
        border: isActive
          ? `2px solid ${primary ? '#4a90d9' : COMP_PRIMARY}`
          : primary ? '1px solid #1e5099' : '1px solid var(--ink-200)',
        transition: 'border-color .15s',
      }}
    >
      <div style={{
        fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase',
        color: primary ? 'rgba(255,255,255,0.7)' : 'var(--ink-500)',
      }}>{label}</div>

      <div className="mono" style={{
        fontSize: fmtValue.length > 14 ? 22 : 28, fontWeight: 700, marginTop: 8, letterSpacing: '-0.015em',
        color: primary ? '#ffffff' : 'var(--ink-900)',
        whiteSpace: 'nowrap', overflow: 'hidden',
      }}>{fmtValue}</div>

      <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        {delta !== null ? (
          primary ? (
            <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 12, fontWeight: 600,
              color: good ? '#4ADE80' : '#F87171', fontFeatureSettings: '"tnum"' }}>
              <Icon name={up ? 'arrow-up' : 'arrow-down'} size={11} strokeWidth={2.5} />
              {Math.abs(delta).toFixed(1)}% vs período anterior
            </div>
          ) : (
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 3,
              fontSize: 12, fontWeight: 600,
              color: good ? 'var(--positive)' : 'var(--danger)',
              background: good ? 'var(--positive-bg)' : 'var(--danger-bg)',
              padding: '3px 7px', borderRadius: 999,
              fontFeatureSettings: '"tnum"',
            }}>
              <Icon name={up ? 'arrow-up' : 'arrow-down'} size={11} strokeWidth={2.5} />
              {Math.abs(delta).toLocaleString('pt-BR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}%
            </span>
          )
        ) : null}
      </div>
    </div>
  );
}

// ── DetailPanel ───────────────────────────────────────────────────────────────
const COMP_DETAIL_DESC = {
  vendas:  'Faturamento total de vendas realizadas no período selecionado.',
  compras: 'Total de compras (entrada de mercadoria) no período selecionado.',
  margem:  'Ex: margem de 40% significa que para cada R$ 1,00 comprado, vendeu-se R$ 1,40.',
};

function CompDetailPanel({ type, comp, prev, currentLabel, prevLabel, profit, prevProfit }) {
  const rows = React.useMemo(() => {
    if (type === 'vendas') return [
      { label: 'Período atual', sub: currentLabel, value: compFmtFull(comp.totalSale), current: true },
      ...(prev ? [{ label: 'Período anterior', sub: prevLabel, value: compFmtFull(prev.totalSale), current: false }] : []),
    ];
    if (type === 'compras') return [
      { label: 'Período atual', sub: currentLabel, value: compFmtFull(comp.totalPurchase), current: true },
      ...(prev ? [{ label: 'Período anterior', sub: prevLabel, value: compFmtFull(prev.totalPurchase), current: false }] : []),
    ];
    return [
      { label: 'Período atual', sub: currentLabel, value: `${profit}%`, current: true },
      { label: 'Venda',   value: compFmtFull(comp.totalSale),     current: true,  isSub: true },
      { label: 'Compra',  value: compFmtFull(comp.totalPurchase), current: true,  isSub: true },
      ...(prevProfit != null ? [
        { label: 'Período anterior', sub: prevLabel, value: `${prevProfit}%`, current: false },
        { label: 'Venda',   value: compFmtFull(prev.totalSale),     current: false, isSub: true },
        { label: 'Compra',  value: compFmtFull(prev.totalPurchase), current: false, isSub: true },
      ] : []),
    ];
  }, [type, comp, prev, currentLabel, prevLabel, profit, prevProfit]);

  return (
    <div style={{ background: '#F9FBFF', borderRadius: 12, border: '1px solid #DDEAFF', padding: 14, marginBottom: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
      <div style={{ fontSize: 12, color: '#666', lineHeight: '17px', marginBottom: 4 }}>{COMP_DETAIL_DESC[type]}</div>
      <div style={{ height: 1, background: '#E8EEFF', marginBottom: 4 }} />
      {rows.map((row, i) => (
        <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingLeft: row.isSub ? 10 : 0, marginTop: row.isSub ? -2 : 0 }}>
          <div style={{ flex: 1, marginRight: 8 }}>
            <div style={{ fontSize: row.isSub ? 11 : 12, color: row.isSub ? '#BBB' : (row.current && !row.isSub ? '#555' : '#888'), fontWeight: row.current && !row.isSub ? 600 : 400 }}>
              {row.label}
            </div>
            {row.sub && <div style={{ fontSize: 10, color: '#AAA', marginTop: 1 }}>{row.sub}</div>}
          </div>
          <div style={{ fontSize: row.isSub ? 11 : 13, fontWeight: row.isSub ? 500 : 800, color: row.current ? '#222' : '#AAA' }}>
            {row.value}
          </div>
        </div>
      ))}
    </div>
  );
}

// ── ClassCard ─────────────────────────────────────────────────────────────────
function CompClassCard({ cls, index, totalSale, prevCls, prevTotalSale, currentLabel, prevLabel }) {
  const [expanded, setExpanded] = React.useState(false);
  const isTop3     = index < 3;
  const barColor   = isTop3 ? COMP_MEDAL_BAR[index]  : 'transparent';
  const cardBg     = isTop3 ? COMP_MEDAL_BG[index]   : 'var(--white)';
  const cardBorder = isTop3 ? COMP_MEDAL_BRD[index]  : '#EEF2FF';
  const pctColor   = isTop3 ? COMP_MEDAL_PCT[index]  : COMP_PRIMARY;

  const pct        = totalSale > 0 ? (cls.totalSale / totalSale) * 100 : 0;
  const hasPrevData = prevCls != null && (prevCls.totalSale ?? 0) > 0;
  const prevPct    = hasPrevData && prevTotalSale > 0 ? (prevCls.totalSale / prevTotalSale) * 100 : undefined;
  const margin     = cls.totalPurchase > 0 ? ((cls.totalSale - cls.totalPurchase) / cls.totalPurchase) * 100 : null;
  const prevMargin = hasPrevData && prevCls.totalPurchase > 0 ? ((prevCls.totalSale - prevCls.totalPurchase) / prevCls.totalPurchase) * 100 : undefined;

  return (
    <div style={{ display: 'flex', alignItems: 'stretch', gap: 0 }}>
      <div style={{ width: 4, borderRadius: 4, background: barColor, flexShrink: 0, marginRight: 8 }} />
      <div onClick={() => setExpanded(v => !v)} style={{
        flex: 1, borderRadius: 12, padding: 12, border: `1px solid ${expanded ? COMP_PRIMARY : cardBorder}`,
        background: cardBg, cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 8,
        overflow: 'hidden',
      }}>
        {/* Header */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', minWidth: 0, height: 20 }}>
          <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', flex: 1, minWidth: 0, marginRight: 8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', lineHeight: '20px' }}>
            {cls.className}
          </span>
          <div style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>
            <CompTrend current={pct} previous={prevPct} isPoints />
            <span style={{ fontSize: 14, fontWeight: 800, color: pctColor, marginLeft: 4, lineHeight: '20px' }}>{pct.toFixed(1)}%</span>
          </div>
        </div>
        {/* Progress bar */}
        <div style={{ height: 4, background: 'var(--ink-100)', borderRadius: 2, overflow: 'hidden', flexShrink: 0 }}>
          <div style={{ height: '100%', width: `${Math.min(pct, 100)}%`, background: COMP_PRIMARY, borderRadius: 2 }} />
        </div>
        {/* Compra / Venda */}
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, height: 16, alignItems: 'center' }}>
          <span style={{ fontSize: 11, color: 'var(--ink-500)', whiteSpace: 'nowrap', lineHeight: '16px' }}>Compra {compFmtCompact(cls.totalPurchase)}</span>
          <span style={{ fontSize: 11, color: 'var(--ink-500)', whiteSpace: 'nowrap', lineHeight: '16px' }}>Venda {compFmtCompact(cls.totalSale)}</span>
        </div>
        {/* Expanded */}
        {expanded && (
          <div style={{ borderTop: '1px solid var(--ink-100)', paddingTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>
            <div style={{ fontSize: 12, color: 'var(--ink-600)', lineHeight: '18px' }}>
              <strong style={{ fontWeight: 800, color: 'var(--ink-900)' }}>{cls.className}</strong>
              {' representou '}
              <strong style={{ fontWeight: 800, color: 'var(--ink-900)' }}>{pct.toFixed(1)}%</strong>
              {' do faturamento total de vendas em '}
              <strong style={{ fontWeight: 800, color: 'var(--ink-900)' }}>{currentLabel}</strong>.
            </div>
            {margin !== null && (
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>Margem bruta</div>
                  <div style={{ fontSize: 10, color: 'var(--ink-300)', marginTop: 1 }}>(Venda − Compra) ÷ Compra</div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <div style={{ fontSize: 15, fontWeight: 800, color: 'var(--ink-900)', fontFeatureSettings: '"tnum"' }}>{margin.toFixed(1)}%</div>
                  {prevMargin !== undefined && (
                    <div style={{ fontSize: 10, color: 'var(--ink-400)', marginTop: 1 }}>anterior {prevMargin.toFixed(1)}%</div>
                  )}
                </div>
              </div>
            )}
            {prevPct !== undefined && (
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>Mesmo período anterior</div>
                  <div style={{ fontSize: 10, color: 'var(--ink-300)', marginTop: 1 }}>{prevLabel}</div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-700)' }}>{compFmtFull(prevCls.totalSale)}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-400)' }}>{prevPct.toFixed(1)}% das vendas</div>
                </div>
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ── CompPage ──────────────────────────────────────────────────────────────────
function CompPage({ comp, prev, currentLabel, prevLabel, compLabel }) {
  const [activeDetail, setActiveDetail] = React.useState(null);
  const toggleDetail = (type) => setActiveDetail(d => d === type ? null : type);
  const profit     = compProfit(comp);
  const prevProfit = prev ? compProfit(prev) : undefined;

  const sortedClasses = React.useMemo(() => {
    const map = {};
    for (const cls of comp.classification) {
      map[cls.className] = map[cls.className]
        ? { ...map[cls.className], totalSale: map[cls.className].totalSale + cls.totalSale, totalPurchase: map[cls.className].totalPurchase + cls.totalPurchase }
        : { ...cls };
    }
    return Object.values(map).sort((a, b) => b.totalSale - a.totalSale);
  }, [comp.classification]);

  const classTotalSale = React.useMemo(() => sortedClasses.reduce((s, c) => s + c.totalSale, 0), [sortedClasses]);
  const prevClsTotal   = React.useMemo(() => prev?.classification.reduce((s, c) => s + c.totalSale, 0) ?? 0, [prev]);

  return (
    <div>
      {/* Comparison label */}
      <div style={{ fontSize: 11, color: '#BBB', textAlign: 'center', marginBottom: 14 }}>{compLabel}</div>

      {/* 3 metric cards */}
      <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
        <CompMetricCard label="Vendas"  value={comp.totalSale}     prevValue={prev?.totalSale}     isActive={activeDetail === 'vendas'}  onClick={() => toggleDetail('vendas')} primary />
        <CompMetricCard label="Compras" value={comp.totalPurchase} prevValue={prev?.totalPurchase} isActive={activeDetail === 'compras'} invertColor onClick={() => toggleDetail('compras')} />
        <CompMetricCard label="Margem"  value={profit}             prevValue={prevProfit}          isPercent isActive={activeDetail === 'margem'}  onClick={() => toggleDetail('margem')} />
      </div>

      {/* Detail panel */}
      {activeDetail && (
        <CompDetailPanel type={activeDetail} comp={comp} prev={prev} currentLabel={currentLabel} prevLabel={prevLabel} profit={profit} prevProfit={prevProfit} />
      )}

      {/* Classes */}
      {sortedClasses.length > 0 && (
        <>
          <div style={{ fontSize: 14, fontWeight: 700, color: '#444', marginBottom: 8, marginTop: 8 }}>Vendas realizadas por classe</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {sortedClasses.map((cls, i) => (
              <CompClassCard
                key={cls.className}
                cls={cls}
                index={i}
                totalSale={classTotalSale > 0 ? classTotalSale : comp.totalSale}
                prevCls={prev?.classification.find(c => c.className === cls.className)}
                prevTotalSale={prevClsTotal || prev?.totalSale}
                currentLabel={currentLabel}
                prevLabel={prevLabel}
              />
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ── CompClassHighlights ───────────────────────────────────────────────────────
function CompClassHighlights({ classItems }) {
  const [openIdx, setOpenIdx] = React.useState(null);

  const withDelta = React.useMemo(() => classItems
    .filter(c => (c.prevTotalSale ?? 0) > 0 && c.totalSale > 0)
    .map(c => ({
      ...c,
      delta:  ((c.totalSale - c.prevTotalSale) / c.prevTotalSale) * 100,
      // CMV invertido: margem sobre vendas = (vendas - compras) / vendas
      margin: c.totalPurchase > 0 && c.totalSale > 0 ? ((c.totalSale - c.totalPurchase) / c.totalSale) * 100 : null,
      prevMargin: (c.prevTotalPurchase ?? 0) > 0 && (c.prevTotalSale ?? 0) > 0 ? ((c.prevTotalSale - c.prevTotalPurchase) / c.prevTotalSale) * 100 : null,
    })), [classItems]);

  if (withDelta.length < 2) return null;

  const sorted      = [...withDelta].sort((a, b) => b.delta - a.delta);
  const topGrowth   = sorted[0];
  const topMargin   = [...withDelta].filter(c => c.margin !== null).sort((a, b) => b.margin - a.margin)[0];

  const spots = [
    topGrowth?.delta > 0 ? {
      label: 'Maior Alta', sub: 'em vendas vs período anterior',
      cls: topGrowth, valueStr: `+${topGrowth.delta.toFixed(1)}%`,
      color: 'var(--positive)', bg: 'var(--positive-bg)', border: '#bbf7d0',
      detail: {
        title: 'Vendas da classe no período',
        rows: [
          { label: 'Período atual',    value: BRL(topGrowth.totalSale),    mono: true },
          { label: 'Período anterior', value: BRL(topGrowth.prevTotalSale), mono: true },
          { label: 'Crescimento',      value: `+${BRL(topGrowth.totalSale - topGrowth.prevTotalSale)}`, mono: true, highlight: 'var(--positive)' },
        ],
      },
    } : null,
    topMargin ? {
      label: 'Melhor Margem', sub: 'margem bruta da classe no período',
      cls: topMargin, valueStr: `${Math.round(topMargin.margin)}%`,
      color: 'var(--brand-500)', bg: 'var(--brand-50)', border: 'var(--brand-200)',
      extra: topMargin.prevMargin != null
        ? (topMargin.margin - topMargin.prevMargin >= 0 ? `↑ ${(topMargin.margin - topMargin.prevMargin).toFixed(1)}%` : `↓ ${Math.abs(topMargin.margin - topMargin.prevMargin).toFixed(1)}%`) + ' vs anterior'
        : null,
      detail: {
        title: 'Margem sobre vendas · (Vendas − Compras) / Vendas',
        rows: [
          { label: 'Vendas (atual)',   value: BRL(topMargin.totalSale),    mono: true },
          { label: 'Compras (atual)',  value: BRL(topMargin.totalPurchase), mono: true },
          { label: 'Margem atual',     value: `${topMargin.margin.toFixed(1)}%`, mono: true, highlight: 'var(--brand-500)' },
          ...(topMargin.prevMargin != null ? [
            { label: 'Margem anterior', value: `${topMargin.prevMargin.toFixed(1)}%`, mono: true },
            { label: 'Diferença',
              value: (topMargin.margin - topMargin.prevMargin >= 0 ? '+' : '') + (topMargin.margin - topMargin.prevMargin).toFixed(1) + '%',
              mono: true,
              highlight: topMargin.margin >= topMargin.prevMargin ? 'var(--positive)' : 'var(--danger)',
            },
          ] : []),
        ],
      },
    } : null,
  ].filter(Boolean);

  if (!spots.length) return null;

  return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 14 }}>Destaques do Período</div>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${spots.length}, 1fr)`, gap: 12 }}>
        {spots.map(({ label, sub, cls, valueStr, color, bg, border, extra, detail }, idx) => {
          const isOpen = openIdx === idx;
          return (
            <div
              key={label}
              onClick={() => setOpenIdx(isOpen ? null : idx)}
              style={{ borderRadius: 12, padding: '14px 16px', background: bg, border: `1px solid ${isOpen ? color : border}`, cursor: 'pointer', transition: 'border-color .15s' }}
            >
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color, textTransform: 'uppercase', letterSpacing: '0.07em', marginBottom: 6 }}>{label}</div>
                <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ flexShrink: 0, marginTop: 1, transition: 'transform .15s', transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)', color }}>
                  <path d="M2 3.5L5 6.5L8 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </div>
              <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 6, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{cls.className}</div>
              <div className="mono" style={{ fontSize: 22, fontWeight: 800, color, letterSpacing: '-0.01em' }}>{valueStr}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 4 }}>{extra ?? sub}</div>

              {isOpen && (
                <div style={{ marginTop: 12, paddingTop: 12, borderTop: `1px solid ${border}` }}>
                  <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 8 }}>{detail.title}</div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                    {detail.rows.map(({ label: rl, value: rv, mono, highlight }) => (
                      <div key={rl} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 }}>
                        <span style={{ fontSize: 11, color: 'var(--ink-500)' }}>{rl}</span>
                        <span className={mono ? 'mono' : ''} style={{ fontSize: 11, fontWeight: 700, color: highlight ?? 'var(--ink-800)', whiteSpace: 'nowrap' }}>{rv}</span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── CompStoresPanel ───────────────────────────────────────────────────────────
function CompStoresPanel({ stores, selectedStoreIds = [], onSelectStores, chartRef }) {
  if (!stores || stores.length === 0) return null;

  const sel = selectedStoreIds ?? [];

  const handleView = (storeId) => {
    if (!onSelectStores) return;
    onSelectStores([storeId]);
    setTimeout(() => {
      const el = chartRef?.current;
      if (!el) return;
      const top = el.getBoundingClientRect().top + window.scrollY - 80;
      window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
    }, 50);
  };

  const handleAdd = (storeId) => {
    if (!onSelectStores) return;
    onSelectStores([...sel, storeId]);
  };

  const handleClear = () => {
    if (!onSelectStores) return;
    onSelectStores([]);
  };

  const anySelected = sel.length > 0;

  return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Desempenho por Filial</div>
        {anySelected && (
          <button onClick={handleClear} style={{
            fontSize: 11, fontWeight: 600, color: 'var(--brand-600)', background: 'var(--brand-50)',
            border: '1px solid var(--brand-200)', borderRadius: 999, padding: '3px 10px',
            cursor: 'pointer', fontFamily: 'inherit',
          }}>Mostrar todas</button>
        )}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 12 }}>
        {stores.map(store => {
          const delta      = store.prevTotalSale > 0 ? ((store.totalSale - store.prevTotalSale) / store.prevTotalSale) * 100 : null;
          const margin     = store.totalPurchase > 0 ? ((store.totalSale - store.totalPurchase) / store.totalPurchase) * 100 : null;
          const prevMargin = store.prevTotalPurchase > 0 ? ((store.prevTotalSale - store.prevTotalPurchase) / store.prevTotalPurchase) * 100 : null;
          const mDelta     = margin != null && prevMargin != null ? margin - prevMargin : null;
          const up         = delta !== null && delta >= 0;
          const mUp        = mDelta !== null && mDelta >= 0;
          const isSelected = sel.includes(store.storeId);

          const DeltaBadge = ({ val, isUp }) => (
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 3,
              fontSize: 11, fontWeight: 600,
              color: isUp ? 'var(--positive)' : 'var(--danger)',
              background: isUp ? 'var(--positive-bg)' : 'var(--danger-bg)',
              padding: '2px 6px', borderRadius: 999, fontFeatureSettings: '"tnum"',
            }}>
              <Icon name={isUp ? 'arrow-up' : 'arrow-down'} size={10} strokeWidth={2.5} />
              {Math.abs(val).toFixed(1)}%
            </span>
          );

          return (
            <div key={store.storeId} style={{
              borderRadius: 12, padding: '14px 16px',
              background: isSelected ? 'var(--brand-50)' : 'var(--ink-50)',
              border: `1px solid ${isSelected ? 'var(--brand-400)' : 'var(--ink-200)'}`,
              transition: 'border-color .15s, background .15s',
            }}>
              <div style={{ fontSize: 11, fontWeight: 600, color: isSelected ? 'var(--brand-600)' : 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 10, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {store.storeName || store.storeId}
              </div>

              {/* Linha Vendas */}
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
                <div>
                  <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 2 }}>Vendas</div>
                  <div className="mono" style={{ fontSize: 18, fontWeight: 800, color: 'var(--ink-900)', letterSpacing: '-0.01em' }}>{compFmtCompact(store.totalSale)}</div>
                </div>
                {delta !== null && <DeltaBadge val={delta} isUp={up} />}
              </div>

              <div style={{ height: 1, background: 'var(--ink-200)', margin: '6px 0' }} />

              {/* Linha Margem */}
              {margin !== null && (
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
                  <div>
                    <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 2 }}>Margem</div>
                    <div className="mono" style={{ fontSize: 18, fontWeight: 800, color: 'var(--ink-900)', letterSpacing: '-0.01em' }}>{Math.round(margin)}%</div>
                  </div>
                  {mDelta !== null && <DeltaBadge val={mDelta} isUp={mUp} />}
                </div>
              )}

              {/* Cápsulas de ação */}
              {onSelectStores && (
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                  {!isSelected ? (
                    <>
                      <button onClick={() => handleView(store.storeId)} style={{
                        fontSize: 11, fontWeight: 600, color: 'var(--brand-600)',
                        background: 'var(--brand-50)', border: '1px solid var(--brand-200)',
                        borderRadius: 999, padding: '3px 10px', cursor: 'pointer', fontFamily: 'inherit',
                      }}>Ver no gráfico</button>
                      {anySelected && (
                        <button onClick={() => handleAdd(store.storeId)} style={{
                          fontSize: 11, fontWeight: 700, color: 'var(--white)',
                          background: 'var(--brand-500)', border: 'none',
                          borderRadius: 999, padding: '4px 12px', cursor: 'pointer', fontFamily: 'inherit',
                          display: 'inline-flex', alignItems: 'center', gap: 4,
                        }}>
                          <span style={{ fontSize: 14, lineHeight: 1 }}>+</span> Adicionar
                        </button>
                      )}
                    </>
                  ) : (
                    <button onClick={() => onSelectStores(sel.filter(id => id !== store.storeId))} style={{
                      fontSize: 11, fontWeight: 600, color: 'var(--brand-600)',
                      background: 'var(--surface)', border: '1px solid var(--brand-400)',
                      borderRadius: 999, padding: '3px 10px', cursor: 'pointer', fontFamily: 'inherit',
                    }}>✓ Selecionada</button>
                  )}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── buildSellersFromDocs ──────────────────────────────────────────────────────
function buildSellersFromDocs(docs, allowedStores, storeFilter) {
  const allowed   = (allowedStores == null || typeof allowedStores === 'boolean') ? null : allowedStores;
  const hasFilter = storeFilter?.length > 0;
  const map = {};
  for (const { data: d } of (docs ?? [])) {
    if (!d?.stores) continue;
    for (const [key, s] of Object.entries(d.stores)) {
      const sid = SF.parseStoreKey(key);
      if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
      if (hasFilter && !storeFilter.includes(sid)) continue;
      const storeName = s.storeName ?? '';
      for (const [, seller] of Object.entries(s.sellers ?? {})) {
        const name = seller.sellerName || `Vendedor ${seller.sellerId ?? '?'}`;
        if (!map[name]) map[name] = { sellerName: name, total: 0, sales: 0, client: 0, storeNames: new Set() };
        map[name].total  += safeN(seller.total);
        map[name].sales  += safeN(seller.sales || seller.salesCount || seller.client);  
        map[name].client += safeN(seller.client);
        if (storeName) map[name].storeNames.add(storeName);
      }
    }
  }
  return Object.values(map).filter(s => s.total > 0).sort((a, b) => b.total - a.total).map(s => ({
    ...s,
    storeLabel: s.storeNames.size === 1 ? [...s.storeNames][0] : s.storeNames.size > 1 ? 'Múltiplas filiais' : '',
  }));
}

// ── CompSellersPanel ──────────────────────────────────────────────────────────
function CompSellerCard({ seller, rank, prev }) {
  const [expanded, setExpanded] = React.useState(false);
  const isMedal   = rank < 3;
  const barColor  = isMedal ? G_MEDAL_DOT[rank] : G_MONTH;
  const cardBg    = isMedal ? G_MEDAL_BG[rank]  : 'var(--white)';
  const cardBorder = isMedal ? G_MEDAL_BRD[rank] : 'rgba(0,0,0,0.08)';

  const ticket     = seller.sales > 0 ? seller.total / seller.sales : 0;
  const prevTicket = prev?.sales > 0  ? prev.total  / prev.sales    : 0;

  const deltaVendas  = prev?.total  > 0 ? ((seller.total  - prev.total)  / prev.total)  * 100 : null;
  const deltaTicket  = prevTicket   > 0 ? ((ticket        - prevTicket)  / prevTicket)  * 100 : null;

  const DeltaBadge = ({ val }) => {
    const up = val >= 0;
    return (
      <span style={{
        display: 'inline-flex', alignItems: 'center', gap: 3,
        fontSize: 11, fontWeight: 600,
        color: up ? 'var(--positive)' : 'var(--danger)',
        background: up ? 'var(--positive-bg)' : 'var(--danger-bg)',
        padding: '2px 8px', borderRadius: 999, fontFeatureSettings: '"tnum"',
      }}>
        <Icon name={up ? 'arrow-up' : 'arrow-down'} size={10} strokeWidth={2.5} />
        {Math.abs(val).toFixed(1)}%
      </span>
    );
  };

  return (
    <div style={{ display: 'flex', alignItems: 'stretch', gap: 8, marginBottom: 10 }}>
      <div style={{ width: 4, borderRadius: 4, background: barColor, flexShrink: 0 }} />
      <div
        onClick={() => setExpanded(p => !p)}
        style={{ flex: 1, borderRadius: 12, padding: '12px 14px', background: cardBg, border: `0.5px solid ${cardBorder}`, cursor: 'pointer' }}
      >
        {/* Cabeçalho do card */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 26, height: 26, borderRadius: '50%', background: barColor + '30', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <span style={{ fontSize: 12, fontWeight: 700, color: barColor }}>{rank + 1}</span>
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{seller.sellerName}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 1, display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
              {seller.storeLabel && (
                <span style={{ background: 'var(--ink-100)', borderRadius: 4, padding: '1px 6px', fontSize: 10, fontWeight: 600, color: 'var(--ink-600)' }}>
                  {seller.storeLabel}
                </span>
              )}
              <span>{seller.sales > 0 ? `${seller.sales.toLocaleString('pt-BR')} vendas no período` : 'sem vendas registradas'}</span>
            </div>
          </div>
          <div style={{ textAlign: 'right', flexShrink: 0 }}>
            <div style={{ fontSize: 18, fontWeight: 800, color: barColor, fontFeatureSettings: '"tnum"' }}>{BRL(seller.total)}</div>
            {deltaVendas !== null && <DeltaBadge val={deltaVendas} />}
          </div>
        </div>

        {/* Expandido */}
        {expanded && (
          <div style={{ marginTop: 12, paddingTop: 12, borderTop: `1px solid ${barColor}40`, display: 'flex', flexDirection: 'column', gap: 10 }}>

            {/* Vendas atual vs anterior */}
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div>
                <div style={{ fontSize: 12, color: 'var(--ink-500)', fontWeight: 600 }}>Vendas no período atual</div>
                {prev && <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>Período anterior: {BRL(prev.total)}</div>}
              </div>
              <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--ink-900)', fontFeatureSettings: '"tnum"' }}>{BRL(seller.total)}</div>
            </div>

            {/* Ticket médio atual vs anterior */}
            {ticket > 0 && (
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 12, color: 'var(--ink-500)', fontWeight: 600 }}>Ticket médio</div>
                  {prevTicket > 0 && <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>Período anterior: {BRL(prevTicket)}</div>}
                </div>
                <div style={{ textAlign: 'right' }}>
                  <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--ink-900)', fontFeatureSettings: '"tnum"' }}>{BRL(ticket)}</div>
                  {deltaTicket !== null && <DeltaBadge val={deltaTicket} />}
                </div>
              </div>
            )}

            {/* Número de vendas */}
            {seller.sales > 0 && (
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 12, color: 'var(--ink-500)', fontWeight: 600 }}>Número de vendas</div>
                  {prev?.sales > 0 && <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>Período anterior: {prev.sales.toLocaleString('pt-BR')} vendas</div>}
                </div>
                <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--ink-900)' }}>{seller.sales.toLocaleString('pt-BR')} <span style={{ fontSize: 12, fontWeight: 500 }}>vendas</span></div>
              </div>
            )}

          </div>
        )}
      </div>
    </div>
  );
}

function CompSellersPanel({ current, prev }) {
  if (!current?.length) return null;
  const prevMap = Object.fromEntries((prev ?? []).map(s => [s.sellerName, s]));
  return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 }}>
        <div>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Vendedores — período atual vs anterior</div>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 2 }}>Variação de vendas e ticket médio em relação ao período comparado · Filtre por filial para focar em uma loja</div>
        </div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 12, padding: '6px 10px', borderRadius: 8, background: 'var(--warning-bg)', border: '1px solid var(--warning)' }}>
        <span style={{ fontSize: 13 }}>⚠</span>
        <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--warning)' }}>Calculado com dias fechados (ontem para trás) O dia atual não é incluso.</span>
      </div>
      {current.map((s, i) => (
        <CompSellerCard key={s.sellerName} seller={s} rank={i} prev={prevMap[s.sellerName]} />
      ))}
    </div>
  );
}

// ── ComparativeView ───────────────────────────────────────────────────────────
function ComparativeView({ data, loading, user, period, customStart, customEnd, selectedStoreIds, onSelectStores }) {
  const [activeDetail, setActiveDetail] = React.useState(null);
  const [tab, setTab]                   = React.useState('classes');
  const chartRef = React.useRef(null);

  const allowed   = (user?.allowedStores == null || typeof user?.allowedStores === 'boolean') ? null : user.allowedStores;
  const hasFilter = selectedStoreIds?.length > 0;

  const todayStr   = SF.today();
  const includesTODAY = period === 'hoje' || (period === 'custom' && (customEnd ?? todayStr) >= todayStr);
  const [warnDismissed, setWarnDismissed] = React.useState(false);
  React.useEffect(() => setWarnDismissed(false), [includesTODAY]);

  // Período: termina ontem, exceto quando o usuário escolhe "Hoje" ou custom c/ data de hoje.
  // Para período custom todo no passado, respeita a data final escolhida (não "ontem" relativo a hoje).
  const compEnd   = includesTODAY
    ? todayStr
    : (period === 'custom' ? (customEnd ?? todayStr) : SF.subtractDays(1));
  const compStart = React.useMemo(() => {
    if (period === 'hoje')   return todayStr;
    if (period === '7d')     return includesTODAY ? SF.subtractDays(6) : SF.subtractDays(7);
    if (period === '30d')    return includesTODAY ? SF.subtractDays(29) : SF.subtractDays(30);
    if (period === 'mtd')    return SF.startOfMonth();
    if (period === 'custom') return customStart ?? todayStr;
    return SF.subtractDays(7);
  }, [period, customStart, includesTODAY, todayStr]);

  // Fetch dos docs do período fechado
  const { docs: compDocs, loading: compLoading } = useDashboardData(user, 'custom', compStart, compEnd);

  // Período anterior baseado nas datas fechadas
  const prevDates = React.useMemo(() => compPrevDates('custom', compStart, compEnd), [compStart, compEnd]);
  const { docs: prevDocs, loading: prevLoading } = useDashboardData(user, 'custom', prevDates?.s ?? null, prevDates?.e ?? null);

  // Comparativos do período atual — apenas dias fechados
  const currentComp = React.useMemo(
    () => sumComps(buildCompFromDocs(compDocs, user?.allowedStores, selectedStoreIds ?? [])),
    [compDocs, user?.allowedStores, selectedStoreIds]
  );

  // Comparativos do período anterior
  const prevComp = React.useMemo(
    () => sumComps(buildCompFromDocs(prevDocs, user?.allowedStores, selectedStoreIds ?? [])),
    [prevDocs, user?.allowedStores, selectedStoreIds]
  );

  // Gráfico: alinha período anterior pelo índice sobre os labels do período atual
  const chartData = React.useMemo(() => {
    if (!compDocs.length) return [];
    const fmtDateLabel = (dateStr) => {
      if (!dateStr) return '';
      const [, m, d] = dateStr.split('-').map(Number);
      return `${d} de ${COMP_MONTHS[m-1]}`;
    };
    const prevDailyArr = (prevDocs ?? [])
      .map(({ date, data: d }) => {
        let tot = 0, cost = 0;
        if (d?.stores) {
          for (const [key, s] of Object.entries(d.stores)) {
            const sid = SF.parseStoreKey(key);
            if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
            if (hasFilter && !selectedStoreIds.includes(sid)) continue;
            tot  += safeN(s.total);
            cost += safeN(s.cost);
          }
        }
        return { tot, cost, label: fmtDateLabel(date) };
      });
    return compDocs.map(({ date, data: d }, i) => {
      let v = 0, k = 0;
      if (d?.stores) {
        for (const [key, s] of Object.entries(d.stores)) {
          const sid = SF.parseStoreKey(key);
          if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
          if (hasFilter && !selectedStoreIds.includes(sid)) continue;
          v += safeN(s.total);
          k += safeN(s.cost);
        }
      }
      return {
        date, v, k, q: 0, p: 0, u: 0,
        l:     chartLabel(date, period),
        c:     prevDailyArr[i]?.tot   ?? 0,
        cp:    prevDailyArr[i]?.cost  ?? 0,
        prevL: prevDailyArr[i]?.label ?? '',
      };
    });
  }, [compDocs, prevDocs, allowed, hasFilter, selectedStoreIds, period]);

  // Mapa de totais do período anterior por classe
  const prevClassMap = React.useMemo(() => {
    const map = {};
    for (const c of prevComp?.classification ?? []) {
      map[c.className] = { totalSale: c.totalSale, totalPurchase: c.totalPurchase };
    }
    return map;
  }, [prevComp]);

  // Mapa de compras por classe do período atual (dos docs fechados)
  const curPurByClass = React.useMemo(() => {
    const map = {};
    for (const { data: d } of compDocs) {
      if (!d?.stores) continue;
      for (const [key, s] of Object.entries(d.stores)) {
        const sid = SF.parseStoreKey(key);
        if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
        if (hasFilter && !selectedStoreIds.includes(sid)) continue;
        for (const [, c] of Object.entries(s.purchases?.classifications ?? {})) {
          if (!c.className) continue;
          map[c.className] = (map[c.className] ?? 0) + safeN(c.total);
        }
      }
    }
    return map;
  }, [compDocs, allowed, hasFilter, selectedStoreIds]);

  // Labels baseados nas datas fechadas
  const currentLabel = compPeriodLabel(compStart, compEnd);
  const prevLabel    = prevDates ? compPeriodLabel(prevDates.s, prevDates.e) : '';
  // Label usa datas reais fechadas (não o period original, que inclui hoje)
  const cmpLabel     = prevDates ? `comparando com ${compPeriodLabel(prevDates.s, prevDates.e)}` : '';

  // Vendedores: período atual vs anterior
  const currentSellers = React.useMemo(
    () => buildSellersFromDocs(compDocs, user?.allowedStores, selectedStoreIds ?? []),
    [compDocs, user?.allowedStores, selectedStoreIds]
  );
  const prevSellers = React.useMemo(
    () => buildSellersFromDocs(prevDocs, user?.allowedStores, selectedStoreIds ?? []),
    [prevDocs, user?.allowedStores, selectedStoreIds]
  );

  const profit     = currentComp ? compProfit(currentComp) : 0;
  const prevProfit = prevComp    ? compProfit(prevComp)    : undefined;
  const toggleDetail = (type) => setActiveDetail(d => d === type ? null : type);

  // Classes e grupos com comparação
  const classItems = React.useMemo(() => {
    if (!currentComp) return [];
    const totalSale = currentComp.classification.reduce((s, c) => s + c.totalSale, 0) || currentComp.totalSale;
    const prevTotalSale = prevComp?.classification.reduce((s, c) => s + c.totalSale, 0) || prevComp?.totalSale || 0;
    const map = {};
    for (const c of currentComp.classification) {
      map[c.className] = map[c.className]
        ? { ...map[c.className], totalSale: map[c.className].totalSale + c.totalSale, totalPurchase: map[c.className].totalPurchase + c.totalPurchase }
        : { ...c };
    }
    return Object.values(map)
      .sort((a, b) => b.totalSale - a.totalSale)
      .map(c => ({
        ...c,
        totalPurchase: curPurByClass[c.className] ?? c.totalPurchase,
        pct: totalSale > 0 ? (c.totalSale / totalSale) * 100 : 0,
        prevPct: (() => { const p = prevClassMap[c.className]; return p && prevTotalSale > 0 ? (p.totalSale / prevTotalSale) * 100 : undefined; })(),
        prevTotalSale: prevClassMap[c.className]?.totalSale,
        prevTotalPurchase: prevClassMap[c.className]?.totalPurchase,
      }));
  }, [currentComp, prevComp, curPurByClass, prevClassMap]);

  // Grupos (via data.groupRanking)
  const groupItems = React.useMemo(() => {
    const groups = data?.groupRanking ?? [];
    const grandTotal = groups.reduce((s, g) => s + g.total, 0);
    const prevGrandTotal = prevComp?.totalSale ?? 0;
    return groups.map((g, i) => {
      const prevData = prevComp?.classification.find(c => c.className === g.name);
      return {
        className: g.name, totalSale: g.total, totalPurchase: 0,
        pct: grandTotal > 0 ? (g.total / grandTotal) * 100 : 0,
        prevPct: prevData && prevGrandTotal > 0 ? (prevData.totalSale / prevGrandTotal) * 100 : undefined,
        prevTotalSale: prevData?.totalSale,
      };
    });
  }, [data?.groupRanking, prevComp]);

  const items   = tab === 'classes' ? classItems : groupItems;
  const isLoading = loading || compLoading || prevLoading;

  const storeComparison = React.useMemo(() => {
    const currStores = buildCompFromDocs(compDocs, user?.allowedStores, selectedStoreIds ?? []);
    const prevStores = buildCompFromDocs(prevDocs, user?.allowedStores, selectedStoreIds ?? []);
    const prevMap    = Object.fromEntries(prevStores.map(s => [s.storeId, s]));
    return currStores.map(s => {
        const prev = prevMap[s.storeId];
        return {
          storeId:           s.storeId,
          storeName:         s.storeName ?? s.storeId,
          totalSale:         s.totalSale,
          totalPurchase:     s.totalPurchase,
          prevTotalSale:     prev?.totalSale     ?? 0,
          prevTotalPurchase: prev?.totalPurchase ?? 0,
        };
      });
  }, [data?.stores, prevDocs, user?.allowedStores, selectedStoreIds]);

  if (isLoading) return (
    <div className="card" style={{ padding: 40, textAlign: 'center' }}>
      <div style={{ width: 32, height: 32, border: `3px solid ${COMP_PRIMARY}`, borderTopColor: 'transparent', borderRadius: '50%', animation: 'sf-spin 0.8s linear infinite', margin: '0 auto 12px' }} />
      <style>{`@keyframes sf-spin { to { transform: rotate(360deg) } }`}</style>
      <div style={{ fontSize: 13, color: 'var(--ink-400)' }}>Carregando comparativo...</div>
    </div>
  );

  if (!currentComp) return <EmptyState text="Sem dados para este período." />;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
      {/* Label de comparação */}
      <div style={{ textAlign: 'center' }}>
        <span style={{ display: 'inline-block', fontSize: 11, fontWeight: 700, color: 'var(--brand-600, #2563EB)', background: 'var(--brand-50, #EFF6FF)', border: '1px solid var(--brand-200, #BFDBFE)', borderRadius: 20, padding: '3px 12px' }}>{cmpLabel}</span>
      </div>

      {/* Aviso: dia em andamento */}
      {includesTODAY && !warnDismissed && (
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
          padding: '10px 16px', borderRadius: 10,
          background: '#FFFBEB', border: '1px solid #FCD34D',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ fontSize: 15 }}>⚠️</span>
            <span style={{ fontSize: 13, color: '#92400E' }}>
              Você está comparando um dia em andamento com um dia fechado. Os valores de hoje podem não ser ideais para tomada de decisão.
            </span>
          </div>
          <button onClick={() => setWarnDismissed(true)} style={{
            fontSize: 12, fontWeight: 700, color: '#92400E',
            background: '#FDE68A', border: '1px solid #FCD34D',
            borderRadius: 999, padding: '4px 14px', cursor: 'pointer',
            fontFamily: 'inherit', flexShrink: 0,
          }}>OK</button>
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 20, alignItems: 'start' }}>

        {/* ── Coluna esquerda ── */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>

          {/* Cards Vendas + Compras + Margem */}
          <div style={{ display: 'flex', gap: 16 }}>
            <CompMetricCard label="Vendas"  value={currentComp.totalSale}     prevValue={prevComp?.totalSale}     isActive={activeDetail === 'vendas'}  onClick={() => toggleDetail('vendas')} primary />
            <CompMetricCard label="Compras" value={currentComp.totalPurchase} prevValue={prevComp?.totalPurchase} isActive={activeDetail === 'compras'} invertColor onClick={() => toggleDetail('compras')} />
            <CompMetricCard label="Margem"  value={profit}                    prevValue={prevProfit}              isPercent isActive={activeDetail === 'margem'}  onClick={() => toggleDetail('margem')} />
          </div>

          {/* Detail panel */}
          {activeDetail && currentComp && (
            <CompDetailPanel type={activeDetail} comp={currentComp} prev={prevComp} currentLabel={currentLabel} prevLabel={prevLabel} profit={profit} prevProfit={prevProfit} />
          )}

          {/* Panel Gráfico */}
          {chartData.length >= 2 && (
            <div ref={chartRef} className="card" style={{ padding: 24 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
                <div style={{ fontSize: 16, fontWeight: 700 }}>Vendas — período atual vs anterior</div>
                <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
                  <LegendDot color={COMP_PRIMARY} label={currentLabel} />
                  {prevLabel && <LegendDot color="var(--ink-500)" label={prevLabel} dashed />}
                </div>
              </div>
              <BigChart data={chartData} lineColor={COMP_PRIMARY} gradientId="comp-grad" tooltipLabel="Vendas" hideVariation comparativeMode />
            </div>
          )}

          {/* Destaques de Classes */}
          <CompClassHighlights classItems={classItems} />

          {/* Desempenho por Filial */}
          <CompStoresPanel stores={storeComparison} selectedStoreIds={selectedStoreIds} onSelectStores={onSelectStores} chartRef={chartRef} />

          {/* Vendedores */}
          <CompSellersPanel current={currentSellers} prev={prevSellers} />

        </div>

        {/* ── Coluna direita: ranking de classes/grupos ── */}
        <div className="card" style={{ padding: 20 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
            <div style={{ fontSize: 16, fontWeight: 700 }}>Por {tab === 'classes' ? 'Classe' : 'Grupo'}</div>
            <div style={{ display: 'flex', background: 'var(--ink-100)', borderRadius: 8, padding: 3 }}>
              {[['classes','Classes'],['grupos','Grupos']].map(([v, l]) => (
                <button key={v} onClick={() => setTab(v)} style={{
                  padding: '4px 10px', fontSize: 11, fontWeight: 600, fontFamily: 'inherit',
                  border: 0, borderRadius: 5, cursor: 'pointer',
                  background: tab === v ? 'var(--white)' : 'transparent',
                  color:      tab === v ? 'var(--ink-900)' : 'var(--ink-500)',
                  boxShadow:  tab === v ? 'var(--shadow-sm)' : 'none',
                  transition: 'all .12s',
                }}>{l}</button>
              ))}
            </div>
          </div>
          {items.length > 0
            ? <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {items.map((item, i) => (
                  <CompClassCard
                    key={item.className}
                    cls={{ className: item.className, totalSale: item.totalSale, totalPurchase: item.totalPurchase ?? 0 }}
                    index={i}
                    totalSale={currentComp.totalSale}
                    prevCls={item.prevTotalSale != null ? { className: item.className, totalSale: item.prevTotalSale, totalPurchase: item.prevTotalPurchase ?? 0 } : undefined}
                    prevTotalSale={prevComp?.totalSale}
                    currentLabel={currentLabel}
                    prevLabel={prevLabel}
                  />
                ))}
              </div>
            : <EmptyState text="Sem dados de classe." />
          }
        </div>

      </div>

    </div>
  );
}

// ─── Analytics View (A.A. — Ano a Ano) ──────────────────────────────────────
// Estilo visual idêntico à tela de Comparativos: CompMetricCard, BigChart, layout 2 colunas.
// Foco: análise YoY mensal — vendas, compras, margem vs ano anterior.

const AN_MONTHS_S = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
const AN_MONTHS_F = ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];

// ── Data helpers ──────────────────────────────────────────────────────────────

const anMSales = (yd, m) => {
  const f = yd.monthData.filter(e => e.month === m && e.type === 'sales');
  return f.reduce((a, e) => ({ total: a.total + e.total, quantity: a.quantity + e.quantity }), { total: 0, quantity: 0 });
};
const anMPurch = (yd, m) => {
  const f = yd.monthData.filter(e => e.month === m && e.type === 'purchases');
  return f.reduce((a, e) => ({ total: a.total + e.total, quantity: a.quantity + e.quantity }), { total: 0, quantity: 0 });
};

const anGroupSum = rawYearData => {
  const byYear = new Map();
  for (const yd of rawYearData) {
    const arr = byYear.get(yd.year) ?? [];
    byYear.set(yd.year, arr.concat(yd.monthData));
  }
  const result = [];
  for (const [year, arr] of byYear.entries()) {
    const m = new Map();
    for (const e of arr) {
      const k = `${e.month}_${e.type}`;
      const ex = m.get(k);
      if (ex) { ex.total += e.total; ex.quantity += e.quantity; }
      else m.set(k, { ...e, storeId: 0 });
    }
    result.push({ year, monthData: [...m.values()] });
  }
  return result.sort((a, b) => b.year - a.year);
};

const anYearlyToData = (year, yearly, allowedStores) => {
  const allowed = (allowedStores == null || allowedStores === true) ? null
    : Array.isArray(allowedStores) ? allowedStores : null;
  const monthData = [], storeSet = new Map();
  for (const [idStr, e] of Object.entries(yearly ?? {})) {
    const sid = SF.parseStoreKey(idStr);
    if (allowed && !SF.isAllowedStore(sid, allowed)) continue;
    storeSet.set(sid, e.storeName ?? '');
    for (const [mk, md] of Object.entries(e.months ?? {})) {
      const m = Number(mk) - 1;
      if (isNaN(m) || m < 0 || m > 11) continue;
      if ((md.salesTotal ?? 0) > 0 || (md.salesQty ?? 0) > 0)
        monthData.push({ month: m, total: md.salesTotal ?? 0, quantity: md.salesQty ?? 0, type: 'sales', storeId: sid, year });
      if ((md.purchasesTotal ?? 0) > 0 || (md.purchasesQty ?? 0) > 0)
        monthData.push({ month: m, total: md.purchasesTotal ?? 0, quantity: md.purchasesQty ?? 0, type: 'purchases', storeId: sid, year });
    }
  }
  return {
    monthData,
    storeList: [...storeSet.entries()].map(([storeId, storeName]) => ({ storeId, storeName })).sort((a, b) => a.storeId - b.storeId),
  };
};

const anBuildRanking = (storeList, rawYearData, yr, limitMonth = 11) => {
  const curr = rawYearData.find(y => y.year === yr);
  const prev = rawYearData.find(y => y.year === yr - 1);
  if (!curr) return [];
  return storeList
    .map(store => {
      const sm    = curr.monthData.filter(d => d.storeId === store.storeId && d.month <= limitMonth);
      const aS    = sm.filter(d => d.type === 'sales').reduce((s, d) => s + d.total, 0);
      const aP    = sm.filter(d => d.type === 'purchases').reduce((s, d) => s + d.total, 0);
      const pm    = (prev?.monthData.filter(d => d.storeId === store.storeId && d.month <= limitMonth)) ?? [];
      const prevS = pm.filter(d => d.type === 'sales').reduce((s, d) => s + d.total, 0);
      return { store, annualSales: aS, annualPurchases: aP, prevAnnualSales: prevS };
    })
    .filter(s => s.annualSales > 0)
    .sort((a, b) => b.annualSales - a.annualSales);
};

// ── Sub-components ────────────────────────────────────────────────────────────

function AnYearNav({ year, onPrev, onNext, canNext }) {
  const btn = dis => ({
    width: 28, height: 28, borderRadius: '50%',
    border: '1px solid var(--ink-200)',
    background: dis ? 'transparent' : 'var(--white)',
    color: dis ? 'var(--ink-300)' : 'var(--ink-700)',
    cursor: dis ? 'default' : 'pointer', display: 'flex',
    alignItems: 'center', justifyContent: 'center',
    fontSize: 16, fontFamily: 'inherit',
    boxShadow: dis ? 'none' : 'var(--shadow-sm)',
  });
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, background: 'var(--ink-50)', borderRadius: 999, padding: '5px 14px', border: '1px solid var(--ink-200)' }}>
      <button style={btn(false)} onClick={onPrev}>‹</button>
      <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-800)', minWidth: 130, textAlign: 'center', fontFeatureSettings: '"tnum"' }}>
        {year} <span style={{ color: 'var(--ink-400)', fontWeight: 500, fontFamily: 'inherit' }}>vs</span> {year - 1}
      </span>
      <button style={btn(!canNext)} onClick={canNext ? onNext : undefined} disabled={!canNext}>›</button>
    </div>
  );
}

const AN_MDL    = ['#FFD700','#C0C0C0','#CD7F32'];
const AN_MDL_BG = ['rgba(255,215,0,0.06)','rgba(192,192,192,0.06)','rgba(205,127,50,0.06)'];

function AnStoreRankYear({ items, year, prevYear }) {
  const [exp, setExp] = React.useState(new Set());
  if (!items.length) return null;
  const tot    = items.reduce((s, i) => s + i.annualSales, 0);
  const toggle = id => setExp(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 2 }}>Ranking {year}</div>
      <div style={{ fontSize: 12, color: 'var(--ink-400)', marginBottom: 14 }}>Por vendas acumuladas · clique para detalhes</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        {items.map((item, rank) => {
          const isMed = rank < 3;
          const bdr   = isMed ? AN_MDL[rank] + '60' : 'var(--ink-200)';
          const bg    = isMed ? AN_MDL_BG[rank] : 'transparent';
          const shr   = tot > 0 ? ((item.annualSales / tot) * 100).toFixed(1) : '0';
          const pr    = item.annualSales > 0 ? item.annualPurchases / item.annualSales : 0;
          const delta = item.prevAnnualSales > 0 ? (item.annualSales - item.prevAnnualSales) / item.prevAnnualSales : null;
          const pC    = pr < 0.5 ? 'var(--positive)' : pr < 0.7 ? '#FF9500' : 'var(--danger)';
          const name  = item.store.storeName || `Loja ${item.store.storeId}`;
          const isExp = exp.has(item.store.storeId);
          return (
            <div key={item.store.storeId} style={{ display: 'flex', gap: 8 }}>
              <div style={{ width: 3, borderRadius: 3, background: isMed ? AN_MDL[rank] : 'transparent', flexShrink: 0 }} />
              <div style={{ flex: 1, borderRadius: 10, padding: '9px 11px', background: bg, border: `0.5px solid ${bdr}`, cursor: 'pointer' }}
                   onClick={() => toggle(item.store.storeId)}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <div style={{ width: 22, height: 22, borderRadius: 11, background: isMed ? AN_MDL[rank] + '25' : 'var(--ink-100)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                    <span style={{ fontSize: 11, fontWeight: 800, color: isMed ? AN_MDL[rank] : 'var(--ink-500)', fontFeatureSettings: '"tnum"' }}>{rank + 1}</span>
                  </div>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{name}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-400)' }}>{shr}% da rede</div>
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 2, flexShrink: 0 }}>
                    <span style={{ fontSize: 12, fontWeight: 700, fontFeatureSettings: '"tnum"', color: COMP_PRIMARY }}>{BRL(item.annualSales)}</span>
                    {delta !== null && (
                      <span style={{ fontSize: 10, fontWeight: 700, fontFeatureSettings: '"tnum"', color: delta >= 0 ? 'var(--positive)' : 'var(--danger)', background: delta >= 0 ? 'var(--positive-bg)' : 'var(--danger-bg)', padding: '1px 5px', borderRadius: 999 }}>
                        {delta >= 0 ? '+' : ''}{(delta * 100).toFixed(1)}%
                      </span>
                    )}
                  </div>
                </div>
                {isExp && (
                  <div style={{ marginTop: 8, paddingTop: 8, borderTop: '1px solid var(--ink-100)', display: 'flex', gap: 10 }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 2 }}>Compras</div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: pC }}>{BRL(item.annualPurchases)}</div>
                    </div>
                    {item.prevAnnualSales > 0 && (
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 2 }}>{prevYear}</div>
                        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-600)' }}>{BRL(item.prevAnnualSales)}</div>
                      </div>
                    )}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function AnMonthGrid({ yearData, year, limitMonth }) {
  const curr = yearData.find(y => y.year === year);
  const prev = yearData.find(y => y.year === year - 1);
  if (!curr) return null;
  const months = Array.from({ length: 12 }, (_, i) => {
    const s   = anMSales(curr, i).total;
    const ps  = prev ? anMSales(prev, i).total : 0;
    const pur = anMPurch(curr, i).total;
    const isFut = i > limitMonth;
    return { month: i, sales: s, prevSales: ps, purchases: pur, delta: ps > 0 ? (s - ps) / ps : null, isFut };
  });
  return (
    <div className="card" style={{ padding: 20 }}>
      <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 2 }}>Desempenho Mensal</div>
      <div style={{ fontSize: 12, color: 'var(--ink-400)', marginBottom: 14 }}>{year} vs {year - 1} · por mês</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8 }}>
        {months.map(m => {
          if (m.isFut) return (
            <div key={m.month} style={{ padding: '10px 12px', borderRadius: 10, background: 'var(--ink-50)', border: '1px solid var(--ink-100)', opacity: 0.4 }}>
              <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-400)', marginBottom: 4 }}>{AN_MONTHS_S[m.month]}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-300)' }}>—</div>
            </div>
          );
          if (!m.sales) return (
            <div key={m.month} style={{ padding: '10px 12px', borderRadius: 10, background: 'var(--ink-50)', border: '1px solid var(--ink-100)' }}>
              <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-400)', marginBottom: 4 }}>{AN_MONTHS_S[m.month]}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-300)' }}>Sem dados</div>
            </div>
          );
          const dC  = m.delta === null ? 'var(--ink-400)' : m.delta >= 0 ? 'var(--positive)' : 'var(--danger)';
          const dBg = m.delta === null ? 'var(--ink-50)'  : m.delta >= 0 ? 'var(--positive-bg)' : 'var(--danger-bg)';
          const pr  = m.sales > 0 && m.purchases > 0 ? m.purchases / m.sales : 0;
          const prColor = pr > 0 ? (pr < 0.7 ? 'var(--positive)' : 'var(--danger)') : 'var(--ink-300)';
          return (
            <div key={m.month} style={{ padding: '10px 12px', borderRadius: 10, background: 'var(--white)', border: '1px solid var(--ink-200)' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 5 }}>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-600)' }}>{AN_MONTHS_S[m.month]}</div>
                {m.delta !== null && (
                  <span style={{ fontSize: 9, fontWeight: 700, fontFeatureSettings: '"tnum"', color: dC, background: dBg, padding: '1px 5px', borderRadius: 999 }}>
                    {m.delta >= 0 ? '+' : ''}{(m.delta * 100).toFixed(1)}%
                  </span>
                )}
              </div>
              <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-900)', fontFeatureSettings: '"tnum"', marginBottom: 2 }}>{BRL(m.sales)}</div>
              {m.prevSales > 0 && <div style={{ fontSize: 10, color: 'var(--ink-400)', marginBottom: 4 }}>{BRL(m.prevSales)} em {year - 1}</div>}
              {pr > 0 && (
                <div style={{ marginTop: 4 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 3 }}>
                    <span style={{ fontSize: 9, color: 'var(--ink-400)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Compras</span>
                    <span style={{ fontSize: 9, fontWeight: 700, fontFeatureSettings: '"tnum"', color: prColor }}>{(pr * 100).toFixed(2)}% das vendas</span>
                  </div>
                  <div style={{ height: 3, borderRadius: 2, background: 'var(--ink-100)', overflow: 'hidden' }}>
                    <div style={{ width: `${Math.min(pr * 100, 100)}%`, height: '100%', background: prColor, borderRadius: 2 }} />
                  </div>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function AnHighlights({ yearData, year, currentYear, limitMonth }) {
  const curr = yearData.find(y => y.year === year);
  const prev = yearData.find(y => y.year === year - 1);
  const [showTable, setShowTable] = React.useState(false);
  const [projInfoOpen, setProjInfoOpen] = React.useState(false);
  if (!curr) return null;
  const now = new Date();
  const curM = now.getMonth();
  // comp = months actually completed (exclude current in-progress month for current year)
  const comp = year < currentYear ? limitMonth + 1 : Math.min(curM, limitMonth + 1);
  const rows = Array.from({ length: 12 }, (_, m) => {
    const s = anMSales(curr, m).total, ps = prev ? anMSales(prev, m).total : 0;
    return { month: m, curr: s, prev: ps, delta: ps > 0 ? (s - ps) / ps : null };
  });
  const completedRows = rows.slice(0, comp);
  const withData = completedRows.filter(r => r.curr > 0);
  const avg    = comp > 0 ? completedRows.reduce((s, r) => s + r.curr, 0) / comp : 0;
  const above  = completedRows.filter(r => r.curr >= avg).length;
  const best   = withData.length ? withData.reduce((a, b) => b.curr > a.curr ? b : a) : null;
  const worst  = withData.length ? withData.reduce((a, b) => b.curr < a.curr ? b : a) : null;
  const ytd    = completedRows.reduce((s, r) => s + r.curr, 0);
  const prevA  = prev ? Array.from({length:12}, (_, m) => anMSales(prev, m).total).reduce((a, b) => a + b, 0) : 0;
  // Projeção do mês atual: ritmo diário × dias no mês (igual ao ProjectionCard de vendas)
  const curMonthMTD  = year === currentYear ? anMSales(curr, curM).total : 0;
  const dayOfMonth   = now.getDate();
  const daysInCurMon = new Date(year, curM + 1, 0).getDate();
  const curMonthProj = year === currentYear && dayOfMonth > 0 ? (curMonthMTD / dayOfMonth) * daysInCurMon : 0;
  const restMonths = 12 - comp - 1;
  const restVal     = avg * restMonths;
  const mi = (v) => `R$ ${(v / 1e6).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} mi`;
  const proj   = comp > 0 ? ytd + curMonthProj + restVal : 0;
  const projD  = prevA > 0 ? (proj - prevA) / prevA : null;
  const hasProj = year === currentYear && comp > 0 && comp < 12;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {hasProj && (
        <div className="card" style={{ padding: 20 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
            <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--ink-500)' }}>Projeção Anual {year}</div>
            <button onClick={() => setProjInfoOpen(o => !o)} style={{
              width: 18, height: 18, borderRadius: '50%', border: 'none', cursor: 'pointer',
              background: projInfoOpen ? 'var(--ink-200)' : 'var(--ink-100)',
              color: 'var(--ink-500)', fontSize: 11, display: 'flex',
              alignItems: 'center', justifyContent: 'center', flexShrink: 0,
              transition: 'background .15s',
            }} title="Como chega nesse valor?">ⓘ</button>
          </div>
          <div style={{ fontSize: 26, fontWeight: 700, fontFeatureSettings: '"tnum"', letterSpacing: '-0.02em', marginBottom: 10, color: projD !== null && projD >= 0 ? 'var(--positive)' : projD !== null ? 'var(--danger)' : 'var(--ink-900)' }}>{BRL(proj)}</div>
          {projD !== null && (
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 12, fontWeight: 700, fontFeatureSettings: '"tnum"', color: projD >= 0 ? 'var(--positive)' : 'var(--danger)', background: projD >= 0 ? 'var(--positive-bg)' : 'var(--danger-bg)', padding: '3px 10px', borderRadius: 999 }}>
              <Icon name={projD >= 0 ? 'arrow-up' : 'arrow-down'} size={11} strokeWidth={2.5} />
              {Math.abs(projD * 100).toFixed(1)}% vs {year - 1}
            </span>
          )}
          <div style={{ marginTop: 10, fontSize: 12, color: 'var(--ink-600)', lineHeight: 1.5 }}>
            {comp} {comp === 1 ? 'mês concluído' : 'meses concluídos'} · {AN_MONTHS_F[curM]} projetado em {BRL(curMonthProj)} (ritmo diário) · {12 - comp - 1} restantes na média de {BRL(avg)}/mês
          </div>

          {projInfoOpen && (
            <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--ink-100)' }}>
              <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', gap: '4px 6px', fontSize: 13 }}>
                <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-800)' }}>{mi(ytd)}</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-400)' }}>({comp} {comp === 1 ? 'mês' : 'meses'})</span>
                <span style={{ color: 'var(--ink-300)' }}>+</span>
                <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-800)' }}>{mi(curMonthProj)}</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-400)' }}>({AN_MONTHS_F[curM]})</span>
                <span style={{ color: 'var(--ink-300)' }}>+</span>
                <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-800)' }}>{mi(restVal)}</span>
                <span style={{ fontSize: 10.5, color: 'var(--ink-400)' }}>({restMonths} {restMonths === 1 ? 'mês' : 'meses'})</span>
                <span style={{ color: 'var(--ink-300)' }}>=</span>
                <span className="mono" style={{ fontWeight: 700, color: 'var(--ink-900)', fontSize: 15 }}>{mi(proj)}</span>
              </div>
            </div>
          )}
        </div>
      )}
      {best && worst && (
        <div className="card" style={{ padding: 20 }}>
          <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)', marginBottom: 14 }}>Destaques do Ano</div>
          {best.month !== worst.month && (
            <div style={{ display: 'flex', gap: 10, marginBottom: 14 }}>
              <div style={{ flex: 1, padding: '12px 14px', borderRadius: 10, background: 'var(--positive-bg)', border: '1px solid rgba(52,199,89,0.25)' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--positive)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>Melhor</div>
                <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-800)' }}>{AN_MONTHS_F[best.month]}</div>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--positive)', fontFeatureSettings: '"tnum"', marginTop: 2 }}>{BRL(best.curr)}</div>
              </div>
              <div style={{ flex: 1, padding: '12px 14px', borderRadius: 10, background: 'var(--danger-bg)', border: '1px solid rgba(255,59,48,0.2)' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--danger)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>Pior</div>
                <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink-800)' }}>{AN_MONTHS_F[worst.month]}</div>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--danger)', fontFeatureSettings: '"tnum"', marginTop: 2 }}>{BRL(worst.curr)}</div>
              </div>
            </div>
          )}
          {comp > 1 && (
            <div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                <div style={{ fontSize: 12, color: 'var(--ink-700)', fontWeight: 600 }}>Consistência</div>
                <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>{above} de {comp} meses ≥ média</div>
              </div>
              <div style={{ height: 8, borderRadius: 4, background: 'var(--danger-bg)', overflow: 'hidden' }}>
                <div style={{ width: `${comp > 0 ? (above / comp) * 100 : 0}%`, height: '100%', borderRadius: 4, background: above / comp >= 0.5 ? 'var(--positive)' : 'var(--danger)' }} />
              </div>
              <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 5 }}>Média de {BRL(avg)}/mês</div>
            </div>
          )}
        </div>
      )}
      {comp > 0 && (
        <div className="card" style={{ padding: 20 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }} onClick={() => setShowTable(p => !p)}>
            <div>
              <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>Mês a Mês</div>
              <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>{year} vs {year - 1}</div>
            </div>
            <span style={{ fontSize: 12, color: COMP_PRIMARY, fontWeight: 600 }}>{showTable ? '▲ fechar' : '▼ expandir'}</span>
          </div>
          {showTable && (
            <div style={{ marginTop: 12 }}>
              <div style={{ display: 'grid', gridTemplateColumns: '40px 1fr 1fr 72px', background: 'var(--ink-50)', borderRadius: 8, padding: '6px 8px', marginBottom: 4 }}>
                {['Mês', String(year), String(year - 1), 'Δ'].map((h, i) => (
                  <div key={i} style={{ fontSize: 11, fontWeight: 700, color: COMP_PRIMARY, textAlign: i > 0 ? 'right' : 'left' }}>{h}</div>
                ))}
              </div>
              {rows.map(r => {
                if (r.month > limitMonth) return null;
                if (r.curr === 0 && r.prev === 0) return null;
                const dC = r.delta === null ? 'var(--ink-400)' : r.delta >= 0 ? 'var(--positive)' : 'var(--danger)';
                return (
                  <div key={r.month} style={{ display: 'grid', gridTemplateColumns: '40px 1fr 1fr 72px', padding: '6px 8px', borderBottom: '1px solid var(--ink-100)' }}>
                    <div style={{ fontSize: 12, color: 'var(--ink-700)' }}>{AN_MONTHS_S[r.month]}</div>
                    <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-900)', textAlign: 'right', fontFeatureSettings: '"tnum"' }}>{BRL(r.curr)}</div>
                    <div style={{ fontSize: 12, color: 'var(--ink-500)', textAlign: 'right', fontFeatureSettings: '"tnum"' }}>{r.prev > 0 ? BRL(r.prev) : '—'}</div>
                    <div style={{ fontSize: 12, fontWeight: 700, color: dC, textAlign: 'right', fontFeatureSettings: '"tnum"' }}>{r.delta === null ? '—' : `${r.delta >= 0 ? '+' : ''}${(r.delta * 100).toFixed(1)}%`}</div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function AnalyticsView({ user, selectedStoreIds }) {
  const [allMonthData, setAll]  = React.useState([]);
  const [storeList,    setSL]   = React.useState([]);
  const [loading,      setLoad] = React.useState(true);
  const now         = new Date();
  const currentYear = now.getFullYear();
  const [selYear,   setSelYear] = React.useState(currentYear);
  const cnpj = user?.cnpj;

  React.useEffect(() => {
    if (!cnpj) return;
    let cancelled = false;
    (async () => {
      const years = [currentYear, currentYear - 1, currentYear - 2];
      const res = await Promise.allSettled(years.map(y => SF.fetchYearlyForAnalyticsMerged(cnpj, y)));
      if (cancelled) return;
      const allMD = [], stores = new Map();
      for (let i = 0; i < res.length; i++) {
        const r = res[i];
        if (r.status !== 'fulfilled' || !r.value) continue;
        const { monthData, storeList: sl } = anYearlyToData(years[i], r.value, user?.allowedStores ?? true);
        allMD.push(...monthData);
        sl.forEach(s => stores.set(s.storeId, s.storeName));
      }

      // Mês em curso: substituir o total cheio do yearly pelo acumulado real dia a dia
      // (evita comparar jun/2025 dias 1-13 vs jun/2024 mês fechado — seria injusto)
      const curMonthIdx = now.getMonth();      // 0-indexed
      const curDay      = now.getDate();
      const prevYear    = currentYear - 1;
      try {
        const partial = await SF.fetchPartialMonthDailyTotals(cnpj, prevYear, curMonthIdx + 1, curDay);
        if (partial && Object.keys(partial).length) {
          // Remove entradas do yearly para o mês atual do ano anterior
          for (let k = allMD.length - 1; k >= 0; k--) {
            if (allMD[k].year === prevYear && allMD[k].month === curMonthIdx) allMD.splice(k, 1);
          }
          // Adiciona os totais parciais dia a dia
          for (const [key, t] of Object.entries(partial)) {
            const sid = SF.parseStoreKey(key);
            if (t.salesTotal > 0)
              allMD.push({ month: curMonthIdx, total: t.salesTotal, quantity: t.salesQty, type: 'sales', storeId: sid, year: prevYear });
            if (t.purchasesTotal > 0)
              allMD.push({ month: curMonthIdx, total: t.purchasesTotal, quantity: 0, type: 'purchases', storeId: sid, year: prevYear });
          }
        }
      } catch (_) { /* falha silenciosa — usa o yearly como fallback */ }

      // Mesmo patch para o ano corrente: garante que jun/2026 dias 1-13 use docs reais
      // (mesmo dado que ProjectionCard de vendas usa, evitando divergência na projeção)
      try {
        const partialCurr = await SF.fetchPartialMonthDailyTotals(cnpj, currentYear, curMonthIdx + 1, curDay);
        if (partialCurr && Object.keys(partialCurr).length) {
          for (let k = allMD.length - 1; k >= 0; k--) {
            if (allMD[k].year === currentYear && allMD[k].month === curMonthIdx) allMD.splice(k, 1);
          }
          for (const [key, t] of Object.entries(partialCurr)) {
            const sid = SF.parseStoreKey(key);
            if (t.salesTotal > 0)
              allMD.push({ month: curMonthIdx, total: t.salesTotal, quantity: t.salesQty, type: 'sales', storeId: sid, year: currentYear });
            if (t.purchasesTotal > 0)
              allMD.push({ month: curMonthIdx, total: t.purchasesTotal, quantity: 0, type: 'purchases', storeId: sid, year: currentYear });
          }
        }
      } catch (_) { /* falha silenciosa — usa o yearly como fallback */ }

      const sl = [...stores.entries()].map(([storeId, storeName]) => ({ storeId, storeName })).sort((a, b) => a.storeId - b.storeId);
      if (!cancelled) { setAll(allMD); setSL(sl); setLoad(false); }
    })();
    return () => { cancelled = true; };
  }, [cnpj]);

  const filteredSL = React.useMemo(() =>
    selectedStoreIds?.length ? storeList.filter(s => selectedStoreIds.includes(s.storeId)) : storeList,
    [storeList, selectedStoreIds]);

  const rawYD = React.useMemo(() => {
    const byYear = new Map();
    for (const m of allMonthData) {
      if (selectedStoreIds?.length && !selectedStoreIds.includes(m.storeId)) continue;
      const arr = byYear.get(m.year) ?? [];
      arr.push(m);
      byYear.set(m.year, arr);
    }
    return [...byYear.entries()].map(([year, monthData]) => ({ year, monthData })).sort((a, b) => b.year - a.year);
  }, [allMonthData, selectedStoreIds]);

  const yearData = React.useMemo(() => anGroupSum(rawYD), [rawYD]);

  // limitMonth: last month with actual data in selYear, to guarantee proportional YoY comparison
  const limitMonth = React.useMemo(() => {
    if (selYear === currentYear) return now.getMonth();
    const curr = yearData.find(y => y.year === selYear);
    if (!curr) return 11;
    for (let m = 11; m >= 0; m--) {
      if (anMSales(curr, m).total > 0) return m;
    }
    return 11;
  }, [yearData, selYear, currentYear]);

  const ranking = React.useMemo(() => anBuildRanking(filteredSL, rawYD, selYear, limitMonth), [filteredSL, rawYD, selYear, limitMonth]);

  const { currSales, prevSales, currPurchases, prevPurchases } = React.useMemo(() => {
    const curr = yearData.find(y => y.year === selYear);
    const prev = yearData.find(y => y.year === selYear - 1);
    let cS = 0, cP = 0, pS = 0, pP = 0;
    for (let m = 0; m <= limitMonth; m++) {
      cS += curr ? anMSales(curr, m).total : 0;
      cP += curr ? anMPurch(curr, m).total : 0;
      pS += prev ? anMSales(prev, m).total : 0;
      pP += prev ? anMPurch(prev, m).total : 0;
    }
    return {
      currSales: cS, prevSales: pS,
      currPurchases: cP, prevPurchases: pP,
    };
  }, [yearData, selYear, limitMonth]);

  const chartData = React.useMemo(() => {
    const curr = yearData.find(y => y.year === selYear);
    const prev = yearData.find(y => y.year === selYear - 1);
    return Array.from({ length: limitMonth + 1 }, (_, i) => ({
      date:  `${selYear}-${String(i + 1).padStart(2, '0')}-01`,
      l:     AN_MONTHS_S[i],
      prevL: `${AN_MONTHS_S[i]} ${selYear - 1}`,
      v:  curr ? anMSales(curr, i).total : 0,
      c:  prev ? anMSales(prev, i).total : 0,
      k:  curr ? anMPurch(curr, i).total : 0,
      cp: prev ? anMPurch(prev, i).total : 0,
    }));
  }, [yearData, selYear, limitMonth]);

  const hasPrev    = chartData.some(d => d.c > 0);
  const yearsAvail = [...new Set(rawYD.map(y => y.year))].sort();
  const canBack    = yearsAvail.some(y => y < selYear);
  const canForward = selYear < currentYear;
  const curMLabel  = AN_MONTHS_S[limitMonth];

  if (loading) return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {[80, 300, 100].map((h, i) => (
        <div key={i} style={{ height: h, borderRadius: 12, background: 'var(--ink-100)', animation: 'sf-pulse 1.4s ease-in-out infinite' }} />
      ))}
    </div>
  );

  if (!yearData.length) return (
    <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--ink-400)' }}>
      Sem dados anuais disponíveis. Verifique se o coletor está gravando o campo <code>yearly</code>.
    </div>
  );

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>

      {/* Cabeçalho */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 10 }}>
        <AnYearNav year={selYear} onPrev={() => canBack && setSelYear(y => y - 1)} onNext={() => canForward && setSelYear(y => y + 1)} canNext={canForward} />
        <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>
          Jan–{curMLabel} · {filteredSL.length} {filteredSL.length === 1 ? 'filial' : 'filiais'}
        </div>
      </div>

      {/* KPI Cards */}
      <div style={{ display: 'flex', gap: 14 }}>
        <CompMetricCard label="Vendas"  value={currSales}     prevValue={prevSales > 0 ? prevSales : null}         primary />
        <CompMetricCard label="Compras" value={currPurchases} prevValue={prevPurchases > 0 ? prevPurchases : null}  invertColor />
      </div>

      {/* Grid 2 colunas */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 20, alignItems: 'start' }}>

        {/* Coluna esquerda */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>

          {chartData.length >= 2 && (
            <div className="card" style={{ padding: 24 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--ink-900)' }}>Vendas mensais — {selYear} vs {selYear - 1}</div>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
                  <LegendDot color={COMP_PRIMARY} label={String(selYear)} />
                  {hasPrev && <LegendDot color="var(--ink-400)" label={String(selYear - 1)} dashed />}
                </div>
              </div>
              <BigChart data={chartData} lineColor={COMP_PRIMARY} gradientId="analytics-grad" tooltipLabel="Vendas" hideVariation comparativeMode />
            </div>
          )}

          <AnMonthGrid yearData={yearData} year={selYear} limitMonth={limitMonth} />
          <AnHighlights yearData={yearData} year={selYear} currentYear={currentYear} limitMonth={limitMonth} />

        </div>

        {/* Coluna direita */}
        <AnStoreRankYear items={ranking} year={selYear} prevYear={selYear - 1} />

      </div>
    </div>
  );
}

// ─── Entregas ─────────────────────────────────────────────────────────────────
function DeliveriesView({ data, loading, syncAt }) {
  const fmtTime = (min) => {
    if (!min || min <= 0) return '—';
    const m = Math.round(min);
    if (m < 60) return `${m} min`;
    const h = Math.floor(m / 60);
    const r = m % 60;
    return r > 0 ? `${h}h ${r}min` : `${h}h`;
  };

  const stores = data?.stores ?? [];

  const deliveryStores = React.useMemo(() =>
    stores
      .filter(s => s.delivery && (s.delivery.balconSales > 0 || s.delivery.deliverySales > 0 || s.delivery.balconCoupon > 0 || s.delivery.deliveryCoupon > 0 || s.delivery.deliveryCount > 0))
      .map(s => {
        const bS  = s.delivery.balconSales    ?? 0;
        const dS  = s.delivery.deliverySales  ?? 0;
        const bC  = s.delivery.balconCoupon   ?? 0;
        const dC  = s.delivery.deliveryCoupon ?? 0;
        const cnt = s.delivery.deliveryCount  ?? 0;
        const tt  = s.delivery.deliveryTotalTime ?? 0;
        const total = bS + dS;
        return {
          storeId:              s.storeId,
          storeName:            s.storeName,
          balconSales:          bS,
          deliverySales:        dS,
          balconCoupon:         bC,
          deliveryCoupon:       dC,
          balconTicket:         bC > 0 ? bS / bC : 0,
          deliveryTicket:       dC > 0 ? dS / dC : 0,
          deliveryCount:        cnt,
          deliveryAvgTime:      cnt > 0 ? tt / cnt : 0,
          topDeliveryManName:    s.delivery.topDeliveryManName ?? null,
          topDeliveryManAvgTime: s.delivery.topDeliveryManAvgTime ?? 0,
          topDeliveryManCount:   s.delivery.topDeliveryManCount ?? 0,
          total,
          percentage:           total > 0 ? (dS / total) * 100 : 0,
        };
      })
      .sort((a, b) => b.deliverySales - a.deliverySales)
  , [stores]);

  const totals = React.useMemo(() => {
    const bS  = deliveryStores.reduce((s, d) => s + d.balconSales,    0);
    const dS  = deliveryStores.reduce((s, d) => s + d.deliverySales,  0);
    const bC  = deliveryStores.reduce((s, d) => s + d.balconCoupon,   0);
    const dC  = deliveryStores.reduce((s, d) => s + d.deliveryCoupon, 0);
    const cnt = deliveryStores.reduce((s, d) => s + d.deliveryCount,  0);
    const tt  = deliveryStores.reduce((s, d) => s + d.deliveryCount * d.deliveryAvgTime, 0);
    const total = bS + dS;
    // top entregador = loja com mais entregas, seu top man
    const topStore = [...deliveryStores].sort((a, b) => b.deliveryCount - a.deliveryCount)[0];
    return {
      balconSales:           bS,  deliverySales:  dS,
      balconCoupon:          bC,  deliveryCoupon: dC,
      balconTicket:          bC > 0 ? bS / bC : 0,
      deliveryTicket:        dC > 0 ? dS / dC : 0,
      deliveryCount:         cnt,
      deliveryAvgTime:       cnt > 0 ? tt / cnt : 0,
      topDeliveryManName:      topStore?.topDeliveryManName ?? null,
      topDeliveryManAvgTime:   topStore?.topDeliveryManAvgTime ?? 0,
      topDeliveryManCount:     topStore?.topDeliveryManCount ?? 0,
      topDeliveryManStoreName: topStore?.storeName ?? null,
      total,
      percentage: total > 0 ? (dS / total) * 100 : 0,
    };
  }, [deliveryStores]);

  const pct = totals.percentage;
  const insight = pct < 5
    ? { label: 'Entrega fraca',        color: 'var(--ink-500)',  bg: 'var(--ink-100)',    text: 'Canal de entrega com baixa relevância. Há espaço para crescer tente com aplicativos de delivery.' }
    : pct < 15
    ? { label: 'Entrega em desenvolvimento', color: 'var(--warning)',  bg: 'var(--warning-bg)', text: 'Canal de entrega em desenvolvimento.' }
    : { label: 'Entrega forte',        color: 'var(--positive)', bg: 'var(--positive-bg)',text: 'Canal de entrega bem. Mais de 15% das vendas ocorrem via entrega.' };

  const hasData = !loading && deliveryStores.length > 0;

  const medalColors = ['#FFD700','#C0C0C0','#CD7F32'];
  const sectionRef  = React.useRef(null);

  return (
    <>
      {/* KPIs */}
      <div ref={sectionRef} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(130px, 1fr))', gap: 12, marginBottom: 20 }}>
        {[
          { label: 'Total Vendas',        value: loading ? '—' : BRL(totals.total) },
          { label: 'Vendas Entrega',      value: loading ? '—' : BRL(totals.deliverySales) },
          { label: '% Entrega',           value: loading ? '—' : `${pct.toFixed(1)}%` },
          { label: 'Entregas Concluídas', value: loading ? '—' : NUM(totals.deliveryCount) },
          { label: 'Tempo Médio Entrega', value: loading ? '—' : fmtTime(totals.deliveryAvgTime) },
        ].map(({ label, value }) => (
          <div key={label} className="card" style={{ padding: '16px 20px' }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>{label}</div>
            <div className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--ink-900)' }}>{value}</div>
          </div>
        ))}
      </div>

      {!loading && !hasData && (
        <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--ink-400)' }}>
          <div style={{ fontSize: 14 }}>Sem dados de entrega para o período selecionado.</div>
        </div>
      )}

      {hasData && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>

          {/* Canal comparison */}
          <div className="card" style={{ padding: 20 }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 16 }}>Canais de Venda</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: 20, alignItems: 'start' }}>
              {/* Balcão */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--brand-500)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Balcão</div>
                <div className="mono" style={{ fontSize: 24, fontWeight: 800, color: 'var(--ink-900)' }}>{BRL(totals.balconSales)}</div>
                <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>{NUM(totals.balconCoupon)} cupons</div>
                <div style={{ display: 'inline-flex', alignItems: 'center', background: 'var(--brand-50)', borderRadius: 6, padding: '4px 8px', alignSelf: 'flex-start', marginTop: 2 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--brand-500)' }}>Ticket médio {BRL(totals.balconTicket)}</span>
                </div>
              </div>
              {/* Divider */}
              <div style={{ width: 1, background: 'var(--ink-200)', height: 80, alignSelf: 'center' }} />
              {/* Entrega */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--positive)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Entrega</div>
                <div className="mono" style={{ fontSize: 24, fontWeight: 800, color: 'var(--positive)' }}>{BRL(totals.deliverySales)}</div>
                <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>
                  {NUM(totals.deliveryCoupon)} entregas registradas
                  {totals.deliveryCount > 0 && <span> · {NUM(totals.deliveryCount)} concluídas</span>}
                  {(totals.deliveryCoupon - totals.deliveryCount) > 0 && <span> · {NUM(totals.deliveryCoupon - totals.deliveryCount)} a caminho do cliente</span>}
                </div>
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 2 }}>
                  <div style={{ display: 'inline-flex', alignItems: 'center', background: 'var(--positive-bg)', borderRadius: 6, padding: '4px 8px' }}>
                    <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--positive)' }}>Ticket médio de entrega {BRL(totals.deliveryTicket)}</span>
                  </div>
                  {totals.deliveryAvgTime > 0 && (
                    <div style={{ display: 'inline-flex', alignItems: 'center', background: 'var(--ink-100)', borderRadius: 6, padding: '4px 8px' }}>
                      <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-600)' }}>{fmtTime(totals.deliveryAvgTime)}</span>
                    </div>
                  )}
                </div>
                {totals.topDeliveryManName != null && (
                  <div style={{ marginTop: 10 }}>
                    <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 5 }}>Melhor entregador</div>
                    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'var(--positive-bg)', border: '1px solid color-mix(in srgb, var(--positive) 30%, transparent)', borderRadius: 20, padding: '5px 12px' }}>
                      <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--positive)' }}>{totals.topDeliveryManName}</span>
                      {totals.topDeliveryManCount > 0 && <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--positive)' }}>· {NUM(totals.topDeliveryManCount)} entregas concluídas</span>}
                      {totals.topDeliveryManAvgTime > 0 && <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--positive)' }}>· {fmtTime(totals.topDeliveryManAvgTime)}</span>}
                      {totals.topDeliveryManStoreName && <span style={{ fontSize: 11, color: 'var(--positive)', opacity: 0.75 }}>· {totals.topDeliveryManStoreName}</span>}
                    </div>
                  </div>
                )}
              </div>
            </div>
            {/* Progress bar */}
            <div style={{ marginTop: 20 }}>
              <div style={{ display: 'flex', height: 8, borderRadius: 4, overflow: 'hidden', background: 'var(--ink-200)' }}>
                <div style={{ width: `${100 - pct}%`, background: 'var(--brand-500)', transition: 'width .4s' }} />
                <div style={{ width: `${pct}%`, background: 'var(--positive)', transition: 'width .4s' }} />
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6 }}>
                <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--brand-500)' }}>Balcão {(100 - pct).toFixed(1)}%</span>
                <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--positive)' }}>Entrega {pct.toFixed(1)}%</span>
              </div>
            </div>
          </div>

          {/* Insight */}
          <div className="card" style={{ padding: '14px 20px', borderLeft: `4px solid ${insight.color}`, background: insight.bg }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: insight.color, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>{insight.label}</div>
            <div style={{ fontSize: 13, color: 'var(--ink-700)' }}>{insight.text}</div>
          </div>

          {/* Store ranking */}
          {deliveryStores.length > 1 && (
            <div className="card" style={{ padding: 24 }}>
              <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 16 }}>Ranking por Entrega</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {deliveryStores.map((store, i) => {
                  const medal  = SL_MEDALS[i] ?? null;
                  const barW   = store.percentage; // % das vendas da loja via entrega (0–100)
                  const shareP = totals.deliverySales > 0 ? (store.deliverySales / totals.deliverySales * 100) : 0;
                  return (
                    <div key={store.storeId} style={{ display: 'flex', alignItems: 'stretch', gap: 8 }}>
                      {/* Barra lateral colorida */}
                      <div style={{ width: 4, borderRadius: 4, flexShrink: 0, background: medal?.dot ?? 'var(--positive)' }} />

                      {/* Card */}
                      <div style={{ flex: 1, borderRadius: 12, padding: '10px 16px', background: medal?.bg ?? 'var(--white)', border: `0.5px solid ${medal?.border ?? 'rgba(0,0,0,0.1)'}` }}>
                        {/* Linha superior: ticket médio entrega + % via entrega */}
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 5 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, color: '#6B7280' }}>
                            {store.deliveryAvgTime > 0
                              ? <><span>Tempo médio</span><span className="mono" style={{ fontWeight: 500 }}>{fmtTime(store.deliveryAvgTime)}</span></>
                              : <span>—</span>
                            }
                          </div>
                          <div style={{ display: 'inline-flex', alignItems: 'center', background: 'var(--positive-bg)', border: '1px solid color-mix(in srgb, var(--positive) 30%, transparent)', borderRadius: 20, padding: '2px 8px', fontSize: 11, fontWeight: 600, color: 'var(--positive)' }}>
                            {store.percentage.toFixed(1)}% das vendas dessa filial são entregas
                          </div>
                        </div>

                        {/* Linha principal: badge + nome | valor + share */}
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
                            <div style={{ width: 26, height: 26, borderRadius: '50%', flexShrink: 0, background: medal ? medal.dot + '30' : 'var(--positive)20', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700, color: medal?.dot ?? 'var(--positive)' }}>{i + 1}</div>
                            <span style={{ fontSize: 14, fontWeight: 500, color: 'var(--ink-900)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{store.storeName || `Loja ${store.storeId}`}</span>
                          </div>
                          <div style={{ textAlign: 'right', flexShrink: 0, marginLeft: 10 }}>
                            <div className="mono" style={{ fontSize: 14, fontWeight: 500, color: 'var(--positive)' }}>{BRL(store.deliverySales)}</div>
                            {shareP > 0 && <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--positive)' }}>representa {shareP.toFixed(1)}% do faturamento de entregas</div>}
                          </div>
                        </div>

                        {/* Barra balcão (azul) + entrega (verde) */}
                        <div style={{ marginTop: 8, display: 'flex', height: 6, borderRadius: 99, overflow: 'hidden' }}>
                          <div style={{ width: `${100 - barW}%`, background: 'var(--brand-500)', transition: 'width .4s' }} />
                          <div style={{ width: `${barW}%`, background: 'var(--positive)', transition: 'width .4s' }} />
                        </div>

                        {/* Melhor entregador */}
                        {store.topDeliveryManName != null && (
                          <div style={{ marginTop: 8, paddingTop: 8, borderTop: '1px solid var(--ink-100)', display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                            <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Melhor entregador</span>
                            <div style={{ display: 'inline-flex', alignItems: 'center', gap: 5, background: 'var(--positive-bg)', border: '1px solid color-mix(in srgb, var(--positive) 30%, transparent)', borderRadius: 20, padding: '3px 10px' }}>
                              <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--positive)' }}>{store.topDeliveryManName}</span>
                              {store.topDeliveryManCount > 0 && <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--positive)' }}>· {NUM(store.topDeliveryManCount)} entregas concluídas</span>}
                              {store.topDeliveryManAvgTime > 0 && <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--positive)' }}>· {fmtTime(store.topDeliveryManAvgTime)}</span>}
                            </div>
                          </div>
                        )}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}
        </div>
      )}
      {hasData && (
        <DeliveriesFloatingPanel stores={deliveryStores} anchorRef={sectionRef} />
      )}
    </>
  );
}

// ─── Painel flutuante — Composição de Entregas por Loja ───────────────────────
function DeliveriesFloatingPanel({ stores, anchorRef }) {
  const [visible,  setVisible]  = React.useState(false);
  const [expanded, setExpanded] = React.useState(false);
  const [selected, setSelected] = React.useState(new Set());

  React.useEffect(() => {
    const el = anchorRef?.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      setVisible(e.isIntersecting || e.boundingClientRect.top < 0);
    }, { threshold: 0 });
    obs.observe(el);
    return () => obs.disconnect();
  }, [anchorRef]);

  const items = stores ?? [];
  const getName = (s) => s.storeName || `Loja ${s.storeId}`;

  const toggle = (name) => setSelected(prev => {
    const next = new Set(prev);
    next.has(name) ? next.delete(name) : next.add(name);
    return next;
  });

  const selItems = items.filter(it => selected.has(getName(it)));
  const selTotal = selItems.reduce((s, it) => s + it.deliverySales, 0);
  const gTotal   = items.reduce((s, it) => s + it.deliverySales, 0);
  const selPct   = gTotal > 0 && selTotal > 0 ? (selTotal / gTotal * 100) : 0;

  const r = 52, circ = 2 * Math.PI * r;
  let offsetD = 0;
  const donutSegs = selItems.map(it => {
    const name     = getName(it);
    const colorIdx = items.findIndex(x => getName(x) === name) % GRP_PALETTE.length;
    const len      = selTotal > 0 ? (it.deliverySales / selTotal) * circ : 0;
    const seg      = { name, len, offset: offsetD, color: GRP_PALETTE[colorIdx] };
    offsetD += len;
    return seg;
  });

  if (!visible) return null;

  return (
    <div style={{ position: 'fixed', bottom: 28, right: 20, zIndex: 200, display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8, pointerEvents: 'none' }}>
      {expanded && (
        <div style={{ pointerEvents: 'auto', width: 340, maxHeight: 'calc(100vh - 140px)', display: 'flex', flexDirection: 'column', background: 'var(--white)', border: '1px solid var(--ink-200)', borderRadius: 16, boxShadow: 'var(--shadow-lg)', overflow: 'hidden' }}>
          {/* Header */}
          <div style={{ padding: '14px 16px 12px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--ink-800)' }}>Composição de Entregas</span>
            {selected.size > 0 && (
              <button onClick={() => setSelected(new Set())} style={{ fontSize: 11, color: 'var(--ink-400)', background: 'none', border: 'none', cursor: 'pointer', padding: '2px 6px', borderRadius: 4 }}>Limpar</button>
            )}
          </div>

          {/* Donut */}
          <div style={{ padding: '16px 16px 12px', borderBottom: '1px solid var(--ink-100)', flexShrink: 0, display: 'flex', alignItems: 'center', gap: 16 }}>
            <div style={{ flexShrink: 0 }}>
              <svg width="128" height="128" viewBox="0 0 128 128">
                <g transform="rotate(-90 64 64)">
                  {selItems.length === 0
                    ? <circle cx="64" cy="64" r={r} fill="none" stroke="var(--ink-100)" strokeWidth="18" />
                    : donutSegs.map(seg => (
                        <circle key={seg.name} cx="64" cy="64" r={r}
                          fill="none" stroke={seg.color} strokeWidth="18"
                          strokeDasharray={`${seg.len} ${circ - seg.len}`}
                          strokeDashoffset={-seg.offset}
                          style={{ transition: 'stroke-dasharray .4s, stroke-dashoffset .4s' }}
                        />
                      ))
                  }
                </g>
                {selItems.length === 0
                  ? <>
                      <text x="64" y="60" textAnchor="middle" fontSize="9.5" fill="var(--ink-400)" fontWeight="600">SELECIONE</text>
                      <text x="64" y="73" textAnchor="middle" fontSize="9.5" fill="var(--ink-400)" fontWeight="600">AS LOJAS</text>
                    </>
                  : <>
                      <text x="64" y="58" textAnchor="middle" fontSize="9" fill="var(--ink-500)" fontWeight="600" letterSpacing="0.06em">SELEÇÃO</text>
                      <text x="64" y="74" textAnchor="middle" fontSize="13" fill="var(--ink-900)" fontWeight="700">{selPct.toFixed(1)}%</text>
                    </>
                }
              </svg>
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              {selItems.length === 0
                ? <div style={{ fontSize: 12, color: 'var(--ink-400)', lineHeight: 1.5 }}>Clique nas lojas para compor as entregas no gráfico.</div>
                : <>
                    <div className="mono" style={{ fontSize: 17, fontWeight: 700, color: 'var(--ink-900)' }}>{BRL(selTotal)}</div>
                    <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 2 }}>{selPct.toFixed(1)}% das entregas</div>
                    <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
                      {donutSegs.map(seg => (
                        <div key={seg.name} style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5 }}>
                          <span style={{ width: 8, height: 8, borderRadius: 2, background: seg.color, flexShrink: 0 }} />
                          <span style={{ color: 'var(--ink-700)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }} title={seg.name}>{seg.name}</span>
                        </div>
                      ))}
                    </div>
                  </>
              }
            </div>
          </div>

          {/* Lista de lojas */}
          <div style={{ flex: 1, overflowY: 'auto' }}>
            {items.length === 0
              ? <div style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-400)', fontSize: 13 }}>Sem dados</div>
              : items.map((store, i) => {
                  const name  = getName(store);
                  const isSel = selected.has(name);
                  const color = GRP_PALETTE[i % GRP_PALETTE.length];
                  const pct   = gTotal > 0 ? (store.deliverySales / gTotal * 100) : 0;
                  const barW  = items[0]?.deliverySales > 0 ? (store.deliverySales / items[0].deliverySales * 100) : 0;
                  return (
                    <button key={store.storeId} onClick={() => toggle(name)} style={{
                      display: 'flex', alignItems: 'center', gap: 10,
                      width: '100%', padding: '8px 14px',
                      background: isSel ? color + '14' : i % 2 === 0 ? 'transparent' : 'var(--ink-50)',
                      border: 'none', borderBottom: '1px solid var(--ink-100)',
                      cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
                      outline: isSel ? `2px solid ${color}33` : 'none',
                      transition: 'background .15s',
                    }}>
                      <span style={{ width: 14, height: 14, borderRadius: 3, flexShrink: 0, background: isSel ? color : 'var(--ink-100)', border: `2px solid ${isSel ? color : 'var(--ink-300)'}`, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all .15s' }}>
                        {isSel && <svg width="8" height="6" viewBox="0 0 8 6"><polyline points="1,3 3,5 7,1" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                      </span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 3 }}>
                          <span style={{ fontSize: 12.5, fontWeight: isSel ? 600 : 400, color: isSel ? 'var(--ink-900)' : 'var(--ink-700)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 140 }} title={name}>{name}</span>
                          <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
                            <span className="mono" style={{ fontSize: 11, color: 'var(--ink-400)' }}>{pct.toFixed(1)}%</span>
                            <span className="mono" style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--ink-800)' }}>{BRL(store.deliverySales)}</span>
                          </div>
                        </div>
                        <div style={{ height: 3, background: 'var(--ink-100)', borderRadius: 99, overflow: 'hidden' }}>
                          <div style={{ height: '100%', borderRadius: 99, background: isSel ? color : 'var(--ink-200)', width: `${barW}%`, transition: 'width .4s, background .2s' }} />
                        </div>
                      </div>
                    </button>
                  );
                })
            }
          </div>

          {/* Footer */}
          <div style={{ padding: '8px 14px', borderTop: '1px solid var(--ink-100)', background: 'var(--ink-50)', flexShrink: 0, display: 'flex', justifyContent: 'space-between', fontSize: 11.5, color: 'var(--ink-500)' }}>
            <span>{items.length} {items.length === 1 ? 'loja' : 'lojas'} no período</span>
            <span style={{ fontWeight: 600, color: 'var(--ink-700)' }}>{selected.size} selecionadas</span>
          </div>
        </div>
      )}

      {/* Botão toggle */}
      <button onClick={() => setExpanded(e => !e)} style={{
        pointerEvents: 'auto',
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '10px 16px',
        background: expanded ? 'var(--positive)' : 'var(--white)',
        color: expanded ? 'var(--white)' : 'var(--positive)',
        border: `1.5px solid ${expanded ? 'var(--positive)' : 'var(--ink-200)'}`,
        borderRadius: 40, cursor: 'pointer', fontFamily: 'inherit',
        fontSize: 13, fontWeight: 600,
        boxShadow: 'var(--shadow-md)',
        transition: 'all .2s',
      }}>
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <circle cx="12" cy="12" r="10"/><path d="M12 2a10 10 0 0 1 0 20M12 2C6.5 2 2 6.5 2 12M12 22C17.5 22 22 17.5 22 12"/>
        </svg>
        {expanded ? 'Fechar' : 'Composição de Entregas'}
        {selected.size > 0 && !expanded && (
          <span style={{ background: 'var(--positive)', color: 'var(--white)', borderRadius: 10, fontSize: 10, fontWeight: 700, padding: '1px 6px', marginLeft: 2 }}>{selected.size}</span>
        )}
      </button>
    </div>
  );
}

// ─── Em breve ─────────────────────────────────────────────────────────────────
function ComingSoon({ route }) {
  const meta = {
    comparative: { label: 'Comparativo',    sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
    analytics:   { label: 'Análises',       sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
    deliveries:  { label: 'Entregas',       sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
    dre:         { label: 'DRE',            sub: 'A Demonstração do Resultado do Exercício é exclusiva do painel web e está sendo desenvolvida. Em breve disponível.' },
    goals:       { label: 'Metas',          sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
    stock:       { label: 'Estoque',        sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
    bills:       { label: 'Contas a Pagar', sub: 'Estamos preparando este módulo. Em breve estará disponível com os mesmos dados do app mobile.' },
  };
  const { label, sub } = meta[route] ?? { label: route, sub: 'Em breve disponível.' };
  return (
    <div className="card" style={{ padding: 64, textAlign: 'center', color: 'var(--ink-500)' }}>
      <div style={{ fontSize: 40, marginBottom: 16 }}>🚧</div>
      <div style={{ fontSize: 18, fontWeight: 700, color: 'var(--ink-800)', marginBottom: 8 }}>
        {label} em construção
      </div>
      <div style={{ fontSize: 14, maxWidth: 380, margin: '0 auto' }}>
        {sub}
      </div>
    </div>
  );
}

// ─── Utilitários de UI ────────────────────────────────────────────────────────
function EmptyState({ text, style: extraStyle }) {
  return (
    <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-400)', fontSize: 14, ...extraStyle }}>
      {text}
    </div>
  );
}

function ErrorState() {
  return (
    <div className="card" style={{ padding: 40, textAlign: 'center' }}>
      <div style={{ fontSize: 32, marginBottom: 12 }}>⚠️</div>
      <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--danger)', marginBottom: 6 }}>Erro ao carregar dados</div>
      <div style={{ fontSize: 13, color: 'var(--ink-500)' }}>Verifique sua conexão e recarregue a página.</div>
    </div>
  );
}

// ─── Painel de Suporte ────────────────────────────────────────────────────────

// Parametros.ultimaComunicacao (heartbeat diário do coletor ≥ 1.0.2.9) → comunicou hoje?
// Retorna true/false quando o campo existe, ou null (coletor antigo) para o chamador
// cair no fallback antigo baseado no doc do dia.
function heartbeatToday(p) {
  const ts = p?.ultimaComunicacao;
  const d  = ts && typeof ts.toDate === 'function' ? ts.toDate() : null;
  if (!d) return null;
  const now = new Date();
  return d.getFullYear() === now.getFullYear()
      && d.getMonth()    === now.getMonth()
      && d.getDate()     === now.getDate();
}

function SupportView({ user, onLogout }) {
  const [clients,      setClients]      = React.useState([]);
  const [loading,      setLoading]      = React.useState(true);
  const [loadError,    setLoadError]    = React.useState(null);
  const [search,       setSearch]       = React.useState('');
  const [searchField,  setSearchField]  = React.useState('all');
  const [onlyProblems,   setOnlyProblems]   = React.useState(false);
  const [onlyNoVersion,  setOnlyNoVersion]  = React.useState(false);
  const [selectedCnpj, setSelectedCnpj] = React.useState(null);
  const [detail,       setDetail]       = React.useState(null);
  const [detailLoad,   setDetailLoad]   = React.useState(false);
  const [impersonate,  setImpersonate]  = React.useState(null);
  const [latestVersion, setLatestVersion] = React.useState(null);

  // Versão de referência do coletor — doc global config/Parametros.Versao
  // (distinto de loja/{cnpj}/config/Parametros.versao, que é a versão instalada em cada loja)
  React.useEffect(() => {
    let cancelled = false;
    SF.db.collection('config').doc('Parametros').get().then(snap => {
      if (cancelled) return;
      const v = snap.exists ? snap.data()?.Versao : null;
      setLatestVersion(v || null);
    }).catch(() => { if (!cancelled) setLatestVersion(null); });
    return () => { cancelled = true; };
  }, []);

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        // loja/{cnpj} são documentos implícitos — busca via collectionGroup
        const snap = await SF.db.collectionGroup('config').get();
        if (cancelled) return;
        const lojaMap = {};
        snap.docs.forEach(doc => {
          const parts = doc.ref.path.split('/');
          if (parts.length === 4 && parts[0] === 'loja') {
            const cnpj = parts[1];
            if (!lojaMap[cnpj]) lojaMap[cnpj] = { cnpj, p: {}, storeNames: [] };
            if (parts[3] === 'Parametros') lojaMap[cnpj].p = doc.data();
          }
        });

        // Carrega doc de hoje de cada loja para capturar storeNames e status de comunicação
        const todayStr = SF.today();
        const cnpjs    = Object.keys(lojaMap);
        const BATCH    = 10;
        for (let i = 0; i < cnpjs.length; i += BATCH) {
          if (cancelled) return;
          const slice   = cnpjs.slice(i, i + BATCH);
          const results = await Promise.allSettled(
            slice.map(cn => SF.db.collection('loja').doc(cn).collection('data').doc(todayStr).get())
          );
          results.forEach((r, j) => {
            const cn = slice[j];
            // Heartbeat do coletor é a fonte exata; doc do dia é fallback p/ coletores antigos
            const hb = heartbeatToday(lojaMap[cn].p);
            if (r.status === 'fulfilled' && r.value.exists) {
              const stores = r.value.data()?.stores ?? {};
              lojaMap[cn].storeNames = [...new Set(Object.values(stores).map(s => s.storeName).filter(Boolean))];
              lojaMap[cn].commToday  = hb ?? (Object.keys(stores).length > 0);
            } else {
              lojaMap[cn].commToday = hb ?? false;
            }
          });
        }

        // Fallback: lojas sem nome (não comunicaram hoje) — busca nos últimos 7 dias
        // Cobre o caso de 00h onde a maioria ainda não enviou o doc do dia
        for (let daysBack = 1; daysBack <= 7; daysBack++) {
          const noName = cnpjs.filter(cn => !lojaMap[cn].storeNames?.length);
          if (!noName.length) break;
          if (cancelled) return;
          const dateStr = SF.subtractDays(daysBack);
          for (let i = 0; i < noName.length; i += BATCH) {
            if (cancelled) return;
            const slice   = noName.slice(i, i + BATCH);
            const results = await Promise.allSettled(
              slice.map(cn => SF.db.collection('loja').doc(cn).collection('data').doc(dateStr).get())
            );
            results.forEach((r, j) => {
              const cn = slice[j];
              if (r.status === 'fulfilled' && r.value.exists) {
                const stores = r.value.data()?.stores ?? {};
                const names  = [...new Set(Object.values(stores).map(s => s.storeName).filter(Boolean))];
                if (names.length) lojaMap[cn].storeNames = names;
              }
            });
          }
        }

        const withP = Object.values(lojaMap).sort((a, b) => {
          const na = a.storeNames[0] ?? a.p?.corporateName ?? a.cnpj;
          const nb = b.storeNames[0] ?? b.p?.corporateName ?? b.cnpj;
          return na.localeCompare(nb, 'pt-BR');
        });
        if (!cancelled) { setClients(withP); setLoading(false); }
      } catch (err) {
        console.error('[SupportView] erro ao carregar clientes:', err);
        if (!cancelled) {
          setLoadError(err?.code || err?.message || String(err));
          setLoading(false);
        }
      }
    })();
    return () => { cancelled = true; };
  }, []);

  const filtered = React.useMemo(() => {
    const q = search.toLowerCase().trim();
    let list = clients;
    if (onlyProblems)  list = list.filter(c => !c.commToday);
    // Desatualizado = versão instalada (loja/{cnpj}/config/Parametros.versao) diferente da
    // versão de referência (config/Parametros.Versao, global). Sem versão instalada conta
    // como desatualizado também (nunca reportou). Enquanto a versão de referência não carrega,
    // cai no comportamento antigo (só sinaliza quem não tem versão nenhuma).
    if (onlyNoVersion) list = list.filter(c => latestVersion ? c.p?.versao !== latestVersion : !c.p?.versao);
    if (!q) return list;
    return list.filter(c => {
      const p  = c.p ?? {};
      const ad = String(p.anydesk ?? p.Anydesk ?? p.AnyDesk ?? '').toLowerCase();
      const cn = String(c.cnpj ?? '');
      const nm = [p.corporateName, p.nome, ...(c.storeNames ?? [])].filter(Boolean).join(' ').toLowerCase();
      if (searchField === 'cnpj')    return cn.includes(q);
      if (searchField === 'nome')    return nm.includes(q);
      if (searchField === 'anydesk') return ad.includes(q);
      return cn.includes(q) || nm.includes(q) || ad.includes(q);
    });
  }, [clients, search, searchField, onlyProblems, onlyNoVersion, latestVersion]);

  const selectClient = async (client) => {
    setSelectedCnpj(client.cnpj);
    setDetail(null);
    setDetailLoad(true);
    try {
      const todayStr   = SF.today();
      const matrizCnpj = client.p?.matrizCnpj ?? null;

      // Se esta loja é filial de outra, carrega Parametros da matriz para obter filiais
      let effectiveCnpj    = client.cnpj;
      let filialEntries    = Object.entries(client.p?.filiais ?? {});
      let matrizP          = client.p;

      if (matrizCnpj) {
        effectiveCnpj = matrizCnpj;
        const mSnap = await SF.db.collection('loja').doc(matrizCnpj).collection('config').doc('Parametros').get();
        matrizP      = mSnap.exists ? mSnap.data() : {};
        filialEntries = Object.entries(matrizP.filiais ?? {});
      }

      const [todaySnap, siteSnap, cfRaw, ...filialPairs] = await Promise.all([
        SF.db.collection('loja').doc(effectiveCnpj).collection('data').doc(todayStr).get(),
        SF.db.collection('loja').doc(effectiveCnpj).collection('config').doc('Site').get(),
        cfGet({ cnpj: effectiveCnpj }).catch(() => cfGet().catch(() => [])),
        ...filialEntries.map(([, fc]) => Promise.all([
          SF.db.collection('loja').doc(fc).collection('config').doc('Parametros').get(),
          SF.db.collection('loja').doc(fc).collection('data').doc(todayStr).get(),
        ]))
      ]);

      const filiaisDetail = filialEntries.map(([storeKey, fc], i) => {
        const [fpSnap, ftSnap] = filialPairs[i] ?? [];
        const fp = fpSnap?.exists ? fpSnap.data() : {};
        const ft = ftSnap?.exists ? ftSnap.data() : null;
        // Nome vem dos dados da própria filial. O prefixo antes do "_" (lojaKey) NÃO é
        // uma chave confiável para consultar Parametros.lojas da matriz: dois storeIds
        // interligados podem colidir no mesmo lojaKey (corrida no registro do coletor)
        // ou coincidir com uma loja "tipo M" não relacionada, contaminando o nome/status
        // de uma filial com o de outra loja completamente diferente.
        const storeName = Object.values(ft?.stores ?? {}).map(s => s.storeName).filter(Boolean)[0]
          ?? fp?.nome ?? null;
        return { storeKey, cnpj: fc, storeName, notFarmax: false, p: fp, today: ft };
      });

      // Filiais ainda sem nome (sem doc hoje e sem Parametros.nome): busca nos últimos 7 dias
      const unnamedFiliais = filiaisDetail.filter(f => !f.storeName);
      for (let d = 1; d <= 7 && unnamedFiliais.some(f => !f.storeName); d++) {
        const stillMissing = unnamedFiliais.filter(f => !f.storeName);
        const dateStr = SF.subtractDays(d);
        const snaps = await Promise.allSettled(
          stillMissing.map(f => SF.db.collection('loja').doc(f.cnpj).collection('data').doc(dateStr).get())
        );
        snaps.forEach((r, j) => {
          if (r.status !== 'fulfilled' || !r.value.exists) return;
          const name = Object.values(r.value.data()?.stores ?? {}).map(s => s.storeName).filter(Boolean)[0];
          if (name) stillMissing[j].storeName = name;
        });
      }

      const linkedUsers = (Array.isArray(cfRaw) ? cfRaw : [])
        .filter(u => (u.customClaims?.cnpj || u.cnpj) === effectiveCnpj)
        .map(u => ({ uid: u.uid, email: u.email || '', name: u.displayName || '', type: u.customClaims?.type ?? u.type ?? '', admin: !!(u.customClaims?.admin ?? u.admin) }));

      setDetail({
        ...client,
        cnpj:        effectiveCnpj,
        p:           matrizP,
        isFilialOf:  matrizCnpj,          // CNPJ original buscado se era filial
        searchedCnpj: matrizCnpj ? client.cnpj : null,
        todayData:   todaySnap.exists ? todaySnap.data() : null,
        siteData:    siteSnap.exists  ? siteSnap.data()  : {},
        filiaisDetail,
        linkedUsers,
      });
    } catch { setDetail(client); }
    finally   { setDetailLoad(false); }
  };

  // ── "Ver no site": abre o Dashboard como se fosse o cliente (admin, só leitura) ──
  const enterImpersonation = (det) => {
    SF.clearSession();      // limpa cache/listeners e reseta mapa de filiais
    SF.setReadOnly(true);   // bloqueia qualquer gravação na conta do cliente
    setImpersonate({
      uid:           user?.uid,
      email:         user?.email,
      name:          det.p?.corporateName || det.p?.nome || det.cnpj,
      cnpj:          det.cnpj,
      corporateName: det.p?.corporateName || det.p?.nome || '',
      status:        'active',
      type:          'M',
      admin:         true,
      allowedPages:  [0, 1, 2, 3, 4, 5, 6, 7],
      allowedStores: true,   // todas as lojas
      impersonated:  true,
    });
  };
  const exitImpersonation = () => {
    SF.setReadOnly(false);
    SF.clearSession();
    setImpersonate(null);
  };

  // Enquanto impersonando, mostra o Dashboard do cliente no lugar do painel
  if (impersonate) {
    return <Dashboard user={impersonate} navigate={() => {}} onLogout={exitImpersonation} />;
  }

  const FIELDS = { all: 'Todos', nome: 'Nome', cnpj: 'CNPJ', anydesk: 'AnyDesk' };

  return (
    <div style={{ minHeight: '100vh', background: 'var(--ink-50)', display: 'flex', flexDirection: 'column' }}>
      <div style={{ background: 'var(--white)', borderBottom: '1px solid var(--ink-200)', padding: '0 24px', height: 56, display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <Logo size={22} />
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-400)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>Painel de Suporte</span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <span style={{ fontSize: 12, color: 'var(--ink-400)' }}>{user?.email}</span>
          <button onClick={onLogout} style={{ fontSize: 12, fontWeight: 700, color: 'var(--danger)', background: 'none', border: 'none', cursor: 'pointer', padding: '4px 10px', borderRadius: 6 }}>Sair</button>
        </div>
      </div>

      <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
        {/* Lista de clientes */}
        <div style={{ width: 360, flexShrink: 0, display: 'flex', flexDirection: 'column', borderRight: '1px solid var(--ink-200)', background: 'var(--white)' }}>
          <div style={{ padding: '14px 14px 10px', borderBottom: '1px solid var(--ink-100)' }}>
            <input
              value={search} onChange={e => setSearch(e.target.value)}
              placeholder="Buscar cliente..."
              style={{ width: '100%', padding: '9px 12px', border: '1px solid var(--ink-200)', borderRadius: 8, fontSize: 13, fontFamily: 'inherit', outline: 'none', background: 'var(--ink-50)', color: 'var(--ink-900)' }}
            />
            <div style={{ display: 'flex', gap: 5, marginTop: 8, flexWrap: 'wrap' }}>
              {Object.entries(FIELDS).map(([f, label]) => (
                <button key={f} onClick={() => setSearchField(f)} style={{
                  fontSize: 10, fontWeight: 700, padding: '3px 8px', borderRadius: 5, cursor: 'pointer', border: '1px solid',
                  background:  searchField === f ? 'var(--brand-500)' : 'var(--ink-100)',
                  color:       searchField === f ? '#fff'             : 'var(--ink-500)',
                  borderColor: searchField === f ? 'var(--brand-500)' : 'var(--ink-200)',
                  textTransform: 'uppercase', letterSpacing: '0.04em',
                }}>{label}</button>
              ))}
            </div>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 8 }}>
              <span style={{ fontSize: 11, color: 'var(--ink-400)' }}>
                {loading ? 'Carregando...' : `${filtered.length} de ${clients.length} clientes`}
              </span>
              {!loading && (
                <div style={{ display: 'flex', gap: 5 }}>
                  <button onClick={() => setOnlyProblems(v => !v)} style={{
                    fontSize: 10, fontWeight: 700, padding: '3px 8px', borderRadius: 5, cursor: 'pointer',
                    border: '1px solid',
                    background:  onlyProblems ? 'var(--danger)'    : 'var(--ink-100)',
                    color:       onlyProblems ? '#fff'             : 'var(--ink-500)',
                    borderColor: onlyProblems ? 'var(--danger)'    : 'var(--ink-200)',
                    textTransform: 'uppercase', letterSpacing: '0.04em',
                  }}>
                    {onlyProblems
                      ? `${clients.filter(c => !c.commToday).length} sem comunicação`
                      : 'Problemas'}
                  </button>
                  <button onClick={() => setOnlyNoVersion(v => !v)}
                    title={latestVersion ? `Versão de referência: ${latestVersion}` : 'Versão de referência não configurada (config/Parametros.Versao)'}
                    style={{
                    fontSize: 10, fontWeight: 700, padding: '3px 8px', borderRadius: 5, cursor: 'pointer',
                    border: '1px solid',
                    background:  onlyNoVersion ? 'var(--warning)'   : 'var(--ink-100)',
                    color:       onlyNoVersion ? '#fff'             : 'var(--ink-500)',
                    borderColor: onlyNoVersion ? 'var(--warning)'   : 'var(--ink-200)',
                    textTransform: 'uppercase', letterSpacing: '0.04em',
                  }}>
                    {onlyNoVersion
                      ? `${clients.filter(c => latestVersion ? c.p?.versao !== latestVersion : !c.p?.versao).length} desatualizados`
                      : 'Versão'}
                  </button>
                </div>
              )}
            </div>
          </div>

          <div style={{ flex: 1, overflowY: 'auto' }}>
            {loading ? (
              <div style={{ display: 'flex', justifyContent: 'center', padding: 32 }}><Spinner size={24} color="var(--brand-500)" /></div>
            ) : loadError ? (
              <div style={{ padding: 20, margin: 14, background: 'var(--danger-bg)', borderRadius: 8, border: '1px solid var(--danger)' }}>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--danger)', marginBottom: 6 }}>Erro ao carregar clientes</div>
                <div className="mono" style={{ fontSize: 11, color: 'var(--danger)', wordBreak: 'break-all' }}>{loadError}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-600)', marginTop: 8 }}>
                  Se for <b>permission-denied</b>, adicione no Firestore Rules:
                  <br /><code style={{ fontSize: 10 }}>allow read, list: if request.auth.token.email == '{user?.email}';</code>
                </div>
              </div>
            ) : filtered.length === 0 ? (
              <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-400)', fontSize: 13 }}>
                {search ? 'Nenhum resultado' : 'Nenhum cliente'}
              </div>
            ) : filtered.map(c => {
              const p   = c.p ?? {};
              const ad  = p.anydesk ?? p.Anydesk ?? p.AnyDesk ?? '';
              const em  = p.email   ?? p.Email   ?? c.email ?? '';
              const sel = c.cnpj === selectedCnpj;
              return (
                <button key={c.cnpj} onClick={() => selectClient(c)} style={{
                  display: 'block', width: '100%', textAlign: 'left', padding: '11px 16px',
                  background: sel ? 'var(--brand-50)' : 'transparent',
                  border: 'none', borderBottom: '1px solid var(--ink-100)', cursor: 'pointer',
                  borderLeft: `3px solid ${sel ? 'var(--brand-500)' : 'transparent'}`,
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 2 }}>
                    {!c.commToday && (
                      <span title="Sem comunicação hoje" style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--danger)', flexShrink: 0, display: 'inline-block' }} />
                    )}
                    <span style={{ fontWeight: 700, fontSize: 13, color: sel ? 'var(--brand-700)' : 'var(--ink-900)' }}>
                      {c.storeNames?.[0] || c.p?.corporateName || c.p?.nome || c.cnpj}
                    </span>
                  </div>
                  <div className="mono" style={{ fontSize: 11, color: 'var(--ink-400)' }}>{c.cnpj}</div>
                  {(em || ad) && (
                    <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 2 }}>
                      {em && <span>{em}</span>}
                      {em && ad && <span style={{ margin: '0 5px', color: 'var(--ink-300)' }}>·</span>}
                      {ad && <span className="mono">AnyDesk {ad}</span>}
                    </div>
                  )}
                </button>
              );
            })}
          </div>
        </div>

        {/* Detalhe do cliente */}
        <div style={{ flex: 1, overflowY: 'auto', padding: 28 }}>
          {!selectedCnpj ? (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--ink-400)', fontSize: 14, flexDirection: 'column', gap: 10 }}>
              <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
              <span>Selecione um cliente para ver os detalhes</span>
            </div>
          ) : detailLoad ? (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
              <Spinner size={28} color="var(--brand-500)" />
            </div>
          ) : detail ? (
            <SupportClientDetail detail={detail} onImpersonate={enterImpersonation} latestVersion={latestVersion} />
          ) : null}
        </div>
      </div>
    </div>
  );
}

// ─── Support: histórico filtrável de observações ──────────────────────────────
function SupportObsHistory({ obs, search, setSearch, dateFilter, setDateFilter, showAll, setShowAll, emptyText }) {
  const PAGE = 10;
  const fmt  = (d) => { if (!d) return ''; const p = d.split('-'); return p.length === 3 ? `${p[2]}/${p[1]}/${p[0]}` : d; };

  const filtered = React.useMemo(() => {
    let list = [...(obs ?? [])].sort((a, b) => (b.ts ?? b.date ?? '').localeCompare(a.ts ?? a.date ?? ''));
    if (dateFilter) list = list.filter(o => o.date?.startsWith(dateFilter));
    if (search.trim()) { const q = search.toLowerCase(); list = list.filter(o => o.text?.toLowerCase().includes(q)); }
    return list;
  }, [obs, search, dateFilter]);

  const visible   = showAll ? filtered : filtered.slice(0, PAGE);
  const remaining = filtered.length - PAGE;

  return (
    <div>
      <div style={{ display: 'flex', gap: 7, marginBottom: 10 }}>
        <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Buscar por palavra..."
          style={{ flex: 1, padding: '6px 10px', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontFamily: 'inherit', background: 'var(--ink-50)', color: 'var(--ink-900)', outline: 'none' }} />
        <input type="date" value={dateFilter} onChange={e => setDateFilter(e.target.value)}
          style={{ padding: '6px 10px', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontFamily: 'inherit', background: 'var(--ink-50)', color: 'var(--ink-900)', outline: 'none' }} />
        {(search || dateFilter) && (
          <button onClick={() => { setSearch(''); setDateFilter(''); }}
            style={{ padding: '6px 10px', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontWeight: 700, color: 'var(--ink-500)', background: 'var(--ink-100)', cursor: 'pointer', fontFamily: 'inherit' }}>✕</button>
        )}
      </div>
      {filtered.length === 0
        ? <div style={{ padding: '10px 0', color: 'var(--ink-400)', fontSize: 12, textAlign: 'center' }}>{(search || dateFilter) ? 'Nenhum resultado.' : emptyText}</div>
        : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
            {visible.map((entry, i) => (
              <div key={i} style={{ padding: '9px 12px', borderRadius: 8, background: 'var(--ink-50)', border: '1px solid var(--ink-100)' }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--ink-400)', marginBottom: 3, letterSpacing: '0.03em' }}>{fmt(entry.date)}</div>
                <div style={{ fontSize: 12, color: 'var(--ink-800)', lineHeight: 1.55, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{entry.text}</div>
              </div>
            ))}
            {!showAll && remaining > 0 && (
              <button onClick={() => setShowAll(true)}
                style={{ padding: '7px 0', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontWeight: 600, color: 'var(--brand-600)', background: 'var(--brand-50)', cursor: 'pointer', fontFamily: 'inherit' }}>
                Mostrar mais {remaining}
              </button>
            )}
          </div>
        )
      }
    </div>
  );
}

// ─── Support: formulário de nova observação ────────────────────────────────────
function SupportObsForm({ date, setDate, text, setText, onSave, saving }) {
  return (
    <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid var(--ink-100)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
        <input type="date" value={date} onChange={e => setDate(e.target.value)}
          style={{ padding: '5px 9px', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontFamily: 'inherit', background: 'var(--ink-50)', color: 'var(--ink-900)', outline: 'none' }} />
        <span style={{ fontSize: 10, fontWeight: 600, color: 'var(--ink-400)' }}>data do registro</span>
      </div>
      <textarea value={text} onChange={e => setText(e.target.value)} placeholder="Escreva a observação..." rows={3}
        style={{ width: '100%', padding: '8px 10px', border: '1px solid var(--ink-200)', borderRadius: 7, fontSize: 12, fontFamily: 'inherit', background: 'var(--ink-50)', color: 'var(--ink-900)', outline: 'none', resize: 'vertical', boxSizing: 'border-box' }} />
      <button onClick={onSave} disabled={!text.trim() || saving}
        style={{ marginTop: 6, padding: '7px 20px', borderRadius: 7, border: 'none', fontSize: 12, fontWeight: 700, cursor: text.trim() && !saving ? 'pointer' : 'default', fontFamily: 'inherit',
          background: text.trim() && !saving ? 'var(--brand-500)' : 'var(--ink-200)',
          color:      text.trim() && !saving ? 'var(--white)'     : 'var(--ink-400)',
        }}>
        {saving ? 'Salvando...' : 'Registrar'}
      </button>
    </div>
  );
}

// ─── Support: switch "Alterar Senha" por linha (config/Parametros, por CNPJ) ────
function SupportAlterarSenhaSwitch({ cnpj, initial }) {
  const [on, setOn]         = React.useState(!!initial);
  const [saving, setSaving] = React.useState(false);

  // Mantém sincronizado quando troca o CNPJ exibido
  React.useEffect(() => { setOn(!!initial); }, [cnpj, initial]);

  const toggle = async () => {
    if (saving) return;
    const next = !on;
    setOn(next);          // otimista
    setSaving(true);
    try {
      await SF.db.collection('loja').doc(cnpj).collection('config').doc('Parametros')
        .set({ alterarSenha: next }, { merge: true });
    } catch (e) {
      setOn(!next);       // reverte em caso de erro
      alert('Erro ao salvar: ' + (e?.message ?? String(e)));
    } finally {
      setSaving(false);
    }
  };

  return (
    <button
      onClick={toggle}
      disabled={saving}
      role="switch"
      aria-checked={on}
      title={on ? 'Ativado — clique para desativar' : 'Desativado — clique para ativar'}
      style={{
        position: 'relative', width: 38, height: 22, borderRadius: 20, border: 'none', flexShrink: 0,
        cursor: saving ? 'default' : 'pointer', padding: 0, transition: 'background .15s', verticalAlign: 'middle',
        background: on ? 'var(--brand-500)' : 'var(--ink-300)', opacity: saving ? 0.55 : 1,
      }}>
      <span style={{
        position: 'absolute', top: 3, left: on ? 19 : 3, width: 16, height: 16, borderRadius: '50%',
        background: 'var(--white)', transition: 'left .15s', boxShadow: '0 1px 2px rgba(0,0,0,0.25)',
      }} />
    </button>
  );
}

// ─── Support: painel completo de observações (rede + por loja) ─────────────────
function SupportObservations({ cnpj, storeRows, initialSuporte }) {
  const [suporte,      setSuporte]      = React.useState(() => initialSuporte ?? { rede: [], lojas: {} });
  const [saving,       setSaving]       = React.useState(null); // null | 'rede' | storeCnpj

  // Estado da seção Rede
  const [redeText,       setRedeText]       = React.useState('');
  const [redeDate,       setRedeDate]       = React.useState(SF.today());
  const [redeSearch,     setRedeSearch]     = React.useState('');
  const [redeDateFilter, setRedeDateFilter] = React.useState('');
  const [redeShowAll,    setRedeShowAll]    = React.useState(false);

  // Estado por loja
  const [expandedStore, setExpandedStore] = React.useState(null);
  const [storeStates,   setStoreStates]   = React.useState({});
  const getSt  = (sc) => storeStates[sc] ?? { text: '', date: SF.today(), search: '', dateFilter: '', showAll: false };
  const setSt  = (sc, field, val) => setStoreStates(prev => ({ ...prev, [sc]: { ...getSt(sc), [field]: val } }));

  const persist = async (newData, tag) => {
    setSaving(tag);
    try {
      await SF.db.collection('loja').doc(cnpj).collection('config').doc('Site').set({ Suporte: newData }, { merge: true });
      setSuporte(newData);
    } catch (e) { alert('Erro ao salvar: ' + (e?.message ?? String(e))); }
    finally     { setSaving(null); }
  };

  const addRede = async () => {
    const t = redeText.trim();
    if (!t || saving) return;
    const entry = { date: redeDate, text: t, ts: new Date().toISOString() };
    await persist({ ...suporte, rede: [entry, ...(suporte.rede ?? [])] }, 'rede');
    setRedeText('');
  };

  const addStore = async (sc) => {
    const s = getSt(sc);
    const t = s.text.trim();
    if (!t || saving) return;
    const entry = { date: s.date, text: t, ts: new Date().toISOString() };
    const lojas = { ...(suporte.lojas ?? {}), [sc]: [entry, ...(suporte.lojas?.[sc] ?? [])] };
    await persist({ ...suporte, lojas }, sc);
    setSt(sc, 'text', '');
  };

  return (
    <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>

      {/* ── Observações da Rede ── */}
      <div className="card" style={{ padding: 16 }}>
        <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 12 }}>Observações da Rede</div>
        <SupportObsHistory
          obs={suporte.rede ?? []}
          search={redeSearch}         setSearch={setRedeSearch}
          dateFilter={redeDateFilter} setDateFilter={setRedeDateFilter}
          showAll={redeShowAll}       setShowAll={setRedeShowAll}
          emptyText="Nenhuma observação registrada para a rede."
        />
        <SupportObsForm
          date={redeDate} setDate={setRedeDate}
          text={redeText} setText={setRedeText}
          onSave={addRede} saving={saving === 'rede'}
        />
      </div>

      {/* ── Observações por Loja ── */}
      <div className="card" style={{ padding: 16 }}>
        <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 12 }}>Observações por Loja</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
          {storeRows.map(row => {
            const isOpen   = expandedStore === row.cnpj;
            const s        = getSt(row.cnpj);
            const storeObs = suporte.lojas?.[row.cnpj] ?? [];

            return (
              <div key={row.cnpj} style={{ borderRadius: 10, border: `1px solid ${isOpen ? 'var(--brand-200)' : 'var(--ink-200)'}`, overflow: 'hidden', transition: 'border-color .15s' }}>
                <button
                  onClick={() => setExpandedStore(isOpen ? null : row.cnpj)}
                  style={{ width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '10px 14px', background: isOpen ? 'var(--brand-50)' : 'var(--white)', border: 'none', cursor: 'pointer', fontFamily: 'inherit', transition: 'background .12s' }}
                >
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontSize: 13, fontWeight: 600, color: isOpen ? 'var(--brand-700)' : 'var(--ink-900)' }}>{row.name}</span>
                    {storeObs.length > 0 && (
                      <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--brand-600)', background: 'var(--brand-50)', border: '1px solid var(--brand-200)', borderRadius: 20, padding: '1px 7px', flexShrink: 0 }}>
                        {storeObs.length}
                      </span>
                    )}
                  </div>
                  <svg width="10" height="10" viewBox="0 0 10 10" fill="none"
                    style={{ flexShrink: 0, transition: 'transform .15s', transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)', color: isOpen ? 'var(--brand-500)' : 'var(--ink-400)' }}>
                    <path d="M2 3.5L5 6.5L8 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                </button>
                {isOpen && (
                  <div style={{ padding: '12px 14px 14px', borderTop: '1px solid var(--ink-100)' }}>
                    <SupportObsHistory
                      obs={storeObs}
                      search={s.search}         setSearch={v => setSt(row.cnpj, 'search', v)}
                      dateFilter={s.dateFilter} setDateFilter={v => setSt(row.cnpj, 'dateFilter', v)}
                      showAll={s.showAll}       setShowAll={v => setSt(row.cnpj, 'showAll', v)}
                      emptyText={`Nenhuma observação registrada para ${row.name}.`}
                    />
                    <SupportObsForm
                      date={s.date} setDate={v => setSt(row.cnpj, 'date', v)}
                      text={s.text} setText={v => setSt(row.cnpj, 'text', v)}
                      onSave={() => addStore(row.cnpj)} saving={saving === row.cnpj}
                    />
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function SupportClientDetail({ detail, onImpersonate, latestVersion }) {
  const p       = detail.p ?? {};
  const anydesk = p.anydesk ?? p.Anydesk ?? p.AnyDesk ?? '';
  const filiaisDetail = detail.filiaisDetail ?? [];

  const pad2 = n => String(n).padStart(2, '0');
  const nd = new Date();
  const todayFmt = `${pad2(nd.getDate())}/${pad2(nd.getMonth()+1)}/${nd.getFullYear()}`;

  // Monta lista unificada: matriz + filiais
  const matrizStores = Object.entries(detail.todayData?.stores ?? {});
  const matrizTotal  = matrizStores.reduce((s, [, st]) => s + safeN(st.total), 0);
  const matrizCount  = matrizStores.reduce((s, [, st]) => s + safeN(st.salesCount), 0);
  // Heartbeat (Parametros.ultimaComunicacao) é a fonte exata; doc do dia é fallback
  const matrizComm   = heartbeatToday(p) ?? (matrizStores.length > 0);
  const matrizName   = matrizStores[0]?.[1]?.storeName
    ?? Object.values(p.lojas ?? {}).find(n => n)   // nome persistido em Parametros.lojas
    ?? p.corporateName ?? p.nome ?? detail.cnpj;

  const allRows = [
    {
      name:    matrizName,
      cnpj:    detail.cnpj,
      anydesk: anydesk,
      comm:    matrizComm,
      total:   matrizTotal,
      count:   matrizCount,
      isMatriz: true,
      versao:  p.versao ?? null,
      alterarSenha: !!p.alterarSenha,
    },
    ...filiaisDetail.map(f => {
      const fSt    = f.today?.stores ?? {};
      const fTotal = Object.values(fSt).reduce((s, st) => s + safeN(st.total), 0);
      const fCount = Object.values(fSt).reduce((s, st) => s + safeN(st.salesCount), 0);
      const fComm  = heartbeatToday(f.p) ?? (Object.keys(fSt).length > 0);
      const fAD    = f.p?.anydesk ?? f.p?.Anydesk ?? f.p?.AnyDesk ?? '';
      const fName  = f.storeName ?? Object.values(fSt)[0]?.storeName ?? f.p?.nome ?? `Loja ${f.storeKey}`;
      return { name: fName, cnpj: f.cnpj, anydesk: fAD, comm: fComm, total: fTotal, count: fCount, isMatriz: false, notFarmax: !!f.notFarmax, versao: f.p?.versao ?? null, alterarSenha: !!f.p?.alterarSenha };
    }),
  ];

  const totalRede     = allRows.reduce((s, r) => s + r.total, 0);
  const commCount     = allRows.filter(r => r.comm).length;
  const notFarmaxCount = allRows.filter(r => r.notFarmax).length;
  const notCommCount  = allRows.filter(r => !r.comm && !r.notFarmax).length;
  const zeroSales     = allRows.filter(r => r.comm && r.total === 0).length;
  const allOk         = notCommCount === 0 && zeroSales === 0;

  const StatusDot = ({ comm, total, notFarmax }) => {
    const color = notFarmax ? '#7C3AED' : !comm ? 'var(--danger)' : total === 0 ? 'var(--warning)' : 'var(--positive)';
    const label = notFarmax ? 'Não Farmax' : !comm ? 'Sem comunicação' : total === 0 ? 'R$ zero' : 'OK';
    return (
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, fontWeight: 700, color }}>
        <span style={{ width: 8, height: 8, borderRadius: '50%', background: color, display: 'inline-block' }} />
        {label}
      </span>
    );
  };

  return (
    <div style={{ maxWidth: 900 }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 20, gap: 16 }}>
        <div>
          <h2 style={{ margin: 0, fontSize: 20, fontWeight: 800, color: 'var(--ink-900)' }}>
            {p.corporateName || p.nome || detail.corporateName || detail.nome || detail.cnpj}
          </h2>
          <div className="mono" style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 3 }}>CNPJ Matriz: {detail.cnpj}</div>
          {filiaisDetail.length > 0 && (
            <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 2 }}>
              Este CNPJ possui <strong>{filiaisDetail.length}</strong> {filiaisDetail.length === 1 ? 'loja vinculada' : 'lojas vinculadas'}
            </div>
          )}
          {onImpersonate && (
            <button
              onClick={() => onImpersonate(detail)}
              title="Abre o dashboard"
              style={{ marginTop: 12, display: 'inline-flex', alignItems: 'center', gap: 7, padding: '8px 16px', background: '#7C3AED', color: '#fff', border: 'none', borderRadius: 8, fontSize: 12.5, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}
            >
             
              Ver no site (ler)
            </button>
          )}
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px',
          borderRadius: 20, fontSize: 12, fontWeight: 700,
          background: allOk ? 'var(--positive-bg)' : 'var(--danger-bg)',
          color: allOk ? 'var(--positive)' : 'var(--danger)',
          border: `1px solid ${allOk ? 'var(--positive)' : 'var(--danger)'}`,
          whiteSpace: 'nowrap', flexShrink: 0,
        }}>
          <span style={{ width: 8, height: 8, borderRadius: '50%', background: 'currentColor', display: 'inline-block' }} />
          {allOk ? `Tudo OK — ${todayFmt}` : [notCommCount > 0 && `${notCommCount} sem comunicação`, zeroSales > 0 && `${zeroSales} com R$ zero`, notFarmaxCount > 0 && `${notFarmaxCount} não Farmax`].filter(Boolean).join(' · ')}
        </div>
      </div>

      {/* Cards resumo */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12, marginBottom: 20 }}>
        <div className="card" style={{ padding: 14 }}>
          <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 8 }}>AnyDesk</div>
          {anydesk
            ? <div className="mono" style={{ fontSize: 20, fontWeight: 800, color: 'var(--ink-900)' }}>{anydesk}</div>
            : <div style={{ fontSize: 12, color: 'var(--ink-400)' }}>Não cadastrado</div>}
        </div>

        <div className="card" style={{ padding: 14 }}>
          <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 8 }}>Vendas Hoje</div>
          <div className="mono" style={{ fontSize: 20, fontWeight: 800, color: totalRede === 0 ? 'var(--danger)' : 'var(--brand-700)' }}>{BRL(totalRede)}</div>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 3 }}>{allRows.reduce((s, r) => s + r.count, 0)} cupons</div>
        </div>

        <div className="card" style={{ padding: 14 }}>
          <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 8 }}>Comunicação Hoje</div>
          <div style={{ fontSize: 20, fontWeight: 800, color: notCommCount > 0 ? 'var(--danger)' : 'var(--positive)' }}>
            {commCount}/{allRows.length}
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-400)', marginTop: 3 }}>
            {notCommCount === 0 && notFarmaxCount === 0 ? 'todas enviando' : [notCommCount > 0 && `${notCommCount} sem comunicação`, notFarmaxCount > 0 && `${notFarmaxCount} não Farmax`].filter(Boolean).join(' · ')}
          </div>
        </div>
      </div>

      {/* Tabela unificada de lojas */}
      <div className="card" style={{ padding: 16 }}>
        <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-400)', marginBottom: 12 }}>
          Lojas — {todayFmt} ({allRows.length} {allRows.length === 1 ? 'loja' : 'lojas'})
        </div>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
          <thead>
            <tr style={{ borderBottom: '1px solid var(--ink-200)' }}>
              <th style={{ padding: '6px 10px 6px 0', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'left' }}>Loja</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'left' }}>CNPJ</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'left' }}>AnyDesk</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'left' }}>Versão</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'center' }}>Status</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'center' }}>Alterar Senha</th>
              <th style={{ padding: '6px 10px', fontWeight: 600, color: 'var(--ink-500)', textAlign: 'right' }}>Vendas</th>
            </tr>
          </thead>
          <tbody>
            {allRows.map((r, i) => {
              const rowBg   = !r.comm ? 'rgba(194,54,58,0.04)' : r.total === 0 ? 'rgba(179,100,10,0.04)' : i % 2 === 0 ? 'transparent' : 'var(--ink-50)';
              const salesColor = !r.comm ? 'var(--ink-300)' : r.total === 0 ? 'var(--danger)' : 'var(--brand-700)';
              return (
                <tr key={r.cnpj} style={{ background: rowBg, borderBottom: '1px solid var(--ink-100)' }}>
                  <td style={{ padding: '10px 10px 10px 0', fontWeight: 700 }}>
                    {r.name}
                    {r.isMatriz && <span style={{ marginLeft: 5, fontSize: 9, fontWeight: 700, color: 'var(--brand-600)', background: 'var(--brand-100)', padding: '1px 4px', borderRadius: 3 }}>M</span>}
                  </td>
                  <td className="mono" style={{ padding: '10px', fontSize: 10, color: 'var(--ink-400)' }}>{r.cnpj}</td>
                  <td className="mono" style={{ padding: '10px', fontSize: 12, fontWeight: 700, color: r.anydesk ? 'var(--ink-900)' : 'var(--ink-300)' }}>{r.anydesk || '—'}</td>
                  <td className="mono" style={{ padding: '10px', fontSize: 12, fontWeight: latestVersion && r.versao !== latestVersion ? 700 : 400, color: !r.versao ? 'var(--ink-300)' : (latestVersion && r.versao !== latestVersion) ? 'var(--warning)' : 'var(--ink-700)' }} title={latestVersion ? `Referência: ${latestVersion}` : undefined}>{r.versao || '—'}</td>
                  <td style={{ padding: '10px', textAlign: 'center' }}><StatusDot comm={r.comm} total={r.total} notFarmax={r.notFarmax} /></td>
                  <td style={{ padding: '10px', textAlign: 'center' }}><SupportAlterarSenhaSwitch cnpj={r.cnpj} initial={r.alterarSenha} /></td>
                  <td className="mono" style={{ padding: '10px', textAlign: 'right', fontWeight: 800, color: salesColor }}>{BRL(r.total)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      <SupportObservations
        cnpj={detail.cnpj}
        storeRows={allRows}
        initialSuporte={detail.siteData?.Suporte}
      />
    </div>
  );
}

function SupportInfoRow({ label, value, mono }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
      <span style={{ fontSize: 12, color: 'var(--ink-500)' }}>{label}</span>
      <span className={mono ? 'mono' : undefined} style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink-900)', maxWidth: '65%', textAlign: 'right', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
        {value}
      </span>
    </div>
  );
}

Object.assign(window, { Dashboard, SupportView });