<script setup>
/*
 * MelissaCadastrosRecebidos — Cadastros recebidos (intake requests externos).
 * Segue blueprint melissa-page-blueprint.md.
 *
 * Layout 2-col:
 *   - COL 1 — Aside (~280px): stats por status (Novo/Convertido/Rejeitado)
 *            + filtros (status pill toggle) + busca
 *   - COL 2 — Lista de cards de cadastro (avatar + nome + telefone/email +
 *            status badge + tempo)
 *
 * Click num card → dialog com detalhes + ações (Rejeitar / Converter em paciente).
 * Reaproveita a lógica de patient_intake_requests da página antiga.
 */
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { useToast } from 'primevue/usetoast';
import { useConfirm } from 'primevue/useconfirm';
import { supabase } from '@/lib/supabase/client';
import { useTenantStore } from '@/stores/tenantStore';
// Fase 2 (Graphify hotspot): convertToPatient duplicado em 2 pages — extração pro repository.
import { createPatient, markIntakeConverted } from '@/features/patients/services/patientsRepository';
import { brToISO, isoToBR } from '@/utils/dateBR';
// Dialog/Textarea/Button auto-imported via PrimeVueResolver

const emit = defineEmits(['close']);
const toast = useToast();
const confirm = useConfirm();
const tenantStore = useTenantStore();

// ── Breakpoints + drawer ───────────────────────────────────
const drawerOpen = ref(false);
const isMobile = ref(false);
let _mqMobile = null;
function _onMqMobileChange(e) {
    isMobile.value = e.matches;
    if (!e.matches) drawerOpen.value = false;
}
function toggleDrawer() { drawerOpen.value = !drawerOpen.value; }
function fecharDrawer() { drawerOpen.value = false; }

// ── Estado ─────────────────────────────────────────────────
const loading = ref(false);
const converting = ref(false);
const rows = ref([]);
const busca = ref('');
const statusFilter = ref('');

const carregandoInicial = computed(
    () => loading.value && rows.value.length === 0
);

// ── Helpers de campos (espelhados da CadastrosRecebidosPage) ──
function pickField(obj, keys) {
    for (const k of keys) {
        const v = obj?.[k];
        if (v !== undefined && v !== null && String(v).trim() !== '') return v;
    }
    return null;
}
const fNome = (i) => pickField(i, ['nome_completo', 'name']);
const fEmail = (i) => pickField(i, ['email_principal', 'email']);
const fEmailAlt = (i) => pickField(i, ['email_alternativo', 'email_alt']);
const fTel = (i) => pickField(i, ['telefone', 'phone']);
const fTelAlt = (i) => pickField(i, ['telefone_alternativo', 'phone_alt']);
const fNasc = (i) => pickField(i, ['data_nascimento', 'birth_date']);
const fGenero = (i) => pickField(i, ['genero', 'gender']);
const fProf = (i) => pickField(i, ['profissao', 'profession']);
const fCep = (i) => pickField(i, ['cep']);
const fEndereco = (i) => pickField(i, ['endereco', 'address_street']);
const fNumero = (i) => pickField(i, ['numero', 'address_number']);
const fBairro = (i) => pickField(i, ['bairro', 'address_neighborhood']);
const fCidade = (i) => pickField(i, ['cidade', 'address_city']);
const fEstado = (i) => pickField(i, ['estado', 'address_state']);
const fEstadoCivil = (i) => pickField(i, ['estado_civil', 'marital_status']);
const fNaturalidade = (i) => pickField(i, ['naturalidade', 'place_of_birth']);
const fEscolaridade = (i) => pickField(i, ['escolaridade', 'education_level']);
const fOndeConheceu = (i) => pickField(i, ['onde_nos_conheceu', 'lead_source']);
const fEncaminhado = (i) => pickField(i, ['encaminhado_por', 'referred_by']);
const fObs = (i) => pickField(i, ['observacoes', 'notes_short']);
const fComplemento = (i) => pickField(i, ['complemento', 'address_complement']);
const fPais = (i) => pickField(i, ['pais', 'country']) || 'Brasil';
const fEmailAlt2 = (i) => pickField(i, ['email_alternativo', 'email_alt']);

