As per your situation, the view online re-executes the AMPScript that's why @today picks up the current date. I had a similar situation and here is my solution. Although notice this method would have few seconds difference compared to the @today in your non-online version.
Having (true) within the Now() function falls back to the original time when the email was sent/previewed.
%%[
SET @today = Now(true)
]%%