<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/agenda/components/AgendaEventDialog.vue
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<script setup>
import { computed, ref, watch, nextTick } from 'vue';
import { generateGoogleCalendarLink, formatGCalDate, addMinutesToHHMM } from '@/utils/googleCalendarLink';
import { useRouter, useRoute } from 'vue-router';
import Select from 'primevue/select';
import Textarea from 'primevue/textarea';
import DatePicker from 'primevue/datepicker';
import InputNumber from 'primevue/inputnumber';
// RadioButton removido (auto-import via PrimeVueResolver caso necessário no template)
import Message from 'primevue/message';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
import { supabase } from '@/lib/supabase/client';
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue';
import AgendaEventoFinanceiroPanel from '@/components/agenda/AgendaEventoFinanceiroPanel.vue';
import ServiceQuickCreateDialog from './ServiceQuickCreateDialog.vue';
import InsurancePlanQuickCreateDialog from './InsurancePlanQuickCreateDialog.vue';
import { useServices } from '@/features/agenda/composables/useServices';
import { useCommitmentServices } from '@/features/agenda/composables/useCommitmentServices';
import { usePatientDiscounts } from '@/features/agenda/composables/usePatientDiscounts';
import { useInsurancePlans } from '@/features/agenda/composables/useInsurancePlans';
// getPatientAgendaPermissions agora é importado dentro do composer (1B)

// A66 sub-sessão 1A — helpers PUROS extraídos pra módulo isolado e
// testável. Antes viviam inline neste arquivo (24 funções, ~150 linhas).
// Agora vem de um único import; lógica idêntica, só repousada.
import {
    patientInitials,
    fmtBRL,
    fmtJornadaHora,
    fmtDateBR,
    fmtDateBRLong,
    fmtTime,
    fmtDuracao,
    nomeDiaSemana,
    fmtWeekdayShort,
    fmtDayNum,
    fmtMonthShort,
    hhmmToMin,
    minToHHMM,
    isPast,
    isNativeSession,
    calcFinalPrice,
    labelStatusSessao,
    statusSeverity,
    statusExtraClass
} from '@/features/agenda/composables/agendaEventHelpers';

// A66 sub-sessão 1B — composable factory com state + computeds derivados.
import { useAgendaEventComposer } from '@/features/agenda/composables/useAgendaEventComposer';

// A66 sub-sessão 1C-i — watchers + handlers de save/delete.
import { useAgendaEventActions } from '@/features/agenda/composables/useAgendaEventActions';

// A66 sub-sessão 1C-ii-a — patient picker + billing items + 2 watchers
// (form.commitment_id auto-fill price, form.insurance_plan_id limpa items).
import { useAgendaEventPickerBilling } from '@/features/agenda/composables/useAgendaEventPickerBilling';

// A66 sub-sessão 1C-ii-b — lifecycle: 4 watchers (modelValue init, tenant
// scope, solicitação pendente, online slots) + series pills + selectSlot
// + quick-creates wiring + onSendManualReminder.
import { useAgendaEventLifecycle } from '@/features/agenda/composables/useAgendaEventLifecycle';

const props = defineProps({
    modelValue: { type: Boolean, default: false },
    eventRow: { type: Object, default: null },
    initialStartISO: { type: String, default: '' },
    initialEndISO: { type: String, default: '' },

    ownerId: { type: String, default: '' },
    planOwnerId: { type: String, default: '' }, // owner dos convênios (clínica); fallback: ownerId
    allowOwnerEdit: { type: Boolean, default: false },
    ownerOptions: { type: Array, default: () => [] },

    tenantId: { type: String, default: '' },

    commitmentOptions: { type: Array, default: () => [] },
    presetCommitmentId: { type: [String, null], default: null },
    lockCommitment: { type: Boolean, default: false },

    restrictPatientsToOwner: { type: Boolean, default: false },
    patientScopeOwnerId: { type: String, default: null },

    workRules: { type: Array, default: () => [] },
    blockedDates: { type: Array, default: () => [] },
    agendaSettings: { type: Object, default: null },
    allEvents: { type: Array, default: () => [] },
    pausasSemanais: { type: Array, default: () => [] },
    feriados: { type: Array, default: () => [] },

    // Rota para cadastro completo de paciente (abre em nova aba)
    newPatientRoute: { type: String, default: '' }
});

const emit = defineEmits(['update:modelValue', 'save', 'delete', 'updateSeriesEvent', 'editSeriesOccurrence', 'updated']);
const confirm = useConfirm();
const toast = useToast();
const router = useRouter();
const route = useRoute();

// ──────────────────────────────────────────────────────────────────
// A66 sub-sessão 1B — state + computeds derivados extraídos pra
// composable factory (testado isoladamente em useAgendaEventComposer.spec).
// Refs externos (commitmentItems, serieEvents) declarados aqui antes
// pra serem passados via extras — o composer usa em canSave e isFirstOccurrence.
// ──────────────────────────────────────────────────────────────────
const commitmentItems = ref([]);
const serieEvents = ref([]);

const _composer = useAgendaEventComposer(props, emit, { commitmentItems, serieEvents });
const {
    visible,
    step,
    editScope,
    recorrenciaType,
    diasSelecionados,
    qtdSessoesMode,
    qtdSessoesCustom,
    dataLimiteManual,
    billingType,
    form,
    isEdit,
    allowBack,
    hasSerie,
    currentRecurrenceDate,
    isFirstOccurrence,
    editScopeOptions,
    qtdSessoesEfetiva,
    diaSemanaRecorrencia,
    proximasOcorrencias,
    dataFimCalculada,
    totalOcorrencias,
    sessoesForaDoPlano,
    ocorrenciasComConflito,
    totalConflitos,
    commitmentCards,
    selectedCommitment,
    selectedCommitmentName,
    selectedCommitmentFields,
    requiresPatient,
    isSessionEvent,
    patientLocked,
    hasInsurance,
    agendaPerms,
    isSessionFuture,
    isArchivedPastEdit,
    isInativoFutureEdit,
    statusOptionsFiltered,
    startTimeDate,
    inicioDateTime,
    fimDateTime,
    dataHoraDisplay,
    previewRange,
    computedTitulo,
    headerTitle,
    canSave,
    timeConflict,
    toggleDiaSelecionado,
    isForaDoPlano,
    resetForm
} = _composer;

// ── recorrência: opções estáticas (consts data — não migradas) ──
const freqOpcoes = [
    { value: 'avulsa', label: 'Avulsa' },
    { value: 'semanal', label: 'Semanal' },
    { value: 'quinzenal', label: 'Quinzenal' },
    { value: 'diasEspecificos', label: 'Dias específicos' }
];

const diasSemanaOpcoes = [
    { value: 1, short: 'Seg' },
    { value: 2, short: 'Ter' },
    { value: 3, short: 'Qua' },
    { value: 4, short: 'Qui' },
    { value: 5, short: 'Sex' },
    { value: 6, short: 'Sáb' },
    { value: 0, short: 'Dom' }
];

function setHoje() {
    form.value.dia = new Date();
}

const qtdSessoesOpcoes = [
    { value: '4', label: '4 sessões' },
    { value: '8', label: '8 sessões' },
    { value: '12', label: '12 sessões' },
    { value: 'personalizar', label: 'Personalizar' }
];
// recorrenciaType, diasSelecionados, toggleDiaSelecionado, qtdSessoesMode,
// qtdSessoesCustom, qtdSessoesEfetiva, diaSemanaRecorrencia → composer (1B)

// proximasOcorrencias, dataFimCalculada, totalOcorrencias, dataLimiteManual,
// isForaDoPlano, sessoesForaDoPlano, conflictForDate, ocorrenciasComConflito,
// totalConflitos, commitmentCards, form (+ resetForm) → composer (1B)
// isNativeSession → agendaEventHelpers (1A)

// Watcher status confirm (cancelado/remarcado), watcher billingType,
// watcher [paciente_id, dia], onSave, onDelete, onEncerrarSerie
// → useAgendaEventActions (1C-i). Refs internos (_skipStatusWatch,
// _prevStatus, _restoringConvenio, samePatientConflict) vêm do composable
// abaixo, declarado depois das dependências (services/insurancePlans/etc).

// ── Precificação / Serviços ─────────────────────────────────────────
const { services, getDefaultPrice, load: loadServices } = useServices();
const { loadItems: _csLoadItems, saveItems: saveCommitmentItems, loadItemsOrTemplate: _csLoadItemsOrTemplate } = useCommitmentServices();
const { loadActive: loadActiveDiscount } = usePatientDiscounts();
const { plans: insurancePlans, load: loadInsurancePlans } = useInsurancePlans();
const selectedPlanService = ref(null);

// Quick-create dialogs (serviceQuickDlgOpen/insuranceQuickDlgOpen) +
// openServiceQuickCreate / onServiceCreated / openInsuranceQuickCreate /
// onInsuranceCreated → useAgendaEventLifecycle (1C-ii-b)

const activePlans = computed(() => insurancePlans.value.filter((p) => p.active !== false));

const planServices = computed(() => {
    if (!form.value.insurance_plan_id) return [];
    return (insurancePlans.value.find((p) => p.id === form.value.insurance_plan_id)?.insurance_plan_services || []).filter((s) => s.active);
});
// ensureServicesLoaded + applyDefaultPrice → useAgendaEventPickerBilling (1C-ii-a)

// ── Itens de serviço (commitment_services) ──────────────────────────
// commitmentItems já declarado no topo (passado ao composer via extras)
const servicePickerSel = ref(null);
// serieValorMode usado no template + watcher init (1C-ii-b) — declarado
// antes do useAgendaEventLifecycle abaixo.
const serieValorMode = ref('multiplicar'); // 'multiplicar' | 'dividir'

// isDynamic precisa existir antes do useAgendaEventPickerBilling abaixo
// (passado como dep). Movido pra cá da seção de computeds derivados.
const isDynamic = computed(() => (props.agendaSettings?.slot_mode ?? 'fixed') === 'dynamic');

// billingType → composer (1B); watcher → useAgendaEventActions (1C-i)
const billingTypeOptions = [
    { label: 'Gratuito', value: 'gratuito' },
    { label: 'Particular', value: 'particular' },
    { label: 'Convênio', value: 'convenio' }
];

// ──────────────────────────────────────────────────────────────────
// A66 sub-sessão 1C-i — watchers + handlers de save/delete via factory.
// Recebe o composer (1B) + refs externos (commitmentItems, servicePickerSel,
// selectedPlanService) + saveCommitmentItems do useCommitmentServices.
// Retorna: refs internos (_skipStatusWatch, _prevStatus, _restoringConvenio,
// samePatientConflict) + handlers (onSave, onDelete, onEncerrarSerie).
// ──────────────────────────────────────────────────────────────────
const _actions = useAgendaEventActions({
    composer: _composer,
    commitmentItems,
    servicePickerSel,
    selectedPlanService,
    saveCommitmentItems,
    props,
    emit
});
const {
    _skipStatusWatch,
    _prevStatus,
    _restoringConvenio,
    samePatientConflict,
    onSave,
    onDelete,
    onEncerrarSerie
} = _actions;

// ──────────────────────────────────────────────────────────────────
// A66 sub-sessão 1C-ii-a — patient picker + billing items + 2 watchers.
// Recebe composer + actions (pra _restoringConvenio e samePatientConflict)
// + refs externos + composables (services, insurancePlans, discounts).
// ──────────────────────────────────────────────────────────────────
const _pickerBilling = useAgendaEventPickerBilling({
    composer: _composer,
    actions: _actions,
    commitmentItems,
    servicePickerSel,
    selectedPlanService,
    services,
    loadServices,
    getDefaultPrice,
    planServices,
    loadActiveDiscount,
    _csLoadItems,
    _csLoadItemsOrTemplate,
    isDynamic,
    props
});
const {
    pacientePickerOpen,
    pacienteSearch,
    pacientesLoading,
    pacientesError,
    patients,
    cadRapidoOpen,
    ensureServicesLoaded,
    resetServicesGate,
    applyDefaultPrice,
    addItem,
    removeItem,
    onItemChange,
    _loadCommitmentItemsForEvent,
    onProcedureSelect,
    selectCommitment,
    goBack,
    openPacientePicker,
    clearPatientsCache,
    loadPatients,
    selectPaciente,
    clearPaciente,
    openCadastroRapido,
    abrirCadastroCompleto
} = _pickerBilling;

