Contrato — cancelar desde el email (003 → 005/007)
PATCH /api/citas/[id]/cancelar ya existe (005). La 003 solo añade el llamante (página app/cancelar/[id]/) con origen:'email' + token de crearEnlaceVerificado (007 FR-006). Sin cambios en lib/cancelacion.ts ni en lib/enlace-verificado.ts.
email ──token(citaId+pacienteId, caduca inicio/72h)──> GET /cancelar/[id]?token=
│ muestra datos vigentes, exige confirmación expresa (GET nunca escribe)
└── POST confirmar ──PATCH /api/citas/[id]/cancelar {origen:'email', token}──> aplicada | YA_CANCELADA | ESTADO_FINAL | PLAZO_VENCIDO | ENLACE_INVALIDO
Textos (únicos, 005 FR-005 + 003 FR-007): plazo vencido → «Ya no se puede cancelar por internet; llama a la clínica al » (en la página se presenta como «fuera de plazo, llama a la clínica al »); duplicada → «Esta cita ya está cancelada»; ajena/final → catálogo 005 sin revelar datos.