The browser reads bc_attempts directly (TowerCooldownRepository) instead of calling
CooldownService.remainingAttempts, so it needs one query per page and gets timestamps for the
countdown. TowerRewardRepository groups towers by resetWindow, so each window is one pair of
queries. The browser is read-only: TowerRunCoordinator.tryEnter alone decides entry, and
RepeatRewardService alone decides payouts.
Battle Core's JDBC blocks, so /bt menu loads on a worker thread and opens on the server thread.
"Trainers cleared" counts distinct stage_index values this period, clamped to the stage count.
Distinct, because a stage with onLoss.endRun: false can be won many times. Clamped, so a tower
cut from 5 stages to 4 does not show 5/4.
RewardResetWindow uses America/New_York, not a fixed offset. DST days are covered by
RewardResetWindowTest.