// ============================================================
// App "Manejo de Pasto" (campo) · telas
// Refinamento UX (rev. 2) para uso EM CAMPO — mão suja / pressa / sol forte:
//   • MENOS cartões: a Home agrupa tudo em poucas superfícies com
//     hierarquia clara (1 ação herói + 1 grupo de ações + 1 lista do dia).
//   • Conexão deixou de ser um cartão grande → virou uma TIRA sucinta de 1 linha.
//   • "Hoje" mostra o que o operador lançou no dia em UMA lista (linhas com
//     divisória, não um cartão por item), com EDITAR e EXCLUIR por toque.
//   • Não há "movimentação" na lista: mover um lote grava uma saída + uma
//     entrada, exibidas como dois lançamentos (só Entradas, Saídas e Eventos).
// Tudo é seleção por SEÇÃO → MÓDULO. Reaproveita o shell móvel + modelo PAST.
// ============================================================
const DS = () => window.FazendaGuanabaraDesignSystem_b62f7e
const P = () => window.PAST
const MC = window.MC

// ---- Vocabulário visual dos tipos de lançamento ---------------------------
// Só existem Entradas e Saídas (e Eventos de manejo). Mover o lote = uma
// saída na origem + uma entrada no destino — exibido como UM "Movido".
const TIPO_UI = {
  entrada: { label: 'Entrada', cor: 'var(--color-success-dark)', icon: 'add-circle-outline' },
  saida:   { label: 'Saída',   cor: 'var(--color-danger)',       icon: 'remove' },
  evento:  { label: 'Evento',  cor: 'var(--color-warning)',      icon: 'clipboard' },
  mover:   { label: 'Movido',  cor: 'var(--color-info)',         icon: 'chevron-forward' },
}

// ---- Tipos de evento — fonte única: Pa.EVENTOS_PASTO (lista do cliente) ----
const grupoDoEvento = (nome) => nome.startsWith('Aplicação Adubo') ? 'Adubação' : (nome === 'Roçada' ? 'Manejo' : 'Defensivos')
const EVENTO_TIPOS = P().EVENTOS_PASTO.map((nome) => ({ grupo: grupoDoEvento(nome), nome }))
const eventoShort = (nome) => {
  if (!nome) return 'Evento'
  if (nome.startsWith('Aplicação Adubo - ')) return 'Adubo – ' + nome.replace('Aplicação Adubo - ', '')
  if (nome.startsWith('Aplicação ')) return nome.replace('Aplicação ', '')
  return nome
}

// Histórico simulado de um PIQUETE — ciclos de pastejo (entrada/saída) + eventos
// de manejo entre eles. Determinístico por módulo+piquete: alguns piquetes têm
// muito histórico (uso intenso), outros pouco.
function histPiquete(fazenda, mod, piquete) {
  const Pa = P()
  const CATS = ['Novilhas Cheias', 'Vaca Parida', 'Boi Gordo', 'Desmama 8 a 12 meses', 'Novilhas 13–18 meses']
  const EVS = ['Roçada', 'Aplicação Adubo - Uréia', 'Aplicação Adubo - NPK', 'Aplicação Herbicida']
  const key = (mod.nome + piquete).split('').reduce((a, c) => a + c.charCodeAt(0), 0)
  let seed = (key * 7919) % 2147483647
  const rnd = () => ((seed = (seed * 16807) % 2147483647) / 2147483647)
  const ciclos = 2 + Math.floor(rnd() * 5)             // 2 a 6 ciclos de pastejo
  const out = []
  let dia = 25 + Math.floor(rnd() * 20)               // dias atrás do 1º evento
  for (let c = 0; c < ciclos; c++) {
    const cat = CATS[Math.floor(rnd() * CATS.length)]
    const qtd = 30 + Math.floor(rnd() * 130)
    const perm = 4 + Math.floor(rnd() * 9)            // dias de permanência
    const entrada = dia
    const saida = dia - perm
    out.push({ id: 70000 + key + c * 7 + 1, tipo: 'entrada', interno: true, fazenda, secao: mod.secao, moduloNome: mod.nome, piquete, categoria: cat, qtd, data: Pa.fmtBR(Pa.addDays(Pa.HOJE, -entrada)) })
    if (saida > 0) out.push({ id: 70000 + key + c * 7 + 2, tipo: 'saida', interno: true, fazenda, secao: mod.secao, moduloNome: mod.nome, piquete, categoria: cat, qtd, data: Pa.fmtBR(Pa.addDays(Pa.HOJE, -saida)) })
    if (rnd() < 0.7 && saida - 2 > 0) {
      out.push({ id: 70000 + key + c * 7 + 3, tipo: 'evento', eventoTipo: EVS[Math.floor(rnd() * EVS.length)], fazenda, secao: mod.secao, moduloNome: mod.nome, piquete, categoria: '', data: Pa.fmtBR(Pa.addDays(Pa.HOJE, -(saida - 2))) })
    }
    dia = entrada + 18 + Math.floor(rnd() * 26)       // ciclo anterior
  }
  // Coerência com o estado atual: se há lote no piquete, a entrada dele é o item
  // mais recente e nenhuma saída simulada pode ser posterior a ela.
  const lote = Pa.loteNoPiquete ? Pa.loteNoPiquete(mod, piquete) : null
  if (lote) {
    const dEnt = lote.diasPiquete
    const corte = Pa.parseBR(Pa.fmtBR(Pa.addDays(Pa.HOJE, -dEnt)))
    const limpo = out.filter((h) => Pa.parseBR(h.data) < corte)
    limpo.unshift({ id: 79000 + key, tipo: 'entrada', interno: true, fazenda, secao: mod.secao, moduloNome: mod.nome, piquete, categoria: lote.categoria, qtd: lote.qtd, data: Pa.fmtBR(Pa.addDays(Pa.HOJE, -dEnt)) })
    return limpo
  }
  return out
}

// Histórico COMPLETO de um módulo — só Entradas / Saídas / Eventos.
function historicoCompleto(fazenda, modNome) {
  const Pa = P()
  return Pa.MOVIMENTOS.filter((x) => x.fazenda === fazenda && x.moduloNome === modNome)
    .slice().sort((a, b) => Pa.parseBR(b.data) - Pa.parseBR(a.data) || (b.id || 0) - (a.id || 0))
}

// Agrupa o histórico por DATA (visão "excel": data como cabeçalho, lançamentos abaixo).
function agruparPorData(hist) {
  const out = []
  const idx = {}
  hist.forEach((h) => {
    if (idx[h.data] == null) { idx[h.data] = out.length; out.push({ data: h.data, itens: [] }) }
    out[idx[h.data]].itens.push(h)
  })
  return out
}
const DOW_HIST = ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado']
function relativoData(data) {
  const Pa = P()
  if (data === Pa.fmtBR(Pa.HOJE)) return 'hoje'
  if (data === Pa.fmtBR(Pa.addDays(Pa.HOJE, -1))) return 'ontem'
  return DOW_HIST[Pa.parseBR(data).getDay()]
}

const SecLabel = ({ children, style }) => (
  <div style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)',
    textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', margin: '24px 0 12px 2px', ...style }}>{children}</div>
)

// ============================================================
// LISTA DO DIA — cada registro é um lançamento (Entrada / Saída / Evento).
// Mover um lote grava saída + entrada: aparecem como dois lançamentos.
// ============================================================
function agrupar(registros) {
  return registros.map((r) => ({ key: 'r' + r.id, recs: [r], hora: r.hora, data: r.data }))
}

// Descritor de exibição de um lançamento.
function descGrupo(g) {
  const Pa = P()
  const r = g.recs[0]
  const base = { synced: r.status === 'sincronizado', status: r.status, hora: g.hora, data: g.data }
  // Título = o que aconteceu; subtítulo = ONDE (nunca cortar o local).
  const local = (r) => `Módulo ${r.modNome}${r.piquete ? `, piquete ${r.piquete}` : ' (inteiro)'}`
  if (r.tipo === 'entrada') return { ...base, tipo: 'entrada', titulo: `Entrada – ${Pa.fmt(r.qtd)} ${r.cat}`, sub: local(r) }
  if (r.tipo === 'saida') return { ...base, tipo: 'saida', titulo: `Saída – ${Pa.fmt(r.qtd)} ${r.cat}`, sub: local(r) }
  return { ...base, tipo: 'evento', titulo: eventoShort(r.eventoTipo).replace(' · ', ' – '), sub: local(r) }
}

// ---- Linha de ação (dentro do cartão único de ações) ----------------------
function ActionRow({ icon, iconColor, title, sub, onClick, last }) {
  const { Icon } = DS()
  return (
    <div onClick={onClick} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '15px 0', cursor: 'pointer',
      borderBottom: last ? 'none' : 'var(--border-neutral-light)' }}>
      <Icon name={icon} size={22} color={iconColor || 'var(--color-primary)'} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 16, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.2 }}>{title}</div>
        {sub && <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 2 }}>{sub}</div>}
      </div>
      <Icon name="chevron-forward" size={20} color="var(--color-neutral-300)" />
    </div>
  )
}