// ── Formatters ─────────────────────────────────────────────
function dash(v) {
    const s = String(v ?? '').trim();
    return s ? s : '—';
}
function onlyDigits(v) { return String(v ?? '').replace(/\D/g, ''); }
function fmtPhoneBR(v) {
    const d = onlyDigits(v);
    if (!d) return '—';
    if (d.length === 11) return `(${d.slice(0,2)}) ${d.slice(2,7)}-${d.slice(7,11)}`;
    if (d.length === 10) return `(${d.slice(0,2)}) ${d.slice(2,6)}-${d.slice(6,10)}`;
    return d;
}
function fmtCPF(v) {
    const d = onlyDigits(v);
    if (!d || d.length !== 11) return d || '—';
    return `${d.slice(0,3)}.${d.slice(3,6)}.${d.slice(6,9)}-${d.slice(9,11)}`;
}
function fmtBirth(v) {
    if (!v) return '—';
    const s = String(v).trim();
    if (/^\d{2}-\d{2}-\d{4}$/.test(s)) return s;
    if (/^\d{4}-\d{2}-\d{2}/.test(s)) return isoToBR(s.slice(0, 10)) || s;
    return s;
}
function normalizeBirthToISO(v) {
    if (!v) return null;
    const s = String(v).trim();
    if (!s) return null;
    if (/^\d{2}-\d{2}-\d{4}$/.test(s)) return brToISO(s);
    if (/^\d{4}-\d{2}-\d{2}/.test(s)) return s.slice(0, 10);
    const d = new Date(s);
    if (Number.isNaN(d.getTime())) return null;
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
function fmtRelative(iso) {
    if (!iso) return '—';
    const d = new Date(iso);
    const now = new Date();
    const diff = Math.floor((now - d) / 1000);
    if (diff < 60) return 'agora';
    if (diff < 3600) return `${Math.floor(diff / 60)}m`;
    if (diff < 86400) return `${Math.floor(diff / 3600)}h`;
    if (diff < 604800) return `${Math.floor(diff / 86400)}d`;
    return d.toLocaleDateString('pt-BR');
}
function pacienteIniciais(nome) {
    if (!nome) return '?';
    const partes = String(nome).trim().split(/\s+/);
    if (partes.length === 1) return partes[0][0]?.toUpperCase() || '?';
    return (partes[0][0] + partes[partes.length - 1][0]).toUpperCase();
}

// ── Avatar URL ─────────────────────────────────────────────
const AVATAR_BUCKET = 'avatars';
function looksLikeUrl(s) { return /^https?:\/\//i.test(String(s || '')); }
function firstNonEmpty(...vals) {
    for (const v of vals) {
        const s = String(v ?? '').trim();
        if (s) return s;
    }
    return '';
}
function avatarUrl(row) {
    const p = row?.payload || row?.data || row?.form || null;
    const direct = firstNonEmpty(row?.avatar_url, row?.foto_url, row?.photo_url, p?.avatar_url, p?.foto_url, p?.photo_url);
    if (direct && looksLikeUrl(direct)) return direct;
    const path = firstNonEmpty(row?.avatar_path, row?.photo_path, p?.avatar_path, p?.photo_path, direct);
    if (!path) return null;
    if (looksLikeUrl(path)) return path;
    const { data } = supabase.storage.from(AVATAR_BUCKET).getPublicUrl(path);
    return data?.publicUrl || null;
}

// ── Status helpers ─────────────────────────────────────────
function statusLabel(s) {
    if (s === 'new') return 'Novo';
    if (s === 'converted') return 'Convertido';
    if (s === 'rejected') return 'Rejeitado';
    return s || '—';
}
function statusClass(s) {
    if (s === 'new') return 'is-new';
    if (s === 'converted') return 'is-done';
    if (s === 'rejected') return 'is-rejected';
    return '';
}

// ── Stats ──────────────────────────────────────────────────
const stats = computed(() => {
    const all = rows.value;
    const n = all.filter((r) => r.status === 'new').length;
    const c = all.filter((r) => r.status === 'converted').length;
    const r = all.filter((r) => r.status === 'rejected').length;
    return [
        { key: 'total', label: 'Total', value: all.length, cls: 'neutral' },
        { key: 'new', label: 'Novos', value: n, cls: n > 0 ? 'info' : 'neutral' },
        { key: 'converted', label: 'Convertidos', value: c, cls: 'ok' },
        { key: 'rejected', label: 'Rejeitados', value: r, cls: r > 0 ? 'danger' : 'neutral' }
    ];
});

const STATUS_FILTER_OPTIONS = [
    { key: 'new', label: 'Novos', icon: 'pi pi-bell' },
    { key: 'converted', label: 'Convertidos', icon: 'pi pi-check' },
    { key: 'rejected', label: 'Rejeitados', icon: 'pi pi-times' }
];

function toggleStatusFilter(s) {
    statusFilter.value = statusFilter.value === s ? '' : s;
}

// ── Filtragem ──────────────────────────────────────────────
const filtered = computed(() => {
    const term = String(busca.value || '').trim().toLowerCase();
    let list = rows.value;
    if (statusFilter.value) list = list.filter((r) => r.status === statusFilter.value);
    if (!term) return list;
    return list.filter((r) => {
        const nome = String(fNome(r) || '').toLowerCase();
        const email = String(fEmail(r) || '').toLowerCase();
        const tel = String(fTel(r) || '').toLowerCase();
        return nome.includes(term) || email.includes(term) || tel.includes(term);
    });
});

// ── Paginação client-side (DataTable controla internamente) ──────
// `fetchIntakes` traz a lista completa de patient_intake_requests do
// tenant — o DataTable pagina internamente via prop `paginator`. A
// gente só precisa controlar `rows` (tamanho da página) e `first`
// (índice do primeiro item) pra reagir a filtros e mudanças de tamanho.
const PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
const rowsMCR = ref(10);
const firstMCR = ref(0);
function onPage(event) {
    firstMCR.value = event.first;
    rowsMCR.value = event.rows;
}
// Reset pra página 1 quando busca/status mudam.
watch([busca, statusFilter], () => { firstMCR.value = 0; });

// Click numa row → abre o dialog. selectionMode="single" é só pra
// marcar visualmente — não persistimos seleção.
function onRowClick(event) {
    if (event?.data) openDetails(event.data);
}
function rowStatusClass(data) {
    return statusClass(data?.status);
}

// ── View mode (list / grid) ────────────────────────────────
// Toggle no topo do main column. Persiste em localStorage pra manter
// a preferência do user entre sessões. List = DataTable; Grid = cards
// num CSS grid + Paginator standalone (compartilha rowsMCR/firstMCR).
const VIEW_MODE_KEY = 'mcr.viewMode.v1';
const viewMode = ref('list');
try {
    const saved = localStorage.getItem(VIEW_MODE_KEY);
    if (saved === 'list' || saved === 'grid') viewMode.value = saved;
} catch (_) { /* localStorage indisponível — mantém default */ }
function setViewMode(m) {
    if (m !== 'list' && m !== 'grid') return;
    viewMode.value = m;
    try { localStorage.setItem(VIEW_MODE_KEY, m); } catch (_) { /* noop */ }
}

// Slice pra grid (DataTable já pagina internamente; grid precisa
// fatiar o `filtered` com base no firstMCR/rowsMCR compartilhados).
const pagedItems = computed(() => {
    return filtered.value.slice(firstMCR.value, firstMCR.value + rowsMCR.value);
});

// ── Fetch ──────────────────────────────────────────────────
async function fetchIntakes() {
    loading.value = true;
    try {
        const { data, error } = await supabase
            .from('patient_intake_requests').select('*')
            .order('created_at', { ascending: false });
        if (error) throw error;
        const weight = (s) => (s === 'new' ? 0 : s === 'converted' ? 1 : s === 'rejected' ? 2 : 9);
        rows.value = (data || []).slice().sort((a, b) => {
            const wa = weight(a.status), wb = weight(b.status);
            if (wa !== wb) return wa - wb;
            return new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime();
        });
    } catch (e) {
        toast.add({ severity: 'error', summary: 'Erro ao carregar', detail: e?.message || String(e), life: 3500 });
    } finally {
        loading.value = false;
    }
}

// ── Dialog detalhes ────────────────────────────────────────
const dlg = ref({ open: false, item: null, saving: false, reject_note: '' });

function openDetails(row) {
    dlg.value.open = true;
    dlg.value.item = row;
    dlg.value.reject_note = row?.rejected_reason || '';
}
function closeDlg() {
    dlg.value.open = false;
    dlg.value.saving = false;
    dlg.value.item = null;
    dlg.value.reject_note = '';
}

const dlgAvatarUrl = computed(() => dlg.value.item ? avatarUrl(dlg.value.item) : null);

const dlgSections = computed(() => {
    const i = dlg.value.item;
    if (!i) return [];
    const sec = (title, rs) => ({ title, rows: rs.filter((r) => r.value && r.value !== '—') });
    return [
        sec('Identificação', [
            { label: 'Email', value: dash(fEmail(i)) },
            { label: 'Email alt.', value: dash(fEmailAlt(i)) },
            { label: 'Telefone', value: fmtPhoneBR(fTel(i)) },
            { label: 'Tel. alt.', value: fmtPhoneBR(fTelAlt(i)) },
            { label: 'Nascimento', value: fmtBirth(fNasc(i)) },
            { label: 'Gênero', value: dash(fGenero(i)) },
            { label: 'Estado civil', value: dash(fEstadoCivil(i)) }
        ]),
        sec('Documentos', [
            { label: 'CPF', value: fmtCPF(i.cpf) },
            { label: 'RG', value: dash(i.rg) }
        ]),
        sec('Endereço', [
            { label: 'CEP', value: dash(fCep(i)) },
            { label: 'Endereço', value: [fEndereco(i), fNumero(i)].filter(Boolean).join(', ') || '—' },
            { label: 'Complemento', value: dash(fComplemento(i)) },
            { label: 'Bairro', value: dash(fBairro(i)) },
            { label: 'Cidade/UF', value: [fCidade(i), fEstado(i)].filter(Boolean).join('/') || '—' },
            { label: 'País', value: fPais(i) }
        ]),
        sec('Profissional', [
            { label: 'Profissão', value: dash(fProf(i)) },
            { label: 'Escolaridade', value: dash(fEscolaridade(i)) },
            { label: 'Naturalidade', value: dash(fNaturalidade(i)) },
            { label: 'Onde conheceu', value: dash(fOndeConheceu(i)) },
            { label: 'Encaminhado por', value: dash(fEncaminhado(i)) }
        ]),
        sec('Observações', [
            { label: 'Obs.', value: dash(fObs(i)) },
            { label: 'Notas internas', value: dash(i.notas_internas || i.notes) }
        ])
    ].filter((s) => s.rows.length > 0);
});

// ── Rejeitar ───────────────────────────────────────────────
async function markRejected() {
    const item = dlg.value.item;
    if (!item) return;
    confirm.require({
        message: 'Marcar este cadastro como rejeitado?',
        header: 'Confirmar rejeição',
        icon: 'pi pi-exclamation-triangle',
        acceptLabel: 'Rejeitar',
        rejectLabel: 'Cancelar',
        acceptSeverity: 'danger',
        accept: async () => {
            dlg.value.saving = true;
            try {
                const reason = String(dlg.value.reject_note || '').trim() || null;
                const { error } = await supabase.from('patient_intake_requests')
                    .update({ status: 'rejected', rejected_reason: reason, updated_at: new Date().toISOString() })
                    .eq('id', item.id);
                if (error) throw error;
                toast.add({ severity: 'success', summary: 'Rejeitado', life: 2200 });
                await fetchIntakes();
                const updated = rows.value.find((r) => r.id === item.id);
                if (updated) openDetails(updated); else closeDlg();
            } catch (e) {
                toast.add({ severity: 'error', summary: 'Erro', detail: e?.message, life: 4500 });
            } finally {
                dlg.value.saving = false;
            }
        }
    });
}

// ── Converter ──────────────────────────────────────────────
async function getResponsibleMemberId(tenantId, userId) {
    const { data, error } = await supabase.from('tenant_members')
        .select('id').eq('tenant_id', tenantId).eq('user_id', userId)
        .eq('status', 'active').maybeSingle();
    if (error) throw error;
    if (!data?.id) throw new Error('Membro responsável não encontrado.');
    return data.id;
}

async function convertToPatient() {
    const item = dlg.value?.item;
    if (!item?.id || converting.value) return;
    if (item.status === 'converted') {
        toast.add({ severity: 'warn', summary: 'Já convertido', life: 2500 });
        return;
    }
    converting.value = true;
    try {
        const { data: userData, error: userErr } = await supabase.auth.getUser();
        if (userErr) throw userErr;
        const ownerId = userData?.user?.id;
        if (!ownerId) throw new Error('Sessão inválida.');

        const tenantId = tenantStore.activeTenantId || tenantStore.tenantId || null;
        if (!tenantId) throw new Error('Tenant não inicializado.');

        const responsibleMemberId = await getResponsibleMemberId(tenantId, ownerId);

        const cleanStr = (v) => { const s = String(v ?? '').trim(); return s ? s : null; };
        const digitsOnly = (v) => { const d = String(v ?? '').replace(/\D/g, ''); return d ? d : null; };
        const intakeAvatar = cleanStr(item.avatar_url) || cleanStr(item.foto_url) || cleanStr(item.photo_url) || null;

        const patientPayload = {
            tenant_id: tenantId,
            responsible_member_id: responsibleMemberId,
            owner_id: ownerId,
            nome_completo: cleanStr(fNome(item)),
            email_principal: cleanStr(fEmail(item))?.toLowerCase() || null,
            email_alternativo: cleanStr(fEmailAlt2(item))?.toLowerCase() || null,
            telefone: digitsOnly(fTel(item)),
            telefone_alternativo: digitsOnly(fTelAlt(item)),
            data_nascimento: normalizeBirthToISO(fNasc(item)),
            naturalidade: cleanStr(fNaturalidade(item)),
            genero: cleanStr(fGenero(item)),
            estado_civil: cleanStr(fEstadoCivil(item)),
            cpf: digitsOnly(item.cpf),
            rg: cleanStr(item.rg),
            pais: cleanStr(fPais(item)) || 'Brasil',
            cep: digitsOnly(fCep(item)),
            cidade: cleanStr(fCidade(item)),
            estado: cleanStr(fEstado(item)) || 'SP',
            endereco: cleanStr(fEndereco(item)),
            numero: cleanStr(fNumero(item)),
            bairro: cleanStr(fBairro(item)),
            complemento: cleanStr(fComplemento(item)),
            escolaridade: cleanStr(fEscolaridade(item)),
            profissao: cleanStr(fProf(item)),
            onde_nos_conheceu: cleanStr(fOndeConheceu(item)),
            encaminhado_por: cleanStr(fEncaminhado(item)),
            observacoes: cleanStr(fObs(item)),
            notas_internas: cleanStr(item.notas_internas || item.notes),
            avatar_url: intakeAvatar
        };
        Object.keys(patientPayload).forEach((k) => {
            if (patientPayload[k] === undefined) delete patientPayload[k];
        });

        // Repository chamadas (Fase 2 — convertToPatient de-dup).
        const created = await createPatient(patientPayload);
        const patientId = created?.id;
        if (!patientId) throw new Error('Falha ao obter ID do paciente.');

        await markIntakeConverted(item.id, patientId);

        toast.add({ severity: 'success', summary: 'Convertido em paciente', life: 2500 });
        closeDlg();
        await fetchIntakes();
    } catch (e) {
        toast.add({ severity: 'error', summary: 'Falha ao converter', detail: e?.message, life: 4500 });
    } finally {
        converting.value = false;
    }
}

onMounted(async () => {
    if (typeof window !== 'undefined' && window.matchMedia) {
        _mqMobile = window.matchMedia('(max-width: 1023px)');
        isMobile.value = _mqMobile.matches;
        _mqMobile.addEventListener('change', _onMqMobileChange);
    }
    if (typeof tenantStore.loadSessionAndTenant === 'function') {
        await tenantStore.loadSessionAndTenant();
    }
    await fetchIntakes();
});
onBeforeUnmount(() => {
    if (_mqMobile) _mqMobile.removeEventListener('change', _onMqMobileChange);
});
</script>

<template>
    <aside
        class="mcr-mobile-drawer"
        :class="{ 'is-open': drawerOpen }"
        v-show="isMobile"
        aria-label="Estatísticas e filtros"
    >
        <div id="mcr-mobile-drawer-target" class="mcr-mobile-drawer__scroll" />
    </aside>
    <Transition name="mcr-drawer-fade">
        <div
            v-if="isMobile && drawerOpen"
            class="mcr-mobile-drawer__backdrop"
            @click="fecharDrawer"
        />
    </Transition>

    <section class="mcr-page">
        <header class="mcr-page__head">
            <button
                class="mcr-menu-btn mcr-menu-btn--mobile-only"
                v-tooltip.bottom="'Estatísticas & filtros'"
                @click="toggleDrawer"
            >
                <i class="pi pi-bars" />
                <span>Menu Cadastros</span>
            </button>
            <div class="mcr-page__title">
                <i class="pi pi-inbox mcr-page__title-icon" />
                <span>Cadastros recebidos</span>
                <span class="mcr-page__count">{{ filtered.length }}</span>
            </div>
            <div class="mcr-page__actions">
                <button
                    class="mcr-head-btn"
                    v-tooltip.bottom="'Recarregar'"
                    :disabled="loading"
                    @click="fetchIntakes"
                >
                    <i :class="loading ? 'pi pi-spin pi-spinner' : 'pi pi-refresh'" />
                </button>
                <button class="mcr-close" v-tooltip.bottom="'Voltar (Esc)'" @click="emit('close')">
                    <i class="pi pi-times" />
                </button>
            </div>
        </header>

        <!-- Subheader explicativo — diferencia visualmente desta página
             (vs. Agendamentos Recebidos, que tem layout idêntico). Padrão
             do melissa-table-page-blueprint.md. -->
        <div class="mcr-subheader">
            <i class="pi pi-info-circle mcr-subheader__icon" />
            <span class="mcr-subheader__text">
                Cadastros completos enviados por pacientes via formulário
                externo (link público). Revise os dados,
                <strong>converta em paciente ativo</strong> com 1 clique ou
                <strong>rejeite</strong> com motivo opcional.
            </span>
        </div>

        <div class="mcr-body">
            <Teleport to="#mcr-mobile-drawer-target" :disabled="!isMobile">
            <aside class="mcr-side">
                <!-- Stats -->
                <div class="mcr-w mcr-w--side">
                    <div class="mcr-w__head">
                        <span class="mcr-w__title"><i class="pi pi-chart-bar" /> Estatísticas</span>
                    </div>
                    <div class="mcr-stats">
                        <template v-if="carregandoInicial">
                            <div v-for="i in 4" :key="`stsk-${i}`" class="mcr-stat" aria-busy="true">
                                <div class="mcr-stat__val melissa-skeleton melissa-skeleton--number" />
                                <div class="mcr-stat__lbl melissa-skeleton melissa-skeleton--text" style="width: 60%; margin-top: 6px;" />
                            </div>
                        </template>
                        <div
                            v-for="s in stats"
                            v-else
                            :key="s.key"
                            class="mcr-stat"
                            :class="`is-${s.cls}`"
                        >
                            <div class="mcr-stat__val">{{ s.value }}</div>
                            <div class="mcr-stat__lbl">{{ s.label }}</div>
                        </div>
                    </div>
                </div>

                <!-- Filtro de status -->
                <div class="mcr-w mcr-w--side">
                    <div class="mcr-w__head">
                        <span class="mcr-w__title"><i class="pi pi-filter" /> Status</span>
                        <span v-if="statusFilter" class="mcr-w__count">1</span>
                    </div>
                    <div class="mcr-side__list">
                        <button
                            v-for="o in STATUS_FILTER_OPTIONS"
                            :key="o.key"
                            class="mcr-side__item"
                            :class="[`is-${o.key}`, { 'is-active': statusFilter === o.key }]"
                            @click="toggleStatusFilter(o.key)"
                        >
                            <i :class="o.icon" />
                            <span>{{ o.label }}</span>
                        </button>
                        <Transition name="mcr-clear">
                            <button
                                v-if="statusFilter"
                                class="mcr-side__item is-clear"
                                @click="statusFilter = ''"
                            >
                                <i class="pi pi-filter-slash" />
                                <span>Limpar filtro</span>
                            </button>
                        </Transition>
                    </div>
                </div>
            </aside>
            </Teleport>

            <div class="mcr-main">
                <!-- Toolbar com busca + toggle de visualização (lista/grade).
                     Search no topo da coluna que contém os dados. Count
                     reflete o filtrado pra feedback imediato ao digitar. -->
                <div class="mcr-toolbar">
                    <div class="mcr-search">
                        <i class="pi pi-search mcr-search__icon" />
                        <input
                            v-model="busca"
                            type="text"
                            placeholder="Buscar por nome, email ou telefone…"
                            class="mcr-search__input"
                        />
                        <button
                            v-if="busca"
                            class="mcr-search__clear"
                            v-tooltip.bottom="'Limpar busca'"
                            @click="busca = ''"
                        >
                            <i class="pi pi-times" />
                        </button>
                    </div>
                    <div class="mcr-view-toggle" role="group" aria-label="Visualização">
                        <button
                            class="mcr-view-toggle__btn"
                            :class="{ 'is-active': viewMode === 'list' }"
                            v-tooltip.bottom="'Lista'"
                            aria-label="Lista"
                            @click="setViewMode('list')"
                        >
                            <i class="pi pi-list" />
                        </button>
                        <button
                            class="mcr-view-toggle__btn"
                            :class="{ 'is-active': viewMode === 'grid' }"
                            v-tooltip.bottom="'Grade'"
                            aria-label="Grade"
                            @click="setViewMode('grid')"
                        >
                            <i class="pi pi-th-large" />
                        </button>
                    </div>
                </div>

                <DataTable
                    v-if="viewMode === 'list'"
                    :value="filtered"
                    :loading="loading"
                    dataKey="id"
                    paginator
                    :rows="rowsMCR"
                    :first="firstMCR"
                    :rowsPerPageOptions="PAGE_SIZE_OPTIONS"
                    paginatorTemplate="RowsPerPageDropdown FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink"
                    currentPageReportTemplate="{first}–{last} de {totalRecords}"
                    :rowClass="rowStatusClass"
                    selectionMode="single"
                    scrollable
                    scrollHeight="flex"
                    tableStyle="min-width: 640px"
                    class="mcr-table"
                    @row-click="onRowClick"
                    @page="onPage"
                >
                    <Column header="Paciente" style="min-width: 220px">
                        <template #body="{ data }">
                            <div class="mcr-row__patient">
                                <span class="mcr-card__avatar mcr-card__avatar--sm">
                                    <img v-if="avatarUrl(data)" :src="avatarUrl(data)" :alt="fNome(data) || 'Avatar'" />
                                    <span v-else>{{ pacienteIniciais(fNome(data)) }}</span>
                                </span>
                                <div class="mcr-row__patient-text">
                                    <span class="mcr-row__name">{{ fNome(data) || '—' }}</span>
                                    <span class="mcr-card__badge" :class="statusClass(data.status)">
                                        {{ statusLabel(data.status) }}
                                    </span>
                                </div>
                            </div>
                        </template>
                    </Column>

                    <Column header="Contato" style="min-width: 220px">
                        <template #body="{ data }">
                            <div class="mcr-row__contact">
                                <span v-if="fEmail(data)"><i class="pi pi-envelope" /> {{ fEmail(data) }}</span>
                                <span v-if="fTel(data)"><i class="pi pi-phone" /> {{ fmtPhoneBR(fTel(data)) }}</span>
                                <span v-if="!fEmail(data) && !fTel(data)" class="mcr-row__empty">—</span>
                            </div>
                        </template>
                    </Column>

                    <Column header="Recebido" style="width: 130px">
                        <template #body="{ data }">
                            <span class="mcr-row__time">
                                <i class="pi pi-clock" /> {{ fmtRelative(data.created_at) }}
                            </span>
                        </template>
                    </Column>

                    <Column
                        header=""
                        :style="{ width: '60px', maxWidth: '60px', minWidth: '60px' }"
                        frozen
                        alignFrozen="right"
                        class="mcr-col-acao"
                    >
                        <template #body="{ data }">
                            <button
                                class="mcr-row__action"
                                v-tooltip.left="'Editar'"
                                aria-label="Editar"
                                @click.stop="openDetails(data)"
                            >
                                <i class="pi pi-pencil" />
                            </button>
                        </template>
                    </Column>

                    <template #empty>
                        <div class="mcr-empty">
                            <i class="pi pi-inbox mcr-empty__icon" />
                            <div class="mcr-empty__title">Nenhum cadastro encontrado</div>
                            <div class="mcr-empty__hint">
                                <template v-if="busca || statusFilter">Ajuste os filtros pra ver mais.</template>
                                <template v-else>Cadastros vindos do agendador externo aparecem aqui.</template>
                            </div>
                        </div>
                    </template>

                    <template #loading>
                        <div class="mcr-table__loading">
                            <i class="pi pi-spin pi-spinner" />
                            <span>Carregando cadastros…</span>
                        </div>
                    </template>
                </DataTable>

                <!-- Grid view — cards num CSS grid, mesma fonte de dados
                     (filtered + pagedItems) e Paginator standalone que
                     compartilha rowsMCR/firstMCR com a list view. -->
                <div v-else-if="viewMode === 'grid'" class="mcr-grid-wrap">
                    <div v-if="loading && filtered.length === 0" class="mcr-table__loading mcr-grid__loading">
                        <i class="pi pi-spin pi-spinner" />
                        <span>Carregando cadastros…</span>
                    </div>
                    <div v-else-if="filtered.length === 0" class="mcr-empty">
                        <i class="pi pi-inbox mcr-empty__icon" />
                        <div class="mcr-empty__title">Nenhum cadastro encontrado</div>
                        <div class="mcr-empty__hint">
                            <template v-if="busca || statusFilter">Ajuste os filtros pra ver mais.</template>
                            <template v-else>Cadastros vindos do agendador externo aparecem aqui.</template>
                        </div>
                    </div>
                    <div v-else class="mcr-grid">
                        <div
                            v-for="r in pagedItems"
                            :key="r.id"
                            class="mcr-grid__card"
                            :class="statusClass(r.status)"
                            role="button"
                            tabindex="0"
                            @click="openDetails(r)"
                            @keydown.enter.prevent="openDetails(r)"
                            @keydown.space.prevent="openDetails(r)"
                        >
                            <div class="mcr-grid__top">
                                <span class="mcr-card__avatar">
                                    <img v-if="avatarUrl(r)" :src="avatarUrl(r)" :alt="fNome(r) || 'Avatar'" />
                                    <span v-else>{{ pacienteIniciais(fNome(r)) }}</span>
                                </span>
                                <div class="mcr-grid__top-right">
                                    <span class="mcr-card__badge" :class="statusClass(r.status)">
                                        {{ statusLabel(r.status) }}
                                    </span>
                                    <button
                                        class="mcr-row__action"
                                        v-tooltip.left="'Editar'"
                                        aria-label="Editar"
                                        @click.stop="openDetails(r)"
                                    >
                                        <i class="pi pi-pencil" />
                                    </button>
                                </div>
                            </div>
                            <div class="mcr-grid__name">{{ fNome(r) || '—' }}</div>
                            <div class="mcr-grid__meta">
                                <span v-if="fEmail(r)"><i class="pi pi-envelope" /> {{ fEmail(r) }}</span>
                                <span v-if="fTel(r)"><i class="pi pi-phone" /> {{ fmtPhoneBR(fTel(r)) }}</span>
                            </div>
                            <div class="mcr-grid__time">
                                <i class="pi pi-clock" /> {{ fmtRelative(r.created_at) }}
                            </div>
                        </div>
                    </div>
                    <Paginator
                        v-if="filtered.length > 0"
                        class="mcr-paginator"
                        :rows="rowsMCR"
                        :totalRecords="filtered.length"
                        :first="firstMCR"
                        :rowsPerPageOptions="PAGE_SIZE_OPTIONS"
                        template="RowsPerPageDropdown FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink"
                        currentPageReportTemplate="{first}–{last} de {totalRecords}"
                        @page="onPage"
                    />
                </div>
            </div>
        </div>

        <!-- Dialog detalhes -->
        <Dialog
            :visible="dlg.open"
            modal
            dismissable-mask
            :style="{ width: '640px', maxWidth: '94vw' }"
            :header="fNome(dlg.item) || 'Cadastro'"
            @update:visible="(v) => !v && closeDlg()"
        >
            <div v-if="dlg.item" class="flex flex-col gap-4">
                <!-- Avatar + status + ações principais -->
                <div class="flex items-start gap-3">
                    <div class="mcr-dlg__avatar">
                        <img v-if="dlgAvatarUrl" :src="dlgAvatarUrl" :alt="fNome(dlg.item)" />
                        <span v-else>{{ pacienteIniciais(fNome(dlg.item)) }}</span>
                    </div>
                    <div class="flex-1 min-w-0">
                        <div class="text-sm font-bold">{{ fNome(dlg.item) }}</div>
                        <div class="text-xs text-[var(--text-color-secondary)] mt-1">
                            <span class="mcr-card__badge" :class="statusClass(dlg.item.status)">{{ statusLabel(dlg.item.status) }}</span>
                            · Criado {{ fmtRelative(dlg.item.created_at) }}
                        </div>
                    </div>
                </div>

                <!-- Seções -->
                <div v-for="sec in dlgSections" :key="sec.title" class="flex flex-col gap-1">
                    <div class="text-[0.62rem] uppercase tracking-wider font-semibold text-[var(--text-color-secondary)] opacity-70">{{ sec.title }}</div>
                    <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
                        <template v-for="r in sec.rows" :key="r.label">
                            <div class="text-[var(--text-color-secondary)]">{{ r.label }}</div>
                            <div class="text-[var(--text-color)]">{{ r.value }}</div>
                        </template>
                    </div>
                </div>

                <!-- Motivo da rejeição -->
                <div v-if="dlg.item.status !== 'converted'" class="flex flex-col gap-1">
                    <label class="text-[0.62rem] uppercase tracking-wider font-semibold text-[var(--text-color-secondary)] opacity-70">
                        Motivo da rejeição (opcional)
                    </label>
                    <Textarea v-model="dlg.reject_note" autoResize rows="2" class="w-full text-xs" :disabled="dlg.saving" />
                </div>
            </div>
            <template #footer>
                <div class="flex items-center gap-2 w-full">
                    <Button label="Fechar" text @click="closeDlg" />
                    <div class="flex-1" />
                    <Button
                        v-if="dlg.item && dlg.item.status !== 'rejected' && dlg.item.status !== 'converted'"
                        label="Rejeitar"
                        severity="danger"
                        outlined
                        :disabled="dlg.saving || converting"
                        @click="markRejected"
                    />
                    <Button
                        v-if="dlg.item && dlg.item.status !== 'converted'"
                        :label="converting ? 'Convertendo…' : 'Converter em paciente'"
                        :loading="converting"
                        :disabled="dlg.saving || converting"
                        @click="convertToPatient"
                    />
                </div>
            </template>
        </Dialog>
    </section>
</template>

<style scoped>
.mcr-page {
    position: absolute;
    inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px;
    z-index: 40;
    display: flex;
    flex-direction: column;
    background: var(--m-bg-medium);
    backdrop-filter: blur(32px) saturate(160%);
    -webkit-backdrop-filter: blur(32px) saturate(160%);
    border: 1px solid var(--m-border);
    border-radius: 18px;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
    overflow: hidden;
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    color: var(--m-text);
    animation: mcr-page-enter 240ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
@keyframes mcr-page-enter {
    from { opacity: 0; transform: scale(0.985); }
    to   { opacity: 1; transform: scale(1); }
}

.mcr-page__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    border-bottom: 1px solid var(--m-border);
    flex-shrink: 0;
    gap: 10px;
}
.mcr-page__title {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 1rem;
    font-weight: 500;
}
/* Ícone do título — cor primary do tema (espelha o token PrimeVue).
   Tamanho levemente maior que o label pra ler como ícone identitário. */
.mcr-page__title-icon {
    color: var(--p-primary-color);
    font-size: 1.05rem;
}
.mcr-page__title > span:not(.mcr-page__count) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mcr-page__count {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--m-accent);
    background: var(--m-accent-soft);
    border: 1px solid color-mix(in srgb, var(--m-accent) 35%, transparent);
    padding: 2px 8px;
    border-radius: 999px;
}
.mcr-page__actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

.mcr-close, .mcr-head-btn {
    width: 32px; height: 32px;
    display: grid; place-items: center;
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border);
    color: var(--m-text);
    border-radius: 9px;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 140ms ease;
}
.mcr-close:hover, .mcr-head-btn:hover { background: var(--m-bg-soft-hover); }
.mcr-head-btn > i { font-size: 0.85rem; }