// ──────────────────────────────────────────────────────────────────
// A66 sub-sessão 1C-ii-b — lifecycle: 4 watchers (modelValue init,
// tenant/scope, solicitação pendente, online slots) + series pills
// + selectSlot + quick-creates wiring + onSendManualReminder.
// ──────────────────────────────────────────────────────────────────
const _lifecycle = useAgendaEventLifecycle({
    composer: _composer,
    actions: _actions,
    pickerBilling: _pickerBilling,
    commitmentItems,
    serieEvents,
    servicePickerSel,
    selectedPlanService,
    serieValorMode,
    services,
    loadServices,
    loadInsurancePlans,
    props,
    emit,
    confirm,
    toast
});
const {
    solicitacaoPendente,
    onlineSlots,
    loadingOnlineSlots,
    serieLoading,
    pillDeleteMenuRef,
    pillDeleteTarget,
    sendingReminder,
    serviceQuickDlgOpen,
    insuranceQuickDlgOpen,
    serieCountByStatus,
    pillDeleteMenuItems,
    loadSerieEvents,
    onPillEditClick,
    onPillStatusChange,
    onPillDeleteClick,
    onPillDelete,
    selectSlot,
    openServiceQuickCreate,
    onServiceCreated,
    openInsuranceQuickCreate,
    onInsuranceCreated,
    onSendManualReminder
} = _lifecycle;

const totalFromItems = computed(() => commitmentItems.value.reduce((sum, item) => sum + (item.final_price ?? 0), 0));

// Duração calculada como soma de services.duration_min dos itens (slot_mode=dynamic)
const dynamicDuration = computed(() => {
    if (!isDynamic.value) return null;
    return commitmentItems.value.reduce((sum, item) => {
        const svc = services.value.find((s) => s.id === item.service_id);
        return sum + (svc?.duration_min ?? 0);
    }, 0);
});

// Preço exibido no resumo: total dos itens quando há itens, form.price caso contrário
const displayPrice = computed(() => (commitmentItems.value.length > 0 ? totalFromItems.value : form.value.price));

// Aviso informativo de valor total da série (não altera os valores gravados)
const serieValorAviso = computed(() => {
    if (recorrenciaType.value === 'avulsa' || !commitmentItems.value.length) return null;
    const n = qtdSessoesEfetiva.value;
    if (!n || !totalFromItems.value) return null;
    if (serieValorMode.value === 'multiplicar') {
        return `Total da série: ${fmtBRL(totalFromItems.value * n)} (${fmtBRL(totalFromItems.value)} × ${n} sessões)`;
    }
    return `Valor por sessão: ${fmtBRL(totalFromItems.value / n)} (${fmtBRL(totalFromItems.value)} ÷ ${n} sessões)`;
});

// Sync: total dos itens → form.price
watch(totalFromItems, (total) => {
    if (commitmentItems.value.length > 0) form.value.price = total;
});

// Sync: duração dinâmica → form.duracaoMin (slot_mode=dynamic)
watch(dynamicDuration, (dur) => {
    if (isDynamic.value && dur != null && dur > 0) form.value.duracaoMin = dur;
});

// calcFinalPrice movido pra agendaEventHelpers.js (A66/1A)

// addItem, removeItem, onItemChange, _loadCommitmentItemsForEvent
// → useAgendaEventPickerBilling (1C-ii-a)

// fmtBRL movido pra agendaEventHelpers.js (A66/1A)

// selectedCommitment, selectedCommitmentName, selectedCommitmentFields,
// requiresPatient, isSessionEvent, patientLocked, hasInsurance → composer (1B)

// ── jornada ────────────────────────────────────────────────
// fmtJornadaHora (antes _fmtH) movido pra agendaEventHelpers.js (A66/1A)

const jornadaDialog = computed(() => {
    const rules = props.workRules;
    if (!rules?.length) return null;
    const d = form.value.dia ? new Date(form.value.dia) : new Date();
    const dow = d.getDay();
    const rule = rules.find((r) => Number(r.dia_semana) === dow);
    if (!rule) return { text: 'Este é um dia de folga na sua agenda.', isOff: true };
    const inicio = String(rule.hora_inicio || '').slice(0, 5);
    const fim = String(rule.hora_fim || '').slice(0, 5);
    const [h1, m1] = inicio.split(':').map(Number);
    const [h2, m2] = fim.split(':').map(Number);
    const totalMin = h2 * 60 + m2 - (h1 * 60 + m1);
    const durH = Math.floor(totalMin / 60);
    const durM = totalMin % 60;
    const durStr = durM > 0 ? `${durH}h${String(durM).padStart(2, '0')}` : `${durH}h`;
    return { text: `Jornada: das ${fmtJornadaHora(inicio)} às ${fmtJornadaHora(fim)} (${durStr})`, isOff: false };
});

// ── bloqueio de dia ────────────────────────────────────────
const blockedSet = computed(() => new Set(props.blockedDates || []));
const formDayISO = computed(() => {
    const d = form.value.dia ? new Date(form.value.dia) : null;
    if (!d) return '';
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
});
const isDayBlocked = computed(() => !!formDayISO.value && blockedSet.value.has(formDayISO.value));
const isSessionBlockedByDay = computed(() => isDayBlocked.value && isSessionEvent.value);

const isDiaFolga = computed(() => {
    if (!form.value.dia || !props.workRules?.length) return false;
    const dow = new Date(form.value.dia).getDay();
    return !props.workRules.some((r) => Number(r.dia_semana) === dow);
});

// ── time picker ────────────────────────────────────────────
const timePickerOpen = ref(false);
// samePatientConflict → useAgendaEventActions (1C-i) — watcher
// [paciente_id, dia] já está dentro do composable, samePatientConflict
// vem do destructuring acima.

const modalidadeOptions = [
    { label: 'Presencial', value: 'presencial' },
    { label: 'Online', value: 'online' }
];

// ── novo paciente ──────────────────────────────────────────
// cadRapidoOpen, abrirCadastroCompleto → useAgendaEventPickerBilling (1C-ii-a)
// onPatientCreatedRapido fica aqui (autocontido, usa form + cadRapidoOpen)
function onPatientCreatedRapido(p) {
    if (!p) return;
    const nome = p.nome_completo || p.nome || p.name || '';
    form.value.paciente_id = p.id;
    form.value.paciente_nome = nome;
    form.value.paciente_avatar = p.avatar_url || null;
    cadRapidoOpen.value = false;
}

// ── paciente picker ────────────────────────────────────────
// pacientePickerOpen, pacienteSearch, pacientesLoading, pacientesError, patients,
// selectCommitment, goBack, openPacientePicker, clearPatientsCache, loadPatients,
// selectPaciente, clearPaciente → useAgendaEventPickerBilling (1C-ii-a)
// filteredPatients depende de patients/pacienteSearch (do composable) — fica no .vue:
const filteredPatients = computed(() => {
    const q = String(pacienteSearch.value || '')
        .trim()
        .toLowerCase();
    // Somente pacientes Ativos podem ser selecionados para novos agendamentos
    const list = (patients.value || []).filter((p) => p.status === 'Ativo');
    if (!q) return list;
    return list.filter((p) => {
        const nome = String(p.nome || '').toLowerCase();
        const email = String(p.email || '').toLowerCase();
        const tel = String(p.telefone || '').toLowerCase();
        return nome.includes(q) || email.includes(q) || tel.includes(q);
    });
});

// 4 watchers (modelValue init, tenant/scope, solicitação pendente, online
// slots) + solicitacaoPendente ref → useAgendaEventLifecycle (1C-ii-b)
// watcher form.commitment_id, form.insurance_plan_id, onProcedureSelect
// → useAgendaEventPickerBilling (1C-ii-a)
// watcher [paciente_id, dia] → useAgendaEventActions (1C-i)

function goToAgendamentosRecebidos() {
    visible.value = false;
    const prefix = route.path.startsWith('/admin') ? '/admin' : '/therapist';
    router.push(`${prefix}/agendamentos-recebidos`);
}

function goToConveniosConfig() {
    visible.value = false;
    const prefix = route.path.startsWith('/admin') ? '/admin' : '/therapist';
    router.push(`${prefix}/configuracoes/convenios`);
}

// ── duração / slots ────────────────────────────────────────
// grouped duration options: default preset first, then com pausa, then sem pausa
const duracaoOptions = computed(() => {
    const defaultDur = props.agendaSettings?.session_duration_min || 50;
    const defaultGap = props.agendaSettings?.session_break_min || 0;

    const ALL_PRESETS = [
        { label: '30 min', dur: 30, gap: 0 },
        { label: '40 min', dur: 40, gap: 0 },
        { label: '45 min', dur: 45, gap: 15 },
        { label: '50 min', dur: 50, gap: 10 },
        { label: '60 min', dur: 60, gap: 0 },
        { label: '90 min', dur: 90, gap: 0 },
        { label: '2h', dur: 120, gap: 0 }
    ];

    const defaultItem = {
        label: `${defaultDur} min${defaultGap > 0 ? ` + ${defaultGap}min pausa` : ''}  ✦ padrão`,
        value: defaultDur
    };

    const others = ALL_PRESETS.filter((p) => p.dur !== defaultDur);
    const wBreak = others.filter((p) => p.gap > 0);
    const noBreak = others.filter((p) => p.gap === 0);

    const groups = [{ label: 'Padrão da agenda', items: [defaultItem] }];
    if (wBreak.length) groups.push({ label: 'Com pausa', items: wBreak.map((p) => ({ label: `${p.label} + ${p.gap}min pausa`, value: p.dur })) });
    if (noBreak.length) groups.push({ label: 'Sem pausa', items: noBreak.map((p) => ({ label: p.label, value: p.dur })) });
    return groups;
});

// hhmmToMin / minToHHMM movidos pra agendaEventHelpers.js (A66/1A)

// Slots disponíveis no dia selecionado (respeitando jornada + pausas + eventos existentes)
const availableSlots = computed(() => {
    const dia = form.value.dia;
    if (!dia || !props.workRules?.length) return [];
    const dow = new Date(dia).getDay();
    const dur = props.agendaSettings?.session_duration_min || 50;
    const gap = props.agendaSettings?.session_break_min || 0;
    const cycle = Math.max(1, dur + gap);

    const windows = (props.workRules || [])
        .filter((r) => Number(r.dia_semana) === dow && r.ativo !== false)
        .map((r) => ({ start: String(r.hora_inicio || '').slice(0, 5), end: String(r.hora_fim || '').slice(0, 5) }))
        .sort((a, b) => a.start.localeCompare(b.start));
    if (!windows.length) return [];

    const breaks = (props.pausasSemanais || []).filter((p) => p.dia_semana == null || Number(p.dia_semana) === dow).map((p) => ({ s: hhmmToMin(p.hora_inicio || '00:00'), e: hhmmToMin(p.hora_fim || '00:00') }));

    // eventos já agendados no mesmo dia (para marcar como ocupado)
    const todayStr = dia instanceof Date ? `${dia.getFullYear()}-${String(dia.getMonth() + 1).padStart(2, '0')}-${String(dia.getDate()).padStart(2, '0')}` : String(dia).slice(0, 10);
    const busySlots = (props.allEvents || [])
        .filter((e) => {
            if (!e.inicio_em) return false;
            return e.inicio_em.slice(0, 10) === todayStr && e.id !== form.value.id;
        })
        .map((e) => ({
            s: hhmmToMin(e.inicio_em.slice(11, 16)),
            e: e.fim_em ? hhmmToMin(e.fim_em.slice(11, 16)) : hhmmToMin(e.inicio_em.slice(11, 16)) + dur
        }));

    const slots = [];
    for (const w of windows) {
        const wStart = hhmmToMin(w.start);
        const wEnd = hhmmToMin(w.end);
        let t = wStart;
        while (t + dur <= wEnd) {
            const aEnd = t + dur;
            const brk = breaks.find((b) => !(aEnd <= b.s || t >= b.e));
            if (brk) {
                t = brk.e;
                continue;
            }
            const busy = busySlots.some((b) => !(aEnd <= b.s || t >= b.e));
            slots.push({ hhmm: minToHHMM(t), endHhmm: minToHHMM(aEnd), busy });
            t += cycle;
        }
    }
    const seen = new Set();
    return slots.filter((s) => {
        if (seen.has(s.hhmm)) return false;
        seen.add(s.hhmm);
        return true;
    });
});