// ---- Linha de lançamento do dia (lista única, divisória, alvo amplo) ------
// Dois estados: sincronizado ✓ · pendente (na fila).
function TodayRow({ d, last, onTap }) {
  const { Icon } = DS()
  const t = TIPO_UI[d.tipo]
  return (
    <div onClick={onTap} style={{ display: 'flex', alignItems: 'center', gap: 13, padding: '14px 0', cursor: 'pointer',
      borderBottom: last ? 'none' : 'var(--border-neutral-light)' }}>
      <span style={{ width: 38, height: 38, borderRadius: 'var(--border-radius-default)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-neutral-light-500)' }}>
        <Icon name={t.icon} size={18} color={t.cor} />
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 16, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.titulo}</div>
        <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.sub}</div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 5, flexShrink: 0 }}>
        <span style={{ fontSize: 12, color: 'var(--color-grayish)', fontWeight: 'var(--font-weight-semibold)' }}>{d.hora}</span>
        <Icon name={d.synced ? 'checkmark-circle-outline' : 'file-tray-full-outline'} size={14} color={d.synced ? 'var(--color-success-dark)' : 'var(--color-grayish)'} />
      </div>
    </div>
  )
}

// ---- Ação dentro do sheet (editar / excluir) ------------------------------
function SheetAction({ icon, label, danger, onClick }) {
  const { Icon } = DS()
  const color = danger ? 'var(--color-danger)' : 'var(--color-grayish-dark)'
  return (
    <div onClick={onClick} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '18px 4px', cursor: 'pointer',
      fontSize: 16, fontWeight: 'var(--font-weight-semibold)', color }}>
      <Icon name={icon} size={22} color={color} /> {label}
    </div>
  )
}

// ============================================================
// HOME — 1 ação herói + 1 grupo de ações + lista do dia (editável).
// ============================================================
function Home({ onRegistrar, onModules, onExit, onTab, fazenda, fazModal, online, registros, syncing, checkingMap, gps, onPull, onEnableGps, onRetry, plataforma }) {
  const { Icon, Card } = DS()
  const Pa = P()
  const nPend = registros.filter((r) => r.status === 'pendente').length
  const abrirManual = () => onRegistrar('mover')
  const abrirPiquete = (m, piquete) => onRegistrar('mover', { secao: m.secao, modId: m.id, piquete, nearby: true })
  // Perto de você → módulo: cai direto na escolha de piquete do módulo (com status de cada piquete)
  const abrirModulo = (m) => onRegistrar('mover', { secao: m.secao, modId: m.id, nearby: true })

  return (
    <window.MPhone title="Manejo de Pasto" leadingNode={onModules ? <ModulesButton onClick={onModules} /> : undefined}
      actionIcon="exit-outline" onAction={onExit}
      footer={<window.PastTabBar active="registrar" onTab={onTab} />} modal={fazModal}>

      {/* 1 · tira de sincronização fina — sempre visível */}
      <SyncBar online={online} nPend={nPend} syncing={syncing} checkingMap={checkingMap} />

      {/* 2 · ações — 1 herói (manual) + 1 secundária */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', margin: '32px 2px 12px' }}>
        <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Registrar</span>
        <span style={{ fontSize: 12, color: 'var(--color-grayish)' }}>Fazenda {fazenda}</span>
      </div>
      <div onClick={abrirManual} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '20px', borderRadius: 'var(--border-radius-default)', background: 'var(--color-primary)', boxShadow: '0 3px 6px rgba(0,0,0,0.18)', cursor: 'pointer', minHeight: 44 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 'var(--font-size-xl)', fontWeight: 'var(--font-weight-bold)', color: '#fff', lineHeight: 1.15 }}>Movimentar lote</div>
          <div style={{ fontSize: 14, color: 'rgba(255,255,255,0.82)', marginTop: 3 }}>Entrada, saída ou troca de piquete</div>
        </div>
        <Icon name="chevron-forward" size={22} color="rgba(255,255,255,0.9)" />
      </div>
      <Card onClick={() => onRegistrar('evento')} padding="18px 20px" style={{ flexDirection: 'row', alignItems: 'center', gap: 14, cursor: 'pointer', marginTop: 12, minHeight: 44 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 18, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.2 }}>Registrar evento</div>
          <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 2 }}>Adubo, defensivo ou roçada</div>
        </div>
        <Icon name="chevron-forward" size={20} color="var(--color-neutral-300)" />
      </Card>

      {/* 3 · perto de você — MÓDULOS por proximidade, GPS embutido */}
      <NearbySection gpsState={gps} fazenda={fazenda} onPickPiquete={abrirPiquete} onPickModulo={abrirModulo} onManual={() => onTab('consultar')} onEnableGps={onEnableGps} onRetry={onRetry} plataforma={plataforma} />
      <div style={{ height: 14 }} />
    </window.MPhone>
  )
}