.mcr-menu-btn {
    display: none;
    height: 32px;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    background: var(--m-accent);
    border: 1px solid var(--m-accent);
    color: white;
    padding: 0 11px;
    border-radius: 9px;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    transition: background-color 140ms ease, transform 140ms ease;
}
.mcr-menu-btn:hover {
    background: color-mix(in srgb, var(--m-accent) 88%, white);
    transform: translateY(-1px);
}

/* Subheader explicativo — faixa abaixo do page__head com texto contextual
   sobre a página. Bg sutil + ícone primary + tipografia menor. Diferencia
   esta página de outras tabelas Melissa que têm layout visual idêntico. */
.mcr-subheader {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 18px;
    border-bottom: 1px solid var(--m-border);
    background: var(--m-bg-soft);
    font-size: 0.78rem;
    color: var(--m-text-muted);
    line-height: 1.45;
    flex-shrink: 0;
}
.mcr-subheader__icon {
    color: var(--p-primary-color);
    font-size: 0.92rem;
    flex-shrink: 0;
    margin-top: 1px;
}
.mcr-subheader__text {
    flex: 1;
    min-width: 0;
}
.mcr-subheader__text strong {
    color: var(--m-text);
    font-weight: 600;
}

.mcr-body {
    flex: 1;
    display: flex;
    min-height: 0;
    gap: 0;
    padding: 0;
}
/* Sidebar — espelha .ma-side do MelissaAgenda: bg colorido próprio
   (--m-bg-soft) + border-right pra separar visualmente da coluna
   principal. Cards internos (.mcr-w--side) ficam mais opacos
   (--m-bg-medium) pra contrastar com o bg da sidebar. */
