<script setup>
/*
 * MelissaCronometro
 * --------------------------------------------------
 * Cronômetro de sessão estilo "janela do Windows":
 *   - Dialog centralizado com select de paciente, display gigante e ações
 *   - Click fora minimiza (chip no canto superior esquerdo)
 *   - X = encerrar sem salvar. Com confirmacao se houver sessao em
 *     andamento ou tempo decorrido (fechar limpo nao pede confirm)
 *   - Botão inferior alterna entre "▶ Começar" e "⏹ Parar"
 *   - "+1 minuto" estende o tempo
 *   - Quando minimizado, o timer continua rodando em background
 *
 * Uso:
 *   <MelissaCronometro
 *       ref="cronoRef"
 *       :pacientes="lista"
 *       default-paciente-id="p1"
 *       :duracao-minutos="50"
 *       @visible-change="cronoVisivel = $event"
 *   />
 *
 *   cronoRef.value.abrir()   // abre / restaura
 *   cronoRef.value.fechar()  // destrói
 */
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { useConfirm } from 'primevue/useconfirm';
import { playToque } from './melissaToques';

const confirm = useConfirm();

const STORAGE_KEY = 'melissa.cronometro.v1';

const props = defineProps({
    pacientes: {
        type: Array,
        default: () => []
        // Cada item: { id: string, nome: string }
    },
    defaultPacienteId: {
        type: [String, Number, null],
        default: null
    },
    duracaoMinutos: {
        type: Number,
        default: 50
    },
    toqueTermino: {
        type: String,
        default: 'sino'
    }
});

const emit = defineEmits(['visible-change', 'close', 'complete', 'session-end']);

// ── Estado interno ─────────────────────────────────────────────
const exists = ref(false);
const minimized = ref(false);
const running = ref(false);
const seconds = ref(props.duracaoMinutos * 60);
const pacienteId = ref(props.defaultPacienteId);
let timer = null;

// Plano da sessao (horario programado original do evento na agenda).
// Setado quando o cronometro abre a partir de um evento da timeline —
// vide abrir({ sessionPlan: { startH, endH } }). Null quando aberto
// manualmente. Persiste em localStorage junto com o resto do snap.
const sessionPlan = ref(null); // { startH: number, endH: number } | null
// Tick a cada 30s pra recomputar atraso conforme o tempo passa (so
// quando cronometro existe; reseta no fechar).
const _planNowTick = ref(Date.now());
let _planNowTimer = null;

// True só durante a transição de minimizar (dialog → chip).
// Permite trocar a animação leave do dialog por "shrink + fly to dock"
// (tipo macOS minimize) em vez do lift normal usado pra fechar.
const isMinimizing = ref(false);

// ── Computeds ──────────────────────────────────────────────────
const visible = computed(() => exists.value && !minimized.value);

const dialogTransitionName = computed(() => (isMinimizing.value ? 'minimize' : 'lift'));