// ============================================================
// CONSULTAR MÓDULO — Seção → Módulo → estado atual + histórico.
// ============================================================
function Consultar({ onClose, onMover, fazenda, gps, onTab, onModules, onExit, fazModal, onEnableGps, plataforma, mapaVariante = 'A', mapaZoom = 'fazenda', mapaEstilo, mapaMarca, mapaIndisp }) {
  const { Icon, Card } = DS()
  const Pa = P()
  const { useState } = React
  const USER_FAZENDA = fazenda || 'Mateira'
  const secoes = Pa.secoesDe(USER_FAZENDA)

  const [secao, setSecao] = useState(null)
  const [mod, setMod] = useState(null)
  const [piqSel, setPiqSel] = useState('')   // '' = todos os piquetes
  const [histMod, setHistMod] = useState(false)   // sub-view: histórico do módulo inteiro
  const [mapa, setMapa] = useState(true)          // mapa é a tela inicial do Consultar

  // Do mapa, o toque cai direto na ficha já existente (módulo / piquete / histórico).
  const irPara = (m, piquete, hist) => {
    setSecao(secoes.find((sx) => sx.nome === m.secao) || null)
    setMod(m); setPiqSel(piquete || ''); setHistMod(!!hist); setMapa(false)
  }
  if (mapa && window.LotacaoMapa) {
    return <window.LotacaoMapa variante={'ABCDE'.includes(mapaVariante) ? mapaVariante : 'A'} zoomInicial={mapaZoom} estilo={mapaEstilo} marcacao={mapaMarca} indispCor={mapaIndisp} gps={gps} onEnableGps={onEnableGps} plataforma={plataforma} leading={onModules ? <ModulesButton onClick={onModules} /> : undefined}
      onSecoes={() => setMapa(false)} onTab={onTab} onExit={onExit} fazModal={fazModal}
      fazenda={USER_FAZENDA} onPiquete={(m, p) => irPara(m, p, false)} />
  }
  if (mapa) {
    return <window.PastMapa fazenda={USER_FAZENDA} gps={gps} root leading={onModules ? <ModulesButton onClick={onModules} /> : undefined}
      onClose={() => setMapa(false)} onSecoes={() => setMapa(false)}
      onPiquete={(m, p) => irPara(m, p, false)} onModulo={(m) => irPara(m, '', false)}
      onHistModulo={(m) => irPara(m, '', true)} onMover={onMover}
      onTab={onTab} onModules={onModules} onExit={onExit} fazModal={fazModal} />
  }

  const back = () => {
    if (histMod) return setHistMod(false)     // histórico do módulo → volta ao módulo
    if (piqSel) return setPiqSel('')          // detalhe do piquete → volta ao módulo
    if (mod) return setMod(null)
    if (secao) return setSecao(null)
    setMapa(true)                             // raiz do Consultar é o mapa
  }
  const title = histMod ? 'Histórico do módulo' : piqSel ? 'Piquete ' + piqSel : mod ? 'Módulo ' + mod.nome : secao ? secao.nome : 'Lotação atual'

  const NavCard = ({ title, sub, onClick }) => (
    <Card onClick={onClick} padding="16px 16px" style={{ flexDirection: 'row', alignItems: 'center', gap: 12, cursor: 'pointer' }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 18, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.2 }}>{title}</div>
        {sub && <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 3, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{sub}</div>}
      </div>
      <Icon name="chevron-forward" size={22} color="var(--color-neutral-300)" />
    </Card>
  )

  let body, footer = null

  if (!secao) {
    body = (
      <>
        <window.MHeading>Selecione a seção</window.MHeading>
        <window.MSub>Fazenda {USER_FAZENDA}</window.MSub>
        <div style={{ height: 4 }} />
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {secoes.map((s) => <NavCard key={s.nome} title={s.nome} sub={`${s.modulos.length} módulos`} onClick={() => setSecao(s)} />)}
        </div>
        {gps === 'ok' && !!computeNearbyMods(USER_FAZENDA, gps).length && (
          <>
            <SecLabel>Perto de você</SecLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
              {computeNearbyMods(USER_FAZENDA, gps).map(({ m, dist }) => (
                <ModuleCard key={m.id} m={m} dist={dist} onTap={() => { setPiqSel(''); setHistMod(false); setSecao(secoes.find((sx) => sx.nome === m.secao) || null); setMod(m) }} />
              ))}
            </div>
          </>
        )}
      </>
    )
  } else if (!mod) {
    body = (
      <>
        <window.MHeading>Selecione o módulo</window.MHeading>
        <window.MSub>{secao.nome} · {USER_FAZENDA}</window.MSub>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {secao.modulos.map((m) => (
            <ModuleCard key={m.id} m={m} onTap={() => { setPiqSel(''); setHistMod(false); setMod(m) }} />
          ))}
        </div>
      </>
    )
  } else {
    const a = mod.atual
    const RD = Pa.RODIZIO_DIAS

    // Estado derivado de um piquete: ocupado (lote aqui) ou em descanso há N dias.
    // Rodízio: o lote entra no 1º piquete e avança 1 a cada RODIZIO_DIAS. O piquete
    // à frente do lote é o que descansa há mais tempo (próximo a receber o rebanho).
    const estadoPiq = (p) => {
      const idx = mod.piquetes.indexOf(p)
      if (!a) return { semLote: true }
      if (idx === a.rodizios) return { ocupado: true }
      const n = mod.piquetes.length
      let base = a.diasModulo - (idx + 1) * RD
      if (base < 0) base += n * RD           // ainda não pastejado neste ciclo → descansa desde o ciclo anterior
      return { descanso: Math.max(0, base) }
    }
    // ---- Ocupação do módulo: PODE haver mais de um lote, cada um num piquete --
    const lotes = Pa.lotesDoMod(mod)
    const loteEm = (p) => lotes.find((l) => l.piquete === p) || null
    const totalCab = lotes.reduce((s, l) => s + l.qtd, 0)
    const totalUA = lotes.reduce((s, l) => s + l.ua, 0)

    const histFull = historicoCompleto(USER_FAZENDA, mod.nome)

    // Fonte única do histórico: lançamentos reais + ciclos simulados de cada piquete.
    const histPorPiquete = {}
    mod.piquetes.forEach((p) => {
      histPorPiquete[p] = [...histFull.filter((h) => h.piquete === p), ...histPiquete(USER_FAZENDA, mod, p)]
        .sort((x, y) => Pa.parseBR(y.data) - Pa.parseBR(x.data))
    })

    // Vocabulário: PASTEJANDO = dias com o lote; DESCANSANDO = dias sem lote.
    // Descanso vem da última SAÍDA registrada no piquete (coerente com o histórico).
    const diasDescanso = {}
    mod.piquetes.forEach((p) => {
      const e = estadoPiq(p)
      const ultSaida = histPorPiquete[p].find((h) => h.tipo === 'saida')
      diasDescanso[p] = ultSaida ? Pa.diasEntre(ultSaida.data) : (e.descanso != null ? e.descanso : 0)
    })

    const histModAll = mod.piquetes.flatMap((p) => histPorPiquete[p]).sort((x, y) => Pa.parseBR(y.data) - Pa.parseBR(x.data))
    const ultimo = histModAll[0]
    const nPastejando = mod.piquetes.filter((p) => !!loteEm(p)).length
    const nDescanso = mod.piquetes.length - nPastejando

    // Lista de histórico agrupada por data — usada no módulo e no piquete.
    const listaHist = (itens, vazio) => itens.length ? (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
        {agruparPorData(itens).map((g) => (
          <div key={g.data}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, padding: '8px 12px', background: 'var(--color-neutral-light-500)', borderRadius: 'var(--border-radius-default)', marginBottom: 2 }}>
              <span style={{ fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)' }}>{g.data}</span>
              <span style={{ fontSize: 12, color: 'var(--color-grayish)', textTransform: 'capitalize' }}>{relativoData(g.data)}</span>
            </div>
            {g.itens.map((h, i) => <HistItem key={(h.id || 'e') + '-' + i} h={h} last={i === g.itens.length - 1} />)}
          </div>
        ))}
      </div>
    ) : (
      <div style={{ padding: '24px 18px', textAlign: 'center', color: 'var(--color-grayish)', fontSize: 14 }}>{vazio}</div>
    )

    if (histMod) {
      // ===== HISTÓRICO DO MÓDULO INTEIRO =====
      body = (
        <>
          <div style={{ fontSize: 13, color: 'var(--color-grayish)', marginBottom: 14 }}>Módulo {mod.nome} · Seção {mod.secao} · todos os piquetes</div>
          {listaHist(histModAll, 'Nenhum lançamento neste módulo.')}
          <div style={{ height: 10 }} />
        </>
      )
      footer = null
    } else if (piqSel) {
      // ===== DETALHE DO PIQUETE: estado + histórico completo daquele piquete =====
      const lp = loteEm(piqSel)
      const e = estadoPiq(piqSel)
      const hist = histPorPiquete[piqSel]
      // Um piquete tem no máximo um lote de uma categoria.
      body = (
        <>
          <div style={{ fontSize: 13, color: 'var(--color-grayish)', marginBottom: 12 }}>Módulo {mod.nome} · Seção {mod.secao}</div>
          {lp ? (
            <Card padding="14px 16px" style={{ gap: 8 }}>
              <span style={{ alignSelf: 'flex-start', fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: '#fff', background: 'var(--color-primary)', padding: '3px 10px', borderRadius: 99, textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Pastejando</span>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                <span style={{ fontSize: 30, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)', lineHeight: 1 }}>{Pa.fmt(lp.qtd)}</span>
                <span style={{ fontSize: 15, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish-dark)' }}>{lp.categoria}</span>
              </div>
              <div style={{ fontSize: 14, color: 'var(--color-grayish)' }}>Pastejando há <b style={{ color: 'var(--color-grayish-dark)' }}>{lp.diasPiquete} {lp.diasPiquete === 1 ? 'dia' : 'dias'}</b> · {Pa.fmt(Math.round(lp.ua))} UA</div>
            </Card>
          ) : !diasDescanso[piqSel] ? (
            <Card padding="14px 16px">
              <div style={{ fontSize: 16, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)' }}>Sem lote</div>
              <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 2 }}>Módulo disponível</div>
            </Card>
          ) : (
            <Card padding="14px 16px" style={{ gap: 8 }}>
              <span style={{ alignSelf: 'flex-start', fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-success-dark)', background: 'var(--color-primary-30)', padding: '3px 10px', borderRadius: 99, textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Descansando</span>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                <span style={{ fontSize: 34, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1 }}>{Pa.fmt(diasDescanso[piqSel])}</span>
                <span style={{ fontSize: 16, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish-dark)' }}>{diasDescanso[piqSel] === 1 ? 'dia' : 'dias'} de descanso</span>
              </div>
            </Card>
          )}

          <SecLabel>Histórico do piquete</SecLabel>
          {listaHist(hist, 'Nenhum lançamento neste piquete.')}
          <div style={{ height: 10 }} />
        </>
      )
      footer = lp
        ? <window.MButton onClick={() => onMover(mod, piqSel)} rightIcon="chevron-forward">Mover lote</window.MButton>
        : <window.MButton onClick={() => onMover(mod, piqSel)} rightIcon="chevron-forward">Registrar entrada neste piquete</window.MButton>
    } else {
      // ===== VISÃO DO MÓDULO: lote atual + grade de piquetes (estrela da tela) =====
      body = (
        <>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, margin: '4px 2px 8px' }}>
            <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Último movimento</span>
            <button onClick={() => setHistMod(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, minHeight: 36, padding: '0 4px', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 13, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)' }}>
              Ver mais<Icon name="chevron-forward" size={15} color="var(--color-primary)" />
            </button>
          </div>
          {ultimo ? (
            <div onClick={() => setHistMod(true)} style={{ display: 'flex', alignItems: 'center', gap: 11, minHeight: 44, padding: '2px 2px', cursor: 'pointer' }}>
              <Icon name={(TIPO_UI[ultimo.tipo] || TIPO_UI.evento).icon} size={19} color={(TIPO_UI[ultimo.tipo] || TIPO_UI.evento).cor} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 15, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.25 }}>{ultimo.tipo === 'evento' ? eventoShort(ultimo.eventoTipo) : ultimo.tipo === 'entrada' ? 'Entrada' : 'Saída'}</div>
                <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 2 }}>Piquete {ultimo.piquete}{ultimo.qtd ? ` · ${Pa.fmt(ultimo.qtd)} cab.` : ''}</div>
              </div>
              <span style={{ flexShrink: 0, fontSize: 13, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish)', whiteSpace: 'nowrap' }}>{ultimo.data}</span>
            </div>
          ) : (
            <div style={{ padding: '2px 2px', fontSize: 14, color: 'var(--color-grayish)' }}>Nenhum movimento registrado</div>
          )}

          {/* ---- Piquetes como TABELA densa: linha por piquete, ~40px. ---- */}
          <div style={{ margin: '30px 2px 6px' }}>
            <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Piquetes</span>
          </div>
          <Card padding="0">
            {/* Totalizador do módulo no lugar do cabeçalho da tabela. */}
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, padding: '10px 12px 10px 14px', borderBottom: 'var(--border-neutral-light)', background: 'var(--color-neutral-light-300)' }}>
              <span style={{ display: 'flex', alignItems: 'baseline', gap: 6, minWidth: 0 }}>
                <span style={{ fontSize: 20, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)', lineHeight: 1 }}>{Pa.fmt(lotes.reduce((a, l) => a + l.qtd, 0))}</span>
                <span style={{ fontSize: 13, color: 'var(--color-grayish)' }}>cabeças{lotes.length > 1 ? ` em ${lotes.length} lotes` : ''}</span>
              </span>
              <span style={{ fontSize: 13, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish)', whiteSpace: 'nowrap' }}>{Pa.fmt(Math.round(lotes.reduce((a, l) => a + l.ua, 0)))} UA</span>
            </div>
            {mod.piquetes.map((p, i) => {
              const l = loteEm(p)
              const d = diasDescanso[p]
              return (
                <div key={p} onClick={() => setPiqSel(p)} style={{ display: 'grid', gridTemplateColumns: '34px 1fr auto 18px', alignItems: 'center', gap: 10, minHeight: 44, padding: '6px 12px 6px 14px', cursor: 'pointer', borderTop: i ? 'var(--border-neutral-light)' : 'none', background: l ? 'var(--color-primary-30)' : 'transparent' }}>
                  <span style={{ fontSize: 15, fontWeight: 'var(--font-weight-bold)', color: l ? 'var(--color-primary)' : 'var(--color-grayish-dark)' }}>{p}</span>
                  <span style={{ minWidth: 0, fontSize: 14, fontWeight: l ? 'var(--font-weight-bold)' : 'var(--font-weight-normal)', color: l ? 'var(--color-grayish-dark)' : 'var(--color-grayish)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {l ? `${Pa.fmt(l.qtd)} ${l.categoria}` : d > 0 ? 'em descanso' : 'sem lote'}
                  </span>
                  <span style={{ fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: l ? 'var(--color-primary)' : 'var(--color-grayish-dark)', whiteSpace: 'nowrap', textAlign: 'right' }}>
                    {l ? `${l.diasPiquete} ${l.diasPiquete === 1 ? 'dia' : 'dias'}` : d > 0 ? `${Pa.fmt(d)} ${d === 1 ? 'dia' : 'dias'}` : '—'}
                  </span>
                  <Icon name="chevron-forward" size={16} color="var(--color-neutral-300)" />
                </div>
              )
            })}
          </Card>
          <div style={{ height: 10 }} />
        </>
      )
      footer = lotes.length
        ? <window.MButton onClick={() => onMover(mod)} rightIcon="chevron-forward">Mover lote</window.MButton>
        : <window.MButton onClick={() => onMover(mod)} rightIcon="chevron-forward">Registrar entrada</window.MButton>
    }
  }

  return <window.MPhone title={title} onBack={back} footer={!mod ? <window.PastTabBar active="consultar" onTab={onTab} /> : footer} modal={fazModal}>{body}</window.MPhone>
}

// ---- Linha de histórico (dentro do cartão único) — só Entradas/Saídas/Eventos
function HistItem({ h, last }) {
  const { Icon } = DS()
  const Pa = P()
  const t = TIPO_UI[h.tipo] || TIPO_UI.evento

  let title, sub
  if (h.tipo === 'entrada') {
    title = h.interno ? 'Entrada' : 'Entrada de lote'
    sub = h.interno ? `Entrou no piquete ${h.piquete}` : `${Pa.fmt(h.qtd)} cab. · ${h.categoria}${h.piquete ? ' · piquete ' + h.piquete : ''}`
  } else if (h.tipo === 'saida') {
    title = h.interno ? 'Saída' : 'Saída de lote'
    sub = h.interno ? `Saiu do piquete ${h.piquete}` : `${Pa.fmt(h.qtd)} cab. · ${h.categoria}${h.piquete ? ' · piquete ' + h.piquete : ''}`
  } else {
    title = eventoShort(h.eventoTipo)
    sub = h.piquete ? 'piquete ' + h.piquete : 'módulo todo'
  }

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 13, padding: '13px 0', borderBottom: last ? 'none' : 'var(--border-neutral-light)' }}>
      <span style={{ width: 36, height: 36, borderRadius: 'var(--border-radius-default)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-neutral-light-500)' }}>
        <Icon name={t.icon} size={17} color={t.cor} />
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 16, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)' }}>{title}</div>
        <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 2 }}>{sub}</div>
      </div>
    </div>
  )
}