const PERIODOS = [
    { label: 'Manhã', icon: 'pi-sun', from: '05:00', to: '12:00' },
    { label: 'Tarde', icon: 'pi-cloud', from: '12:00', to: '18:00' },
    { label: 'Noite', icon: 'pi-moon', from: '18:00', to: '24:00' }
];
const selectedPeriodo = ref(null);

// Apenas períodos que têm ao menos 1 slot disponível
const activePeriodos = computed(() => PERIODOS.filter((p) => availableSlots.value.some((s) => s.hhmm >= p.from && s.hhmm < p.to)));

// Set dos horários online pré-configurados (para marcar nos pills)
const onlineConfigSet = computed(() => new Set((onlineSlots.value || []).map((s) => s.hhmm)));

const filteredSlots = computed(() => {
    const base = selectedPeriodo.value ? availableSlots.value.filter((s) => s.hhmm >= selectedPeriodo.value.from && s.hhmm < selectedPeriodo.value.to) : availableSlots.value;
    return base.map((s) => ({ ...s, isOnlineConfigured: onlineConfigSet.value.has(s.hhmm) }));
});

// selectSlot, onlineSlots/loadingOnlineSlots refs + watcher
// → useAgendaEventLifecycle (1C-ii-b)

const onlineAtivo = computed(() => !!props.agendaSettings?.online_ativo);
const onlineDisponivel = computed(() => (form.value.modalidade === 'online' ? onlineAtivo.value && onlineSlots.value.length > 0 : true));

const dayWorkRule = computed(() => {
    if (!form.value.dia || !props.workRules?.length) return null;
    const dow = new Date(form.value.dia).getDay();
    return props.workRules.find((r) => Number(r.dia_semana) === dow) ?? null;
});

// ── série: info do banner ──────────────────────────────────
const serieDiaSemana = computed(() => {
    if (props.eventRow?.serie_dia_semana != null) return Number(props.eventRow.serie_dia_semana);
    const iso = props.eventRow?.inicio_em;
    if (iso) return new Date(iso).getDay();
    return form.value.dia ? new Date(form.value.dia).getDay() : null;
});
const serieHoraDisplay = computed(() => {
    if (props.eventRow?.serie_hora) return props.eventRow.serie_hora;
    return form.value.startTime || null;
});

// ── série: lista de sessões ────────────────────────────────
// serieEvents já declarado no topo (passado ao composer via extras).
// serieLoading, pillDeleteMenuRef, pillDeleteTarget, serieCountByStatus,
// pillDeleteMenuItems, generateRuleDates, loadSerieEvents,
// onPillEditClick, onPillStatusChange, onPillDeleteClick, onPillDelete
// → useAgendaEventLifecycle (1C-ii-b)

const statusOptions = [
    { label: 'Agendado', value: 'agendado' },
    { label: 'Realizado', value: 'realizado' },
    { label: 'Faltou', value: 'faltou' },
    { label: 'Cancelado', value: 'cancelado' },
    { label: 'Remarcar', value: 'remarcado' }
];

// isPast movido pra agendaEventHelpers.js (A66/1A)

// agendaPerms, isSessionFuture, isArchivedPastEdit, isInativoFutureEdit,
// statusOptionsFiltered → composer (1B)
// fmtWeekdayShort / fmtDayNum / fmtMonthShort → agendaEventHelpers (1A)

// timeConflict, startTimeDate, inicioDateTime, fimDateTime, dataHoraDisplay,
// previewRange, computedTitulo, canSave, headerTitle → composer (1B)

// ── save / delete ──────────────────────────────────────────
// onSave / onDelete / onEncerrarSerie → useAgendaEventActions (1C-i)

// onSendManualReminder + sendingReminder → useAgendaEventLifecycle (1C-ii-b)

// onDelete → useAgendaEventActions (1C-i)

// ── helpers ────────────────────────────────────────────────
// addMinutesDate / fmtDateBR / fmtDateBRLong / fmtTime / fmtDuracao /
// fmtSerieHora / nomeDiaSemana / isoToHHMM / calcMinutes movidos pra
// agendaEventHelpers.js (A66/1A)

// resetForm → composer (1B). Se algum watcher do .vue precisa resetar
// o form (ex: ao reabrir o dialog), chamar `form.value = resetForm()`
// usando a função exportada do composer.

// ── Google Calendar link ────────────────────────────────────────
const googleCalendarUrl = computed(() => {
    const dia = form.value.dia;
    const hora = form.value.startTime;
    if (!dia || !hora) return null;

    const start = formatGCalDate(dia, hora);
    const endHHMM = addMinutesToHHMM(hora, form.value.duracaoMin || 50);
    const end = formatGCalDate(dia, endHHMM);

    const paciente = form.value.paciente_nome ? ` — ${form.value.paciente_nome}` : '';
    const title = (form.value.titulo_custom?.trim() || 'Sessão') + paciente;
    const location = form.value.modalidade === 'online' ? 'Atendimento Online' : '';

    return generateGoogleCalendarLink({
        title,
        description: form.value.observacoes?.trim() || '',
        location,
        start,
        end
    });
});

// labelStatusSessao / statusSeverity / statusExtraClass movidos pra
// agendaEventHelpers.js (A66/1A)
</script>