.mcr-side {
    width: 280px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 0;
    overflow-y: auto;
    background: var(--m-bg-soft);
    border-right: 1px solid var(--m-border);
}
.mcr-side::-webkit-scrollbar { width: 5px; }
.mcr-side::-webkit-scrollbar-thumb {
    background: var(--m-border-strong);
    border-radius: 3px;
}

/* Card-base — alinhado com .ma-w (MelissaAgenda) e .mp-w (MelissaPacientes):
   surface --m-bg-medium pra destacar do bg da sidebar (--m-bg-soft). */
.mcr-w {
    background: var(--m-bg-medium);
    border: 1px solid var(--m-border);
    border-radius: 12px;
    padding: 12px;
}
/* Modificador pra usar .mcr-w dentro da sidebar (col 1) — adiciona
   margem nas bordas (a .mcr-side não tem padding interno) + sombra
   sutil pra elevação visual sobre o bg colorido da sidebar.
   Espelha .ma-w--side do MelissaAgenda. */
.mcr-w--side {
    margin: 12px 12px 0;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
.mcr-w--side:last-of-type { margin-bottom: 12px; }
/* Header e título no padrão do .ma-w__head/__title — uppercase com
   letter-spacing pra hierarquia visual de label da sidebar. */
.mcr-w__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}
.mcr-w__title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--m-text-muted);
    font-weight: 600;
}
.mcr-w__title > i { color: var(--m-text-muted); font-size: 0.7rem; }
.mcr-w__count {
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--m-accent);
    background: var(--m-accent-soft);
    border: 1px solid color-mix(in srgb, var(--m-accent) 35%, transparent);
    padding: 1px 7px;
    border-radius: 999px;
}