// ============================================================
// SALVO — tela de sucesso pós-lançamento. Throughput em campo:
// "Registrar outro" reabre o assistente mantendo fazenda + GPS.
// ============================================================
function Done({ resumo, records, fazenda, onOutro, onHome }) {
  const { Icon, Card } = DS()
  const Pa = P()
  const eventoShort = window.PastEventoShort
  const recs = records || []
  const hora = recs[0] && recs[0].hora
  const recDesc = (r) => {
    const t = TIPO_UI[r.tipo] || TIPO_UI.evento
    if (r.tipo === 'entrada') return { t, label: 'Entrada', sub: `${Pa.fmt(r.qtd)} ${r.cat} · piquete ${r.piquete}` }
    if (r.tipo === 'saida') return { t, label: 'Saída', sub: `${Pa.fmt(r.qtd)} ${r.cat} · piquete ${r.piquete}` }
    return { t, label: eventoShort(r.eventoTipo), sub: r.piquete ? `Piquete ${r.piquete}` : 'Módulo inteiro' }
  }
  const footer = (
    <>
      <window.MButton onClick={onOutro} rightIcon="add">Registrar outro</window.MButton>
      <window.MButton variant="ghost" onClick={onHome} style={{ marginTop: 6 }}>Voltar ao início</window.MButton>
    </>
  )
  return (
    <window.MPhone title="Salvo" onBack={onHome} footer={footer}>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', paddingTop: 24 }}>
        <span style={{ width: 68, height: 68, borderRadius: '50%', background: 'var(--color-primary-30)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 14 }}>
          <Icon name="checkmark-circle-outline" size={40} color="var(--color-success-dark)" />
        </span>
        <div style={{ fontSize: 22, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)' }}>{resumo.title}</div>
        <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 6 }}>Fazenda {fazenda}{hora ? ` · hoje ${hora}` : ''}</div>
      </div>

      {/* Recibo da DIVISÃO — uma linha por movimentação gravada. */}
      {/* Recibo — o que foi gravado. Mover = uma saída + uma entrada (dois registros). */}
      {recs.length > 0 && (
        <Card padding="0" style={{ marginTop: 22 }}>
          {recs.map((r, i) => {
            const d = recDesc(r)
            return (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 13, padding: '14px 16px', borderTop: i ? 'var(--border-neutral-light)' : 'none' }}>
                <span style={{ width: 38, height: 38, borderRadius: 'var(--border-radius-default)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-neutral-light-500)' }}>
                  <Icon name={d.t.icon} size={18} color={d.t.cor} />
                </span>
                <div style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
                  <div style={{ fontSize: 15, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)' }}>{d.label} · Módulo {r.modNome}</div>
                  <div style={{ fontSize: 13, color: 'var(--color-grayish)', marginTop: 2 }}>{d.sub}</div>
                </div>
              </div>
            )
          })}
        </Card>
      )}

      {/* Status offline-first — transparência do que vai para a fila. */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 12, padding: '11px 14px', borderRadius: 'var(--border-radius-default)', background: 'var(--color-alert-light-100)' }}>
        <Icon name="file-tray-full-outline" size={16} color="var(--color-alert-dark)" />
        <span style={{ flex: 1, fontSize: 13, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-alert-dark)', textAlign: 'left', lineHeight: 1.35 }}>
          Registro salvo no dispositivo. Será sincronizado quando você estiver online.
        </span>
      </div>
    </window.MPhone>
  )
}

// ============================================================
// BOTTOMBAR — navegação principal (4 abas). Edge-to-edge no rodapé do MPhone.
// ============================================================
// ---- Controle "Módulos" do header — abre o menu de módulos do app (grade 2×2 + rótulo + caret). ----
function ModulesButton({ onClick }) {
  return (
    <span onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', cursor: 'pointer', margin: '-8px 0', padding: '8px 12px 8px 0', minHeight: 44 }}>
      <span style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', width: 19, height: 14 }}>
        {[0, 1, 2].map((i) => <span key={i} style={{ height: 2.5, borderRadius: 2, background: '#fff' }} />)}
      </span>
    </span>
  )
}