<template>
    <Dialog v-model:visible="visible" modal :draggable="false" :dismissableMask="true" :style="{ width: '1000px', maxWidth: '96vw' }" :breakpoints="{ '960px': '96vw', '640px': '98vw' }" class="agenda-event-composer" pt:mask:class="backdrop-blur-xs">
        <template #header>
            <div class="w-full flex items-center justify-between gap-3">
                <div class="flex items-center gap-2 min-w-0">
                    <div class="header-dot shrink-0" :style="selectedCommitment?.bg_color ? { background: `#${selectedCommitment.bg_color}` } : {}" />
                    <div class="min-w-0">
                        <div class="font-semibold truncate text-base">{{ headerTitle }}</div>
                        <div v-if="step === 2" class="text-xs text-color-secondary truncate">{{ previewRange }}</div>
                    </div>
                </div>

                <!-- actions moved to footer -->
            </div>
        </template>

        <!-- ConfirmDialog renderizado na página pai para evitar conflito de z-index com o Dialog -->

        <!-- ══════════════════════════════════════════════ -->
        <!-- STEP 1 — escolha o tipo                        -->
        <!-- ══════════════════════════════════════════════ -->
        <div v-if="step === 1" class="p-2">
            <div class="mb-4 text-sm text-color-secondary">Selecione o tipo de compromisso para começar.</div>

            <Message v-if="isDayBlocked" severity="warn" class="mb-4" :closable="false">
                <i class="pi pi-lock mr-1" />
                <span>Este dia está bloqueado. Compromissos do tipo <strong>Sessão</strong> não podem ser agendados.</span>
            </Message>

            <div class="commitment-grid">
                <button
                    v-for="c in commitmentCards"
                    :key="c.id"
                    class="commitment-card"
                    :class="{ 'commitment-card--blocked': isDayBlocked && isNativeSession(c) }"
                    :style="c.bg_color ? { '--card-color': `#${c.bg_color}`, boxShadow: `0 4px 20px #${c.bg_color}30` } : {}"
                    :disabled="isDayBlocked && isNativeSession(c)"
                    @click="isDayBlocked && isNativeSession(c) ? null : selectCommitment(c)"
                >
                    <div class="commitment-card__inner">
                        <div class="commitment-card__icon" :style="c.bg_color ? { background: `#${c.bg_color}20`, color: `#${c.bg_color}` } : {}">
                            <i :class="isNativeSession(c) ? 'pi pi-user' : 'pi pi-calendar'" />
                        </div>
                        <div class="min-w-0 flex-1">
                            <div class="font-semibold truncate">{{ c.name }}</div>
                            <div v-if="c.description" class="text-xs text-color-secondary line-clamp-2 mt-0.5">
                                {{ c.description }}
                            </div>
                            <div class="flex flex-wrap gap-1 mt-2">
                                <Tag v-if="isNativeSession(c)" value="Exige paciente" severity="info" />
                                <Tag v-if="isDayBlocked && isNativeSession(c)" value="Dia bloqueado" severity="danger" icon="pi pi-lock" />
                            </div>
                        </div>
                        <i class="pi pi-chevron-right text-color-secondary opacity-50 shrink-0 mt-1" />
                    </div>
                </button>
            </div>
        </div>

        <!-- ══════════════════════════════════════════════ -->
        <!-- STEP 2 — formulário + painel lateral          -->
        <!-- ══════════════════════════════════════════════ -->
        <div v-else class="composer-grid">
            <!-- ─── COLUNA ESQUERDA — campos ─── -->
            <div class="composer-left">
                <!-- Avisos topo -->
                <Message v-if="form.conflito" severity="warn" class="mb-3" :closable="false"> <span class="font-semibold">Conflito:</span> {{ form.conflito }} </Message>
                <Message v-if="isDayBlocked && !isSessionEvent" severity="warn" class="mb-3" :closable="false">
                    <i class="pi pi-lock mr-1" />
                    Atenção: dia bloqueado. Este tipo pode ser salvo normalmente.
                </Message>
                <Message v-if="jornadaDialog" :severity="jornadaDialog.isOff ? 'warn' : 'info'" class="mb-3" :closable="false">
                    <i :class="jornadaDialog.isOff ? 'pi pi-moon mr-1' : 'pi pi-clock mr-1'" />
                    {{ jornadaDialog.text }}
                </Message>
                <Message v-if="isDiaFolga && isSessionEvent" severity="warn" class="mb-3" :closable="false">
                    <i class="pi pi-moon mr-1" />
                    Este dia é folga na sua jornada. Você ainda pode salvar se necessário.
                </Message>

                <!-- ── Restrições de status do paciente ───────────── -->
                <Message v-if="isArchivedPastEdit" severity="warn" class="mb-3" :closable="false">
                    <i class="pi pi-lock mr-1" />
                    <b>Paciente arquivado.</b> O histórico de sessões é somente leitura.
                </Message>
                <Message v-if="isEdit && form.paciente_status === 'Inativo' && isSessionFuture" severity="warn" class="mb-3" :closable="false">
                    <i class="pi pi-ban mr-1" />
                    <b>Paciente inativo.</b> Remarcação de sessões está bloqueada.
                </Message>
                <Message v-if="!isEdit && isSessionEvent && form.paciente_id && !agendaPerms.canCreateSession" severity="error" class="mb-3" :closable="false">
                    <i class="pi pi-ban mr-1" />
                    <b>{{ form.paciente_status === 'Arquivado' ? 'Paciente arquivado.' : 'Paciente inativo.' }}</b>
                    Novos agendamentos estão bloqueados.
                </Message>

                <!-- ── Alerta: solicitação pendente neste horário ─── -->
                <Message v-if="solicitacaoPendente && isSessionEvent && !isEdit" severity="info" class="mb-3" :closable="false">
                    <div class="flex items-center justify-between gap-3 w-full flex-wrap">
                        <span>
                            <i class="pi pi-inbox mr-1" />
                            Solicitação pendente de
                            <b>{{ solicitacaoPendente.paciente_nome }} {{ solicitacaoPendente.paciente_sobrenome }}</b>
                            para este horário.
                        </span>
                        <Button label="Ver agendamentos recebidos" icon="pi pi-arrow-right" size="small" severity="info" outlined class="rounded-full shrink-0" @click="goToAgendamentosRecebidos" />
                    </div>
                </Message>

                <!-- ── PACIENTE (destaque total) ──────────────────── -->
                <div v-if="isSessionEvent" class="patient-hero mb-4">
                    <div class="patient-hero__label">
                        <div class="flex items-center gap-1.5">
                            <i class="pi pi-user" />
                            <span>Paciente</span>
                        </div>
                        <div class="flex items-center gap-1 ml-auto">
                            <Button label="Cadastro Rápido" icon="pi pi-user-plus" size="small" severity="secondary" outlined class="rounded-full text-xs h-7" @click="cadRapidoOpen = true" />
                            <Button icon="pi pi-external-link" size="small" severity="secondary" text class="rounded-full h-7 w-7" v-tooltip.top="'Cadastro completo (nova aba)'" @click="abrirCadastroCompleto" />
                        </div>
                    </div>

                    <!-- Sem paciente selecionado -->
                    <button v-if="!form.paciente_id" class="patient-hero__empty" @click="openPacientePicker">
                        <div class="patient-hero__empty-icon">
                            <i class="pi pi-user-plus" />
                        </div>
                        <div>
                            <div class="font-semibold">Selecionar paciente</div>
                            <div class="text-xs text-color-secondary">Toque para buscar</div>
                        </div>
                        <i class="pi pi-chevron-right ml-auto text-color-secondary opacity-50" />
                    </button>

                    <!-- Com paciente selecionado -->
                    <div v-else class="patient-hero__selected">
                        <Avatar v-if="form.paciente_avatar" :image="form.paciente_avatar" shape="circle" size="large" class="shrink-0" />
                        <Avatar v-else :label="patientInitials(form.paciente_nome)" shape="circle" size="large" class="shrink-0 patient-avatar-bg" />
                        <div class="min-w-0 flex-1">
                            <div class="font-bold text-base truncate">{{ form.paciente_nome }}</div>
                            <div class="flex items-center gap-1.5 flex-wrap">
                                <span class="text-xs text-color-secondary">Paciente vinculado</span>
                                <span
                                    v-if="form.paciente_status === 'Inativo' || form.paciente_status === 'Arquivado'"
                                    style="display: inline-block; background: #f97316; color: #fff; font-size: 9px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; padding: 1px 6px; border-radius: 3px; line-height: 1.5"
                                    >{{ form.paciente_status === 'Arquivado' ? 'arquivado' : 'desativado' }}</span
                                >
                            </div>
                        </div>
                        <div class="flex gap-1 shrink-0">
                            <Button v-if="!patientLocked" icon="pi pi-pencil" severity="secondary" outlined size="small" class="rounded-full h-8 w-8" v-tooltip.top="'Trocar'" @click="openPacientePicker" />
                            <Button v-if="!patientLocked" icon="pi pi-times" severity="secondary" text size="small" class="rounded-full h-8 w-8" v-tooltip.top="'Limpar'" @click="clearPaciente" />
                            <span v-if="patientLocked" v-tooltip.top="'Paciente não pode ser alterado após criação'" class="flex items-center gap-1 text-xs text-color-secondary px-2"><i class="pi pi-lock" /></span>
                        </div>
                    </div>

                    <!-- Modalidade dentro do card de paciente -->
                    <div class="patient-hero__modalidade">
                        <span class="patient-hero__mod-label">Modalidade</span>
                        <SelectButton v-model="form.modalidade" :options="modalidadeOptions" optionLabel="label" optionValue="value" :allowEmpty="false" size="small" />
                    </div>

                    <Message v-if="samePatientConflict && isSessionEvent" severity="warn" class="mt-2" :closable="false">
                        Paciente já tem sessão neste dia:
                        <b>{{ fmtTime(new Date(samePatientConflict.inicio_em)) }} → {{ fmtTime(new Date(samePatientConflict.fim_em)) }}</b>
                    </Message>
                </div>

                <!-- ── DATA E HORÁRIO ─────────────────────────────── -->
                <div class="field-card mb-4">
                    <div class="field-card__header">
                        <i class="pi pi-calendar" />
                        <span>Data e Horário</span>
                        <div class="flex items-center gap-1 ml-auto">
                            <Button label="Ajustar horário" icon="pi pi-pencil" size="small" severity="secondary" outlined class="rounded-full text-xs h-7" @click="timePickerOpen = true" />
                        </div>
                    </div>
                    <div class="field-card__body">
                        <div class="time-hero" @click="timePickerOpen = true">
                            <div class="time-hero__block">
                                <span class="time-hero__label">Data</span>
                                <span class="time-hero__value">{{ form.dia ? fmtDateBR(form.dia) : '—' }}</span>
                            </div>
                            <div class="time-hero__sep" />
                            <div class="time-hero__block">
                                <span class="time-hero__label">Início</span>
                                <span class="time-hero__value">{{ form.startTime || '—' }}</span>
                            </div>
                            <div class="time-hero__sep" />
                            <div class="time-hero__block">
                                <span class="time-hero__label">Duração</span>
                                <span class="time-hero__value">{{ fmtDuracao(form.duracaoMin) }}</span>
                            </div>
                            <div class="time-hero__sep" />
                            <div class="time-hero__block">
                                <span class="time-hero__label">Término</span>
                                <span class="time-hero__value">{{ fimDateTime ? fmtTime(fimDateTime) : '—' }}</span>
                            </div>
                        </div>
                        <Message v-if="timeConflict" severity="warn" class="m-2" :closable="false">
                            <i class="pi pi-exclamation-triangle mr-1" />
                            {{ timeConflict }}
                        </Message>
                    </div>
                    <!-- /field-card__body -->
                </div>
                <!-- /field-card -->

                <!-- ── STATUS DA SESSÃO ───────────────────────────── -->
                <div v-if="isSessionEvent && isEdit" class="field-card mb-4">
                    <div class="field-card__header">
                        <i class="pi pi-tag" />
                        <span>Status da Sessão</span>
                    </div>
                    <div class="field-card__body">
                        <SelectButton v-model="form.status" :options="statusOptionsFiltered" optionLabel="label" optionValue="value" optionDisabled="disabled" :allowEmpty="false" :disabled="isArchivedPastEdit" class="w-full status-select-btn" />
                    </div>
                </div>

                <!-- ── DEMAIS CAMPOS ──────────────────────────────── -->
                <div class="fields-grid">
                    <!-- Título (apenas para não-sessão) -->
                    <div v-if="!isSessionEvent">
                        <FloatLabel variant="on">
                            <IconField>
                                <InputIcon class="pi pi-pencil" />
                                <InputText id="aed-titulo" v-model="form.titulo_custom" class="w-full" variant="filled" />
                            </IconField>
                            <label for="aed-titulo">Título</label>
                        </FloatLabel>
                    </div>

                    <!-- Profissional -->
                    <div v-if="allowOwnerEdit">
                        <FloatLabel variant="on">
                            <IconField>
                                <InputIcon class="pi pi-briefcase" />
                                <Select inputId="aed-owner" class="w-full" v-model="form.owner_id" :options="ownerOptions" optionLabel="label" optionValue="value" variant="filled" />
                            </IconField>
                            <label for="aed-owner">Profissional</label>
                        </FloatLabel>
                    </div>

                    <!-- Campos extras do compromisso -->
                    <template v-if="selectedCommitmentFields.length">
                        <div v-for="f in selectedCommitmentFields" :key="f.key">
                            <FloatLabel variant="on">
                                <Textarea v-if="f.field_type === 'textarea'" :id="`aed-extra-${f.key}`" v-model="form.extra_fields[f.key]" class="w-full" variant="filled" rows="2" autoResize />
                                <InputText v-else :id="`aed-extra-${f.key}`" v-model="form.extra_fields[f.key]" class="w-full" variant="filled" />
                                <label :for="`aed-extra-${f.key}`">{{ f.label }}{{ f.required ? ' *' : '' }}</label>
                            </FloatLabel>
                        </div>
                    </template>

                    <!-- Observação (somente quando não é sessão — para sessões fica no card direito) -->
                    <div v-if="!isSessionEvent" class="col-span-full">
                        <FloatLabel variant="on">
                            <Textarea id="aed-observacoes" v-model="form.observacoes" class="w-full" variant="filled" rows="3" autoResize />
                            <label for="aed-observacoes">Observação</label>
                        </FloatLabel>
                    </div>
                </div>

                <!-- ── RECORRÊNCIAS APLICADAS ──────────────────────────── -->
                <div v-if="hasSerie" class="serie-panel mt-4">
                    <div class="serie-panel__header">
                        <i class="pi pi-refresh shrink-0" />
                        <span>Recorrências Aplicadas</span>
                        <div v-if="!serieLoading && serieEvents.length" class="serie-panel__stats">
                            <span>{{ serieEvents.length }} sessões</span>
                            <span v-if="serieCountByStatus.realizado"> · {{ serieCountByStatus.realizado }} realizadas</span>
                            <span v-if="serieCountByStatus.faltou"> · {{ serieCountByStatus.faltou }} faltaram</span>
                            <span v-if="serieCountByStatus.cancelado"> · {{ serieCountByStatus.cancelado }} canceladas</span>
                            <span v-if="serieCountByStatus.remarcado"> · {{ serieCountByStatus.remarcado }} para remarcar</span>
                        </div>
                        <span v-if="serieLoading" class="ml-auto text-xs opacity-50">Carregando…</span>
                    </div>

                    <div v-if="!serieLoading && serieEvents.length" class="serie-pills-wrap">
                        <div
                            v-for="ev in serieEvents"
                            :key="ev.id || ev.recurrence_date"
                            class="serie-pill"
                            :class="[`serie-pill--${ev._status || 'agendado'}`, { 'serie-pill--current': ev.recurrence_date === currentRecurrenceDate, 'serie-pill--past': isPast(ev.inicio_em) }]"
                        >
                            <div class="serie-pill__date">
                                <span class="serie-pill__weekday">{{ fmtWeekdayShort(ev.inicio_em) }}</span>
                                <span class="serie-pill__daynum">{{ fmtDayNum(ev.inicio_em) }}</span>
                                <span class="serie-pill__month">{{ fmtMonthShort(ev.inicio_em) }}</span>
                            </div>
                            <span class="serie-pill__time">{{ fmtTime(new Date(ev.inicio_em)) }}</span>
                            <Select v-model="ev._status" :options="statusOptions" optionLabel="label" optionValue="value" size="small" variant="filled" class="serie-pill__status-sel" @change="onPillStatusChange(ev)" />
                            <span v-if="ev.recurrence_date === currentRecurrenceDate" class="serie-pill__cur-badge">atual</span>
                            <Button label="Editar" icon="pi pi-pencil" text size="small" severity="secondary" class="serie-pill__edit" v-tooltip.left="'Editar esta sessão'" @click="onPillEditClick(ev)" />
                            <Button icon="pi pi-times" text size="small" severity="secondary" class="serie-pill__del" v-tooltip.left="'Remover'" @click="onPillDeleteClick(ev, $event)" />
                        </div>
                    </div>

                    <div v-if="!serieLoading && !serieEvents.length" class="serie-panel__empty">Nenhuma sessão encontrada na série.</div>

                    <Menu ref="pillDeleteMenuRef" :model="pillDeleteMenuItems" :popup="true" />
                </div>
            </div>

            <!-- ─── COLUNA DIREITA — painel ─── -->
            <div class="composer-right">
                <!-- ── RESUMO ─────────────────────────────────────── -->
                <div class="side-card mb-3">
                    <div class="side-card__title">Resumo</div>

                    <div class="flex items-center gap-2 mb-2">
                        <span v-if="selectedCommitment?.bg_color" class="commit-badge" :style="{ background: `#${selectedCommitment.bg_color}20`, color: `#${selectedCommitment.bg_color}`, borderColor: `#${selectedCommitment.bg_color}40` }">{{
                            selectedCommitmentName
                        }}</span>
                        <Tag v-else :value="selectedCommitmentName" severity="info" />
                        <Tag v-if="isSessionEvent" :value="labelStatusSessao(form.status)" :severity="statusSeverity(form.status)" :class="statusExtraClass(form.status)" />
                    </div>

                    <div class="summary-row">
                        <i class="pi pi-user summary-icon" />
                        <span class="truncate">{{ form.paciente_nome || (isSessionEvent ? 'Sem paciente' : '—') }}</span>
                    </div>
                    <div class="summary-row">
                        <i class="pi pi-calendar summary-icon" />
                        <span class="truncate">{{ form.dia ? fmtDateBR(form.dia) : '—' }}</span>
                    </div>
                    <div class="summary-row">
                        <i class="pi pi-clock summary-icon" />
                        <span>{{ form.startTime || '—' }} → {{ fimDateTime ? fmtTime(fimDateTime) : '—' }}</span>
                    </div>
                    <div class="summary-row">
                        <i class="pi pi-map-marker summary-icon" />
                        <span class="capitalize">{{ form.modalidade || '—' }}</span>
                    </div>
                    <div v-if="isSessionEvent" class="summary-row">
                        <i class="pi pi-wallet summary-icon" />
                        <span>{{ displayPrice != null ? fmtBRL(displayPrice) : '—' }}</span>
                    </div>
                </div>

                <!-- ── ESCOPO DE EDIÇÃO — só em modo edição de série ── -->
                <div v-if="isEdit && hasSerie" class="side-card mb-3">
                    <div class="side-card__title mb-2">Aplicar alterações em</div>
                    <SelectButton v-model="editScope" :options="editScopeOptions" optionLabel="label" optionValue="value" :optionDisabled="(o) => !!o.disabled" class="w-full" size="small" />
                    <Message v-if="isFirstOccurrence" severity="info" :closable="false" class="mt-2">
                        <span class="text-sm"> Esta é a primeira sessão da série. Para alterar todas as ocorrências, use <b>Todos</b> ou <b>Todos sem exceção</b>. </span>
                    </Message>
                </div>

                <!-- ── FINANCEIRO (só sessão) ────────────────────────── -->
                <div v-if="isSessionEvent" class="side-card mb-3">
                    <!-- SelectButton: Gratuito / Particular / Convênio -->
                    <SelectButton v-model="billingType" :options="billingTypeOptions" optionLabel="label" optionValue="value" class="w-full mb-3" />

                    <!-- PARTICULAR: seletor de serviço -->
                    <template v-if="billingType === 'particular'">
                        <div class="flex gap-2 mb-2">
                            <Select
                                v-if="services.filter((s) => s.active).length"
                                v-model="servicePickerSel"
                                :options="services.filter((s) => s.active)"
                                optionLabel="name"
                                optionValue="id"
                                placeholder="Adicionar serviço..."
                                class="flex-1"
                                size="small"
                                @update:modelValue="
                                    (id) => {
                                        addItem(services.find((s) => s.id === id));
                                        servicePickerSel = null;
                                    }
                                "
                            />
                            <span v-else class="flex-1 text-xs text-color-secondary py-2 px-3 italic">Sem serviços cadastrados</span>
                            <Button
                                icon="pi pi-plus"
                                v-tooltip.top="'Cadastrar novo serviço'"
                                size="small"
                                outlined
                                severity="secondary"
                                class="aed-quick-btn"
                                @click="openServiceQuickCreate"
                            />
                        </div>
                    </template>

                    <!-- CONVÊNIO: fluxo progressivo -->
                    <template v-if="billingType === 'convenio'">
                        <!-- PASSO 1 — Select do plano + quick-create (sempre visível) -->
                        <div class="flex gap-2 mb-2">
                            <Select
                                v-model="form.insurance_plan_id"
                                :options="activePlans"
                                optionLabel="name"
                                optionValue="id"
                                placeholder="Selecionar convênio..."
                                showClear
                                class="flex-1"
                                size="small"
                            />
                            <Button
                                icon="pi pi-plus"
                                v-tooltip.top="'Cadastrar novo convênio'"
                                size="small"
                                outlined
                                severity="secondary"
                                class="aed-quick-btn"
                                @click="openInsuranceQuickCreate"
                            />
                        </div>

                        <!-- PASSO 2 — Procedimento (visível quando plano selecionado) -->
                        <template v-if="hasInsurance">
                            <template v-if="planServices.length > 0">
                                <label class="text-xs text-color-secondary mb-1 block">Procedimento</label>
                                <Select
                                    v-model="selectedPlanService"
                                    :options="planServices"
                                    optionLabel="name"
                                    optionValue="id"
                                    placeholder="Selecionar procedimento..."
                                    showClear
                                    class="w-full mb-2"
                                    size="small"
                                    @update:modelValue="onProcedureSelect"
                                />
                            </template>
                            <div v-else class="flex items-center justify-between gap-2 mb-2 p-2 rounded-lg bg-surface-100">
                                <span class="text-xs text-color-secondary">Este convênio não tem procedimentos cadastrados.</span>
                                <Button label="Cadastrar" icon="pi pi-plus" size="small" severity="secondary" outlined class="rounded-full shrink-0 text-xs h-7" @click="goToConveniosConfig" />
                            </div>

                            <!-- PASSO 3 — Nº da Guia + Valor (visível após selecionar procedimento ou sem procedimentos) -->
                            <template v-if="selectedPlanService != null || planServices.length === 0">
                                <div class="flex gap-2">
                                    <div class="flex-1">
                                        <label class="text-xs text-color-secondary mb-1 block">Nº da Guia</label>
                                        <InputText v-model="form.insurance_guide_number" placeholder="Ex: 123456789" class="w-full" size="small" />
                                    </div>
                                    <div class="flex-1">
                                        <label class="text-xs text-color-secondary mb-1 block">Valor (R$)</label>
                                        <InputNumber v-model="form.insurance_value" mode="currency" currency="BRL" locale="pt-BR" :readonly="planServices.length > 0 && selectedPlanService != null" class="w-full" size="small" />
                                    </div>
                                </div>
                            </template>
                        </template>
                    </template>
                </div>

                <!-- ── RECORRÊNCIA (só sessão) ───────────────────── -->
                <div v-if="isSessionEvent" class="side-card">
                    <div class="mb-3">
                        <!-- Lista de itens adicionados -->
                        <div v-if="commitmentItems.length" class="commitment-items-list mb-2">
                            <div v-for="(item, idx) in commitmentItems" :key="idx" class="commitment-item-row">
                                <!-- linha 1: nome + remover -->
                                <div class="commitment-item-header">
                                    <span class="commitment-item-name">{{ item.service_name }}</span>
                                    <Button icon="pi pi-times" size="small" severity="danger" text @click="removeItem(idx)" />
                                </div>

                                <!-- linha 2: qtd | preço unit (editável) | desconto % | desconto fixo | total -->
                                <div class="commitment-item-controls">
                                    <div class="commitment-item-field">
                                        <label class="commitment-item-label">Qtd</label>
                                        <InputNumber v-model="item.quantity" :min="1" :max="99" size="small" inputClass="w-12 text-center" @update:modelValue="onItemChange(item)" />
                                    </div>
                                    <div class="commitment-item-field">
                                        <label class="commitment-item-label">Preço unit.</label>
                                        <InputNumber v-model="item.unit_price" mode="currency" currency="BRL" locale="pt-BR" :min="0" :minFractionDigits="2" size="small" inputClass="w-24" @update:modelValue="onItemChange(item)" />
                                    </div>
                                    <div class="commitment-item-field">
                                        <label class="commitment-item-label">Desc %</label>
                                        <InputNumber v-model="item.discount_pct" :min="0" :max="100" suffix="%" size="small" inputClass="w-16 text-center" @update:modelValue="onItemChange(item)" />
                                    </div>
                                    <div class="commitment-item-field">
                                        <label class="commitment-item-label">Desc R$</label>
                                        <InputNumber v-model="item.discount_flat" mode="currency" currency="BRL" locale="pt-BR" :min="0" :minFractionDigits="2" size="small" inputClass="w-20" @update:modelValue="onItemChange(item)" />
                                    </div>
                                    <div class="commitment-item-field commitment-item-field--final">
                                        <label class="commitment-item-label">Total</label>
                                        <span class="commitment-item-price">{{ fmtBRL(item.final_price) }}</span>
                                    </div>
                                </div>
                            </div>

                            <div class="commitment-items-total">
                                <span class="text-sm text-color-secondary">Total da sessão</span>
                                <span class="font-semibold">{{ fmtBRL(totalFromItems) }}</span>
                            </div>
                        </div>
                    </div>

                    <!-- Observação -->
                    <div class="mb-3">
                        <FloatLabel variant="on">
                            <Textarea id="aed-observacoes-side" v-model="form.observacoes" class="w-full" variant="filled" rows="3" autoResize :disabled="isArchivedPastEdit" />
                            <label for="aed-observacoes-side">Observação</label>
                        </FloatLabel>
                    </div>

                    <!-- ── COBRANÇA DA SESSÃO ──────────────────────────── -->
                    <AgendaEventoFinanceiroPanel v-if="isSessionEvent && isEdit && eventRow?.id" :evento="eventRow" class="mb-3" />

                    <!-- Opção de recorrência para sessão SEM série (criação ou avulsa) -->
                    <template v-if="!hasSerie">
                        <div class="side-card__title mb-2">Frequência</div>
                        <Message v-if="isSessionEvent && form.paciente_id && !agendaPerms.canCreateRecurrence" severity="warn" class="mb-3" :closable="false">
                            <i class="pi pi-ban mr-1" />
                            <b>{{ form.paciente_status === 'Arquivado' ? 'Paciente arquivado.' : 'Paciente inativo.' }}</b>
                            Criação de recorrências está bloqueada.
                        </Message>

                        <!-- Data de início (= form.dia) com botão Hoje -->
                        <div class="rec-startdate-row mb-3">
                            <div class="flex items-center gap-1.5 min-w-0">
                                <i class="pi pi-calendar text-primary-500 shrink-0" />
                                <span class="text-sm font-medium truncate">{{ form.dia ? fmtDateBR(form.dia) : 'Selecione a data' }}</span>
                            </div>
                            <Button label="Hoje" size="small" severity="secondary" outlined class="rounded-full shrink-0" @click="setHoje" />
                        </div>

                        <!-- Chips de frequência -->
                        <div class="freq-chips mb-3">
                            <button
                                v-for="f in freqOpcoes"
                                :key="f.value"
                                class="freq-chip"
                                :class="{
                                    'freq-chip--active': recorrenciaType === f.value,
                                    'opacity-40 cursor-not-allowed': f.value !== 'avulsa' && !agendaPerms.canCreateRecurrence
                                }"
                                :disabled="f.value !== 'avulsa' && !agendaPerms.canCreateRecurrence"
                                @click="!agendaPerms.canCreateRecurrence && f.value !== 'avulsa' ? null : (recorrenciaType = f.value)"
                            >
                                {{ f.label }}
                            </button>
                        </div>

                        <!-- Info semanal/quinzenal -->
                        <div v-if="recorrenciaType === 'semanal' || recorrenciaType === 'quinzenal'" class="recorrencia-preview mb-3">
                            <i class="pi pi-refresh text-primary-500" />
                            <span class="text-sm font-medium">
                                {{ recorrenciaType === 'quinzenal' ? 'A cada 2 semanas, toda' : 'Toda' }}
                                {{ nomeDiaSemana(diaSemanaRecorrencia) }}, às {{ form.startTime || '—' }}
                            </span>
                        </div>

                        <!-- Seletor de dias da semana (diasEspecificos) -->
                        <div v-if="recorrenciaType === 'diasEspecificos'" class="mb-3">
                            <label class="block text-xs font-semibold text-color-secondary mb-1.5">Dias da semana</label>
                            <div class="dias-semana-grid">
                                <button v-for="d in diasSemanaOpcoes" :key="d.value" class="dia-chip" :class="{ 'dia-chip--active': diasSelecionados.includes(d.value) }" @click="toggleDiaSelecionado(d.value)">{{ d.short }}</button>
                            </div>
                        </div>

                        <!-- Quantidade de sessões (apenas quando não é avulsa) -->
                        <div v-if="recorrenciaType !== 'avulsa'" class="mb-3">
                            <label class="block text-xs font-semibold text-color-secondary mb-1.5">Quantidade de sessões</label>
                            <div class="flex gap-1.5 flex-wrap">
                                <button v-for="opt in qtdSessoesOpcoes" :key="opt.value" class="freq-tab" :class="{ 'freq-tab--active': qtdSessoesMode === opt.value }" @click="qtdSessoesMode = opt.value">{{ opt.label }}</button>
                            </div>
                            <div v-if="qtdSessoesMode === 'personalizar'" class="personalizar-box mt-2">
                                <InputNumber v-model="qtdSessoesCustom" :min="1" :max="200" showButtons buttonLayout="horizontal" fluid>
                                    <template #decrementbuttonicon><i class="pi pi-minus" /></template>
                                    <template #incrementbuttonicon><i class="pi pi-plus" /></template>
                                </InputNumber>
                            </div>
                        </div>

                        <!-- Preview das ocorrências -->
                        <div v-if="recorrenciaType !== 'avulsa' && ocorrenciasComConflito.length" class="ocorrencias-preview">
                            <div class="ocorrencias-header">
                                <span class="text-xs font-semibold text-color-secondary">{{ totalOcorrencias }} sessões previstas</span>
                                <span v-if="totalConflitos > 0" class="ocorrencias-aviso">
                                    <i class="pi pi-exclamation-triangle" />
                                    {{ totalConflitos }} com conflito
                                </span>
                            </div>
                            <div class="ocorrencias-scroll">
                                <div
                                    v-for="(o, i) in ocorrenciasComConflito"
                                    :key="i"
                                    class="ocorrencia-item"
                                    :class="{
                                        'ocorrencia-item--fora': o.conflict?.type === 'feriado' || o.conflict?.type === 'bloqueado',
                                        'ocorrencia-item--warn': o.conflict?.type === 'folga' || o.conflict?.type === 'pausa'
                                    }"
                                >
                                    <i
                                        class="pi"
                                        :class="{
                                            'pi-circle-fill': !o.conflict,
                                            'pi-exclamation-triangle': o.conflict?.type === 'folga' || o.conflict?.type === 'pausa',
                                            'pi-ban': o.conflict?.type === 'feriado' || o.conflict?.type === 'bloqueado'
                                        }"
                                        style="font-size: 9px"
                                    />
                                    <span class="text-xs">{{ fmtDateBRLong(o.date) }}</span>
                                    <span v-if="o.conflict" class="ocorrencia-conflict-label">{{ o.conflict.label }}</span>
                                </div>
                            </div>
                            <Message v-if="totalConflitos > 0" severity="warn" class="mt-2" :closable="false">
                                <span class="text-xs">{{ totalConflitos }} sessão(ões) com conflito serão marcadas automaticamente para ajuste.</span>
                            </Message>
                        </div>

                        <!-- Modo de valor da série — informativo, só quando há serviços + recorrência -->
                        <div v-if="recorrenciaType !== 'avulsa' && commitmentItems.length > 0" class="mb-1 mt-3">
                            <label class="block text-xs font-semibold text-color-secondary mb-1.5">Como interpretar o valor</label>
                            <SelectButton
                                v-model="serieValorMode"
                                :options="[
                                    { label: 'Por sessão', value: 'multiplicar' },
                                    { label: 'Pacote fechado', value: 'dividir' }
                                ]"
                                optionLabel="label"
                                optionValue="value"
                                size="small"
                                class="w-full mb-2"
                            />
                            <Message v-if="serieValorAviso" severity="info" :closable="false">
                                <span class="text-xs">{{ serieValorAviso }}</span>
                            </Message>
                        </div>
                    </template>
                </div>
            </div>
        </div>

        <!-- ══════════════════════════════════════════════ -->
        <!-- Patient Picker Dialog                          -->
        <!-- ══════════════════════════════════════════════ -->
        <Dialog v-model:visible="pacientePickerOpen" modal header="Selecionar paciente" :draggable="false" :style="{ width: '860px', maxWidth: '96vw' }" :breakpoints="{ '960px': '96vw', '640px': '98vw' }">
            <div class="flex flex-col gap-3">
                <div class="flex flex-wrap items-center gap-3">
                    <div class="flex-1 min-w-[240px]">
                        <FloatLabel variant="on">
                            <IconField>
                                <InputIcon class="pi pi-search" />
                                <InputText id="aed-paciente-search" v-model="pacienteSearch" class="w-full" autocomplete="off" variant="filled" />
                            </IconField>
                            <label for="aed-paciente-search">Buscar por nome, e-mail ou telefone…</label>
                        </FloatLabel>
                    </div>
                    <Button label="Recarregar" icon="pi pi-refresh" severity="secondary" outlined class="rounded-full" :loading="pacientesLoading" @click="loadPatients(true)" />
                </div>

                <Message v-if="pacientesError" severity="warn">{{ pacientesError }}</Message>
                <div v-if="pacientesLoading" class="text-sm text-color-secondary">Carregando pacientes…</div>
                <div v-else-if="filteredPatients.length === 0" class="text-sm text-color-secondary">Nenhum paciente encontrado.</div>

                <div v-else class="patient-list">
                    <button v-for="p in filteredPatients" :key="p.id" class="patient-item" @click="selectPaciente(p)">
                        <Avatar v-if="p.avatar_url" :image="p.avatar_url" shape="circle" class="shrink-0" />
                        <Avatar v-else :label="patientInitials(p.nome)" shape="circle" class="shrink-0" />
                        <div class="min-w-0 flex-1">
                            <div class="font-semibold truncate">{{ p.nome || 'Sem nome' }}</div>
                            <div class="text-xs text-color-secondary truncate">
                                <span v-if="p.email">{{ p.email }}</span>
                                <span v-if="p.email && p.telefone"> • </span>
                                <span v-if="p.telefone">{{ p.telefone }}</span>
                            </div>
                        </div>
                        <div class="shrink-0 flex items-center gap-2">
                            <Tag v-if="p.status" :value="String(p.status)" severity="secondary" />
                            <Button icon="pi pi-check" class="rounded-full" size="small" />
                        </div>
                    </button>
                </div>
            </div>
            <template #footer>
                <Button label="Fechar" icon="pi pi-times" text @click="pacientePickerOpen = false" />
            </template>
        </Dialog>

        <!-- ══════════════════════════════════════════════ -->
        <!-- Time Picker Dialog                             -->
        <!-- ══════════════════════════════════════════════ -->
        <Dialog v-model:visible="timePickerOpen" modal header="Data e Horário" :draggable="false" :style="{ width: '560px', maxWidth: '96vw' }" :breakpoints="{ '640px': '98vw' }">
            <div class="flex flex-col gap-4">
                <!-- Data -->
                <div>
                    <label class="block text-sm font-medium mb-2">Data</label>
                    <DatePicker v-model="form.dia" dateFormat="dd/mm/yy" showIcon fluid inline />
                </div>

                <!-- ── Horários disponíveis (presencial e online unificado) ──── -->
                <div v-if="availableSlots.length">
                    <!-- Info online: lista os slots pré-configurados e orienta o usuário -->
                    <template v-if="form.modalidade === 'online'">
                        <Message v-if="!onlineAtivo" severity="warn" :closable="false" class="mb-2"> Atendimento online não está ativado. Ative em <strong>Configurações → Online</strong>. </Message>
                        <div v-else class="online-info-bar mb-2">
                            <i class="pi pi-video shrink-0" />
                            <span v-if="loadingOnlineSlots" class="opacity-60">Carregando horários online…</span>
                            <span v-else-if="onlineSlots.length">
                                Horários online configurados para hoje:
                                <strong>{{ onlineSlots.map((s) => s.hhmm).join(', ') }}</strong>
                                <span class="online-info-bar__hint"> — marcados com <span class="online-dot-inline">●</span></span>
                            </span>
                            <span v-else class="opacity-70"> Nenhum horário online configurado para este dia — você pode agendar em qualquer horário da sua jornada. </span>
                        </div>
                    </template>

                    <!-- Header: rótulo + period chips (só períodos com slots) -->
                    <div class="flex items-center justify-between mb-2">
                        <label class="text-sm font-medium">Horários disponíveis</label>
                        <div class="flex gap-1">
                            <button v-for="p in activePeriodos" :key="p.label" class="periodo-chip" :class="{ 'periodo-chip--active': selectedPeriodo === p }" @click="selectedPeriodo = selectedPeriodo === p ? null : p">
                                <i :class="`pi ${p.icon}`" />
                                {{ p.label }}
                            </button>
                        </div>
                    </div>

                    <!-- Grid de pills -->
                    <div class="slots-grid">
                        <button
                            v-for="s in filteredSlots"
                            :key="s.hhmm"
                            class="slot-pill"
                            :class="{
                                'slot-pill--busy': s.busy,
                                'slot-pill--current': form.startTime === s.hhmm,
                                'slot-pill--online-cfg': form.modalidade === 'online' && s.isOnlineConfigured
                            }"
                            :title="s.busy ? 'Horário ocupado' : form.modalidade === 'online' && s.isOnlineConfigured ? `${s.hhmm} – ${s.endHhmm} · horário online configurado` : `${s.hhmm} – ${s.endHhmm}`"
                            @click="!s.busy && selectSlot(s.hhmm)"
                        >
                            {{ s.hhmm }}
                            <span v-if="form.modalidade === 'online' && s.isOnlineConfigured" class="slot-online-dot" />
                        </button>
                    </div>
                    <div v-if="filteredSlots.length === 0" class="text-xs opacity-50 mt-1">Nenhum horário disponível neste período.</div>
                </div>
                <div v-else-if="form.dia" class="text-xs opacity-50">Nenhum horário disponível para este dia (verifique a jornada nas configurações).</div>

                <Divider class="my-1" />

                <!-- Início + Duração -->
                <div class="time-controls">
                    <div class="time-picker-wrap">
                        <label class="time-label">Início</label>
                        <DatePicker v-model="startTimeDate" timeOnly hourFormat="24" :stepMinute="5" showIcon iconDisplay="input" fluid placeholder="HH:MM">
                            <template #inputicon="slotProps">
                                <i class="pi pi-clock" @click="slotProps.clickCallback" />
                            </template>
                        </DatePicker>
                    </div>

                    <div class="time-picker-wrap">
                        <label class="time-label">Duração</label>
                        <Select
                            v-model="form.duracaoMin"
                            :options="duracaoOptions"
                            optionLabel="label"
                            optionValue="value"
                            optionGroupLabel="label"
                            optionGroupChildren="items"
                            variant="filled"
                            class="w-full"
                            :disabled="isDynamic && commitmentItems.length > 0"
                        />
                        <small v-if="isDynamic && commitmentItems.length > 0" class="text-color-secondary text-xs mt-1 block"> Calculado pelos serviços adicionados </small>
                    </div>
                </div>

                <!-- Término em destaque -->
                <div v-if="fimDateTime" class="time-fim-card">
                    <div class="time-fim-card__label">
                        <i class="pi pi-flag-fill" />
                        Término
                    </div>
                    <div class="time-fim-card__value">{{ fmtTime(fimDateTime) }}</div>
                </div>

                <Message v-if="timeConflict" severity="warn" class="mt-2" :closable="false">
                    <i class="pi pi-exclamation-triangle mr-1" />
                    {{ timeConflict }}
                </Message>
            </div>
            <template #footer>
                <Button label="Confirmar" icon="pi pi-check" @click="timePickerOpen = false" />
            </template>
        </Dialog>

        <!-- Footer -->
        <template v-if="step === 2" #footer>
            <div class="flex items-center justify-between gap-2">
                <div class="flex items-center gap-2">
                    <Button v-if="!isEdit && allowBack" label="Voltar" icon="pi pi-arrow-left" severity="secondary" outlined size="small" class="rounded-full" @click="goBack" />
                    <Button v-if="isEdit && hasSerie" label="Encerrar série" icon="pi pi-trash" severity="danger" outlined size="small" class="rounded-full text-xs h-8" @click="onEncerrarSerie" />
                    <Button v-if="isEdit && !hasSerie" icon="pi pi-trash" severity="danger" outlined size="small" class="rounded-full h-9 w-9" v-tooltip.bottom="'Remover'" @click="onDelete" />

                    <!-- Lembrar paciente (WhatsApp on-demand) -->
                    <Button
                        v-if="isEdit && isSessionEvent && form.paciente_id"
                        icon="pi pi-whatsapp"
                        severity="success"
                        outlined
                        size="small"
                        class="rounded-full h-9 w-9"
                        v-tooltip.bottom="'Enviar lembrete WhatsApp agora'"
                        :loading="sendingReminder"
                        @click="onSendManualReminder"
                    />

                    <!-- Google Calendar link -->
                    <a
                        v-if="isEdit && googleCalendarUrl"
                        :href="googleCalendarUrl"
                        target="_blank"
                        rel="noopener noreferrer"
                        class="gcal-btn"
                        v-tooltip.top="'Abre o Google Agenda com o compromisso pré-preenchido. Em breve: sincronização automática e bidirecional com sua conta Google.'"
                    >
                        <svg class="gcal-btn__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
                            <rect x="3" y="4" width="18" height="18" rx="2" stroke="currentColor" stroke-width="1.7" />
                            <path d="M3 9h18" stroke="currentColor" stroke-width="1.7" />
                            <path d="M8 2v4M16 2v4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" />
                            <path d="M8 13h.01M12 13h.01M16 13h.01M8 17h.01M12 17h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
                        </svg>
                        <span>Google Agenda</span>
                    </a>
                </div>
                <Button label="Salvar" icon="pi pi-check" size="small" class="rounded-full" :disabled="!canSave" @click="onSave" />
            </div>
        </template>

        <!-- ══════════════════════════════════════════════ -->
        <!-- Cadastro Rápido de Paciente                    -->
        <!-- ══════════════════════════════════════════════ -->
        <ComponentCadastroRapido
            v-model="cadRapidoOpen"
            title="Novo Paciente"
            table-name="patients"
            name-field="nome_completo"
            email-field="email_principal"
            phone-field="telefone"
            :extra-payload="{ status: 'Ativo' }"
            @created="onPatientCreatedRapido"
        />

        <!-- ── Quick-create: Serviço ─────────────────────────── -->
        <ServiceQuickCreateDialog
            v-model="serviceQuickDlgOpen"
            :owner-id="ownerId"
            @created="onServiceCreated"
        />

        <!-- ── Quick-create: Convênio ────────────────────────── -->
        <InsurancePlanQuickCreateDialog
            v-model="insuranceQuickDlgOpen"
            :owner-id="planOwnerId || ownerId"
            @created="onInsuranceCreated"
        />
    </Dialog>