.mcr-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px;
}
.mcr-stat {
    /* Mais sutil que o card-pai (.mcr-w em --m-bg-medium) pra criar contraste
       interno. Antes usava --m-bg-medium e ficava igual ao pai. */
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border);
    border-radius: 10px;
    padding: 8px 10px;
}
.mcr-stat__val {
    font-size: 1.1rem;
    font-weight: 600;
    line-height: 1.1;
}
.mcr-stat__lbl {
    font-size: 0.65rem;
    color: var(--m-text-muted);
    margin-top: 4px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
/* Cores fortes (Tailwind 600) pra ler bem em ambos os modos. Light:
   suficientemente escuras pra contrastar com bg branco. Dark: ainda
   saturadas o bastante pra destacar do bg quase preto. */
.mcr-stat.is-info .mcr-stat__val   { color: rgb(37, 99, 235); }   /* blue-600 */
.mcr-stat.is-ok .mcr-stat__val     { color: rgb(22, 163, 74); }   /* green-600 */
.mcr-stat.is-danger .mcr-stat__val { color: rgb(220, 38, 38); }   /* red-600 */

.mcr-side__list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
/* Botões do filtro de status — coloridos em 3 níveis:
   - Default:  tint 5% bg + 18% border + ícone full color → identifica
               o status mesmo sem clicar
   - Hover:    tint 10% bg + 30% border
   - Active:   tint 16% bg + 55% border + box-shadow ring pro destaque
   Cores Tailwind 600: blue/green/red — fortes pra ler em light. */
.mcr-side__item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: transparent;
    border: 1px solid transparent;
    color: var(--m-text);
    border-radius: 10px;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.82rem;
    text-align: left;
    transition: background-color 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
}
.mcr-side__item > i {
    font-size: 0.78rem;
    width: 14px;
    text-align: center;
}