// ---- Menu lateral (drawer): abre do lado do botão de menu, no topo esquerdo. ----
function ModulosSheet({ mods, atual, onPick, onClose }) {
  const { Icon } = DS()
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 60, background: 'rgba(0,0,0,0.4)', display: 'flex' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: '82%', maxWidth: 320, height: '100%', display: 'flex', flexDirection: 'column', background: '#fff', boxShadow: '4px 0 20px rgba(24,40,22,0.22)', animation: 'fgDrawer .22s ease-out', overflowY: 'auto' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '16px 16px 12px', background: 'var(--color-primary)' }}>
          <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'rgba(255,255,255,0.75)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Ir para</span>
          <button onClick={onClose} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 36, height: 36, border: 'none', background: 'transparent', cursor: 'pointer' }}>
            <Icon name="close" size={20} color="#fff" />
          </button>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', padding: '4px 0' }}>
          {(mods || []).map((m, i) => {
            const on = m.id === atual
            return (
              <div key={m.id} onClick={() => onPick(m.id)} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', minHeight: 44, cursor: 'pointer', background: on ? 'var(--color-primary-30)' : 'transparent', borderBottom: i < mods.length - 1 ? 'var(--border-neutral-light)' : 'none' }}>
                <span style={{ width: 40, height: 40, borderRadius: 'var(--border-radius-default)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: on ? '#fff' : 'var(--color-primary-30)' }}>
                  {m.iconNode ? m.iconNode : <Icon name={m.icon} size={21} color="var(--color-primary)" />}
                </span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 16, fontWeight: 'var(--font-weight-bold)', color: on ? 'var(--color-primary)' : 'var(--color-grayish-dark)', lineHeight: 1.2 }}>{m.title}</div>
                  <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 2, lineHeight: 1.3 }}>{m.sub}</div>
                </div>
                {on
                  ? <span style={{ flexShrink: 0, fontSize: 11, fontWeight: 'var(--font-weight-bold)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', padding: '2px 7px', borderRadius: 99, background: '#fff', color: 'var(--color-success-dark)' }}>Atual</span>
                  : <Icon name="chevron-forward" size={18} color="var(--color-neutral-300)" />}
              </div>
            )
          })}
        </div>
        <div style={{ marginTop: 'auto', display: 'flex', alignItems: 'center', gap: 8, padding: '14px 16px', borderTop: 'var(--border-neutral-light)', fontSize: 12, color: 'var(--color-grayish)' }}>
          <Icon name="person-circle-outline" size={15} color="var(--color-grayish)" />
          <span>Siogo Pereira</span>
          <span style={{ color: 'var(--color-neutral-300)' }}>·</span>
          <span>v2.4.0</span>
        </div>
      </div>
    </div>
  )
}

// ---- Distância por CENTROIDE de PIQUETE (offline). Raio ~700 m. ----
// O operador está dentro de um piquete, não de um módulo: a sugestão é piquete a piquete.
function computeNearby(fazenda, gpsState) {
  const Pa = P()
  const all = Pa.secoesDe(fazenda).flatMap((sx) => sx.modulos).flatMap((m) => m.piquetes.map((p) => ({ m, piquete: p })))
  const DIST_SEQ = [90, 160, 240, 330, 420, 510, 620, 690, 810, 950, 1150, 1400]
  const off = gpsState === 'vazio' ? 900 : 0
  const withDist = all.map((x, i) => ({ ...x, dist: (DIST_SEQ[i] != null ? DIST_SEQ[i] : 1600 + (i - 12) * 260) + off })).sort((a, b) => a.dist - b.dist)
  const noRaio = withDist.filter((x) => x.dist <= 700)
  const temRaio = noRaio.length > 0
  return { sugeridos: (temRaio ? noRaio : withDist).slice(0, 3), temRaio }
}
const fmtDist = (d) => d < 1000 ? `${d} m` : `${(d / 1000).toFixed(1).replace('.', ',')} km`

// Módulos próximos — distância do piquete mais perto de cada módulo.
function computeNearbyMods(fazenda, gpsState) {
  const P_ = P()
  const all = P_.secoesDe(fazenda).flatMap((sx) => sx.modulos).flatMap((m) => m.piquetes.map((p) => ({ m, piquete: p })))
  const DIST_SEQ = [90, 160, 240, 330, 420, 510, 620, 690, 810, 950, 1150, 1400]
  const off = gpsState === 'vazio' ? 900 : 0
  const withDist = all.map((x, i) => ({ ...x, dist: (DIST_SEQ[i] != null ? DIST_SEQ[i] : 1600 + (i - 12) * 260) + off })).sort((a, b) => a.dist - b.dist)
  const seen = {}
  const out = []
  withDist.forEach(({ m, dist }) => { if (seen[m.id] == null) { seen[m.id] = 1; out.push({ m, dist }) } })
  return out.filter((x) => x.dist <= 700).slice(0, 3)   // fora do raio não é "perto de você"
}

// Selo do módulo: vazio × com lote.
function ModTag({ vazio }) {
  return (
    <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', padding: '2px 8px', borderRadius: 99, whiteSpace: 'nowrap', flexShrink: 0,
      background: vazio ? 'var(--color-neutral-light-500)' : 'var(--color-primary-30)', color: vazio ? 'var(--color-grayish)' : 'var(--color-success-dark)' }}>{vazio ? 'Sem lote' : 'Com lote'}</span>
  )
}

// ---- Selo de status do MÓDULO: Ocupado · Disponível · Indisponível --------
// Piquete usa "Com lote / Sem lote" (ModTag); módulo usa o status derivado.
const STATUS_TAG = {
  ocupado:      { label: 'Ocupado',      bg: 'var(--color-danger-light-100)',  fg: 'var(--color-danger-dark)' },
  disponivel:   { label: 'Disponível',   bg: 'var(--color-info-light-100)',    fg: 'var(--color-info-dark)' },
  indisponivel: { label: 'Indisponível', bg: 'var(--color-alert-light-100)',   fg: 'var(--color-alert-dark)' },
}
function StatusTag({ status }) {
  const t = STATUS_TAG[status] || STATUS_TAG.disponivel
  return (
    <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', padding: '2px 8px', borderRadius: 99, whiteSpace: 'nowrap', flexShrink: 0, background: t.bg, color: t.fg }}>{t.label}</span>
  )
}