</template>

<style scoped>
.agenda-event-composer :deep(.p-dialog-content) {
    padding: 0.75rem;
}

/* ═══════════════════════════════════════════════════════════════════
   Polish visual disruptivo (V2) — overrides aplicados sobre o template
   existente sem mexer na lógica. Foco:
   1. Header do dialog com gradiente sutil + tipografia hierárquica
   2. Botões de quick-create destacados (mais "convidativos" no fluxo)
   3. Footer sticky com Salvar primário + sombra superior leve
   4. Dialog mask com backdrop-blur uniforme
   5. Borda dos cards laterais com cor de seção (paciente/quando/quê)
   ═══════════════════════════════════════════════════════════════════ */

/* Backdrop unificado pra Dialog do composer */
.agenda-event-composer :deep(.p-dialog-mask) {
    backdrop-filter: blur(6px) saturate(110%);
    -webkit-backdrop-filter: blur(6px) saturate(110%);
    background: rgba(15, 23, 42, 0.32);
}

/* Header do Dialog — gradient sutil + título com peso firme */
.agenda-event-composer :deep(.p-dialog-header) {
    background:
        radial-gradient(ellipse 600px 80px at 0% 0%, color-mix(in srgb, var(--p-primary-color) 8%, transparent), transparent 70%),
        var(--surface-card, #fff);
    border-bottom: 1px solid var(--surface-border);
}
.agenda-event-composer :deep(.p-dialog-title) {
    font-weight: 700;
    letter-spacing: -0.01em;
}

/* Footer sticky com sombra leve no topo, Salvar primary destacado */
.agenda-event-composer :deep(.p-dialog-footer) {
    border-top: 1px solid var(--surface-border);
    background: var(--surface-card, #fff);
    box-shadow: 0 -4px 18px -10px color-mix(in srgb, var(--p-primary-color) 25%, transparent);
}

/* Botão "+" de cadastro rápido (serviço/convênio) — outlined sutil que
   convida pra ação sem competir com o Select ao lado. */
.aed-quick-btn {
    flex-shrink: 0;
    border-radius: 10px !important;
    border-style: dashed !important;
    border-color: color-mix(in srgb, var(--p-primary-color) 40%, var(--surface-border)) !important;
    color: var(--p-primary-color) !important;
    transition: background-color 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.aed-quick-btn:hover {
    background: color-mix(in srgb, var(--p-primary-color) 8%, transparent) !important;
    border-style: solid !important;
    transform: translateY(-1px);
}

/* "side-card" (paciente/financeiro/quando) — borda esquerda colorida pra
   diferenciar visualmente as seções. Cores diferentes ajudam a localizar
   info rapidamente sem precisar ler o título. */
.agenda-event-composer :deep(.side-card) {
    position: relative;
    background: var(--surface-card);
    border: 1px solid var(--surface-border);
    border-radius: 12px;
    padding: 14px;
    transition: border-color 160ms ease, box-shadow 160ms ease;
}
.agenda-event-composer :deep(.side-card)::before {
    content: '';
    position: absolute;
    top: 12px;
    bottom: 12px;
    left: 0;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: color-mix(in srgb, var(--p-primary-color) 60%, transparent);
    opacity: 0.7;
}
.agenda-event-composer :deep(.side-card):hover {
    border-color: color-mix(in srgb, var(--p-primary-color) 25%, var(--surface-border));
    box-shadow: 0 2px 12px -4px color-mix(in srgb, var(--p-primary-color) 18%, transparent);
}
.agenda-event-composer :deep(.side-card):hover::before {
    opacity: 1;
}

/* ── tag: remarcado (roxo — sem severity nativo no PrimeVue) ─ */
:deep(.tag-remarcado) {
    background: #a855f7 !important;
    color: #fff !important;
}

/* ── header dot ─────────────────────────────────── */
.header-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--p-primary-500, #6366f1);
    flex-shrink: 0;
}

/* ── step 1: commitment grid ────────────────────── */
.commitment-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
}
@media (max-width: 640px) {
    .commitment-grid {
        grid-template-columns: 1fr;
    }
}

.commitment-card {
    width: 100%;
    text-align: left;
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    background: color-mix(in srgb, var(--surface-card), transparent 10%);
    transition:
        box-shadow 0.12s ease,
        transform 0.12s ease,
        border-color 0.12s;
    overflow: hidden;
}
.commitment-card:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--card-color, var(--p-primary-500)) 60%, transparent);
}
.commitment-card__inner {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    padding: 1rem;
}
.commitment-card__icon {
    display: grid;
    place-items: center;
    width: 2.2rem;
    height: 2.2rem;
    border-radius: 0.75rem;
    background: color-mix(in srgb, var(--p-primary-500) 12%, transparent);
    color: var(--p-primary-500);
    font-size: 1rem;
    flex-shrink: 0;
}
.commitment-card--blocked {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
    border-color: color-mix(in srgb, var(--red-500) 40%, var(--surface-border));
    background: color-mix(in srgb, var(--red-50, #fef2f2) 30%, var(--surface-card));
}

/* ── step 2: grid layout ────────────────────────── */
.composer-grid {
    display: grid;
    grid-template-columns: 1.3fr 0.7fr;
    gap: 1rem;
    align-items: start;
    margin-top: 2px;
}
@media (max-width: 960px) {
    .composer-grid {
        grid-template-columns: 1fr;
    }
}

/* ── paciente hero ──────────────────────────────── */
.patient-hero {
    border: 1.5px solid var(--surface-border);
    border-radius: 6px;
    overflow: hidden;
    background: color-mix(in srgb, var(--surface-card), transparent 10%);
}
.patient-hero__label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.6rem 0.4rem 0.9rem;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    border-bottom: 1px solid var(--surface-border);
    background: color-mix(in srgb, var(--surface-ground), transparent 30%);
}

/* Modalidade dentro do card de paciente */
.patient-hero__modalidade {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px 10px;
    border-top: 1px solid var(--surface-border);
}
.patient-hero__mod-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    white-space: nowrap;
}

