- Hijri (قمری) calendar added as 3rd calendar option with tabular conversion
- Calendar type select menu (like accent) with gregorian/jalali/hijri
- Calendar type applied to exports and reports
- Midnight crossover: virtual splits at computation time (no synthetic DB events)
- Handles both UTC and local timezone boundaries
- Timezone changes handled naturally (recomputed on-the-fly)
- Rate limiting per user ID instead of chat ID
- WAL mode enabled for SQLite
- Smart reply keyboard updates after every state change
- Overlapping time edits rejected (must preserve in/out alternation)
- Single event deletion warns if timeline needs repair
- Day off no longer prevents clock in/out
- Accent colors shown with descriptive names in select menu
- Delete button label changed to DEL for visual distinction
- Midnight crossover: auto-close previous day session on clock-in
- Rate limiting: 2s cooldown per chat_id in both messages and callbacks
- Smart reply keyboard: single button changes to Clock In/Out based on state
- Status info on main menu shows: date, work type, working status, today's total
- Settings sub-menu with inline buttons for accent, report toggle, report time
- Accent selection inline keyboard with > indicator and back button
- Notes displayed in daily report (e.g. 'auto midnight')
- Back buttons on all sub-menus (work type, accent, settings)
- Settings button added to main menu
- Add chat_id to events, settings, and days_off for per-user data
- Add proper graceful shutdown (signal handling, WaitGroup)
- Separate polling and webhook modes with goroutine management
- Add schema migration from single-user to multi-user schema
- Refactor handlers with shared actionFunc pattern (msg + callback)
- Fix schema path for Docker deployment (/app/db/schema.sql)
- Remove emoji from output text for cleaner formatting
- Show main menu with Clock In/Out, Report, Export, Remote, Day Off buttons
- Each action result includes a 'Back to Menu' button
- Handle callback queries alongside text commands
- Delete stale webhook before switching to polling
- Log i18n initialization error instead of silently ignoring it