// Card de módulo — lista do Consultar (Seção → Módulo).
function ModuleCard({ m, dist, onTap, semLoteTag, aqui }) {
  const { Icon, Card } = DS()
  return (
    <Card onClick={onTap} padding="14px 12px 14px 16px" style={{ flexDirection: 'row', alignItems: 'center', gap: 12, cursor: 'pointer', minHeight: 44 }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, minHeight: 24 }}>
          <span style={{ fontSize: 18, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', whiteSpace: 'nowrap' }}>Módulo {m.nome}</span>
          {semLoteTag && m.status === 'disponivel' ? <ModTag vazio /> : <StatusTag status={m.status} />}
        </div>
        <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 3, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>Seção {m.secao}</div>
      </div>
      {aqui && <span style={{ flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: '#1f5fa8', background: '#e3eefb', padding: '3px 9px', borderRadius: 99, whiteSpace: 'nowrap' }}><span style={{ width: 7, height: 7, borderRadius: 99, background: '#2f7cd6', boxShadow: '0 0 0 2px #fff' }}></span>Aqui</span>}
      {!aqui && dist != null && <span style={{ flexShrink: 0, fontSize: 12, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish)', background: 'var(--color-neutral-light-500)', padding: '3px 9px', borderRadius: 99, whiteSpace: 'nowrap' }}>{fmtDist(dist)}</span>}
      <Icon name="chevron-forward" size={20} color="var(--color-neutral-300)" />
    </Card>
  )
}

// Card de piquete — usado em "Perto de você" (com distância pelo centroide do piquete).
function PiqueteCard({ m, piquete, dist, onTap }) {
  const { Icon, Card } = DS()
  const Pa = P()
  const comLote = !!(Pa.loteNoPiquete && Pa.loteNoPiquete(m, piquete))
  const titulo = `Piquete ${piquete}`
  const detalhe = `Módulo ${m.nome} · Seção ${m.secao}`
  return (
    <Card onClick={onTap} padding="14px 12px 14px 16px" style={{ flexDirection: 'row', alignItems: 'center', gap: 12, cursor: 'pointer', minHeight: 44 }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, minHeight: 24 }}>
          <span style={{ fontSize: 18, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', whiteSpace: 'nowrap' }}>{titulo}</span>
          {/* Lote é atributo do PIQUETE: o selo diz sempre em que estado o piquete está. */}
          <ModTag vazio={!comLote} />
        </div>
        <div style={{ fontSize: 12, color: 'var(--color-grayish)', marginTop: 3, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{detalhe}</div>
      </div>
      {dist != null && (
        <span style={{ flexShrink: 0, width: 62, display: 'flex', justifyContent: 'flex-end' }}>
          <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-grayish)', background: 'var(--color-neutral-light-500)', padding: '3px 9px', borderRadius: 99, whiteSpace: 'nowrap' }}>{fmtDist(dist)}</span>
        </span>
      )}
      <Icon name="chevron-forward" size={20} color="var(--color-neutral-300)" />
    </Card>
  )
}

// Seção "Perto de você" da Home — lista OPORTUNISTA de PIQUETES por GPS.
// Só renderiza piquetes quando o GPS entrega um fix com piquetes no raio; sem isso,
// nada de fallback (recência/"com lote") — sugestão errada = registro no lugar errado.
// Sem lista, vira só um chip de status compacto com a ação certa por caso. O caminho
// manual seguro é o herói "Movimentar lote"; a seção nunca oferece "escolher manual".
function NearbySection({ gpsState, fazenda, onPickPiquete, onPickModulo, onEnableGps, onRetry, plataforma = 'android' }) {
  const { Icon } = DS()
  // "Atualizar" só existe quando há cartões; nos estados de aviso a ação fica no próprio chip.
  const header = (comCartoes) => (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, margin: '26px 0 10px 2px' }}>
      <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Perto de você</span>
      {comCartoes && <button onClick={onRetry} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, minHeight: 36, padding: '0 4px', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 13, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)', whiteSpace: 'nowrap' }}>
        <Icon name="refresh" size={15} color="var(--color-primary)" />Atualizar
      </button>}
    </div>
  )

  // Chip de status: compacto, intencional. Uma ação por caso (retry só quando resolve).
  const Chip = ({ label, actionLabel, onAction }) => (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, minHeight: 60, padding: '10px 14px', maxWidth: '100%', background: '#fff', border: 'var(--border-neutral-light)', borderRadius: 'var(--border-radius-default)' }}>
      <svg width="17" height="17" viewBox="0 0 20 20" fill="none" stroke="var(--color-grayish)" strokeWidth="1.8" strokeLinecap="round" style={{ flexShrink: 0 }}><circle cx="10" cy="10" r="3.2"></circle><circle cx="10" cy="10" r="6.6"></circle><path d="M10 1.5v2.2M10 16.3v2.2M1.5 10h2.2M16.3 10h2.2"></path></svg>
      <span style={{ flex: 1, minWidth: 0, fontSize: 14, color: 'var(--color-grayish)' }}>{label}</span>
      {actionLabel && <button onClick={onAction} style={{ minHeight: 44, padding: '0 6px', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)', whiteSpace: 'nowrap' }}>{actionLabel}</button>}
    </div>
  )

  // Só há lista quando o GPS entrega módulos (fix válido, módulos no raio).
  const temFix = gpsState === 'ok'
  if (!temFix) {
    let chip
    if (gpsState === 'aproximada') chip = <Chip label="Localização aproximada" actionLabel="Ativar" onAction={onEnableGps} />
    else if (gpsState === 'aparelho') chip = <Chip label="Localização do aparelho desligada" actionLabel={plataforma === 'android' ? 'Ativar' : undefined} onAction={onEnableGps} />
    else if (gpsState === 'negada') chip = <Chip label="Localização desligada" actionLabel="Ativar" onAction={onEnableGps} /> // permissão: retry não resolve
    else if (gpsState === 'buscando') chip = <Chip label="Obtendo sua localização…" />
    else if (gpsState === 'vazio') chip = <Chip label="Nenhum módulo por perto" actionLabel="Tentar de novo" onAction={onRetry} />
    else chip = <Chip label="Sem sinal de GPS" actionLabel="Tentar de novo" onAction={onRetry} /> // transitório: refaz o fix
    return <div>{header(false)}{chip}</div>
  }

  // Sugestão por MÓDULO: status do módulo (ocupado / disponível); o piquete se escolhe na tela seguinte.
  const Pa = P(), ms = Pa.initModState()
  const MOD_DIST = [90, 240, 410, 620, 950, 1400]
  const mods = Pa.secoesDe(fazenda).flatMap((sx) => sx.modulos)
    .map((m, i) => ({ m: { ...m, status: Pa.statusDe(m, ms) }, dist: MOD_DIST[i] != null ? MOD_DIST[i] : 1600 + i * 260 }))
    .filter((x) => x.m.status !== 'arquivado' && x.dist <= 700).slice(0, 3)
  if (!mods.length) return <div>{header(false)}<Chip label="Nenhum módulo por perto" actionLabel="Tentar de novo" onAction={onRetry} /></div>
  return (
    <div>{header(true)}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
        {mods.map(({ m, dist }, i) => <ModuleCard key={m.id} m={m} dist={dist} semLoteTag aqui={i === 0 && dist <= 100} onTap={() => (onPickModulo ? onPickModulo(m) : onPickPiquete(m, m.piquetes[0]))} />)}
      </div>
    </div>
  )
}

// Tira de sincronização fina (topo da Home) — sempre visível, sem botão "Conectar".
function SyncBar({ online, nPend, syncing, checkingMap }) {
  const { Icon } = DS()
  const Pa = P()
  const strip = (bg, fg, inner) => (
    <div style={{ display: 'flex', alignItems: 'center', gap: 9, margin: '-20px -20px 0', padding: '11px 20px', minHeight: 42, background: bg }}>{inner}</div>
  )
  if (checkingMap || syncing) {
    const fg = 'var(--color-info-dark)'
    return strip('var(--color-info-light-100)', fg, (<>
      <span style={{ display: 'flex', animation: 'fgSpin 1s linear infinite' }}><Icon name="loading" size={16} color={fg} /></span>
      <span style={{ flex: 1, fontSize: 14, fontWeight: 'var(--font-weight-semibold)', color: fg }}>{checkingMap ? 'Verificando atualização do mapa…' : 'Enviando fila…'}</span>
    </>))
  }
  if (nPend === 0) {
    const fg = online ? 'var(--color-success-dark)' : 'var(--color-grayish)'
    return strip(online ? 'var(--color-primary-30)' : 'var(--color-neutral-light-300)', fg, (<>
      <Icon name={online ? 'checkmark-circle-outline' : 'information-circle-outline'} size={16} color={fg} />
      <span style={{ flex: 1, fontSize: 14, fontWeight: 'var(--font-weight-semibold)', color: fg }}>{online ? 'Conectado · tudo sincronizado' : 'Sem internet · nada na fila'}</span>
    </>))
  }
  const fg = 'var(--color-alert-dark)'
  return strip('var(--color-alert-light-100)', fg, (<>
    <Icon name="file-tray-full-outline" size={16} color={fg} />
    <span style={{ flex: 1, fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: fg }}>{online ? `${Pa.fmt(nPend)} na fila` : `Sem internet · ${Pa.fmt(nPend)} na fila`}</span>
    <span style={{ fontSize: 12, fontWeight: 'var(--font-weight-semibold)', color: fg, opacity: 0.85, whiteSpace: 'nowrap' }}>{online ? 'sincronizando…' : 'envia ao reconectar'}</span>
  </>))
}

function TabBar({ active, onTab }) {
  const { Icon } = DS()
  const items = [
    { id: 'historico', label: 'Histórico', icon: 'list-outline' },
    { id: 'registrar', label: 'Registrar', icon: 'add-circle-outline' },
    { id: 'consultar', label: 'Lotação', icon: 'search' },
    { id: 'fazenda',   label: 'Fazenda',   icon: 'folder' },
  ]
  return (
    <div style={{ display: 'flex', margin: '-12px -20px -20px', padding: '7px 6px 11px', borderTop: '2px solid var(--color-neutral-200)', background: 'var(--color-background-dark-1)', boxShadow: '0 -4px 14px rgba(24,40,22,0.12)' }}>
      {items.map((it) => {
        const on = active === it.id
        return (
          <div key={it.id} onClick={() => onTab(it.id)} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, padding: '4px 0', cursor: 'pointer', minHeight: 44 }}>
            <span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 58, height: 32, borderRadius: 99, background: on ? 'var(--color-primary)' : 'transparent', transition: 'background .16s ease' }}>
              <Icon name={it.icon} size={24} color={on ? '#fff' : 'var(--color-grayish-dark)'} />
            </span>
            <span style={{ fontSize: 13, fontWeight: on ? 'var(--font-weight-bold)' : 'var(--font-weight-semibold)', color: on ? 'var(--color-primary)' : 'var(--color-grayish-dark)', letterSpacing: '0.1px' }}>{it.label}</span>
          </div>
        )
      })}
    </div>
  )
}