/* Card genérico para seções (data/horário, etc.) */
.field-card {
    border-radius: 6px;
    border: 1px solid var(--surface-border);
    background: var(--surface-card);
    overflow: hidden;
}
.field-card__header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    border-bottom: 1px solid var(--surface-border);
    background: color-mix(in srgb, var(--surface-ground), transparent 30%);
    cursor: pointer;
}
.field-card__body {
    padding: 0;
}
.status-select-btn :deep(.p-selectbutton) {
    display: flex;
    width: 100%;
}
.status-select-btn :deep(.p-togglebutton) {
    flex: 1;
    justify-content: center;
    border-radius: 0 !important;
    border: none !important;
    border-right: 1px solid var(--surface-border) !important;
    padding: 0.65rem 0.5rem;
}
.status-select-btn :deep(.p-togglebutton:last-child) {
    border-right: none !important;
}
.patient-hero__empty {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    width: 100%;
    padding: 0.9rem;
    text-align: left;
    cursor: pointer;
    transition: background 0.1s;
}
.patient-hero__empty:hover {
    background: color-mix(in srgb, var(--p-primary-500) 5%, transparent);
}
.patient-hero__empty-icon {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    flex-shrink: 0;
    background: color-mix(in srgb, var(--p-primary-500) 12%, transparent);
    color: var(--p-primary-500);
    font-size: 1.1rem;
}
.patient-hero__selected {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.75rem 0.9rem;
}
.patient-avatar-bg :deep(.p-avatar) {
    background: color-mix(in srgb, var(--p-primary-500) 18%, transparent);
    color: var(--p-primary-500);
}