/* Novo — azul */
.mcr-side__item.is-new {
    background: rgba(37, 99, 235, 0.05);
    border-color: rgba(37, 99, 235, 0.18);
}
.mcr-side__item.is-new > i { color: rgb(37, 99, 235); }
.mcr-side__item.is-new:hover {
    background: rgba(37, 99, 235, 0.10);
    border-color: rgba(37, 99, 235, 0.30);
}
.mcr-side__item.is-active.is-new {
    background: rgba(37, 99, 235, 0.16);
    border-color: rgba(37, 99, 235, 0.55);
    box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.35);
}

/* Convertido — verde */
.mcr-side__item.is-converted {
    background: rgba(22, 163, 74, 0.05);
    border-color: rgba(22, 163, 74, 0.18);
}
.mcr-side__item.is-converted > i { color: rgb(22, 163, 74); }
.mcr-side__item.is-converted:hover {
    background: rgba(22, 163, 74, 0.10);
    border-color: rgba(22, 163, 74, 0.30);
}
.mcr-side__item.is-active.is-converted {
    background: rgba(22, 163, 74, 0.16);
    border-color: rgba(22, 163, 74, 0.55);
    box-shadow: 0 0 0 1px rgba(22, 163, 74, 0.35);
}

/* Rejeitado — vermelho */
.mcr-side__item.is-rejected {
    background: rgba(220, 38, 38, 0.05);
    border-color: rgba(220, 38, 38, 0.18);
}
.mcr-side__item.is-rejected > i { color: rgb(220, 38, 38); }
.mcr-side__item.is-rejected:hover {
    background: rgba(220, 38, 38, 0.10);
    border-color: rgba(220, 38, 38, 0.30);
}
.mcr-side__item.is-active.is-rejected {
    background: rgba(220, 38, 38, 0.16);
    border-color: rgba(220, 38, 38, 0.55);
    box-shadow: 0 0 0 1px rgba(220, 38, 38, 0.35);
}

/* Limpar filtro — botão neutro que aparece como 4ª linha quando há
   um filtro ativo. Cor sutil, apenas pra ler como ação secundária. */
.mcr-side__item.is-clear {
    margin-top: 4px;
    background: var(--m-bg-soft);
    border-color: var(--m-border);
    color: var(--m-text-muted);
    font-style: italic;
}
.mcr-side__item.is-clear > i { color: var(--m-text-muted); }
.mcr-side__item.is-clear:hover {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
    color: var(--m-text);
}
.mcr-side__item.is-clear:hover > i { color: var(--m-text); }

/* Transition do botão "Limpar filtro" — fade + leve slide vertical
   pra entrada/saída suave. Aproveita os 4px de margin-top como ponto
   de partida do slide (sai de cima pra encaixar). */
.mcr-clear-enter-active,
.mcr-clear-leave-active {
    transition: opacity 220ms ease, transform 220ms ease, max-height 220ms ease, margin-top 220ms ease;
    overflow: hidden;
}
.mcr-clear-enter-from,
.mcr-clear-leave-to {
    opacity: 0;
    transform: translateY(-4px);
    max-height: 0;
    margin-top: 0;
}
.mcr-clear-enter-to,
.mcr-clear-leave-from {
    opacity: 1;
    transform: translateY(0);
    max-height: 40px;
}

/* Main column (col 2) — busca no topo (toolbar) + lista scrollável +
   paginator fixo no rodapé. min-height:0 é CRÍTICO pro flex:1 da lista
   funcionar dentro de um flex container (sem isso, .mcr-list cresce
   indefinidamente em vez de scrollar). */
.mcr-main {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 12px;
    gap: 10px;
}

/* Toolbar — wrapper da busca no topo da coluna principal. Mesmo padrão
   visual do .ma-tsearch (toolbar de busca da MelissaAgenda). */