// ---- Bottom sheet: escolher a fazenda em foco ------------------------------
function FazendaSheet({ fazenda, fazendas, onPick, onClose }) {
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 60, background: 'rgba(0,0,0,0.4)', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: '#fff', borderRadius: '18px 18px 0 0', padding: '12px 20px 28px', minHeight: '46%', maxHeight: '82%', overflowY: 'auto' }}>
        <div style={{ width: 40, height: 4, borderRadius: 99, background: 'var(--color-neutral-200)', margin: '0 auto 16px' }} />
        <div style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', margin: '0 0 6px 2px' }}>Trocar fazenda</div>
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          {(fazendas || []).map((f, i) => {
            const on = f === fazenda
            return (
              <div key={f} onClick={() => onPick(f)} style={{ display: 'flex', alignItems: 'center', gap: 13, padding: '16px 4px', cursor: 'pointer', borderBottom: i < fazendas.length - 1 ? 'var(--border-neutral-light)' : 'none' }}>
                <span style={{ width: 22, height: 22, borderRadius: '50%', flexShrink: 0, border: '2px solid ' + (on ? 'var(--color-primary)' : 'var(--color-neutral-300)'), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {on && <span style={{ width: 10, height: 10, borderRadius: '50%', background: 'var(--color-primary)' }} />}
                </span>
                <span style={{ fontSize: 18, fontWeight: on ? 'var(--font-weight-bold)' : 'var(--font-weight-semibold)', color: on ? 'var(--color-primary)' : 'var(--color-grayish-dark)' }}>{f}</span>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

// ============================================================
// INÍCIO — lançador de módulos do app (sem bottombar).
// ============================================================
function Launcher({ onModule, mods, onExit }) {
  const { Icon } = DS()
  const footer = (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, fontSize: 12, color: 'var(--color-grayish)' }}>
      <span>Fazenda Guanabara</span>
      <span style={{ color: 'var(--color-neutral-300)' }}>·</span>
      <span>v2.4.0</span>
    </div>
  )
  return (
    <window.MPhone logo actionIcon="exit-outline" onAction={onExit} footer={footer}>
      {/* Boas-vindas no corpo claro, logo acima da lista de módulos. */}
      <div style={{ margin: '4px 2px 0' }}>
        <div style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-light)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)' }}>Bem-vindo</div>
        <div style={{ fontSize: 24, fontWeight: 'var(--font-weight-bold)', lineHeight: 1.2, color: 'var(--color-grayish-dark)', marginTop: 4, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>Siogo Pereira</div>
        <div style={{ fontSize: 14, color: 'var(--color-grayish)', marginTop: 18 }}>O que você quer acessar agora?</div>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 12 }}>
        {mods.map((m) => (
          <div key={m.id} onClick={() => onModule(m.id)} style={{ display: 'flex', alignItems: 'center', gap: 14, minHeight: 44, padding: '16px 14px 16px 16px', cursor: 'pointer', background: '#fff', borderRadius: 16, boxShadow: '0 1px 3px rgba(24,40,22,0.13)' }}>
            <span style={{ width: 52, height: 52, borderRadius: 14, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-primary-30)' }}>
              {m.iconNode ? m.iconNode : <Icon name={m.icon} size={26} color="var(--color-primary)" />}
            </span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 17, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.25 }}>{m.title}</div>
              <div style={{ fontSize: 13, color: 'var(--color-grayish)', marginTop: 4, lineHeight: 1.35 }}>{m.sub}</div>
            </div>
            <Icon name="chevron-forward" size={20} color="var(--color-neutral-300)" />
          </div>
        ))}
      </div>
    </window.MPhone>
  )
}

// ============================================================
// HISTÓRICO — todos os lançamentos do dia (editável por toque).
// ============================================================
// Histórico dos últimos 7 dias — hoje = registros vivos (na fila/enviados);
// dias anteriores = amostra determinística, todos já sincronizados (leitura).
function buildHistPast(fazenda) {
  const Pa = P()
  const mods = Pa.secoesDe(fazenda).flatMap((s) => s.modulos)
  if (!mods.length) return []
  let seed = 17
  const rnd = () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280 }
  const counts = [0, 2, 3, 1, 2, 0, 1]   // por offset de dia (1..6)
  const horas = ['07:10', '09:35', '11:20', '14:05', '16:40']
  const out = []
  for (let off = 1; off <= 6; off++) {
    const data = Pa.fmtBR(Pa.addDays(Pa.HOJE, -off))
    for (let i = 0; i < counts[off]; i++) {
      const m = mods[Math.floor(rnd() * mods.length)]
      const piquete = m.piquetes[Math.floor(rnd() * m.piquetes.length)]
      const base = { id: 90000 + off * 100 + i, modNome: m.nome, secao: m.secao, data, hora: horas[i % horas.length], status: 'sincronizado' }
      const k = rnd()
      if (k < 0.42) out.push({ ...base, tipo: 'entrada', cat: 'Novilhas Cheias', qtd: 40 + Math.floor(rnd() * 90), piquete })
      else if (k < 0.72) out.push({ ...base, tipo: 'saida', cat: 'Boi Gordo', qtd: 30 + Math.floor(rnd() * 70), piquete })
      else out.push({ ...base, tipo: 'evento', eventoTipo: 'Roçada', piquete })
    }
  }
  return out
}

// ---- Bottom sheet: filtros do Histórico -----------------------------------
// Escopo por local (Seção → Módulo → Piquete), status de envio e tipo do lançamento.
// "Movimentação" e "Evento" são exclusivos entre si (um lançamento é um ou outro).
function HistFiltroSheet({ fazenda, value, onApply, onClose }) {
  const { Icon, Button } = DS()
  const Pa = P()
  const { useState } = React
  const [f, setF] = useState(value)
  const set = (patch) => setF((p) => ({ ...p, ...patch }))

  const secoes = Pa.secoesDe(fazenda)
  const mods = f.secao ? (secoes.find((x) => x.nome === f.secao) || { modulos: [] }).modulos : []
  const piquetes = f.modNome ? ((mods.find((m) => m.nome === f.modNome) || { piquetes: [] }).piquetes) : []

  const Chip = ({ label, on, onClick }) => (
    <button onClick={onClick} style={{ minHeight: 40, padding: '0 13px', borderRadius: 'var(--border-radius-chip)', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 13, fontWeight: 'var(--font-weight-bold)', whiteSpace: 'nowrap',
      border: '1px solid ' + (on ? 'var(--color-primary)' : 'var(--color-neutral-200)'),
      background: on ? 'var(--color-primary)' : 'var(--color-neutral-light-100)',
      color: on ? '#fff' : 'var(--color-grayish-dark)' }}>{label}</button>
  )
  const Group = ({ label, children }) => (
    <div style={{ marginBottom: 18 }}>
      <div style={{ fontSize: 12, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish)', textTransform: 'uppercase', letterSpacing: 'var(--letter-space-xs)', marginBottom: 8 }}>{label}</div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>{children}</div>
    </div>
  )

  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 60, background: 'rgba(0,0,0,0.4)', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
      <div onClick={(e) => e.stopPropagation()} style={{ display: 'flex', flexDirection: 'column', background: '#fff', borderRadius: '18px 18px 0 0', maxHeight: '88%' }}>
        <div style={{ padding: '12px 20px 0', flexShrink: 0 }}>
          <div style={{ width: 40, height: 4, borderRadius: 99, background: 'var(--color-neutral-200)', margin: '0 auto 14px' }} />
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, marginBottom: 14 }}>
            <span style={{ fontSize: 20, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)' }}>Filtrar histórico</span>
            <button onClick={() => setF(HIST_FILTRO_VAZIO)} style={{ minHeight: 44, padding: '0 4px', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)' }}>Limpar</button>
          </div>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: '2px 20px 8px' }}>
          <Group label="Seção">
            <Chip label="Todas" on={!f.secao} onClick={() => set({ secao: '', modNome: '', piquete: '' })} />
            {secoes.map((sx) => <Chip key={sx.nome} label={'Seção ' + sx.nome} on={f.secao === sx.nome} onClick={() => set({ secao: sx.nome, modNome: '', piquete: '' })} />)}
          </Group>
          {!!f.secao && (
            <Group label="Módulo">
              <Chip label="Todos" on={!f.modNome} onClick={() => set({ modNome: '', piquete: '' })} />
              {mods.map((m) => <Chip key={m.id} label={'Mód. ' + m.nome} on={f.modNome === m.nome} onClick={() => set({ modNome: m.nome, piquete: '' })} />)}
            </Group>
          )}
          {!!f.modNome && (
            <Group label="Piquete">
              <Chip label="Todos" on={!f.piquete} onClick={() => set({ piquete: '' })} />
              {piquetes.map((p) => <Chip key={p} label={p} on={f.piquete === p} onClick={() => set({ piquete: p })} />)}
            </Group>
          )}
          <Group label="Tipo de evento">
            <Chip label="Tudo" on={!f.evento} onClick={() => set({ evento: '' })} />
            {EVENTO_TIPOS.map((e) => <Chip key={e.nome} label={eventoShort(e.nome)} on={f.evento === e.nome} onClick={() => set({ evento: e.nome })} />)}
          </Group>
        </div>

        <div style={{ flexShrink: 0, padding: '12px 20px 24px', borderTop: 'var(--border-neutral-light)' }}>
          <Button size="large" style={{ width: '100%' }} onClick={() => onApply(f)}>Aplicar filtros</Button>
        </div>
      </div>
    </div>
  )
}

