Previously the restore check only ran lazily on a player's next
message, so files could stay wrongly-removed for an arbitrary amount
of time after the window expired. The frontend now schedules a
setTimeout (using the server-provided deadline, mirroring the terminal
lock's reveal timer) that pings the backend right at the deadline so
the check runs promptly regardless of player activity. Restored via
data-files-removal-deadline on page load too, so a refresh mid-window
doesn't lose the timer.
Connects the previously-disconnected /decode command to per-player
messages: player 1 unlocks sudo for everyone, player 2 unlocks a scan
command that reveals which files the AI virus has locked, player 3
learns those files should be removed.
Also adds a retaliation mechanic: successfully removing a file locks
the player's terminal. The AI virus locks it out in red immediately;
the mainframe reveals a 12-character recovery code in green after 30
seconds, which can be submitted via /unlock to restore access early,
otherwise the terminal auto-recovers after 45 seconds.
Overlay a rotated CSS badge on the homepage hero and briefing page
image instead of baking it into the PNG, so it's easy to remove once
the game ships.
Give the public homepage a themed breach-alert hero and add a /briefing
page with the full mission narrative, both linking into the existing
game dashboard flow.
Adds a Marketing column to the admin users table and a total opt-in
count in the header, so admins can see who signed up for updates on
future projects.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Registration now requires agreeing to the Terms and Conditions and lets
users opt in to hear about future projects. The opt-in is persisted on
the user via a new marketing_opt_in column (migration included).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Admins now see an Admin link in the main navigation, and the admin
section inherits the branded header/footer from layout/site.html.twig
instead of the bare base layout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When the last player clicked ready, toggleReady() published a
redundant 'player_ready' event on top of checkAllPlayersReady()'s
'all_ready', and the client reloaded on every message with no guard
and without closing the EventSource. Chrome kept the old page's
script (and its EventSource) alive across the overlapping reload
calls, causing repeated reconnects to the Mercure hub; Firefox
apparently tore the page down fast enough to mask it. Skip the
redundant publish server-side, and make the client reload idempotent
by tracking whether it already fired and closing the EventSource
before reloading.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
confirmation_email.html.twig and reset_password/email.html.twig were
plain unstyled HTML with no shared structure. Extract a table-based
layout (templates/emails/layout.html.twig) with header/logo, content
block, and footer, so future transactional emails can extend it
instead of starting from scratch.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>