/* ── time hero ──────────────────────────────────── */
.time-hero {
    display: flex;
    align-items: stretch;
    cursor: pointer;
    transition:
        border-color 0.12s,
        box-shadow 0.12s;
    background: color-mix(in srgb, var(--surface-card), transparent 10%);
}
.time-hero:hover {
    border-color: var(--p-primary-400, #818cf8);
    box-shadow: 0 2px 12px color-mix(in srgb, var(--p-primary-500) 15%, transparent);
}
.time-hero__block {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
    padding: 0.7rem 0.5rem;
    min-width: 0;
}
.time-hero__label {
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    white-space: nowrap;
}
.time-hero__value {
    font-size: 0.92rem;
    font-weight: 700;
    color: var(--text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.time-hero__sep {
    width: 1px;
    background: var(--surface-border);
    margin: 0.5rem 0;
}
.time-hero__edit {
    display: flex;
    align-items: center;
    padding: 0 0.75rem;
    color: var(--text-color-secondary);
    opacity: 0.5;
    font-size: 0.8rem;
}

/* ── fields grid ────────────────────────────────── */
.fields-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.85rem;
}
.fields-grid .col-span-full {
    grid-column: 1 / -1;
}
@media (max-width: 640px) {
    .fields-grid {
        grid-template-columns: 1fr;
    }
}

/* ── side panel ─────────────────────────────────── */
.composer-right {
    position: sticky;
    top: 0;
}
.side-card {
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    padding: 0.9rem 1rem;
    background: color-mix(in srgb, var(--surface-card), transparent 10%);
}
.side-card__title {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    margin-bottom: 0.65rem;
}

/* resumo rows */
.summary-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-size: 0.82rem;
    color: var(--text-color);
    padding: 0.3rem 0;
    border-bottom: 1px solid color-mix(in srgb, var(--surface-border), transparent 40%);
}
.summary-row:last-child {
    border-bottom: none;
}
.summary-icon {
    font-size: 0.8rem;
    color: var(--text-color-secondary);
    opacity: 0.7;
    width: 1rem;
    flex-shrink: 0;
}

/* commit badge */
.commit-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.65rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    border: 1px solid transparent;
    white-space: nowrap;
}