const HIST_FILTRO_VAZIO = { secao: '', modNome: '', piquete: '', evento: '' }
const histFiltroAtivo = (f) => Object.keys(HIST_FILTRO_VAZIO).filter((k) => f[k]).length

function Historico({ registros, fazenda, onDelete, onTab, onModules, onExit, fazModal }) {
  const { Icon, Button } = DS()
  const Pa = P()
  const { useState } = React
  const [filtro, setFiltro] = useState(HIST_FILTRO_VAZIO)  // filtros do bottom sheet
  const [sheet, setSheet] = useState(false)
  const nAtivos = histFiltroAtivo(filtro)
  const todayBR = Pa.fmtBR(Pa.HOJE)
  const todayCount = registros.filter((r) => r.data === todayBR).length || registros.length

  // Lista única: hoje (vivo) + últimos 6 dias (leitura).
  const all = [...registros, ...buildHistPast(fazenda)]
  const visiveis = all.filter((r) => (
    (!filtro.secao || r.secao === filtro.secao) &&
    (!filtro.modNome || r.modNome === filtro.modNome) &&
    (!filtro.piquete || r.piquete === filtro.piquete) &&
    (!filtro.evento || (r.tipo === 'evento' && r.eventoTipo === filtro.evento))
  ))
  // Agrupa por data, do mais recente (hoje) ao mais antigo.
  const dias = []
  for (let off = 0; off <= 6; off++) {
    const data = Pa.fmtBR(Pa.addDays(Pa.HOJE, -off))
    const itens = visiveis.filter((r) => r.data === data).sort((a, b) => (b.hora || '').localeCompare(a.hora || ''))
    if (itens.length) dias.push({ data, itens })
  }

  const HistRow = ({ r, last }) => {
    const d = descGrupo({ recs: [r], hora: r.hora, data: r.data })
    const t = TIPO_UI[d.tipo]
    const pend = r.status === 'pendente'
    const pillBg = pend ? 'var(--color-alert-light-100)' : 'var(--color-primary-30)'
    const pillFg = pend ? 'var(--color-alert-dark)' : 'var(--color-success-dark)'
    return (
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '13px 6px 13px 12px', minHeight: 44, borderLeft: '4px solid ' + (pend ? 'var(--color-alert)' : 'transparent'), borderBottom: last ? 'none' : 'var(--border-neutral-light)' }}>
        <span style={{ width: 38, height: 38, borderRadius: 'var(--border-radius-default)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--color-neutral-light-500)' }}>
          <Icon name={t.icon} size={18} color={t.cor} />
        </span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 15, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', lineHeight: 1.25, textWrap: 'pretty' }}>{d.titulo}</div>
          <div style={{ fontSize: 13, color: 'var(--color-grayish)', marginTop: 2, lineHeight: 1.3 }}>{d.sub}</div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 5, flexShrink: 0 }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, background: pillBg, color: pillFg, fontSize: 12, fontWeight: 'var(--font-weight-bold)', padding: '4px 9px', borderRadius: 99, whiteSpace: 'nowrap' }}>
            <Icon name={pend ? 'file-tray-full-outline' : 'checkmark-circle-outline'} size={13} color={pillFg} />
            {pend ? 'Na fila' : 'Enviado'}
          </span>
          <span style={{ fontSize: 12, color: 'var(--color-grayish)', fontWeight: 'var(--font-weight-semibold)' }}>{r.hora}</span>
        </div>
      </div>
    )
  }

  // Resumo dos filtros ativos — cada um removível por toque no ×.
  const ativos = [
    filtro.secao && { k: ['secao', 'modNome', 'piquete'], label: 'Seção ' + filtro.secao },
    filtro.modNome && { k: ['modNome', 'piquete'], label: 'Mód. ' + filtro.modNome },
    filtro.piquete && { k: ['piquete'], label: filtro.piquete },
    filtro.evento && { k: ['evento'], label: eventoShort(filtro.evento) },
  ].filter(Boolean)
  const dropKeys = (ks) => setFiltro((p) => { const n = { ...p }; ks.forEach((k) => { n[k] = '' }); return n })

  return (
    <window.MPhone title="Últimos registros" leadingNode={onModules ? <ModulesButton onClick={onModules} /> : undefined}
      actionIcon="exit-outline" onAction={onExit}
      footer={<window.PastTabBar active="historico" onTab={onTab} />}
      modal={fazModal || (sheet ? (
        <HistFiltroSheet fazenda={fazenda} value={filtro}
          onApply={(f) => { setFiltro(f); setSheet(false) }} onClose={() => setSheet(false)} />
      ) : null)}>

      {/* Filtrar — escopo por local, status de envio e tipo do lançamento (7 dias). */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: ativos.length ? 10 : 16 }}>
        <span style={{ flex: 1, fontSize: 13, color: 'var(--color-grayish)' }}>{Pa.fmt(visiveis.length)} {visiveis.length === 1 ? 'lançamento' : 'lançamentos'}</span>
        <button onClick={() => setSheet(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 8, minHeight: 44, padding: '0 15px', borderRadius: 'var(--border-radius-chip)', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 14, fontWeight: 'var(--font-weight-bold)',
          border: 'none', background: 'transparent', color: 'var(--color-primary)' }}>
          <Icon name="filter" size={17} color="var(--color-primary)" />
          Filtrar
          {!!nAtivos && <span style={{ minWidth: 20, height: 20, borderRadius: 99, background: 'var(--color-primary)', color: '#fff', fontSize: 12, fontWeight: 'var(--font-weight-bold)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0 5px' }}>{nAtivos}</span>}
        </button>
      </div>
      {!!ativos.length && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7, marginBottom: 16 }}>
          {ativos.map((a) => (
            <button key={a.label} onClick={() => dropKeys(a.k)} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, minHeight: 32, padding: '0 8px 0 11px', borderRadius: 99, cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 13, fontWeight: 'var(--font-weight-semibold)', border: 'none', background: 'var(--color-neutral-light-500)', color: 'var(--color-grayish-dark)' }}>
              {a.label}<Icon name="close" size={14} color="var(--color-grayish)" />
            </button>
          ))}
          <button onClick={() => setFiltro(HIST_FILTRO_VAZIO)} style={{ minHeight: 32, padding: '0 8px', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: 'var(--font-family)', fontSize: 13, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-primary)' }}>Limpar tudo</button>
        </div>
      )}

      {dias.length ? dias.map((g) => (
        <div key={g.data} style={{ marginBottom: 16 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, padding: '8px 12px', background: 'var(--color-neutral-light-500)', borderRadius: 'var(--border-radius-default)', marginBottom: 2 }}>
            <span style={{ fontSize: 14, fontWeight: 'var(--font-weight-bold)', color: 'var(--color-grayish-dark)', textTransform: 'capitalize' }}>{relativoData(g.data)}</span>
            <span style={{ fontSize: 12, color: 'var(--color-grayish)' }}>{g.data}</span>
          </div>
          {g.itens.map((r, i) => <HistRow key={r.id} r={r} last={i === g.itens.length - 1} />)}
        </div>
      )) : (
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, padding: '40px 18px', color: 'var(--color-grayish)' }}>
          <Icon name="file-tray-full-outline" size={22} color="var(--color-grayish-light)" />
          <span style={{ fontSize: 14, textAlign: 'center' }}>{nAtivos ? 'Nenhum lançamento com esses filtros.' : 'Nenhum lançamento nos últimos 7 dias.'}</span>
        </div>
      )}
    </window.MPhone>
  )
}

window.PastHome = Home
window.PastConsultar = Consultar
window.PastDone = Done
window.PastHistorico = Historico
window.PastLauncher = Launcher
window.PastTabBar = TabBar
window.PastModulosSheet = ModulosSheet
window.PastHistFiltroSheet = HistFiltroSheet
window.FazendaSheet = FazendaSheet
window.PastStatusTag = StatusTag
// Dias de descanso de um piquete = dias desde a última saída registrada nele.
window.PastDiasDescanso = (fazenda, mod, piquete) => {
  const Pa = P()
  if (Pa.loteNoPiquete && Pa.loteNoPiquete(mod, piquete)) return 0
  const h = [...historicoCompleto(fazenda, mod.nome).filter((x) => x.piquete === piquete), ...histPiquete(fazenda, mod, piquete)]
    .sort((x, y) => Pa.parseBR(y.data) - Pa.parseBR(x.data))
  const s = h.find((x) => x.tipo === 'saida')
  return s ? Pa.diasEntre(s.data) : 0
}
window.PastTipoUI = TIPO_UI
window.PastEventoTipos = EVENTO_TIPOS
window.PastEventoShort = eventoShort