const display = computed(() => {
    const total = seconds.value;
    const sign = total < 0 ? '-' : '';
    const abs = Math.abs(total);
    const m = Math.floor(abs / 60);
    const s = abs % 60;
    return `${sign}${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
});

const excedido = computed(() => seconds.value < 0);

const status = computed(() => {
    if (running.value) return 'Em andamento';
    if (seconds.value === props.duracaoMinutos * 60) return 'Pronto';
    return 'Pausado';
});

const pacienteNome = computed(() => {
    if (!pacienteId.value) return 'Atividade livre';
    const p = props.pacientes.find((x) => x.id === pacienteId.value);
    return p ? p.nome : '';
});

// Formatador hh:mm a partir do startH decimal (ex: 11.5 → "11:30").
function _fmtHora(h) {
    if (typeof h !== 'number' || Number.isNaN(h)) return '';
    const hh = Math.floor(h);
    const mm = Math.round((h - hh) * 60);
    return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;
}

const sessionPlanLabel = computed(() => {
    const p = sessionPlan.value;
    if (!p || typeof p.startH !== 'number' || typeof p.endH !== 'number') return '';
    return `Programado: ${_fmtHora(p.startH)} – ${_fmtHora(p.endH)}`;
});

// Atraso em minutos vs horario programado. Calcula em relacao ao
// _planNowTick (atualizado a cada 30s) pra nao recomputar a cada frame.
const atrasoMin = computed(() => {
    const p = sessionPlan.value;
    if (!p || typeof p.startH !== 'number') return 0;
    // ref usada so pra forcar recompute periodico
    void _planNowTick.value;
    const d = new Date();
    const hNow = d.getHours() + d.getMinutes() / 60;
    const diff = hNow - p.startH;
    if (diff <= 0) return 0;
    return Math.round(diff * 60);
});

// ── Watch: avisa parent quando dialog aparece/some ─────────────
watch(visible, (v) => emit('visible-change', v));

// ── Ações ──────────────────────────────────────────────────────
// abrir({ pacienteId, autostart }) — opts permitem pre-selecionar
// um paciente (ex: click no botao "iniciar sessao" da timeline) e
// auto-iniciar a contagem. Sem opts mantem comportamento legado.
// Retorna { opened, alreadyRunning, pacienteId } pra caller decidir
// (ex: mostrar toast se ja tem cronometro rodando de outro paciente).
function abrir(opts = {}) {
    const requestedPid = Object.prototype.hasOwnProperty.call(opts, 'pacienteId')
        ? opts.pacienteId
        : props.defaultPacienteId;
    if (exists.value) {
        // Ja existe — comportamento opcao (b): nao troca paciente. Apenas
        // restaura visualmente se estava minimizado. Caller decide se
        // mostra toast quando o paciente requisitado e' diferente do atual.
        if (minimized.value) minimized.value = false;
        return {
            opened: false,
            alreadyRunning: !!running.value,
            pacienteId: pacienteId.value,
            samePaciente: pacienteId.value === requestedPid
        };
    }
    seconds.value = props.duracaoMinutos * 60;
    pacienteId.value = requestedPid;
    running.value = false;
    minimized.value = false;
    exists.value = true;
    // Plano programado (vem do evento da timeline) — usado pra exibir
    // "Programado: HH:MM – HH:MM" e badge de atraso. Sanitiza pra
    // garantir 2 numeros validos; senao limpa.
    const plan = opts.sessionPlan;
    if (plan && typeof plan.startH === 'number' && typeof plan.endH === 'number'
        && !Number.isNaN(plan.startH) && !Number.isNaN(plan.endH)) {
        sessionPlan.value = { startH: plan.startH, endH: plan.endH };
    } else {
        sessionPlan.value = null;
    }
    if (opts.autostart) {
        // Defer pra rodar dps do mount/render do dialog (toggle precisa
        // do setInterval em proximo tick pra contar a partir do segundo
        // cheio, nao perde a fracao do tick atual).
        setTimeout(() => { if (exists.value && !running.value) toggle(); }, 0);
    }
    return {
        opened: true,
        alreadyRunning: false,
        pacienteId: pacienteId.value,
        samePaciente: true
    };
}

function toggle() {
    if (running.value) {
        if (timer) clearInterval(timer);
        timer = null;
        running.value = false;

        // Tempo total cronometrado antes do reset. Pode estourar a duracao
        // (seconds < 0) — somamos o overshoot. Ex: 50min config, parou em
        // 53min => seconds=-180 => elapsed = 3000-(-180) = 3180s.
        const elapsedSec = (props.duracaoMinutos * 60) - seconds.value;

        // Zera ao parar — sessão acabou, deixa pronto pra próxima
        seconds.value = props.duracaoMinutos * 60;

        // Emite pro pai gravar no DB. Threshold de 5s pra ignorar
        // start/stop acidentais. So' faz sentido se ha paciente.
        if (pacienteId.value && elapsedSec > 5) {
            emit('session-end', {
                pacienteId: pacienteId.value,
                elapsedSec,
                stoppedAt: new Date().toISOString()
            });
        }
    } else {
        if (timer) clearInterval(timer);
        timer = setInterval(() => {
            const wasPositive = seconds.value > 0;
            seconds.value -= 1;
            // Toca exatamente na transição positivo → zero/negativo (uma única vez)
            if (wasPositive && seconds.value <= 0) {
                playToque(props.toqueTermino);
            }
        }, 1000);
        running.value = true;
    }
}

function minimizar() {
    isMinimizing.value = true;
    minimized.value = true;
    // Reset do flag depois da animação completar (340ms + buffer)
    setTimeout(() => { isMinimizing.value = false; }, 380);
}

function restaurar() {
    minimized.value = false;
}

function fechar() {
    if (timer) clearInterval(timer);
    timer = null;
    running.value = false;
    minimized.value = false;
    exists.value = false;
    sessionPlan.value = null; // limpa pra proxima abertura comecar zerada
    emit('close');
}

// Fechar com confirmacao quando ha sessao em andamento ou tempo
// decorrido sem salvar. Estado "clean" (parado + nada decorrido)
// fecha direto pra nao atrapalhar quem abriu por engano.
function confirmarFechar() {
    const totalInicial = props.duracaoMinutos * 60;
    const temAtividade = running.value || seconds.value !== totalInicial;
    if (!temAtividade) {
        fechar();
        return;
    }
    confirm.require({
        message: running.value
            ? 'Sessão em andamento — encerrar agora descarta o tempo cronometrado sem salvar no DB.'
            : 'O cronômetro tem tempo decorrido que ainda não foi salvo. Quer descartar?',
        header: 'Encerrar sessão sem salvar?',
        icon: 'pi pi-exclamation-triangle',
        acceptLabel: 'Encerrar sem salvar',
        rejectLabel: 'Continuar sessão',
        acceptClass: 'p-button-danger',
        accept: () => fechar()
    });
}

function ajustarMinutos(delta) {
    seconds.value += delta * 60;
    // Re-baselina savedAt pra restore após reload pegar o novo valor correto
    saveState();
}

// ── Persistência (localStorage) ────────────────────────────────
// Snapshot inclui timestamp pra calcular elapsed real no restore —
// caso o usuário recarregue/feche a aba enquanto o timer roda, ao
// voltar o seconds reflete o tempo real (a sessão acontece na sala,
// não na aba).
function saveState() {
    if (!exists.value) {
        try { localStorage.removeItem(STORAGE_KEY); } catch {}
        return;
    }
    const snap = {
        pacienteId: pacienteId.value,
        minimized: !!minimized.value,
        running: !!running.value,
        seconds: seconds.value,
        savedAt: Date.now(),
        sessionPlan: sessionPlan.value // null | { startH, endH }
    };
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(snap)); } catch {}
}

function loadState() {
    let raw;
    try { raw = localStorage.getItem(STORAGE_KEY); } catch { return false; }
    if (!raw) return false;

    let snap;
    try { snap = JSON.parse(raw); } catch { return false; }
    if (!snap || typeof snap !== 'object') return false;

    // Sanitização: cada campo do storage é input externo, não confiar
    const validIds = new Set(props.pacientes.map((p) => p.id));
    const pid = snap.pacienteId === null || validIds.has(snap.pacienteId)
        ? snap.pacienteId
        : props.defaultPacienteId;

    const savedSeconds = Number(snap.seconds);
    if (!Number.isFinite(savedSeconds)) return false;

    const savedAt = Number(snap.savedAt);
    const wasRunning = !!snap.running;

    // Se rodava, desconta tempo real desde o save (limite 24h pra evitar abuso de relógio)
    let restoredSeconds = savedSeconds;
    if (wasRunning && Number.isFinite(savedAt)) {
        const elapsed = Math.max(0, Math.min(86400, Math.floor((Date.now() - savedAt) / 1000)));
        restoredSeconds = savedSeconds - elapsed;
    }

    pacienteId.value = pid;
    minimized.value = !!snap.minimized;
    seconds.value = restoredSeconds;
    exists.value = true;
    running.value = false; // toggle abaixo flipa pra true
    // Restaura plano programado se foi serializado. Sanitiza shape:
    // {startH:number, endH:number} valido ou null.
    const sp = snap.sessionPlan;
    if (sp && typeof sp.startH === 'number' && typeof sp.endH === 'number'
        && !Number.isNaN(sp.startH) && !Number.isNaN(sp.endH)) {
        sessionPlan.value = { startH: sp.startH, endH: sp.endH };
    } else {
        sessionPlan.value = null;
    }

    if (wasRunning) {
        // Retoma o interval. NÃO toca o toque retroativo — se o tempo
        // já está negativo no restore, foi "perdido" durante o reload.
        toggle();
    }
    return true;
}

// Watch nas mudanças de estado discreto (não em seconds enquanto roda — savedAt+delta dá conta)
watch([exists, minimized, running, pacienteId], () => saveState());

// Tick a cada 30s pra recomputar atraso. So roda quando o cronometro
// existe E tem plano programado — senao desperdicio.
watch([exists, sessionPlan], ([e, p]) => {
    if (e && p) {
        if (!_planNowTimer) {
            _planNowTimer = setInterval(() => { _planNowTick.value = Date.now(); }, 30_000);
        }
    } else if (_planNowTimer) {
        clearInterval(_planNowTimer);
        _planNowTimer = null;
    }
});

// ── Mount / Cleanup ────────────────────────────────────────────
onMounted(() => {
    loadState();
});

onBeforeUnmount(() => {
    if (timer) clearInterval(timer);
    if (_planNowTimer) clearInterval(_planNowTimer);
});

defineExpose({ abrir, fechar, minimizar, restaurar, toggle, visible });
</script>

<template>
    <!-- Dialog centralizado.
         Transition name dinâmico: 'minimize' quando user minimiza (anima
         shrink + fly em direção ao dock no bottom-left), 'lift' default
         pros outros casos (fechar, abrir). -->
    <Transition :name="dialogTransitionName">
        <div v-if="visible" class="mc-layer" @click.self="minimizar">
            <div class="mc-panel">
                <div class="flex items-center justify-between mb-5">
                    <div>
                        <div class="text-white/55 text-xs uppercase tracking-[0.2em]">Cronômetro</div>
                        <div class="text-white text-lg font-light mt-1">{{ status }}</div>
                    </div>
                    <div class="flex items-center gap-1.5">
                        <button class="mc-glass-btn" title="Minimizar" @click="minimizar">
                            <i class="pi pi-window-minimize text-white/90 text-xs" />
                        </button>
                        <button class="mc-glass-btn" title="Encerrar sem salvar" @click="confirmarFechar">
                            <i class="pi pi-times text-white/90 text-sm" />
                        </button>
                    </div>
                </div>

                <!-- Select de paciente / atividade -->
                <div class="mb-6">
                    <label class="text-[0.65rem] uppercase tracking-widest text-white/50 mb-2 block">
                        Paciente / atividade
                    </label>
                    <div class="relative">
                        <select v-model="pacienteId" class="mc-select">
                            <option :value="null">— Atividade livre (sem paciente)</option>
                            <option v-for="p in pacientes" :key="p.id" :value="p.id">
                                {{ p.nome }}
                            </option>
                        </select>
                        <i class="pi pi-chevron-down mc-select-icon" />
                    </div>
                    <!-- Plano programado da sessao (so quando aberto via
                         evento da timeline). Mostra horario original +
                         badge de atraso se aplicavel — analista decide,
                         cronometro nao auto-ajusta. -->
                    <div v-if="sessionPlanLabel" class="mc-session-plan">
                        <i class="pi pi-calendar text-white/55 text-[0.7rem]" />
                        <span class="text-white/70 text-[0.78rem]">{{ sessionPlanLabel }}</span>
                        <span v-if="atrasoMin > 0" class="mc-session-plan__late">
                            atrasada {{ atrasoMin }} min
                        </span>
                    </div>
                </div>

                <!-- Display gigante + steppers manuais (+5 / -5) -->
                <div class="mb-7">
                    <div class="flex items-center justify-center gap-4">
                        <div class="flex flex-col gap-1.5">
                            <button
                                class="mc-step-btn"
                                title="Adicionar 5 minutos"
                                @click="ajustarMinutos(5)"
                            >
                                +5
                            </button>
                            <button
                                class="mc-step-btn"
                                title="Remover 5 minutos"
                                :disabled="excedido"
                                @click="ajustarMinutos(-5)"
                            >
                                −5
                            </button>
                        </div>
                        <div class="mc-display" :class="{ 'is-excedido': excedido }">
                            {{ display }}
                        </div>
                    </div>
                    <div class="text-xs mt-2 text-center" :class="excedido ? 'text-red-400 font-medium' : 'text-white/50'">
                        {{ excedido ? 'tempo excedido' : 'tempo restante' }}
                    </div>
                </div>

                <!-- Ações -->
                <div class="flex gap-3">
                    <button class="mc-btn mc-btn--secondary flex-1" @click="ajustarMinutos(1)">
                        +1 minuto
                    </button>
                    <button
                        class="mc-btn flex-1"
                        :class="running ? 'mc-btn--danger' : 'mc-btn--primary'"
                        @click="toggle"
                    >
                        <i :class="running ? 'pi pi-stop-circle' : 'pi pi-play'" class="text-xs" />
                        {{ running ? 'Parar' : 'Começar' }}
                    </button>
                </div>
            </div>
        </div>
    </Transition>

    <!-- Chip minimizado — teleporta pro .melissa-dock (taskbar Win11
         no bottom). Vive ao lado do ψ (à direita dele, dentro do flex
         do dock). Transition envolve o Teleport (pattern oficial Vue):
         o Teleport some/aparece como unidade, sem deixar comment
         placeholder no target compartilhado — o que evitaria o bug
         "emitsOptions: null" causado por múltiplos Teleports + v-if
         interno apontando pro mesmo target. -->
    <Transition name="chip-pop">
        <Teleport v-if="exists && minimized" to=".melissa-dock">
            <button
                class="mc-chip"
                title="Restaurar cronômetro"
                @click="restaurar"
            >
                <i
                    class="pi pi-stopwatch text-sm"
                    :class="running ? 'text-emerald-300 mc-chip-pulse' : 'text-white/70'"
                />
                <span class="mc-chip-time" :class="{ 'text-red-300': excedido }">
                    {{ display }}
                </span>
                <span class="mc-chip-name">{{ pacienteNome }}</span>
            </button>
        </Teleport>
    </Transition>
</template>

<style scoped>
/* ─── Dialog ───────────────────────────────────────────────── */
.mc-layer {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
}
.mc-panel {
    width: min(420px, 100%);
    background: var(--m-bg-medium);
    backdrop-filter: blur(32px) saturate(160%);
    -webkit-backdrop-filter: blur(32px) saturate(160%);
    border: 1px solid var(--m-border-strong);
    border-radius: 22px;
    padding: 1.75rem;
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}

.mc-glass-btn {
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    background: var(--m-bg-soft);
    backdrop-filter: blur(20px);
    border: 1px solid var(--m-border-strong);
    border-radius: 9999px;
    cursor: pointer;
    transition: background-color 160ms ease;
}
.mc-glass-btn:hover {
    background: var(--m-bg-soft-hover);
}

/* ─── Select customizado ───────────────────────────────────── */
.mc-select {
    width: 100%;
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border-strong);
    color: var(--m-text);
    padding: 11px 38px 11px 14px;
    border-radius: 10px;
    font-size: 0.95rem;
    font-family: inherit;
    outline: none;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    transition: border-color 140ms ease, background-color 140ms ease;
}
.mc-select:hover {
    background: var(--m-bg-soft-hover);
}
.mc-select:focus {
    border-color: var(--m-border-strong);
}
.mc-select option {
    /* renderizado pelo OS — usa tokens semânticos pra acompanhar dark/light */
    background: var(--p-content-background);
    color: var(--m-text);
}
.mc-select-icon {
    position: absolute;
    right: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--m-text-muted);
    font-size: 0.7rem;
    pointer-events: none;
}

/* Plano programado — linha abaixo do select com horario original e
   badge de atraso quando aplicavel. Tom secundario pra nao roubar
   atencao do display gigante do cronometro. */
.mc-session-plan {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    padding: 4px 0;
}
.mc-session-plan__late {
    margin-left: 4px;
    padding: 1px 8px;
    border-radius: 9999px;
    background: rgba(251, 146, 60, 0.18);
    color: rgb(253, 186, 116);
    font-size: 0.7rem;
    font-weight: 500;
    line-height: 1.4;
    border: 1px solid rgba(251, 146, 60, 0.35);
}

/* ─── Display gigante ──────────────────────────────────────── */
.mc-display {
    font-size: 5rem;
    font-weight: 200;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--m-text);
    font-variant-numeric: tabular-nums;
    text-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
    transition: color 200ms ease;
}
.mc-display.is-excedido {
    color: #ef4444;
    text-shadow: 0 4px 24px rgba(239, 68, 68, 0.45);
    animation: mc-excedido-pulse 1.4s ease-in-out infinite;
}
@keyframes mc-excedido-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.65; }
}

/* ─── Steppers manuais (+5 / -5) ───────────────────────────── */
.mc-step-btn {
    width: 44px;
    height: 36px;
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border-strong);
    color: var(--m-text);
    border-radius: 8px;
    font-size: 0.85rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    font-family: inherit;
    cursor: pointer;
    transition: background-color 140ms ease, border-color 140ms ease, transform 100ms ease;
}
.mc-step-btn:hover {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
}
.mc-step-btn:active {
    transform: scale(0.96);
}
.mc-step-btn:disabled {
    background: var(--m-bg-soft);
    border-color: var(--m-border);
    color: var(--m-text-faint);
    cursor: not-allowed;
    pointer-events: none;
}

/* ─── Botões de ação ───────────────────────────────────────── */
.mc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 12px 14px;
    border-radius: 12px;
    font-size: 0.9rem;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background-color 140ms ease, border-color 140ms ease;
}
.mc-btn--secondary {
    background: var(--m-bg-soft);
    border: 1px solid var(--m-border-strong);
    color: var(--m-text);
}
.mc-btn--secondary:hover {
    background: var(--m-bg-soft-hover);
    border-color: var(--m-border-strong);
}
.mc-btn--primary {
    background: var(--m-accent);
    border: 1px solid var(--m-accent);
    color: var(--p-primary-contrast-color, white);
}
.mc-btn--primary:hover {
    background: color-mix(in srgb, var(--m-accent) 80%, white);
}
.mc-btn--danger {
    background: rgba(239, 68, 68, 0.55);
    border: 1px solid rgba(239, 68, 68, 0.7);
    color: white;
}
.mc-btn--danger:hover {
    background: rgba(239, 68, 68, 0.75);
}

/* ─── Chip minimizado (teleportado pro .melissa-dock) ──────
   Vive como filho flex do dock — não usa position fixed. Dock
   posiciona ele à esquerda (depois do ψ, via padding-left). */
.mc-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px 8px 12px;
    background: var(--m-bg-medium);
    backdrop-filter: blur(24px) saturate(160%);
    -webkit-backdrop-filter: blur(24px) saturate(160%);
    border: 1px solid var(--m-border-strong);
    border-radius: 9999px;
    color: var(--m-text);
    cursor: pointer;
    transition: background-color 200ms ease, transform 200ms ease, border-color 200ms ease;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
.mc-chip:hover {
    background: var(--m-bg-medium);
    transform: translateY(-2px);
    border-color: var(--m-border-strong);
}
.mc-chip-time {
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    font-size: 0.85rem;
}
.mc-chip-name {
    font-size: 0.72rem;
    color: var(--m-text-muted);
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-left: 6px;
    border-left: 1px solid var(--m-border-strong);
}
/* Em mobile (<md=768px) o chip vive num dock estreito que precisa
   acomodar 4 builtins + ψ + tray. Esconde o nome do paciente — o
   icone + timer ja sinalizam o estado, e o nome continua disponivel
   ao restaurar o cronometro (click). */
@media (max-width: 767px) {
    .mc-chip-name { display: none; }
    .mc-chip { padding: 8px 12px; }
}
.mc-chip-pulse {
    animation: mc-pulse 1.6s ease-in-out infinite;
}
@keyframes mc-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

/* ─── Transições ───────────────────────────────────────────── */
.lift-enter-active,
.lift-leave-active {
    transition: opacity 240ms ease, transform 280ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
.lift-enter-from {
    opacity: 0;
    transform: scale(0.96) translateY(12px);
}
.lift-leave-to {
    opacity: 0;
    transform: scale(0.98) translateY(8px);
}

.chip-pop-enter-active,
.chip-pop-leave-active {
    transition: opacity 200ms ease, transform 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
}
.chip-pop-enter-from,
.chip-pop-leave-to {
    opacity: 0;
    transform: scale(0.85) translateY(-8px);
}
/* Chip aparecendo após minimize: leve atraso pra deixar o dialog
   "voar" antes de aparecer no dock (sensação macOS) */
.chip-pop-enter-active {
    transition-delay: 120ms;
}

/* ─── Animação "minimize" (macOS-style): dialog encolhe + voa pro
   bottom-left em direção ao chip que vai aparecer no dock. Aplicada
   só durante minimizar() — fechar normal usa lift. ────────────── */
.minimize-enter-active,
.minimize-leave-active {
    transition: opacity 280ms ease, transform 340ms cubic-bezier(0.5, 0, 0.6, 1);
    /* origem ~= posição do chip no dock (left:96px após ψ, bottom centro
       da faixa de 76px = ~38px). Faz o scale "encolher em direção" lá. */
    transform-origin: 96px calc(100% - 38px);
}
.minimize-enter-from {
    opacity: 0;
    transform: scale(0.05);
}
.minimize-leave-to {
    opacity: 0;
    transform: scale(0.05);
}

/* ─── Light mode tweaks ────────────────────────────────────
   Texto já usa var(--m-text) (flipa automático), mas alguns
   shadows escuros agressivos precisam ser suavizados. */
html:not(.app-dark) .mc-display {
    text-shadow: none;
}
html:not(.app-dark) .mc-chip {
    box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08), 0 1px 3px rgba(15, 23, 42, 0.06);
}
html:not(.app-dark) .mc-panel {
    box-shadow: 0 12px 36px rgba(15, 23, 42, 0.12), 0 2px 6px rgba(15, 23, 42, 0.06);
}
</style>