/* ── serie banner ───────────────────────────────── */
.serie-banner {
    border-radius: 6px;
    padding: 0.75rem 0.9rem;
    background: color-mix(in srgb, var(--blue-500, #3b82f6) 8%, var(--surface-card));
    border: 1px solid color-mix(in srgb, var(--blue-400, #60a5fa) 30%, transparent);
}
.serie-banner__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--blue-600, #2563eb);
}
.serie-banner__detail {
    font-size: 0.78rem;
    color: var(--text-color-secondary);
    margin-top: 0.25rem;
}

/* escopo de edição */
.scope-option {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    cursor: pointer;
}

/* ── freq tabs ──────────────────────────────────── */
.freq-tab {
    padding: 0.25rem 0.75rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    border: 1px solid var(--surface-border);
    background: transparent;
    color: var(--text-color-secondary);
    cursor: pointer;
    transition:
        background 0.1s,
        color 0.1s,
        border-color 0.1s;
}
.freq-tab:hover {
    border-color: var(--p-primary-400);
    color: var(--p-primary-500);
}
.freq-tab--active {
    background: var(--p-primary-500, #6366f1);
    border-color: var(--p-primary-500, #6366f1);
    color: #fff;
}

/* ── recorrência preview ────────────────────────── */
.recorrencia-preview {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    background: color-mix(in srgb, var(--p-primary-500) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--p-primary-400) 25%, transparent);
}

/* ocorrências */
.ocorrencias-preview {
    border-top: 1px solid var(--surface-border);
    padding-top: 0.65rem;
    margin-top: 0.65rem;
}
.ocorrencias-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}
.ocorrencias-aviso {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--orange-600, #ea580c);
}
.ocorrencias-scroll {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    max-height: 300px;
    overflow-y: auto;
    padding-right: 0.2rem;
}
.ocorrencia-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.3rem;
    border-radius: 0.4rem;
    color: var(--text-color);
    transition: background 0.1s;
}
.ocorrencia-item i {
    color: var(--p-primary-400);
    flex-shrink: 0;
}
.ocorrencia-item--fora {
    background: color-mix(in srgb, var(--red-500) 8%, transparent);
    color: var(--red-600, #dc2626);
}
.ocorrencia-item--fora i {
    color: var(--red-400, #f87171);
}
.ocorrencia-fora-label {
    margin-left: auto;
    font-size: 0.65rem;
    font-weight: 700;
    color: var(--red-500, #ef4444);
    white-space: nowrap;
    flex-shrink: 0;
}
.ocorrencia-item--warn {
    background: color-mix(in srgb, var(--orange-500) 8%, transparent);
    color: var(--orange-700, #c2410c);
}
.ocorrencia-item--warn i {
    color: var(--orange-400, #fb923c);
}
.ocorrencia-conflict-label {
    margin-left: auto;
    font-size: 0.65rem;
    font-weight: 700;
    color: inherit;
    white-space: nowrap;
    flex-shrink: 0;
    opacity: 0.8;
}

/* ── time picker ────────────────────────────────── */
.time-controls {
    display: flex;
    gap: 1rem;
    align-items: flex-end;
    flex-wrap: wrap;
}
.time-picker-wrap {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 120px;
    flex: 1;
}
.time-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-color-secondary);
}
.time-fim {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    justify-content: flex-end;
    padding-bottom: 0.45rem;
}
.time-fim-value {
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-color);
    white-space: nowrap;
}

/* Término em destaque */
.time-fim-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border-radius: 6px;
    background: color-mix(in srgb, var(--primary-500, #6366f1) 8%, var(--surface-card));
    border: 1px solid color-mix(in srgb, var(--primary-400, #818cf8) 25%, transparent);
}
.time-fim-card__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--primary-500, #6366f1);
    opacity: 0.75;
}
.time-fim-card__value {
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--primary-600, #4f46e5);
    margin-left: auto;
}

/* Período filter chips */
.periodo-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    border: 1px solid var(--surface-border);
    background: var(--surface-card);
    color: var(--text-color-secondary);
    cursor: pointer;
    transition:
        background 0.12s,
        color 0.12s;
}
.periodo-chip--active {
    background: var(--primary-500, #6366f1);
    color: #fff;
    border-color: var(--primary-500, #6366f1);
}

/* Slots grid */
.slots-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    max-height: 140px;
    overflow-y: auto;
}

.slot-pill {
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 700;
    border: 1px solid var(--surface-border);
    background: var(--surface-card);
    color: var(--text-color);
    cursor: pointer;
    transition:
        background 0.1s,
        color 0.1s;
    letter-spacing: -0.01em;
}
.slot-pill:hover:not(.slot-pill--busy) {
    background: var(--primary-100, #e0e7ff);
    border-color: var(--primary-400, #818cf8);
    color: var(--primary-700, #3730a3);
}
.slot-pill--current {
    background: var(--primary-500, #6366f1) !important;
    color: #fff !important;
    border-color: var(--primary-500, #6366f1) !important;
}
.slot-pill--busy {
    opacity: 0.4;
    cursor: not-allowed;
    text-decoration: line-through;
}

/* Online slots card */
/* Info bar online (substitui o card separado) */
.online-info-bar {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 12px;
    border-radius: 0.75rem;
    background: color-mix(in srgb, var(--blue-50, #eff6ff) 70%, var(--surface-card));
    border: 1px solid color-mix(in srgb, var(--blue-300, #93c5fd) 35%, transparent);
    font-size: 0.78rem;
    color: var(--blue-800, #1e40af);
}
.online-info-bar .pi-video {
    color: var(--blue-500, #3b82f6);
    margin-top: 1px;
}
.online-info-bar__hint {
    opacity: 0.7;
}

/* Dot inline para o texto de dica */
.online-dot-inline {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--blue-500, #3b82f6);
    vertical-align: middle;
}

/* Pill marcado como configurado para online */
.slot-pill--online-cfg {
    border-color: color-mix(in srgb, var(--blue-400, #60a5fa) 60%, transparent) !important;
    background: color-mix(in srgb, var(--blue-50, #eff6ff) 80%, var(--surface-card)) !important;
    color: var(--blue-700, #1d4ed8) !important;
}
.slot-pill--online-cfg:hover:not(.slot-pill--busy) {
    background: var(--blue-100, #dbeafe) !important;
}

/* Dot azul dentro do pill quando é slot online configurado */
.slot-online-dot {
    display: inline-block;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--blue-500, #3b82f6);
    margin-left: 3px;
    vertical-align: middle;
    flex-shrink: 0;
}

/* ── rec startdate row ──────────────────────────── */
.rec-startdate-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.45rem 0.65rem;
    border-radius: 6px;
    background: color-mix(in srgb, var(--surface-ground), transparent 30%);
    border: 1px solid var(--surface-border);
}

/* ── freq chips (frequência principal) ──────────── */
.freq-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.freq-chip {
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    border: 1px solid var(--surface-border);
    background: transparent;
    color: var(--text-color-secondary);
    cursor: pointer;
    transition:
        background 0.1s,
        color 0.1s,
        border-color 0.1s;
    white-space: nowrap;
}
.freq-chip:hover {
    border-color: var(--p-primary-400);
    color: var(--p-primary-500);
}
.freq-chip--active {
    background: var(--p-primary-500, #6366f1);
    border-color: var(--p-primary-500, #6366f1);
    color: #fff;
}

/* ── dias da semana grid ────────────────────────── */
.dias-semana-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}
.dia-chip {
    padding: 0.3rem 0.6rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 700;
    border: 1px solid var(--surface-border);
    background: transparent;
    color: var(--text-color-secondary);
    cursor: pointer;
    transition:
        background 0.1s,
        color 0.1s,
        border-color 0.1s;
    min-width: 2.6rem;
    text-align: center;
}
.dia-chip:hover {
    border-color: var(--p-primary-400);
    color: var(--p-primary-500);
}
.dia-chip--active {
    background: var(--p-primary-500, #6366f1);
    border-color: var(--p-primary-500, #6366f1);
    color: #fff;
}

/* ── personalizar box ───────────────────────────── */
.personalizar-box {
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    padding: 0.75rem;
    background: color-mix(in srgb, var(--surface-ground), transparent 40%);
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
}

/* ── patient list ───────────────────────────────── */
.patient-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-height: 56vh;
    overflow: auto;
    padding-right: 0.25rem;
}
.patient-item {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    text-align: left;
    padding: 0.85rem 0.95rem;
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    background: color-mix(in srgb, var(--surface-card), transparent 10%);
    transition:
        box-shadow 0.12s ease,
        transform 0.12s ease;
}
.patient-item:hover {
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
    transform: translateY(-1px);
}

/* ── serie panel (Recorrências Aplicadas) ─────────── */
.serie-panel {
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    overflow: hidden;
}
.serie-panel__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.9rem;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    background: color-mix(in srgb, var(--surface-ground), transparent 20%);
    border-bottom: 1px solid var(--surface-border);
}
.serie-panel__stats {
    display: flex;
    gap: 0.3rem;
    font-size: 0.7rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    opacity: 0.75;
    margin-left: 0.25rem;
}
.serie-panel__empty {
    padding: 0.85rem;
    font-size: 0.82rem;
    color: var(--text-color-secondary);
    text-align: center;
}
.serie-pills-wrap {
    display: flex;
    flex-direction: column;
    max-height: 340px;
    overflow-y: auto;
}
.serie-pill {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.9rem;
    border-bottom: 1px solid color-mix(in srgb, var(--surface-border), transparent 55%);
    transition: background 0.1s;
    border-left: 3px solid transparent;
}
.serie-pill:last-child {
    border-bottom: none;
}
.serie-pill:hover {
    background: color-mix(in srgb, var(--surface-ground), transparent 10%);
}
.serie-pill--current {
    background: color-mix(in srgb, var(--p-primary-500) 7%, transparent);
    border-left-color: var(--p-primary-500);
}
.serie-pill--past {
    opacity: 0.5;
}
.serie-pill--realizado {
    border-left-color: var(--green-400, #4ade80);
}
.serie-pill--faltou {
    border-left-color: var(--red-400, #f87171);
}
.serie-pill--cancelado {
    border-left-color: var(--surface-border);
}
.serie-pill--remarcado {
    border-left-color: var(--orange-400, #fb923c);
}
.serie-pill__date {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 2.5rem;
    flex-shrink: 0;
    line-height: 1.15;
}
.serie-pill__weekday {
    font-size: 0.58rem;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--text-color-secondary);
    letter-spacing: 0.04em;
}
.serie-pill__daynum {
    font-size: 1rem;
    font-weight: 800;
    color: var(--text-color);
}
.serie-pill__month {
    font-size: 0.58rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--text-color-secondary);
}
.serie-pill__time {
    font-size: 0.72rem;
    color: var(--text-color-secondary);
    flex-shrink: 0;
    min-width: 2.8rem;
}
.serie-pill__status-sel {
    flex: 1;
    min-width: 0;
    max-width: 125px;
}
.serie-pill__cur-badge {
    font-size: 0.58rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--p-primary-500);
    background: color-mix(in srgb, var(--p-primary-500) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--p-primary-500) 30%, transparent);
    border-radius: 999px;
    padding: 0.1rem 0.45rem;
    flex-shrink: 0;
    white-space: nowrap;
}
.serie-pill__del {
    flex-shrink: 0;
    width: 2rem;
}

/* ── Commitment items (serviços vinculados ao evento) ── */
.commitment-items-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    border: 1px solid var(--surface-border);
    border-radius: 6px;
    padding: 0.5rem;
}
.commitment-item-row {
    margin-bottom: 4px;
}
.commitment-item-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
}
.commitment-item-name {
    flex: 1;
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.commitment-item-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: flex-end;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--p-content-border-color);
    margin-bottom: 8px;
}
.commitment-item-field {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.commitment-item-field--final {
    margin-left: auto;
}
.commitment-item-label {
    font-size: 0.65rem;
    color: var(--p-text-muted-color);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.commitment-item-price {
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    min-width: 5rem;
    text-align: right;
}
.commitment-items-total {
    display: flex;
    justify-content: space-between;
    padding-top: 0.35rem;
    margin-top: 0.25rem;
    border-top: 1px solid var(--surface-border);
}

/* ── Google Calendar button ─────────────────────── */
.gcal-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.45rem 0.9rem;
    border-radius: 6px;
    border: 1px solid #4285f4;
    background: transparent;
    color: #4285f4;
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
    transition:
        background 0.15s ease,
        color 0.15s ease;
    cursor: pointer;
    white-space: nowrap;
}
.gcal-btn:hover {
    background: #4285f4;
    color: #fff;
}
.gcal-btn__icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}
</style>