.mcr-toolbar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 10px;
}
.mcr-search {
    position: relative;
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
}
.mcr-search__icon {
    position: absolute;
    left: 12px;
    color: var(--m-text-muted);
    font-size: 0.78rem;
    pointer-events: none;
}
.mcr-search__input {
    width: 100%;
    background: var(--m-bg-medium);
    border: 1px solid var(--m-border);
    color: var(--m-text);
    padding: 9px 36px 9px 34px;
    border-radius: 10px;
    font-size: 0.85rem;
    font-family: inherit;
    outline: none;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.mcr-search__input::placeholder { color: var(--m-text-faint); }
.mcr-search__input:focus {
    border-color: var(--m-border-strong);
    background: var(--m-bg-medium);
}
.mcr-search__clear {
    position: absolute;
    right: 8px;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    background: transparent;
    border: none;
    color: var(--m-text-muted);
    border-radius: 6px;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 140ms ease, color 140ms ease;
}
.mcr-search__clear:hover {
    background: var(--m-bg-soft-hover);
    color: var(--m-text);
}
.mcr-search__clear > i { font-size: 0.7rem; }

/* Toggle de visualização (lista/grade) — segmented control compacto
   à direita da busca. Botão ativo ganha bg primary-tinted. */
.mcr-view-toggle {
    flex-shrink: 0;
    display: inline-flex;
    background: var(--m-bg-medium);
    border: 1px solid var(--m-border);
    border-radius: 10px;
    padding: 2px;
    gap: 2px;
}
.mcr-view-toggle__btn {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    background: transparent;
    border: none;
    color: var(--m-text-muted);
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 140ms ease, color 140ms ease;
}
.mcr-view-toggle__btn:hover {
    background: var(--m-bg-soft-hover);
    color: var(--m-text);
}
.mcr-view-toggle__btn.is-active {
    background: var(--m-accent-soft);
    color: var(--m-accent);
}
.mcr-view-toggle__btn > i { font-size: 0.85rem; }

/* ─── DataTable (.mcr-table) ─────────────────────────────────────
   Substitui a antiga lista de cards. `:loading="loading"` cobre o
   estado de carregamento (template #loading) e `paginator` integra
   o seletor de tamanho + navegação. scrollHeight="flex" faz a tabela
   preencher o flex restante da .mcr-main e scrollar internamente. */
.mcr-table {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.mcr-table :deep(.p-datatable) {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: transparent;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    overflow: hidden;
}
.mcr-table :deep(.p-datatable-table-container) {
    flex: 1;
    min-height: 0;
    background: transparent;
}
/* Header — usa --p-content-background (token "surface" do tema PrimeVue).
   Em light mode → branco; em dark mode → surface configurado (dark).
   Mesma fonte que os cards Estatísticas/Status visualizam (eles usam
   --m-bg-medium, que em light é --p-content-background opaco). */
.mcr-table :deep(.p-datatable-thead),
.mcr-table :deep(.p-datatable-thead > tr) {
    background: transparent !important;
}
.mcr-table :deep(.p-datatable-thead > tr > th) {
    background: var(--p-content-background) !important;
    color: var(--m-text);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 10px 14px;
    border-bottom: 1px solid var(--m-border);
}
.mcr-table :deep(.p-datatable-tbody > tr) {
    background: transparent;
    color: var(--m-text);
    cursor: pointer;
    transition: background-color 140ms ease;
    border-left: 3px solid var(--m-border);
}
.mcr-table :deep(.p-datatable-tbody > tr > td) {
    padding: 10px 14px;
    border-bottom: 1px solid var(--m-border);
    background: transparent;
    vertical-align: middle;
}
.mcr-table :deep(.p-datatable-tbody > tr:hover) {
    background: var(--m-bg-soft-hover);
}
.mcr-table :deep(.p-datatable-tbody > tr.p-datatable-row-selected) {
    background: var(--m-accent-soft);
}
/* Border-left colorido por status — espelha o padrão .ma-sess. */
.mcr-table :deep(.p-datatable-tbody > tr.is-new)      { border-left-color: rgb(37, 99, 235); }
.mcr-table :deep(.p-datatable-tbody > tr.is-done)     { border-left-color: rgb(22, 163, 74); }
.mcr-table :deep(.p-datatable-tbody > tr.is-rejected) { border-left-color: rgb(220, 38, 38); opacity: 0.85; }

/* Loading overlay */
.mcr-table :deep(.p-datatable-loading-overlay) {
    background: color-mix(in srgb, var(--m-bg-medium) 70%, transparent);
    backdrop-filter: blur(2px);
}
.mcr-table__loading {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--m-text);
    font-size: 0.85rem;
}

/* Paginator integrado do DataTable — centralizado (sem refresh à
   esquerda; o refresh já vive no header da página). */
.mcr-table :deep(.p-paginator) {
    background: var(--m-bg-medium);
    border: none;
    border-top: 1px solid var(--m-border);
    padding: 8px 12px;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
}
.mcr-table :deep(.p-paginator-current) {
    color: var(--m-text-muted);
    font-size: 0.78rem;
    background: transparent;
    border: none;
    padding: 0 6px;
}
.mcr-table :deep(.p-paginator-first),
.mcr-table :deep(.p-paginator-prev),
.mcr-table :deep(.p-paginator-next),
.mcr-table :deep(.p-paginator-last),
.mcr-table :deep(.p-paginator-page) {
    min-width: 30px;
    height: 30px;
    color: var(--m-text);
    background: transparent;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    font-size: 0.8rem;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.mcr-table :deep(.p-paginator-page.p-paginator-page-selected) {
    background: var(--m-accent-soft);
    border-color: var(--m-accent-strong);
    color: var(--m-accent);
}
.mcr-table :deep(.p-paginator-first:not(.p-disabled):hover),
.mcr-table :deep(.p-paginator-prev:not(.p-disabled):hover),
.mcr-table :deep(.p-paginator-next:not(.p-disabled):hover),
.mcr-table :deep(.p-paginator-last:not(.p-disabled):hover),
.mcr-table :deep(.p-paginator-page:not(.p-paginator-page-selected):hover) {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
}
.mcr-table :deep(.p-select) {
    background: transparent;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    height: 30px;
    font-size: 0.78rem;
    color: var(--m-text);
    display: inline-flex;
    align-items: center;
}
.mcr-table :deep(.p-select-label) {
    padding: 0 8px;
    color: var(--m-text);
    font-size: 0.78rem;
    display: flex;
    align-items: center;
    line-height: 1;
    height: 100%;
    background: transparent;
}

/* ─── Grid view ───────────────────────────────────────────────
   Cards num CSS grid responsivo (auto-fill 220px+). Cada card tem
   avatar + badge no topo, nome em destaque, contato e tempo abaixo.
   Border-left colorido por status (mesmo padrão da list view). */
.mcr-grid-wrap {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    border: 1px solid var(--m-border);
    border-radius: 10px;
    overflow: hidden;
    background: transparent;
}
.mcr-grid {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 12px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 10px;
    align-content: start;
}
.mcr-grid::-webkit-scrollbar { width: 5px; }
.mcr-grid::-webkit-scrollbar-thumb { background: var(--m-border-strong); border-radius: 3px; }
.mcr-grid__loading {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px;
}
.mcr-grid__card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border);
    border-left: 3px solid var(--m-border-strong);
    border-radius: 10px;
    color: var(--m-text);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.mcr-grid__card:hover {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
    transform: translateY(-1px);
}
.mcr-grid__card:focus-visible {
    outline: 2px solid var(--p-primary-color);
    outline-offset: 2px;
}
.mcr-grid__card.is-new      { border-left-color: rgb(37, 99, 235); }
.mcr-grid__card.is-done     { border-left-color: rgb(22, 163, 74); }
.mcr-grid__card.is-rejected { border-left-color: rgb(220, 38, 38); opacity: 0.85; }
.mcr-grid__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
/* Cluster badge + pencil à direita do top do card. */
.mcr-grid__top-right {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.mcr-grid__name {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--m-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mcr-grid__meta {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.75rem;
    color: var(--m-text-muted);
    min-width: 0;
}
.mcr-grid__meta i { margin-right: 5px; font-size: 0.7rem; }
.mcr-grid__meta span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mcr-grid__time {
    font-size: 0.7rem;
    color: var(--m-text-faint);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: auto;
}
.mcr-grid__time i { font-size: 0.65rem; }

/* Paginator standalone (grid view) — espelha o estilo do paginator
   interno do DataTable, mas via classe direta (sem :deep). Centralizado
   e sem bg de surface (consistente com o pedido de "fundo transparente"). */
.mcr-paginator.p-paginator {
    background: var(--m-bg-medium);
    border: none;
    border-top: 1px solid var(--m-border);
    padding: 8px 12px;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
    flex-shrink: 0;
}
.mcr-paginator.p-paginator .p-paginator-current {
    color: var(--m-text-muted);
    font-size: 0.78rem;
    background: transparent;
    border: none;
    padding: 0 6px;
}
.mcr-paginator.p-paginator .p-paginator-first,
.mcr-paginator.p-paginator .p-paginator-prev,
.mcr-paginator.p-paginator .p-paginator-next,
.mcr-paginator.p-paginator .p-paginator-last,
.mcr-paginator.p-paginator .p-paginator-page {
    min-width: 30px;
    height: 30px;
    color: var(--m-text);
    background: transparent;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    font-size: 0.8rem;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.mcr-paginator.p-paginator .p-paginator-page.p-paginator-page-selected {
    background: var(--m-accent-soft);
    border-color: var(--m-accent-strong);
    color: var(--m-accent);
}
.mcr-paginator.p-paginator .p-paginator-first:not(.p-disabled):hover,
.mcr-paginator.p-paginator .p-paginator-prev:not(.p-disabled):hover,
.mcr-paginator.p-paginator .p-paginator-next:not(.p-disabled):hover,
.mcr-paginator.p-paginator .p-paginator-last:not(.p-disabled):hover,
.mcr-paginator.p-paginator .p-paginator-page:not(.p-paginator-page-selected):hover {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
}
.mcr-paginator.p-paginator .p-select {
    background: transparent;
    border: 1px solid var(--m-border);
    border-radius: 8px;
    height: 30px;
    font-size: 0.78rem;
    color: var(--m-text);
    display: inline-flex;
    align-items: center;
}
.mcr-paginator.p-paginator .p-select-label {
    padding: 0 8px;
    color: var(--m-text);
    font-size: 0.78rem;
    display: flex;
    align-items: center;
    line-height: 1;
    height: 100%;
    background: transparent;
}

/* Conteúdo das células — substitui o antigo .mcr-card__* (interno). */
.mcr-row__patient {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}
.mcr-row__patient-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.mcr-row__name {
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--m-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mcr-row__contact {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.78rem;
    color: var(--m-text-muted);
    min-width: 0;
}
.mcr-row__contact i { margin-right: 5px; font-size: 0.7rem; }
.mcr-row__contact span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mcr-row__empty { color: var(--m-text-faint); }
.mcr-row__time {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.78rem;
    color: var(--m-text-muted);
}
.mcr-row__time i { font-size: 0.7rem; }

/* Botão de ação (pencil) — ícone primary + bg matching da célula
   pra não ficar "vazado" durante scroll horizontal (a célula frozen
   da Ação usa --m-bg-medium; o botão herda o mesmo token, que adapta
   a dark/light automaticamente). @click.stop no template impede
   propagar pro row-click do DataTable. */
.mcr-row__action {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    background: var(--p-content-background);
    border: 1px solid color-mix(in srgb, var(--p-primary-color) 30%, var(--m-border));
    color: var(--p-primary-color);
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}
.mcr-row__action:hover {
    background: color-mix(in srgb, var(--p-primary-color) 12%, var(--p-content-background));
    border-color: var(--p-primary-color);
    color: var(--p-primary-color);
}
.mcr-row__action > i { font-size: 0.78rem; }

/* Coluna "Ação" frozen à direita — bg sólido em ambos os modos
   (--p-content-background): branco no light, surface configurada
   no dark. Mesma referência visual que o header e os cards. */
.mcr-table :deep(td.p-datatable-frozen-column),
.mcr-table :deep(th.p-datatable-frozen-column) {
    background: var(--p-content-background) !important;
    box-shadow: -3px 0 6px -3px rgba(0, 0, 0, 0.18);
    z-index: 1;
}
.mcr-table :deep(.p-datatable-tbody > tr:hover td.p-datatable-frozen-column) {
    background: var(--m-bg-soft-hover);
}
.mcr-table :deep(.p-datatable-tbody > tr.p-datatable-row-selected td.p-datatable-frozen-column) {
    background: var(--m-accent-soft);
}

/* Avatar — usado no row da tabela (modificador --sm) e no dialog (size full). */
.mcr-card__avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--m-accent-strong);
    border: 1px solid var(--m-accent);
    color: white;
    font-size: 0.78rem;
    font-weight: 600;
    display: grid; place-items: center;
    flex-shrink: 0;
    overflow: hidden;
}
.mcr-card__avatar img { width: 100%; height: 100%; object-fit: cover; }
.mcr-card__avatar--sm {
    width: 32px; height: 32px;
    font-size: 0.7rem;
}

.mcr-card__badge {
    display: inline-flex;
    align-items: center;
    font-size: 0.62rem;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border: 1px solid;
}
.mcr-card__badge.is-new {
    color: rgb(37, 99, 235);
    background: rgba(37, 99, 235, 0.12);
    border-color: rgba(37, 99, 235, 0.3);
}
.mcr-card__badge.is-done {
    color: rgb(22, 163, 74);
    background: rgba(22, 163, 74, 0.12);
    border-color: rgba(22, 163, 74, 0.3);
}
.mcr-card__badge.is-rejected {
    color: rgb(220, 38, 38);
    background: rgba(220, 38, 38, 0.12);
    border-color: rgba(220, 38, 38, 0.3);
}
.mcr-empty {
    margin: 24px 0;
    padding: 56px 28px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    color: var(--m-text-muted);
    border: 2px dashed var(--m-border-strong);
    border-radius: 12px;
    background: color-mix(in srgb, var(--m-bg-soft) 40%, transparent);
    gap: 8px;
}
.mcr-empty__icon { font-size: 2rem; color: var(--m-text-faint); margin-bottom: 4px; }
.mcr-empty__title { font-size: 0.92rem; font-weight: 600; color: var(--m-text); }
.mcr-empty__hint { font-size: 0.78rem; }

/* Dialog avatar */
.mcr-dlg__avatar {
    width: 56px; height: 56px;
    border-radius: 50%;
    background: var(--m-accent-strong);
    border: 1px solid var(--m-accent);
    color: white;
    font-size: 1rem;
    font-weight: 600;
    display: grid; place-items: center;
    flex-shrink: 0;
    overflow: hidden;
}
.mcr-dlg__avatar img { width: 100%; height: 100%; object-fit: cover; }

/* Drawer mobile */
.mcr-mobile-drawer {
    position: fixed;
    top: 0; left: 0;
    height: 100vh;
    height: 100dvh;
    width: min(360px, 88vw);
    z-index: 80;
    background: var(--m-bg-medium);
    backdrop-filter: blur(28px) saturate(160%);
    -webkit-backdrop-filter: blur(28px) saturate(160%);
    border-right: 1px solid var(--m-border);
    transform: translateX(-100%);
    transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
    color: var(--m-text);
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
.mcr-mobile-drawer.is-open { transform: translateX(0); }
.mcr-mobile-drawer__scroll {
    height: 100%;
    overflow-y: auto;
    padding: 12px 12px 24px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.mcr-mobile-drawer__scroll::-webkit-scrollbar { width: 5px; }
.mcr-mobile-drawer__scroll::-webkit-scrollbar-thumb {
    background: var(--m-border-strong);
    border-radius: 3px;
}
/* Reset da .mcr-side quando teleportada pro drawer mobile — o drawer
   já tem bg/border próprios (.mcr-mobile-drawer), então a sidebar não
   precisa do bg/border-right que tem em desktop. */
.mcr-mobile-drawer__scroll .mcr-side {
    width: 100%;
    flex: 1;
    min-height: 0;
    overflow: visible;
    padding: 0;
    background: transparent;
    border-right: none;
    display: flex;
    flex-direction: column;
}
.mcr-mobile-drawer__scroll .mcr-w--side {
    margin: 0 0 12px;
}
.mcr-mobile-drawer__scroll .mcr-w--side:last-of-type {
    margin-bottom: 0;
}
.mcr-mobile-drawer__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: 79;
}
.mcr-drawer-fade-enter-active,
.mcr-drawer-fade-leave-active { transition: opacity 200ms ease; }
.mcr-drawer-fade-enter-from,
.mcr-drawer-fade-leave-to { opacity: 0; }

@media (max-width: 1023px) {
    /* Em mobile, a sidebar é teleportada pro drawer (mcr-mobile-drawer),
       então .mcr-body só contém .mcr-main. Padding de 0 no body, padding
       no .mcr-main já cobre o spacing interno. */
    .mcr-body { flex-direction: column; padding: 0; }
    .mcr-main { width: 100%; padding: 8px; }
    .mcr-page__title > span:first-of-type { display: none; }
    .mcr-page__title-icon { display: none; }
    .mcr-menu-btn--mobile-only { display: inline-flex; }
    /* Em mobile mostra todas as colunas — scroll horizontal habilitado
       via tableStyle min-width:640px. Coluna "Ação" frozen à direita
       continua fixa enquanto o user scrolla horizontalmente. */
}

/* ═══════ Fake dialog: largura adaptativa (>=1024px) ═══════
   Espelha pattern de MelissaAgendador/Negocio — fica mesma janela,
   drawer a esquerda, sobra espaco a direita pra dock e contexto.
   - 1024–1012px : full-width (right: 6px) — overlap minimo
   - 1012–2012px : width = 1000px fixo (right cresce com viewport)
   - >= 2012px   : width = ~50% do viewport (right: 50%) */
@media (min-width: 1024px) {
    .mcr-page {
        right: max(6px, min(50%, calc(100% - 1006px)));
    }
}
</style>
